diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /connectivity/source/commontools | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
40 files changed, 16513 insertions, 0 deletions
diff --git a/connectivity/source/commontools/AutoRetrievingBase.cxx b/connectivity/source/commontools/AutoRetrievingBase.cxx new file mode 100644 index 0000000000..99327f27ee --- /dev/null +++ b/connectivity/source/commontools/AutoRetrievingBase.cxx @@ -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 . + */ + +#include <AutoRetrievingBase.hxx> + +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> + +namespace connectivity +{ + OUString OAutoRetrievingBase::getTransformedGeneratedStatement(const OUString& _sInsertStatement) const + { + OSL_ENSURE( m_bAutoRetrievingEnabled,"Illegal call here. isAutoRetrievingEnabled is false!"); + OUString sStmt = _sInsertStatement.toAsciiUpperCase(); + if ( sStmt.startsWith("INSERT") ) + { + static const char sTable[] = "$table"; + const sal_Int32 nColumnIndex {m_sGeneratedValueStatement.indexOf("$column")}; + if ( nColumnIndex>=0 ) + { // we need a column + } + const sal_Int32 nTableIndex {m_sGeneratedValueStatement.indexOf(sTable)}; + if ( nTableIndex>=0 ) + { // we need a table name + sal_Int32 nIntoIndex = sStmt.indexOf("INTO ") + 5; + while (nIntoIndex<sStmt.getLength() && sStmt[nIntoIndex]==' ') ++nIntoIndex; + const std::u16string_view sTableName = o3tl::getToken(sStmt, 0, ' ', nIntoIndex); + return m_sGeneratedValueStatement.replaceAt(nTableIndex, strlen(sTable), sTableName); + } + return m_sGeneratedValueStatement; + } + return OUString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/BlobHelper.cxx b/connectivity/source/commontools/BlobHelper.cxx new file mode 100644 index 0000000000..f1f048a731 --- /dev/null +++ b/connectivity/source/commontools/BlobHelper.cxx @@ -0,0 +1,63 @@ +/* -*- 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 <connectivity/BlobHelper.hxx> +#include <comphelper/seqstream.hxx> +#include <connectivity/dbexception.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <o3tl/unreachable.hxx> + +using namespace connectivity; +using namespace dbtools; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; + +BlobHelper::BlobHelper(const css::uno::Sequence< sal_Int8 >& _val) : m_aValue(_val) +{ +} + +::sal_Int64 SAL_CALL BlobHelper::length( ) +{ + return m_aValue.getLength(); +} + +css::uno::Sequence< ::sal_Int8 > SAL_CALL BlobHelper::getBytes( ::sal_Int64 pos, ::sal_Int32 _length ) +{ + if ( sal_Int32(pos + _length) > m_aValue.getLength() ) + throw css::sdbc::SQLException(); + return css::uno::Sequence< ::sal_Int8 >(m_aValue.getConstArray() + sal_Int32(pos),_length); +} + +css::uno::Reference< css::io::XInputStream > SAL_CALL BlobHelper::getBinaryStream( ) +{ + return new ::comphelper::SequenceInputStream(m_aValue); +} + +::sal_Int64 SAL_CALL BlobHelper::position( const css::uno::Sequence< ::sal_Int8 >& /*pattern*/, ::sal_Int64 /*start*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XBlob::position", *this ); + O3TL_UNREACHABLE; +} + +::sal_Int64 SAL_CALL BlobHelper::positionOfBlob( const css::uno::Reference< css::sdbc::XBlob >& /*pattern*/, ::sal_Int64 /*start*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XBlob::positionOfBlob", *this ); + O3TL_UNREACHABLE; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/CommonTools.cxx b/connectivity/source/commontools/CommonTools.cxx new file mode 100644 index 0000000000..596be7097d --- /dev/null +++ b/connectivity/source/commontools/CommonTools.cxx @@ -0,0 +1,235 @@ +/* -*- 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 <connectivity/CommonTools.hxx> +#include <connectivity/dbtools.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/java/JavaVirtualMachine.hpp> +#if HAVE_FEATURE_JAVA +#include <jvmaccess/virtualmachine.hxx> +#endif +#include <osl/diagnose.h> +#include <rtl/character.hxx> +#include <rtl/process.h> +#include <comphelper/diagnose_ex.hxx> + +using namespace ::comphelper; +static sal_Unicode rtl_ascii_toUpperCase( sal_Unicode ch ) +{ + return ch >= 0x0061 && ch <= 0x007a ? ch + 0x20 : ch; +} + +namespace connectivity +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::java; + using namespace dbtools; + + const sal_Unicode CHAR_PLACE = '_'; + const sal_Unicode CHAR_WILD = '%'; + + bool match(const sal_Unicode* pWild, const sal_Unicode* pStr, const sal_Unicode cEscape) + { + int pos=0; + int flag=0; + + while ( *pWild || flag ) + { + switch (*pWild) + { + case CHAR_PLACE: + if ( *pStr == 0 ) + return false; + break; + default: + if (*pWild && (*pWild == cEscape) && ((*(pWild+1)== CHAR_PLACE) || (*(pWild+1) == CHAR_WILD)) ) + pWild++; + if ( rtl_ascii_toUpperCase(*pWild) != rtl_ascii_toUpperCase(*pStr) ) + if ( !pos ) + return false; + else + pWild += pos; + else + break; + // WARNING/TODO: in certain circumstances it will run into + // the next 'case'! + [[fallthrough]]; + case CHAR_WILD: + while ( *pWild == CHAR_WILD ) + pWild++; + if ( *pWild == 0 ) + return true; + flag = 1; + pos = 0; + if ( *pStr == 0 ) + return ( *pWild == 0 ); + while ( *pStr && *pStr != *pWild ) + { + if ( *pWild == CHAR_PLACE ) { + pWild++; + while ( *pWild == CHAR_WILD ) + pWild++; + } + pStr++; + if ( *pStr == 0 ) + return ( *pWild == 0 ); + } + break; + } + if ( *pWild != 0 ) + pWild++; + if ( *pStr != 0 ) + pStr++; + else + flag = 0; + if ( flag ) + pos--; + } + return ( *pStr == 0 ) && ( *pWild == 0 ); + } + +#if HAVE_FEATURE_JAVA + ::rtl::Reference< jvmaccess::VirtualMachine > getJavaVM(const Reference<XComponentContext >& _rxContext) + { + ::rtl::Reference< jvmaccess::VirtualMachine > aRet; + OSL_ENSURE(_rxContext.is(),"No XMultiServiceFactory a.v.!"); + if(!_rxContext.is()) + return aRet; + + try + { + Reference< XJavaVM > xVM = JavaVirtualMachine::create(_rxContext); + + Sequence<sal_Int8> processID(17); // 16 + 1 + auto pprocessID = processID.getArray(); + rtl_getGlobalProcessId( reinterpret_cast<sal_uInt8*>(pprocessID) ); + pprocessID[16] = 0; // RETURN_VIRTUALMACHINE + + Any uaJVM = xVM->getJavaVM( processID ); + sal_Int64 nTemp; + if (!(uaJVM >>= nTemp)) { + throw Exception("cannot get result for getJavaVM", nullptr); // -5 + } + aRet = reinterpret_cast<jvmaccess::VirtualMachine *>( + static_cast<sal_IntPtr>(nTemp)); + } + catch (Exception&) + { + TOOLS_WARN_EXCEPTION("connectivity.commontools", "getJavaVM failed:"); + } + + return aRet; + } + + bool existsJavaClassByName( const ::rtl::Reference< jvmaccess::VirtualMachine >& _pJVM,std::u16string_view _sClassName ) + { + bool bRet = false; + if ( _pJVM.is() ) + { + jvmaccess::VirtualMachine::AttachGuard aGuard(_pJVM); + JNIEnv* pEnv = aGuard.getEnvironment(); + if( pEnv ) + { + OString sClassName = OUStringToOString(_sClassName, RTL_TEXTENCODING_ASCII_US); + sClassName = sClassName.replace('.','/'); + jobject out = pEnv->FindClass(sClassName.getStr()); + bRet = out != nullptr; + pEnv->DeleteLocalRef( out ); + } + } + return bRet; + } +#endif +} + +namespace dbtools +{ + +static bool isCharOk(sal_Unicode c, std::u16string_view _rSpecials) +{ + + return ( ((c >= 97) && (c <= 122)) || ((c >= 65) && (c <= 90)) || ((c >= 48) && (c <= 57)) || + c == '_' || _rSpecials.find(c) != std::u16string_view::npos); +} + + +bool isValidSQLName(const OUString& rName, std::u16string_view _rSpecials) +{ + // Test for correct naming (in SQL sense) + // This is important for table names for example + const sal_Unicode* pStr = rName.getStr(); + if (*pStr > 127 || rtl::isAsciiDigit(*pStr)) + return false; + + for (; *pStr; ++pStr ) + if(!isCharOk(*pStr,_rSpecials)) + return false; + + if ( !rName.isEmpty() + && ( (rName.toChar() == '_') + || ( (rName.toChar() >= '0') + && (rName.toChar() <= '9') + ) + ) + ) + return false; + // the SQL-Standard requires the first character to be an alphabetic character, which + // isn't easy to decide in UniCode... + // So we just prohibit the characters which already lead to problems... + // 11.04.00 - 74902 - FS + + return true; +} + +// Creates a new name if necessary +OUString convertName2SQLName(const OUString& rName, std::u16string_view _rSpecials) +{ + if(isValidSQLName(rName,_rSpecials)) + return rName; + + const sal_Unicode* pStr = rName.getStr(); + // if not valid + if (*pStr >= 128 || rtl::isAsciiDigit(*pStr)) + return OUString(); + + OUStringBuffer aNewName(rName); + sal_Int32 nLength = rName.getLength(); + for (sal_Int32 i=0; i < nLength; ++i) + if(!isCharOk(aNewName[i],_rSpecials)) + aNewName[i] = '_'; + + return aNewName.makeStringAndClear(); +} + +OUString quoteName(std::u16string_view _rQuote, const OUString& _rName) +{ + OUString sName = _rName; + if( !_rQuote.empty() && _rQuote[0] != ' ') + sName = _rQuote + _rName + _rQuote; + return sName; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/ConnectionWrapper.cxx b/connectivity/source/commontools/ConnectionWrapper.cxx new file mode 100644 index 0000000000..df5ef04ee5 --- /dev/null +++ b/connectivity/source/commontools/ConnectionWrapper.cxx @@ -0,0 +1,238 @@ +/* -*- 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 <connectivity/ConnectionWrapper.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <comphelper/uno3.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/hash.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/reflection/ProxyFactory.hpp> +#include <algorithm> + +using namespace connectivity; + +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::reflection; + +OConnectionWrapper::OConnectionWrapper() +{ + +} + +void OConnectionWrapper::setDelegation(Reference< XAggregation >& _rxProxyConnection,oslInterlockedCount& _rRefCount) +{ + OSL_ENSURE(_rxProxyConnection.is(),"OConnectionWrapper: Connection must be valid!"); + osl_atomic_increment( &_rRefCount ); + if (_rxProxyConnection.is()) + { + // transfer the (one and only) real ref to the aggregate to our member + m_xProxyConnection = _rxProxyConnection; + _rxProxyConnection = nullptr; + ::comphelper::query_aggregation(m_xProxyConnection,m_xConnection); + m_xTypeProvider.set(m_xConnection,UNO_QUERY); + m_xUnoTunnel.set(m_xConnection,UNO_QUERY); + m_xServiceInfo.set(m_xConnection,UNO_QUERY); + + // set ourself as delegator + Reference<XInterface> xIf = static_cast< XUnoTunnel* >( this ); + m_xProxyConnection->setDelegator( xIf ); + + } + osl_atomic_decrement( &_rRefCount ); +} + +void OConnectionWrapper::setDelegation(const Reference< XConnection >& _xConnection + ,const Reference< XComponentContext>& _rxContext + ,oslInterlockedCount& _rRefCount) +{ + OSL_ENSURE(_xConnection.is(),"OConnectionWrapper: Connection must be valid!"); + osl_atomic_increment( &_rRefCount ); + + m_xConnection = _xConnection; + m_xTypeProvider.set(m_xConnection,UNO_QUERY); + m_xUnoTunnel.set(m_xConnection,UNO_QUERY); + m_xServiceInfo.set(m_xConnection,UNO_QUERY); + + Reference< XProxyFactory > xProxyFactory = ProxyFactory::create( _rxContext ); + Reference< XAggregation > xConProxy = xProxyFactory->createProxy(_xConnection); + if (xConProxy.is()) + { + // transfer the (one and only) real ref to the aggregate to our member + m_xProxyConnection = xConProxy; + + // set ourself as delegator + Reference<XInterface> xIf = static_cast< XUnoTunnel* >( this ); + m_xProxyConnection->setDelegator( xIf ); + + } + osl_atomic_decrement( &_rRefCount ); +} + +void OConnectionWrapper::disposing() +{ +m_xConnection.clear(); +} + +OConnectionWrapper::~OConnectionWrapper() +{ + if (m_xProxyConnection.is()) + m_xProxyConnection->setDelegator(nullptr); +} + +// XServiceInfo + +OUString SAL_CALL OConnectionWrapper::getImplementationName( ) +{ + return "com.sun.star.sdbc.drivers.OConnectionWrapper"; +} + + +css::uno::Sequence< OUString > SAL_CALL OConnectionWrapper::getSupportedServiceNames( ) +{ + // first collect the services which are supported by our aggregate + Sequence< OUString > aSupported; + if ( m_xServiceInfo.is() ) + aSupported = m_xServiceInfo->getSupportedServiceNames(); + + // append our own service, if necessary + OUString sConnectionService( "com.sun.star.sdbc.Connection" ); + if ( ::comphelper::findValue( aSupported, sConnectionService ) == -1 ) + { + sal_Int32 nLen = aSupported.getLength(); + aSupported.realloc( nLen + 1 ); + aSupported.getArray()[ nLen ] = sConnectionService; + } + + // outta here + return aSupported; +} + + +sal_Bool SAL_CALL OConnectionWrapper::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + + +Any SAL_CALL OConnectionWrapper::queryInterface( const Type& _rType ) +{ + Any aReturn = OConnection_BASE::queryInterface(_rType); + return aReturn.hasValue() ? aReturn : (m_xProxyConnection.is() ? m_xProxyConnection->queryAggregation(_rType) : aReturn); +} + +Sequence< Type > SAL_CALL OConnectionWrapper::getTypes( ) +{ + return ::comphelper::concatSequences( + OConnection_BASE::getTypes(), + m_xTypeProvider->getTypes() + ); +} + +// css::lang::XUnoTunnel +sal_Int64 SAL_CALL OConnectionWrapper::getSomething( const Sequence< sal_Int8 >& rId ) +{ + if (comphelper::isUnoTunnelId<OConnectionWrapper>(rId)) + return comphelper::getSomething_cast(this); + + if(m_xUnoTunnel.is()) + return m_xUnoTunnel->getSomething(rId); + return 0; +} + + +const Sequence< sal_Int8 > & OConnectionWrapper::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +namespace +{ + class TPropertyValueLessFunctor + { + public: + TPropertyValueLessFunctor() + {} + bool operator() (const css::beans::PropertyValue& lhs, const css::beans::PropertyValue& rhs) const + { + return lhs.Name.compareToIgnoreAsciiCase(rhs.Name) < 0; + } + }; + +} + + +// creates a unique id out of the url and sequence of properties +void OConnectionWrapper::createUniqueId( const OUString& _rURL + ,Sequence< PropertyValue >& _rInfo + ,sal_uInt8* _pBuffer + ,const OUString& _rUserName + ,const OUString& _rPassword) +{ + // first we create the digest we want to have + ::comphelper::Hash sha1(::comphelper::HashType::SHA1); + sha1.update(reinterpret_cast<unsigned char const*>(_rURL.getStr()), _rURL.getLength() * sizeof(sal_Unicode)); + if ( !_rUserName.isEmpty() ) + sha1.update(reinterpret_cast<unsigned char const*>(_rUserName.getStr()), _rUserName.getLength() * sizeof(sal_Unicode)); + if ( !_rPassword.isEmpty() ) + sha1.update(reinterpret_cast<unsigned char const*>(_rPassword.getStr()), _rPassword.getLength() * sizeof(sal_Unicode)); + // now we need to sort the properties + auto [begin, end] = asNonConstRange(_rInfo); + std::sort(begin,end,TPropertyValueLessFunctor()); + + for (PropertyValue const & prop : std::as_const(_rInfo)) + { + // we only include strings an integer values + OUString sValue; + if ( prop.Value >>= sValue ) + ; + else + { + sal_Int32 nValue = 0; + if ( prop.Value >>= nValue ) + sValue = OUString::number(nValue); + else + { + Sequence< OUString> aSeq; + if ( prop.Value >>= aSeq ) + { + for(OUString const & s : std::as_const(aSeq)) + sha1.update(reinterpret_cast<unsigned char const*>(s.getStr()), s.getLength() * sizeof(sal_Unicode)); + } + } + } + if ( !sValue.isEmpty() ) + { + // we don't have to convert this into UTF8 because we don't store on a file system + sha1.update(reinterpret_cast<unsigned char const*>(sValue.getStr()), sValue.getLength() * sizeof(sal_Unicode)); + } + } + + std::vector<unsigned char> result(sha1.finalize()); + std::copy(result.begin(), result.end(), _pBuffer); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/DateConversion.cxx b/connectivity/source/commontools/DateConversion.cxx new file mode 100644 index 0000000000..0895881d7e --- /dev/null +++ b/connectivity/source/commontools/DateConversion.cxx @@ -0,0 +1,517 @@ +/* -*- 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 <connectivity/dbconversion.hxx> +#include <connectivity/dbtools.hxx> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/sdb/XColumnUpdate.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <comphelper/extract.hxx> +#include <TConnection.hxx> +#include <comphelper/numbers.hxx> +#include <comphelper/types.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> + + +using namespace ::connectivity; +using namespace ::comphelper; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::dbtools; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::uno; + +OUString DBTypeConversion::toSQLString(sal_Int32 eType, const Any& _rVal, + const Reference< XTypeConverter >& _rxTypeConverter) +{ + OUStringBuffer aRet; + if (_rVal.hasValue()) + { + try + { + switch (eType) + { + case DataType::INTEGER: + case DataType::BIT: + case DataType::BOOLEAN: + case DataType::TINYINT: + case DataType::SMALLINT: + if (_rVal.getValueType().getTypeClass() == css::uno::TypeClass_BOOLEAN) + { + if (::cppu::any2bool(_rVal)) + aRet.append("1"); + else + aRet.append("0"); + } + else + { + OUString sTemp; + _rxTypeConverter->convertToSimpleType(_rVal, TypeClass_STRING) >>= sTemp; + aRet.append(sTemp); + } + break; + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + aRet.append("'"); + { + OUString aTemp; + _rxTypeConverter->convertToSimpleType(_rVal, TypeClass_STRING) >>= aTemp; + aTemp = aTemp.replaceAll(u"\'", u"\'\'"); + aRet.append(aTemp); + } + aRet.append("'"); + break; + case DataType::REAL: + case DataType::DOUBLE: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::BIGINT: + default: + { + OUString sTemp; + _rxTypeConverter->convertToSimpleType(_rVal, TypeClass_STRING) >>= sTemp; + aRet.append(sTemp); + } + break; + case DataType::TIMESTAMP: + { + DateTime aDateTime; + bool bOk = false; + if (_rVal.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE) + { + double nValue = 0.0; + _rVal >>= nValue; + aDateTime = DBTypeConversion::toDateTime(nValue); + bOk = true; + } + else if (_rVal.getValueType().getTypeClass() == css::uno::TypeClass_STRING) + { + OUString sValue; + _rVal >>= sValue; + aDateTime = DBTypeConversion::toDateTime(sValue); + bOk = true; + } + else + bOk = _rVal >>= aDateTime; + + OSL_ENSURE( bOk, "DBTypeConversion::toSQLString: _rVal is not datetime!"); + // check if this is really a timestamp or only a date + if ( bOk ) + { + aRet.append("{ts '" + + DBTypeConversion::toDateTimeString(aDateTime) + + "'}"); + break; + } + break; + } + case DataType::DATE: + { + Date aDate; + bool bOk = false; + if (_rVal.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE) + { + double nValue = 0.0; + _rVal >>= nValue; + aDate = DBTypeConversion::toDate(nValue); + bOk = true; + } + else if (_rVal.getValueType().getTypeClass() == css::uno::TypeClass_STRING) + { + OUString sValue; + _rVal >>= sValue; + aDate = DBTypeConversion::toDate(sValue); + bOk = true; + } + else + bOk = _rVal >>= aDate; + OSL_ENSURE( bOk, "DBTypeConversion::toSQLString: _rVal is not date!"); + aRet.append("{d '" + + DBTypeConversion::toDateString(aDate) + + "'}"); + } break; + case DataType::TIME: + { + css::util::Time aTime; + bool bOk = false; + if (_rVal.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE) + { + double nValue = 0.0; + _rVal >>= nValue; + aTime = DBTypeConversion::toTime(nValue); + bOk = true; + } + else if (_rVal.getValueType().getTypeClass() == css::uno::TypeClass_STRING) + { + OUString sValue; + _rVal >>= sValue; + aTime = DBTypeConversion::toTime(sValue); + bOk = true; + } + else + bOk = _rVal >>= aTime; + OSL_ENSURE( bOk,"DBTypeConversion::toSQLString: _rVal is not time!"); + aRet.append("{t '" + + DBTypeConversion::toTimeString(aTime) + + "'}"); + } break; + } + } + catch ( const Exception& ) + { + OSL_FAIL("TypeConversion Error"); + } + } + else + aRet.append(" NULL "); + return aRet.makeStringAndClear(); +} + +Date DBTypeConversion::getNULLDate(const Reference< XNumberFormatsSupplier > &xSupplier) +{ + OSL_ENSURE(xSupplier.is(), "getNULLDate : the formatter doesn't implement a supplier !"); + if (xSupplier.is()) + { + try + { + // get the null date + Date aDate; + xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= aDate; + return aDate; + } + catch ( const Exception& ) + { + } + } + + return getStandardDate(); +} + +void DBTypeConversion::setValue(const Reference<XColumnUpdate>& xVariant, + const Reference<XNumberFormatter>& xFormatter, + const Date& rNullDate, + const OUString& rString, + sal_Int32 nKey, + sal_Int16 nFieldType, + sal_Int16 nKeyType) +{ + if (!rString.isEmpty()) + { + // Does the String need to be formatted? + sal_Int16 nTypeClass = nKeyType & ~NumberFormat::DEFINED; + bool bTextFormat = nTypeClass == NumberFormat::TEXT; + sal_Int32 nKeyToUse = bTextFormat ? 0 : nKey; + sal_Int16 nRealUsedTypeClass = nTypeClass; + // for a Text-Format the formatter needs some more freedom, otherwise + // convertStringToNumber will throw a NotNumericException + try + { + double fValue = xFormatter->convertStringToNumber(nKeyToUse, rString); + Reference< XNumberFormats > xFormats(xFormatter->getNumberFormatsSupplier()->getNumberFormats()); + Reference< XNumberFormatTypes > xFormatTypes(xFormats, UNO_QUERY); + sal_Int32 nStandardKey(0); + if(xFormatTypes.is()) + { + const Reference< XPropertySet > xFormatProps(xFormats->getByKey(nKeyToUse)); + if (xFormatProps.is()) + { + css::lang::Locale loc; + if (xFormatProps->getPropertyValue("Locale") >>= loc) + nStandardKey = xFormatTypes->getStandardIndex(loc); + else + { + assert(false); + } + } + else + { + SAL_WARN("connectivity.commontools", "no format by key " << nKeyToUse); + } + } + else + { + assert(false); + } + // Why use nStandardKey rather than nKeyToUse here? I'm not sure, but "it was always like that". + // Previously had hardcoded 0 instead of nStandardKey, which led to problems with dates + // because of differences M/D/Y vs D/M/Y. This at least fixes those problems, but possibly + // nKeyToUse is an even better choice than nStandardKey. + // OTOH, using nKeyToUse nullifies the special treatment for percent formats, + // leading to "5" (in a percent format) to be understood as "500%" instead of "5%". + sal_Int32 nRealUsedKey = xFormatter->detectNumberFormat(nStandardKey, rString); + if (nRealUsedKey != nKeyToUse) + nRealUsedTypeClass = getNumberFormatType(xFormatter, nRealUsedKey) & ~NumberFormat::DEFINED; + + // and again a special treatment, this time for percent formats + if ((NumberFormat::NUMBER == nRealUsedTypeClass) && (NumberFormat::PERCENT == nTypeClass)) + { // formatting should be "percent", but the String provides just a simple number -> adjust + OUString sExpanded = rString + "%"; + fValue = xFormatter->convertStringToNumber(nKeyToUse, sExpanded); + } + + switch (nRealUsedTypeClass) + { + case NumberFormat::DATE: + case NumberFormat::DATETIME: + case NumberFormat::TIME: + DBTypeConversion::setValue(xVariant,rNullDate,fValue,nRealUsedTypeClass); + break; + case NumberFormat::CURRENCY: + case NumberFormat::NUMBER: + case NumberFormat::SCIENTIFIC: + case NumberFormat::FRACTION: + case NumberFormat::PERCENT: + xVariant->updateDouble(fValue); + break; + default: + xVariant->updateString(rString); + } + } + catch(const Exception& ) + { + xVariant->updateString(rString); + } + } + else + { + switch (nFieldType) + { + case css::sdbc::DataType::CHAR: + case css::sdbc::DataType::VARCHAR: + case css::sdbc::DataType::LONGVARCHAR: + xVariant->updateString(rString); + break; + default: + xVariant->updateNull(); + } + } +} + + +void DBTypeConversion::setValue(const Reference<XColumnUpdate>& xVariant, + const Date& rNullDate, + const double& rValue, + sal_Int16 nKeyType) +{ + switch (nKeyType & ~NumberFormat::DEFINED) + { + case NumberFormat::DATE: + xVariant->updateDate(toDate( rValue, rNullDate)); + break; + case NumberFormat::DATETIME: + xVariant->updateTimestamp(toDateTime(rValue,rNullDate)); + break; + case NumberFormat::TIME: + xVariant->updateTime(toTime(rValue)); + break; + default: + { + double nValue = rValue; +// Reference<XPropertySet> xProp(xVariant,UNO_QUERY); +// if ( xProp.is() +// && xProp->getPropertySetInfo()->hasPropertyByName(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISSIGNED)) +// && !::comphelper::getBOOL(xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISSIGNED))) ) +// { +// switch (::comphelper::getINT32(xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)))) +// { +// case DataType::TINYINT: +// nValue = static_cast<sal_uInt8>(rValue); +// break; +// case DataType::SMALLINT: +// nValue = static_cast<sal_uInt16>(rValue); +// break; +// case DataType::INTEGER: +// nValue = static_cast<sal_uInt32>(rValue); +// break; +// case DataType::BIGINT: +// nValue = static_cast<sal_uInt64>(rValue); +// break; +// } +// } + xVariant->updateDouble(nValue); + } + } +} + + +double DBTypeConversion::getValue( const Reference< XColumn >& i_column, const Date& i_relativeToNullDate ) +{ + try + { + const Reference< XPropertySet > xProp( i_column, UNO_QUERY_THROW ); + + const sal_Int32 nColumnType = ::comphelper::getINT32( xProp->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_TYPE ) ) ); + switch ( nColumnType ) + { + case DataType::DATE: + return toDouble( i_column->getDate(), i_relativeToNullDate ); + + case DataType::TIME: + return toDouble( i_column->getTime() ); + + case DataType::TIMESTAMP: + return toDouble( i_column->getTimestamp(), i_relativeToNullDate ); + + default: + { + bool bIsSigned = true; + OSL_VERIFY( xProp->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ISSIGNED ) ) >>= bIsSigned ); + if ( !bIsSigned ) + { + switch ( nColumnType) + { + case DataType::TINYINT: + return static_cast<double>(static_cast<sal_uInt8>(i_column->getByte())); + case DataType::SMALLINT: + return static_cast<double>(static_cast<sal_uInt16>(i_column->getShort())); + case DataType::INTEGER: + return static_cast<double>(static_cast<sal_uInt32>(i_column->getInt())); + case DataType::BIGINT: + return static_cast<double>(static_cast<sal_uInt64>(i_column->getLong())); + } + } + } + return i_column->getDouble(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + return 0.0; + } +} + +OUString DBTypeConversion::getFormattedValue(const Reference< XPropertySet>& _xColumn, + const Reference<XNumberFormatter>& _xFormatter, + const css::lang::Locale& _rLocale, + const Date& _rNullDate) +{ + OSL_ENSURE(_xColumn.is() && _xFormatter.is(), "DBTypeConversion::getFormattedValue: invalid arg !"); + if (!_xColumn.is() || !_xFormatter.is()) + return OUString(); + + sal_Int32 nKey(0); + try + { + _xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY)) >>= nKey; + } + catch (const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "DBTypeConversion::getValue: caught an exception while asking for the format key!"); + } + + if (!nKey) + { + Reference<XNumberFormats> xFormats( _xFormatter->getNumberFormatsSupplier()->getNumberFormats() ); + + nKey = ::dbtools::getDefaultNumberFormat(_xColumn, + Reference< XNumberFormatTypes > (xFormats, UNO_QUERY), + _rLocale); + + } + + sal_Int16 nKeyType = getNumberFormatType(_xFormatter, nKey) & ~NumberFormat::DEFINED; + + return DBTypeConversion::getFormattedValue(Reference< XColumn > (_xColumn, UNO_QUERY), _xFormatter, _rNullDate, nKey, nKeyType); +} + + +OUString DBTypeConversion::getFormattedValue(const Reference<XColumn>& xVariant, + const Reference<XNumberFormatter>& xFormatter, + const Date& rNullDate, + sal_Int32 nKey, + sal_Int16 nKeyType) +{ + OUString aString; + if (xVariant.is()) + { + try + { + switch (nKeyType & ~NumberFormat::DEFINED) + { + case NumberFormat::DATE: + case NumberFormat::DATETIME: + { + // get a value which represents the given date, relative to the given null date + double fValue = getValue( xVariant, rNullDate ); + if ( !xVariant->wasNull() ) + { + // get the null date of the formatter + Date aFormatterNullDate( rNullDate ); + try + { + Reference< XNumberFormatsSupplier > xSupplier( xFormatter->getNumberFormatsSupplier(), UNO_SET_THROW ); + Reference< XPropertySet > xFormatterSettings( xSupplier->getNumberFormatSettings(), UNO_SET_THROW ); + OSL_VERIFY( xFormatterSettings->getPropertyValue("NullDate") >>= aFormatterNullDate ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + // get a value which represents the given date, relative to the null date of the formatter + fValue -= toDays( rNullDate, aFormatterNullDate ); + // format this value + aString = xFormatter->convertNumberToString( nKey, fValue ); + } + } + break; + case NumberFormat::TIME: + case NumberFormat::NUMBER: + case NumberFormat::SCIENTIFIC: + case NumberFormat::FRACTION: + case NumberFormat::PERCENT: + { + double fValue = xVariant->getDouble(); + if (!xVariant->wasNull()) + aString = xFormatter->convertNumberToString(nKey, fValue); + } break; + case NumberFormat::CURRENCY: + { + double fValue = xVariant->getDouble(); + if (!xVariant->wasNull()) + aString = xFormatter->getInputString(nKey, fValue); + } break; + case NumberFormat::TEXT: + aString = xFormatter->formatString(nKey, xVariant->getString()); + break; + default: + aString = xVariant->getString(); + } + } + catch(const Exception& ) + { + aString = xVariant->getString(); + } + } + return aString; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/DriversConfig.cxx b/connectivity/source/commontools/DriversConfig.cxx new file mode 100644 index 0000000000..26a241e3f8 --- /dev/null +++ b/connectivity/source/commontools/DriversConfig.cxx @@ -0,0 +1,249 @@ +/* -*- 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_fuzzers.h> + +#include <connectivity/DriversConfig.hxx> +#include <o3tl/string_view.hxx> +#include <tools/wldcrd.hxx> +#include <comphelper/sequence.hxx> +#include <utility> + +using namespace connectivity; +using namespace utl; +using namespace ::com::sun::star; + +namespace +{ + void lcl_convert(const uno::Sequence< OUString >& _aSource,uno::Any& _rDest) + { + uno::Sequence<uno::Any> aRet(_aSource.getLength()); + uno::Any* pAny = aRet.getArray(); + const OUString* pIter = _aSource.getConstArray(); + const OUString* pEnd = pIter + _aSource.getLength(); + for (;pIter != pEnd ; ++pIter,++pAny) + { + *pAny <<= *pIter; + } + _rDest <<= aRet; + } + void lcl_fillValues(const ::utl::OConfigurationNode& _aURLPatternNode,const OUString& _sNode,::comphelper::NamedValueCollection& _rValues) + { + const ::utl::OConfigurationNode aPropertiesNode = _aURLPatternNode.openNode(_sNode); + if ( !aPropertiesNode.isValid() ) + return; + + uno::Sequence< OUString > aStringSeq; + const uno::Sequence< OUString > aProperties = aPropertiesNode.getNodeNames(); + const OUString* pPropertiesIter = aProperties.getConstArray(); + const OUString* pPropertiesEnd = pPropertiesIter + aProperties.getLength(); + for (;pPropertiesIter != pPropertiesEnd ; ++pPropertiesIter) + { + uno::Any aValue = aPropertiesNode.getNodeValue(*pPropertiesIter + "/Value"); + if ( aValue >>= aStringSeq ) + { + lcl_convert(aStringSeq,aValue); + } + _rValues.put(*pPropertiesIter,aValue); + } // for (;pPropertiesIter != pPropertiesEnd ; ++pPropertiesIter,++pNamedIter) + } + void lcl_readURLPatternNode(const ::utl::OConfigurationTreeRoot& _aInstalled,const OUString& _sEntry,TInstalledDriver& _rInstalledDriver) + { + const ::utl::OConfigurationNode aURLPatternNode = _aInstalled.openNode(_sEntry); + if ( !aURLPatternNode.isValid() ) + return; + + OUString sParentURLPattern; + aURLPatternNode.getNodeValue("ParentURLPattern") >>= sParentURLPattern; + if ( !sParentURLPattern.isEmpty() ) + lcl_readURLPatternNode(_aInstalled,sParentURLPattern,_rInstalledDriver); + + OUString sDriverFactory; + aURLPatternNode.getNodeValue("Driver") >>= sDriverFactory; + if ( !sDriverFactory.isEmpty() ) + _rInstalledDriver.sDriverFactory = sDriverFactory; + + OUString sDriverTypeDisplayName; + aURLPatternNode.getNodeValue("DriverTypeDisplayName") >>= sDriverTypeDisplayName; + OSL_ENSURE(!sDriverTypeDisplayName.isEmpty(),"No valid DriverTypeDisplayName property!"); + if ( !sDriverTypeDisplayName.isEmpty() ) + _rInstalledDriver.sDriverTypeDisplayName = sDriverTypeDisplayName; + + lcl_fillValues(aURLPatternNode,"Properties",_rInstalledDriver.aProperties); + lcl_fillValues(aURLPatternNode,"Features",_rInstalledDriver.aFeatures); + lcl_fillValues(aURLPatternNode,"MetaData",_rInstalledDriver.aMetaData); + } +} + +DriversConfigImpl::DriversConfigImpl() +{ +} + +const TInstalledDrivers& DriversConfigImpl::getInstalledDrivers(const uno::Reference< uno::XComponentContext >& _rxORB) const +{ + if ( m_aDrivers.empty() ) + { + if ( !m_aInstalled.isValid() ) + { + m_aInstalled = ::utl::OConfigurationTreeRoot::createWithComponentContext(_rxORB, + "org.openoffice.Office.DataAccess.Drivers/Installed", -1, ::utl::OConfigurationTreeRoot::CM_READONLY); + } + + if ( m_aInstalled.isValid() ) + { + const uno::Sequence< OUString > aURLPatterns = m_aInstalled.getNodeNames(); + const OUString* pPatternIter = aURLPatterns.getConstArray(); + const OUString* pPatternEnd = pPatternIter + aURLPatterns.getLength(); + for (;pPatternIter != pPatternEnd ; ++pPatternIter) + { + TInstalledDriver aInstalledDriver; + lcl_readURLPatternNode(m_aInstalled,*pPatternIter,aInstalledDriver); + if ( !aInstalledDriver.sDriverFactory.isEmpty() ) + m_aDrivers.emplace(*pPatternIter,aInstalledDriver); + } + } // if ( m_aInstalled.isValid() ) + } + return m_aDrivers; +} + +DriversConfig::DriversConfig(uno::Reference< uno::XComponentContext > _xORB) +:m_xORB(std::move(_xORB)) +{ +} + + +DriversConfig::~DriversConfig() +{ +} + + +DriversConfig::DriversConfig( const DriversConfig& _rhs ) +{ + *this = _rhs; +} + + +DriversConfig& DriversConfig::operator=( const DriversConfig& _rhs ) +{ + if ( this != &_rhs ) + { + m_aNode = _rhs.m_aNode; + } + return *this; +} + + +OUString DriversConfig::getDriverFactoryName(std::u16string_view _sURL) const +{ +#if ENABLE_FUZZERS + if (o3tl::starts_with(_sURL, u"sdbc:dbase:")) + return "com.sun.star.comp.sdbc.dbase.ODriver"; +#endif + + const TInstalledDrivers& rDrivers = m_aNode->getInstalledDrivers(m_xORB); + OUString sRet; + OUString sOldPattern; + for(const auto& [rPattern, rDriver] : rDrivers) + { + WildCard aWildCard(rPattern); + if ( sOldPattern.getLength() < rPattern.getLength() && aWildCard.Matches(_sURL) ) + { + sRet = rDriver.sDriverFactory; + sOldPattern = rPattern; + } + } + + return sRet; +} + +OUString DriversConfig::getDriverTypeDisplayName(std::u16string_view _sURL) const +{ + const TInstalledDrivers& rDrivers = m_aNode->getInstalledDrivers(m_xORB); + OUString sRet; + OUString sOldPattern; + for(const auto& [rPattern, rDriver] : rDrivers) + { + WildCard aWildCard(rPattern); + if ( sOldPattern.getLength() < rPattern.getLength() && aWildCard.Matches(_sURL) ) + { + sRet = rDriver.sDriverTypeDisplayName; + sOldPattern = rPattern; + } + } + + return sRet; +} + +const ::comphelper::NamedValueCollection& DriversConfig::getProperties(std::u16string_view _sURL) + const +{ + return impl_get(_sURL,1); +} + +const ::comphelper::NamedValueCollection& DriversConfig::getFeatures(std::u16string_view _sURL) + const +{ + return impl_get(_sURL,0); +} + +const ::comphelper::NamedValueCollection& DriversConfig::getMetaData(std::u16string_view _sURL) + const +{ + return impl_get(_sURL,2); +} + +const ::comphelper::NamedValueCollection& DriversConfig::impl_get(std::u16string_view _sURL,sal_Int32 _nProps) const +{ + const TInstalledDrivers& rDrivers = m_aNode->getInstalledDrivers(m_xORB); + const ::comphelper::NamedValueCollection* pRet = nullptr; + OUString sOldPattern; + for(const auto& [rPattern, rDriver] : rDrivers) + { + WildCard aWildCard(rPattern); + if ( sOldPattern.getLength() < rPattern.getLength() && aWildCard.Matches(_sURL) ) + { + switch(_nProps) + { + case 0: + pRet = &rDriver.aFeatures; + break; + case 1: + pRet = &rDriver.aProperties; + break; + case 2: + pRet = &rDriver.aMetaData; + break; + } + sOldPattern = rPattern; + } + } // for(;aIter != aEnd;++aIter) + if ( pRet == nullptr ) + { + static const ::comphelper::NamedValueCollection s_sEmpty; + pRet = &s_sEmpty; + } + return *pRet; +} + +uno::Sequence< OUString > DriversConfig::getURLs() const +{ + const TInstalledDrivers& rDrivers = m_aNode->getInstalledDrivers(m_xORB); + return comphelper::mapKeysToSequence(rDrivers); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/FDatabaseMetaDataResultSet.cxx b/connectivity/source/commontools/FDatabaseMetaDataResultSet.cxx new file mode 100644 index 0000000000..d4ae8760f2 --- /dev/null +++ b/connectivity/source/commontools/FDatabaseMetaDataResultSet.cxx @@ -0,0 +1,839 @@ +/* -*- 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 <ParameterSubstitution.hxx> +#include <FDatabaseMetaDataResultSet.hxx> +#include <FDatabaseMetaDataResultSetMetaData.hxx> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <connectivity/dbexception.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/unreachable.hxx> +#include <TConnection.hxx> + +using namespace connectivity; +using namespace dbtools; +using namespace cppu; + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +ODatabaseMetaDataResultSet::ODatabaseMetaDataResultSet() + :ODatabaseMetaDataResultSet_BASE(m_aMutex) + ,::comphelper::OPropertyContainer(ODatabaseMetaDataResultSet_BASE::rBHelper) + ,m_nColPos(0) + ,m_bBOF(true) + ,m_bEOF(true) +{ + construct(); +} + + +ODatabaseMetaDataResultSet::ODatabaseMetaDataResultSet( MetaDataResultSetType _eType ) + :ODatabaseMetaDataResultSet_BASE(m_aMutex) + ,::comphelper::OPropertyContainer(ODatabaseMetaDataResultSet_BASE::rBHelper) + ,m_nColPos(0) + ,m_bBOF(true) + ,m_bEOF(true) +{ + construct(); + + setType(_eType); +} + + +ODatabaseMetaDataResultSet::~ODatabaseMetaDataResultSet() +{ +} + +void ODatabaseMetaDataResultSet::construct() +{ + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), PROPERTY_ID_FETCHSIZE, 0,&m_nFetchSize, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), PROPERTY_ID_RESULTSETTYPE, PropertyAttribute::READONLY,&m_nResultSetType, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), PROPERTY_ID_FETCHDIRECTION, 0, &m_nFetchDirection, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), PROPERTY_ID_RESULTSETCONCURRENCY, PropertyAttribute::READONLY,&m_nResultSetConcurrency, ::cppu::UnoType<sal_Int32>::get()); +} + +void ODatabaseMetaDataResultSet::setType(MetaDataResultSetType _eType) +{ + switch( _eType ) + { + case eCatalogs: setCatalogsMap(); break; + case eSchemas: setSchemasMap(); break; + case eColumnPrivileges: setColumnPrivilegesMap(); break; + case eColumns: setColumnsMap(); break; + case eTables: setTablesMap(); break; + case eTableTypes: setTableTypes(); break; + case eProcedureColumns: setProcedureColumnsMap(); break; + case eProcedures: setProceduresMap(); break; + case eExportedKeys: setExportedKeysMap(); break; + case eImportedKeys: setImportedKeysMap(); break; + case ePrimaryKeys: setPrimaryKeysMap(); break; + case eIndexInfo: setIndexInfoMap(); break; + case eTablePrivileges: setTablePrivilegesMap(); break; + case eCrossReference: setCrossReferenceMap(); break; + case eTypeInfo: setTypeInfoMap(); break; + case eBestRowIdentifier: setBestRowIdentifierMap(); break; + case eVersionColumns: setVersionColumnsMap(); break; + case eUDTs: setUDTsMap(); break; + default: + OSL_FAIL("Wrong type!"); + } +} + +void ODatabaseMetaDataResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + m_aStatement.clear(); + m_xMetaData.clear(); + m_aRowsIter = m_aRows.end(); + m_aRows.clear(); + m_aRowsIter = m_aRows.end(); +} + +void SAL_CALL ODatabaseMetaDataResultSet::acquire() noexcept +{ + ODatabaseMetaDataResultSet_BASE::acquire(); +} + +void SAL_CALL ODatabaseMetaDataResultSet::release() noexcept +{ + ODatabaseMetaDataResultSet_BASE::release(); +} + +Any SAL_CALL ODatabaseMetaDataResultSet::queryInterface( const Type & rType ) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + return aRet.hasValue() ? aRet : ODatabaseMetaDataResultSet_BASE::queryInterface(rType); +} + +Sequence< Type > SAL_CALL ODatabaseMetaDataResultSet::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<css::beans::XMultiPropertySet>::get(), + cppu::UnoType<css::beans::XFastPropertySet>::get(), + cppu::UnoType<css::beans::XPropertySet>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),ODatabaseMetaDataResultSet_BASE::getTypes()); +} + +void ODatabaseMetaDataResultSet::setRows(ORows&& _rRows) +{ + m_aRows = std::move(_rRows); + m_bBOF = true; + m_bEOF = m_aRows.empty(); +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::findColumn( const OUString& columnName ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + + Reference< XResultSetMetaData > xMeta = getMetaData(); + sal_Int32 nLen = xMeta->getColumnCount(); + sal_Int32 i = 1; + for(;i<=nLen;++i) + { + if(xMeta->isCaseSensitive(i) ? columnName == xMeta->getColumnName(i) : + columnName.equalsIgnoreAsciiCase(xMeta->getColumnName(i)) + ) + return i; + } + + ::dbtools::throwInvalidColumnException( columnName, *this ); + O3TL_UNREACHABLE; +} + +void ODatabaseMetaDataResultSet::checkIndex(sal_Int32 columnIndex ) +{ + if(columnIndex < 1 || o3tl::make_unsigned(columnIndex) >= (*m_aRowsIter).size()) + ::dbtools::throwInvalidIndexException(*this); +} + +Reference< css::io::XInputStream > SAL_CALL ODatabaseMetaDataResultSet::getBinaryStream( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + +Reference< css::io::XInputStream > SAL_CALL ODatabaseMetaDataResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::getBoolean( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getBool(); +} + + +sal_Int8 SAL_CALL ODatabaseMetaDataResultSet::getByte( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getInt8(); +} + + +Sequence< sal_Int8 > SAL_CALL ODatabaseMetaDataResultSet::getBytes( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getSequence(); +} + + +css::util::Date SAL_CALL ODatabaseMetaDataResultSet::getDate( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDate(); +} + + +double SAL_CALL ODatabaseMetaDataResultSet::getDouble( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDouble(); +} + + +float SAL_CALL ODatabaseMetaDataResultSet::getFloat( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getFloat(); +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::getInt( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getInt32(); +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::getRow( ) +{ + return 0; +} + + +sal_Int64 SAL_CALL ODatabaseMetaDataResultSet::getLong( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getLong(); +} + + +Reference< XResultSetMetaData > SAL_CALL ODatabaseMetaDataResultSet::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + + if(!m_xMetaData.is()) + m_xMetaData = new ODatabaseMetaDataResultSetMetaData(); + + return m_xMetaData; +} + +Reference< XArray > SAL_CALL ODatabaseMetaDataResultSet::getArray( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +Reference< XClob > SAL_CALL ODatabaseMetaDataResultSet::getClob( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + +Reference< XBlob > SAL_CALL ODatabaseMetaDataResultSet::getBlob( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +Reference< XRef > SAL_CALL ODatabaseMetaDataResultSet::getRef( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +Any SAL_CALL ODatabaseMetaDataResultSet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + return getValue(columnIndex).makeAny(); +} + + +sal_Int16 SAL_CALL ODatabaseMetaDataResultSet::getShort( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getInt16(); +} + + +OUString SAL_CALL ODatabaseMetaDataResultSet::getString( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getString(); +} + + +css::util::Time SAL_CALL ODatabaseMetaDataResultSet::getTime( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getTime(); +} + + +css::util::DateTime SAL_CALL ODatabaseMetaDataResultSet::getTimestamp( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDateTime(); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isAfterLast( ) +{ + return m_bEOF; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isFirst( ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isLast( ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + + +void SAL_CALL ODatabaseMetaDataResultSet::beforeFirst( ) +{ + ::dbtools::throwFunctionSequenceException(*this); +} + +void SAL_CALL ODatabaseMetaDataResultSet::afterLast( ) +{ + ::dbtools::throwFunctionSequenceException(*this); +} + + +void SAL_CALL ODatabaseMetaDataResultSet::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + } + dispose(); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::first( ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::last( ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::absolute( sal_Int32 /*row*/ ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::relative( sal_Int32 /*row*/ ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::previous( ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + + +Reference< XInterface > SAL_CALL ODatabaseMetaDataResultSet::getStatement( ) +{ + return m_aStatement.get(); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowDeleted( ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowInserted( ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowUpdated( ) +{ + ::dbtools::throwFunctionSequenceException(*this); + O3TL_UNREACHABLE; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isBeforeFirst( ) +{ + return m_bBOF; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::next( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + if ( m_bBOF ) + { + m_aRowsIter = m_aRows.begin(); + m_bBOF = false; + } + else + { + if ( m_bEOF ) + throwFunctionSequenceException( *this ); + else + if ( m_aRowsIter != m_aRows.end() ) + ++m_aRowsIter; + } + + bool bSuccess = m_aRowsIter != m_aRows.end(); + if ( !bSuccess ) + { + m_bEOF = true; + m_bBOF = m_aRows.empty(); + } + return bSuccess; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::wasNull( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + + if(m_aRowsIter == m_aRows.end() || !(*m_aRowsIter)[m_nColPos].is()) + return true; + + return (*m_aRowsIter)[m_nColPos]->getValue().isNull(); +} + +void SAL_CALL ODatabaseMetaDataResultSet::refreshRow( ) +{ +} + + +void SAL_CALL ODatabaseMetaDataResultSet::cancel( ) +{ +} + +void SAL_CALL ODatabaseMetaDataResultSet::clearWarnings( ) +{ +} + +Any SAL_CALL ODatabaseMetaDataResultSet::getWarnings( ) +{ + return Any(); +} + +::cppu::IPropertyArrayHelper* ODatabaseMetaDataResultSet::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +::cppu::IPropertyArrayHelper & ODatabaseMetaDataResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +void ODatabaseMetaDataResultSet::setProceduresMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setProceduresMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setCatalogsMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setCatalogsMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setSchemasMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setSchemasMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setColumnPrivilegesMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setColumnPrivilegesMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setColumnsMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setColumnsMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setTablesMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setTablesMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setProcedureColumnsMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setProcedureColumnsMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setPrimaryKeysMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setPrimaryKeysMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setIndexInfoMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setIndexInfoMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setTablePrivilegesMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setTablePrivilegesMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setCrossReferenceMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setCrossReferenceMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setVersionColumnsMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setVersionColumnsMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setBestRowIdentifierMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setBestRowIdentifierMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setTypeInfoMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setTypeInfoMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setUDTsMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setUDTsMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setTableTypes() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setTableTypes(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setExportedKeysMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setExportedKeysMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setImportedKeysMap() +{ + rtl::Reference<ODatabaseMetaDataResultSetMetaData> pMetaData = new ODatabaseMetaDataResultSetMetaData(); + pMetaData->setImportedKeysMap(); + m_xMetaData = pMetaData; +} + +Reference< css::beans::XPropertySetInfo > SAL_CALL ODatabaseMetaDataResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +ORowSetValueDecorator& ORowSetValueDecorator::operator=(const ORowSetValue& _aValue) +{ + m_aValue = _aValue; + return *this; +} + +const ORowSetValue& ODatabaseMetaDataResultSet::getValue(sal_Int32 columnIndex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + if ( isBeforeFirst() || isAfterLast() ) + ::dbtools::throwFunctionSequenceException( *this ); + + checkIndex(columnIndex ); + m_nColPos = columnIndex; + + if(m_aRowsIter != m_aRows.end() && (*m_aRowsIter)[columnIndex].is()) + return *(*m_aRowsIter)[columnIndex]; + return m_aEmptyValue; +} + +/// return an empty ORowSetValueDecorator +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getEmptyValue() +{ + static ORowSetValueDecoratorRef aEmptyValueRef = new ORowSetValueDecorator(); + return aEmptyValueRef; +} + +/// return an ORowSetValueDecorator with 0 as value +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::get0Value() +{ + static ORowSetValueDecoratorRef a0ValueRef = new ORowSetValueDecorator(sal_Int32(0)); + return a0ValueRef; +} + +/// return an ORowSetValueDecorator with 1 as value +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::get1Value() +{ + static ORowSetValueDecoratorRef a1ValueRef = new ORowSetValueDecorator(sal_Int32(1)); + return a1ValueRef; +} + +/// return an ORowSetValueDecorator with ColumnSearch::BASIC as value +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getBasicValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(ColumnSearch::BASIC); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getSelectValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("SELECT")); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getInsertValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("INSERT")); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getDeleteValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("DELETE")); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getUpdateValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("UPDATE")); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getCreateValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("CREATE")); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getReadValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("READ")); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getAlterValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("ALTER")); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getDropValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("DROP")); + return aValueRef; +} + +ORowSetValueDecoratorRef const & ODatabaseMetaDataResultSet::getQuoteValue() +{ + static ORowSetValueDecoratorRef aValueRef = new ORowSetValueDecorator(OUString("'")); + return aValueRef; +} + +void SAL_CALL ODatabaseMetaDataResultSet::initialize( const Sequence< Any >& _aArguments ) +{ + if ( _aArguments.getLength() != 2 ) + return; + + sal_Int32 nResultSetType = 0; + if ( !(_aArguments[0] >>= nResultSetType)) + return; + + setType(static_cast<MetaDataResultSetType>(nResultSetType)); + Sequence< Sequence<Any> > aRows; + if ( !(_aArguments[1] >>= aRows) ) + return; + + ORows aRowsToSet; + const Sequence<Any>* pRowsIter = aRows.getConstArray(); + const Sequence<Any>* pRowsEnd = pRowsIter + aRows.getLength(); + for (; pRowsIter != pRowsEnd;++pRowsIter) + { + ORow aRowToSet; + const Any* pRowIter = pRowsIter->getConstArray(); + const Any* pRowEnd = pRowIter + pRowsIter->getLength(); + for (; pRowIter != pRowEnd;++pRowIter) + { + ORowSetValueDecoratorRef aValue; + switch( pRowIter->getValueTypeClass() ) + { + case TypeClass_BOOLEAN: + { + bool bValue = false; + *pRowIter >>= bValue; + aValue = new ORowSetValueDecorator(ORowSetValue(bValue)); + } + break; + case TypeClass_BYTE: + { + sal_Int8 nValue(0); + *pRowIter >>= nValue; + aValue = new ORowSetValueDecorator(ORowSetValue(nValue)); + } + break; + case TypeClass_SHORT: + case TypeClass_UNSIGNED_SHORT: + { + sal_Int16 nValue(0); + *pRowIter >>= nValue; + aValue = new ORowSetValueDecorator(ORowSetValue(nValue)); + } + break; + case TypeClass_LONG: + case TypeClass_UNSIGNED_LONG: + { + sal_Int32 nValue(0); + *pRowIter >>= nValue; + aValue = new ORowSetValueDecorator(ORowSetValue(nValue)); + } + break; + case TypeClass_HYPER: + case TypeClass_UNSIGNED_HYPER: + { + sal_Int64 nValue(0); + *pRowIter >>= nValue; + aValue = new ORowSetValueDecorator(ORowSetValue(nValue)); + } + break; + case TypeClass_FLOAT: + { + float nValue(0.0); + *pRowIter >>= nValue; + aValue = new ORowSetValueDecorator(ORowSetValue(nValue)); + } + break; + case TypeClass_DOUBLE: + { + double nValue(0.0); + *pRowIter >>= nValue; + aValue = new ORowSetValueDecorator(ORowSetValue(nValue)); + } + break; + case TypeClass_STRING: + { + OUString sValue; + *pRowIter >>= sValue; + aValue = new ORowSetValueDecorator(ORowSetValue(sValue)); + } + break; + default: + break; + } + aRowToSet.push_back(aValue); + } + aRowsToSet.push_back(aRowToSet); + } // for (; pRowsIter != pRowsEnd;++pRowsIter + setRows(std::move(aRowsToSet)); +} +// XServiceInfo + + + OUString SAL_CALL ODatabaseMetaDataResultSet::getImplementationName( ) + { + return "org.openoffice.comp.helper.DatabaseMetaDataResultSet"; + } + + sal_Bool SAL_CALL ODatabaseMetaDataResultSet::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + Sequence< OUString > SAL_CALL ODatabaseMetaDataResultSet::getSupportedServiceNames( ) + { + return Sequence<OUString>{ "com.sun.star.sdbc.ResultSet" }; + } + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +connectivity_dbtools_ODatabaseMetaDataResultSet_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new ODatabaseMetaDataResultSet()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/FDatabaseMetaDataResultSetMetaData.cxx b/connectivity/source/commontools/FDatabaseMetaDataResultSetMetaData.cxx new file mode 100644 index 0000000000..561953a079 --- /dev/null +++ b/connectivity/source/commontools/FDatabaseMetaDataResultSetMetaData.cxx @@ -0,0 +1,357 @@ +/* -*- 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 <FDatabaseMetaDataResultSetMetaData.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; + +ODatabaseMetaDataResultSetMetaData::~ODatabaseMetaDataResultSetMetaData() +{ +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnDisplaySize( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getColumnDisplaySize(); + + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnType( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getColumnType(); + return 1; +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnCount( ) +{ + return m_mColumns.size(); +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isCaseSensitive( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isCaseSensitive(); + return true; +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getSchemaName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnName( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getColumnName(); + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getTableName( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getTableName(); + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getCatalogName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnTypeName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnLabel( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getColumnLabel(); + return getColumnName(column); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnServiceName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isCurrency( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isCurrency(); + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isAutoIncrement( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isAutoIncrement(); + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isSigned( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isSigned(); + return false; +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getPrecision( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getPrecision(); + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getScale( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getScale(); + + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::isNullable( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isNullable(); + + return sal_Int32(false); +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isSearchable( sal_Int32 column ) +{ + if((m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isSearchable(); + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isReadOnly( sal_Int32 /*column*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isDefinitelyWritable( sal_Int32 /*column*/ ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isWritable( sal_Int32 column ) +{ + return isDefinitelyWritable(column); +} + +void ODatabaseMetaDataResultSetMetaData::setColumnPrivilegesMap() +{ + setColumnMap(); + m_mColumns[5] = OColumn(OUString(),"GRANTOR", ColumnValue::NULLABLE, 3,3,0, DataType::VARCHAR); + m_mColumns[6] = OColumn(OUString(),"GRANTEE", ColumnValue::NULLABLE, 3,3,0, DataType::VARCHAR); + m_mColumns[7] = OColumn(OUString(),"PRIVILEGE", ColumnValue::NULLABLE, 3,3,0, DataType::VARCHAR); + m_mColumns[8] = OColumn(OUString(),"IS_GRANTABLE", ColumnValue::NULLABLE, 3,3,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setTableNameMap() +{ + m_mColumns[1] = OColumn(OUString(),"TABLE_CAT", ColumnValue::NULLABLE, 3,3,0, DataType::VARCHAR); + m_mColumns[2] = OColumn(OUString(),"TABLE_SCHEM", ColumnValue::NULLABLE, 3,3,0, DataType::VARCHAR); + m_mColumns[3] = OColumn(OUString(),"TABLE_NAME", ColumnValue::NO_NULLS, 3,3,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setColumnMap() +{ + setTableNameMap(); + m_mColumns[4] = OColumn(OUString(),"COLUMN_NAME", ColumnValue::NO_NULLS, 3,3,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setColumnsMap() +{ + setColumnMap(); + + m_mColumns[5] = OColumn(OUString(),"DATA_TYPE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[6] = OColumn(OUString(),"TYPE_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[7] = OColumn(OUString(),"COLUMN_SIZE", ColumnValue::NO_NULLS, 3,3,0, DataType::INTEGER); + m_mColumns[8] = OColumn(OUString(),"BUFFER_LENGTH", ColumnValue::NULLABLE, 3,3,0, DataType::INTEGER); + m_mColumns[9] = OColumn(OUString(),"DECIMAL_DIGITS", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[10] = OColumn(OUString(),"NUM_PREC_RADIX", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[11] = OColumn(OUString(),"NULLABLE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[12] = OColumn(OUString(),"REMARKS", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[13] = OColumn(OUString(),"COLUMN_DEF", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[14] = OColumn(OUString(),"SQL_DATA_TYPE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[15] = OColumn(OUString(),"SQL_DATETIME_SUB", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[16] = OColumn(OUString(),"CHAR_OCTET_LENGTH", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[17] = OColumn(OUString(),"ORDINAL_POSITION", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[18] = OColumn(OUString(),"IS_NULLABLE", ColumnValue::NO_NULLS, 1,1,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setTablesMap() +{ + setTableNameMap(); + m_mColumns[4] = OColumn(OUString(),"TABLE_TYPE", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[5] = OColumn(OUString(),"REMARKS", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setProcedureNameMap() +{ + m_mColumns[1] = OColumn(OUString(),"PROCEDURE_CAT", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[2] = OColumn(OUString(),"PROCEDURE_SCHEM", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[3] = OColumn(OUString(),"PROCEDURE_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setProcedureColumnsMap() +{ + setProcedureNameMap(); + m_mColumns[4] = OColumn(OUString(),"COLUMN_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[5] = OColumn(OUString(),"COLUMN_TYPE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[6] = OColumn(OUString(),"DATA_TYPE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[7] = OColumn(OUString(),"TYPE_NAME", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[8] = OColumn(OUString(),"PRECISION", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[9] = OColumn(OUString(),"LENGTH", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[10] = OColumn(OUString(),"SCALE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[11] = OColumn(OUString(),"RADIX", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[12] = OColumn(OUString(),"NULLABLE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[13] = OColumn(OUString(),"REMARKS", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setPrimaryKeysMap() +{ + setColumnMap(); + m_mColumns[5] = OColumn(OUString(),"KEY_SEQ", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[6] = OColumn(OUString(),"PK_NAME", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setIndexInfoMap() +{ + setTableNameMap(); + m_mColumns[4] = OColumn(OUString(),"NON_UNIQUE", ColumnValue::NO_NULLS, 1,1,0, DataType::BIT); + m_mColumns[5] = OColumn(OUString(),"INDEX_QUALIFIER", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[6] = OColumn(OUString(),"INDEX_NAME", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[7] = OColumn(OUString(),"TYPE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[8] = OColumn(OUString(),"ORDINAL_POSITION", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[9] = OColumn(OUString(),"COLUMN_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[10] = OColumn(OUString(),"ASC_OR_DESC", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[11] = OColumn(OUString(),"CARDINALITY", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[12] = OColumn(OUString(),"PAGES", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[13] = OColumn(OUString(),"FILTER_CONDITION", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setTablePrivilegesMap() +{ + setTableNameMap(); + m_mColumns[4] = OColumn(OUString(),"GRANTOR", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[5] = OColumn(OUString(),"GRANTEE", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[6] = OColumn(OUString(),"PRIVILEGE", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[7] = OColumn(OUString(),"IS_GRANTABLE", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setCrossReferenceMap() +{ + m_mColumns[1] = OColumn(OUString(),"PKTABLE_CAT", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[2] = OColumn(OUString(),"PKTABLE_SCHEM", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[3] = OColumn(OUString(),"PKTABLE_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[4] = OColumn(OUString(),"PKCOLUMN_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[5] = OColumn(OUString(),"FKTABLE_CAT", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[6] = OColumn(OUString(),"FKTABLE_SCHEM", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[7] = OColumn(OUString(),"FKTABLE_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[8] = OColumn(OUString(),"FKCOLUMN_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + + m_mColumns[9] = OColumn(OUString(),"KEY_SEQ", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[10] = OColumn(OUString(),"UPDATE_RULE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[11] = OColumn(OUString(),"DELETE_RULE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[12] = OColumn(OUString(),"FK_NAME", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[13] = OColumn(OUString(),"PK_NAME", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[14] = OColumn(OUString(),"DEFERRABILITY", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); +} + +void ODatabaseMetaDataResultSetMetaData::setTypeInfoMap() +{ + m_mColumns[1] = OColumn(OUString(),"TYPE_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[2] = OColumn(OUString(),"DATA_TYPE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[3] = OColumn(OUString(),"PRECISION", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[4] = OColumn(OUString(),"LITERAL_PREFIX", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[5] = OColumn(OUString(),"LITERAL_SUFFIX", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[6] = OColumn(OUString(),"CREATE_PARAMS", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[7] = OColumn(OUString(),"NULLABLE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[8] = OColumn(OUString(),"CASE_SENSITIVE", ColumnValue::NO_NULLS, 1,1,0, DataType::BIT); + m_mColumns[9] = OColumn(OUString(),"SEARCHABLE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[10] = OColumn(OUString(),"UNSIGNED_ATTRIBUTE", ColumnValue::NO_NULLS, 1,1,0, DataType::BIT); + m_mColumns[11] = OColumn(OUString(),"FIXED_PREC_SCALE", ColumnValue::NO_NULLS, 1,1,0, DataType::BIT); + m_mColumns[12] = OColumn(OUString(),"AUTO_INCREMENT", ColumnValue::NO_NULLS, 1,1,0, DataType::BIT); + m_mColumns[13] = OColumn(OUString(),"LOCAL_TYPE_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[14] = OColumn(OUString(),"MINIMUM_SCALE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[15] = OColumn(OUString(),"MAXIMUM_SCALE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[16] = OColumn(OUString(),"SQL_DATA_TYPE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[17] = OColumn(OUString(),"SQL_DATETIME_SUB", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); + m_mColumns[18] = OColumn(OUString(),"NUM_PREC_RADIX", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); +} + +void ODatabaseMetaDataResultSetMetaData::setProceduresMap() +{ + setProcedureNameMap(); + m_mColumns[4] = OColumn(OUString(),"RESERVED1", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[5] = OColumn(OUString(),"RESERVED2", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[6] = OColumn(OUString(),"RESERVED3", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[7] = OColumn(OUString(),"REMARKS", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[8] = OColumn(OUString(),"PROCEDURE_TYPE", ColumnValue::NO_NULLS, 1,1,0, DataType::INTEGER); +} + +void ODatabaseMetaDataResultSetMetaData::setTableTypes() +{ + m_mColumns[1] = OColumn(OUString(),"TABLE_TYPE", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setCatalogsMap() +{ + m_mColumns[1] = OColumn(OUString(),"TABLE_CAT", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setSchemasMap() +{ + m_mColumns[1] = OColumn(OUString(),"TABLE_SCHEM", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setVersionColumnsMap() +{ + m_mColumns[1] = OColumn(OUString(),"SCOPE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[2] = OColumn(OUString(),"COLUMN_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[3] = OColumn(OUString(),"DATA_TYPE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[4] = OColumn(OUString(),"TYPE_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[5] = OColumn(OUString(),"COLUMN_SIZE", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[6] = OColumn(OUString(),"BUFFER_LENGTH", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); + m_mColumns[7] = OColumn(OUString(),"DECIMAL_DIGITS", ColumnValue::NULLABLE, 0,0,0, DataType::INTEGER); + m_mColumns[8] = OColumn(OUString(),"PSEUDO_COLUMN", ColumnValue::NO_NULLS, 0,0,0, DataType::INTEGER); +} + +void ODatabaseMetaDataResultSetMetaData::setUDTsMap() +{ + m_mColumns[1] = OColumn(OUString(),"TYPE_CAT", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[2] = OColumn(OUString(),"TYPE_SCHEM", ColumnValue::NULLABLE, 0,0,0, DataType::VARCHAR); + m_mColumns[3] = OColumn(OUString(),"TYPE_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[4] = OColumn(OUString(),"CLASS_NAME", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[5] = OColumn(OUString(),"DATA_TYPE", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); + m_mColumns[6] = OColumn(OUString(),"REMARKS", ColumnValue::NO_NULLS, 0,0,0, DataType::VARCHAR); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/FValue.cxx b/connectivity/source/commontools/FValue.cxx new file mode 100644 index 0000000000..4ac0235ac4 --- /dev/null +++ b/connectivity/source/commontools/FValue.cxx @@ -0,0 +1,2473 @@ +/* -*- 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 <connectivity/FValue.hxx> +#include <connectivity/dbconversion.hxx> +#include <comphelper/extract.hxx> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/sdbc/XClob.hpp> +#include <com/sun/star/sdbc/XBlob.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +using namespace ::dbtools; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; + +namespace connectivity +{ + +namespace { + bool isStorageCompatible(sal_Int32 _eType1, sal_Int32 _eType2) + { + bool bIsCompatible = true; + + if (_eType1 != _eType2) + { + SAL_INFO( "connectivity.commontools", "ORowSetValue::isStorageCompatible _eType1 != _eType2" ); + switch (_eType1) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + bIsCompatible = (DataType::CHAR == _eType2) + || (DataType::VARCHAR == _eType2) + || (DataType::DECIMAL == _eType2) + || (DataType::NUMERIC == _eType2) + || (DataType::LONGVARCHAR == _eType2); + break; + + case DataType::DOUBLE: + case DataType::REAL: + bIsCompatible = (DataType::DOUBLE == _eType2) + || (DataType::REAL == _eType2); + break; + + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + bIsCompatible = (DataType::BINARY == _eType2) + || (DataType::VARBINARY == _eType2) + || (DataType::LONGVARBINARY == _eType2); + break; + + case DataType::INTEGER: + bIsCompatible = (DataType::SMALLINT == _eType2) + || (DataType::TINYINT == _eType2) + || (DataType::BIT == _eType2) + || (DataType::BOOLEAN == _eType2); + break; + case DataType::SMALLINT: + bIsCompatible = (DataType::TINYINT == _eType2) + || (DataType::BIT == _eType2) + || (DataType::BOOLEAN == _eType2); + break; + case DataType::TINYINT: + bIsCompatible = (DataType::BIT == _eType2) + || (DataType::BOOLEAN == _eType2); + break; + + case DataType::BLOB: + case DataType::CLOB: + case DataType::OBJECT: + bIsCompatible = (DataType::BLOB == _eType2) + || (DataType::CLOB == _eType2) + || (DataType::OBJECT == _eType2); + break; + + default: + bIsCompatible = false; + } + } + return bIsCompatible; + } + + bool isStorageComparable(sal_Int32 _eType1, sal_Int32 _eType2) + { + bool bIsComparable = true; + + if (_eType1 != _eType2) + { + SAL_INFO( "connectivity.commontools", "ORowSetValue::isStorageCompatible _eType1 != _eType2" ); + switch (_eType1) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + bIsComparable = (DataType::CHAR == _eType2) + || (DataType::VARCHAR == _eType2) + || (DataType::LONGVARCHAR == _eType2); + break; + + case DataType::DECIMAL: + case DataType::NUMERIC: + bIsComparable = (DataType::DECIMAL == _eType2) + || (DataType::NUMERIC == _eType2); + break; + + case DataType::DOUBLE: + case DataType::REAL: + bIsComparable = (DataType::DOUBLE == _eType2) + || (DataType::REAL == _eType2); + break; + + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + bIsComparable = (DataType::BINARY == _eType2) + || (DataType::VARBINARY == _eType2) + || (DataType::LONGVARBINARY == _eType2); + break; + + case DataType::INTEGER: + bIsComparable = (DataType::SMALLINT == _eType2) + || (DataType::TINYINT == _eType2) + || (DataType::BIT == _eType2) + || (DataType::BOOLEAN == _eType2); + break; + case DataType::SMALLINT: + bIsComparable = (DataType::TINYINT == _eType2) + || (DataType::BIT == _eType2) + || (DataType::BOOLEAN == _eType2); + break; + case DataType::TINYINT: + bIsComparable = (DataType::BIT == _eType2) + || (DataType::BOOLEAN == _eType2); + break; + + case DataType::BLOB: + case DataType::CLOB: + case DataType::OBJECT: + bIsComparable = (DataType::BLOB == _eType2) + || (DataType::CLOB == _eType2) + || (DataType::OBJECT == _eType2); + break; + + default: + bIsComparable = false; + } + } + return bIsComparable; + } +} + +void ORowSetValue::setTypeKind(sal_Int32 _eType) +{ + if ( !m_bNull && !isStorageCompatible(_eType, m_eTypeKind) ) + { + switch(_eType) + { + case DataType::VARCHAR: + case DataType::CHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + (*this) = getString(); + break; + case DataType::BIGINT: + { + sal_Int64 nVal(getLong()); + sal_uInt64 nuVal(getULong()); + if (nVal == 0 && nuVal != 0) + (*this) = nuVal; + else + (*this) = nVal; + break; + } + + case DataType::FLOAT: + (*this) = getFloat(); + break; + case DataType::DOUBLE: + case DataType::REAL: + (*this) = getDouble(); + break; + case DataType::TINYINT: + (*this) = getInt8(); + break; + case DataType::SMALLINT: + (*this) = getInt16(); + break; + case DataType::INTEGER: + { + sal_Int32 nVal(getInt32()); + sal_uInt32 nuVal(getUInt32()); + if (nVal == 0 && nuVal != 0) + (*this) = nuVal; + else + (*this) = nVal; + break; + } + case DataType::BIT: + case DataType::BOOLEAN: + (*this) = getBool(); + break; + case DataType::DATE: + (*this) = getDate(); + break; + case DataType::TIME: + (*this) = getTime(); + break; + case DataType::TIMESTAMP: + (*this) = getDateTime(); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + (*this) = getSequence(); + break; + case DataType::BLOB: + case DataType::CLOB: + case DataType::OBJECT: + case DataType::OTHER: + (*this) = makeAny(); + break; + default: + (*this) = makeAny(); + SAL_WARN( "connectivity.commontools","ORowSetValue::setTypeKind(): UNSUPPORTED TYPE!"); + } + } + + m_eTypeKind = _eType; +} + + +void ORowSetValue::free() noexcept +{ + if(m_bNull) + return; + + switch(m_eTypeKind) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + OSL_ENSURE(m_aValue.m_pString,"String pointer is null!"); + rtl_uString_release(m_aValue.m_pString); + m_aValue.m_pString = nullptr; + break; + case DataType::DATE: + delete static_cast<css::util::Date*>(m_aValue.m_pValue); + m_aValue.m_pValue = nullptr; + break; + case DataType::TIME: + delete static_cast<css::util::Time*>(m_aValue.m_pValue); + m_aValue.m_pValue = nullptr; + break; + case DataType::TIMESTAMP: + delete static_cast<css::util::DateTime*>(m_aValue.m_pValue); + m_aValue.m_pValue = nullptr; + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + delete static_cast<Sequence<sal_Int8>*>(m_aValue.m_pValue); + m_aValue.m_pValue = nullptr; + break; + case DataType::BLOB: + case DataType::CLOB: + case DataType::OBJECT: + delete static_cast<Any*>(m_aValue.m_pValue); + m_aValue.m_pValue = nullptr; + break; + case DataType::BIT: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::BIGINT: + case DataType::BOOLEAN: + case DataType::FLOAT: + case DataType::DOUBLE: + case DataType::REAL: + break; + default: + if ( m_aValue.m_pValue ) + { + delete static_cast<Any*>(m_aValue.m_pValue); + m_aValue.m_pValue = nullptr; + } + break; + + } + m_bNull = true; +} + +ORowSetValue& ORowSetValue::operator=(const ORowSetValue& _rRH) +{ + if(&_rRH == this) + return *this; + + if ( m_eTypeKind != _rRH.m_eTypeKind || (_rRH.m_bNull && !m_bNull) || m_bSigned != _rRH.m_bSigned) + free(); + + m_bBound = _rRH.m_bBound; + m_eTypeKind = _rRH.m_eTypeKind; + m_bSigned = _rRH.m_bSigned; + + if(m_bNull && !_rRH.m_bNull) + { + switch(_rRH.m_eTypeKind) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + rtl_uString_acquire(_rRH.m_aValue.m_pString); + m_aValue.m_pString = _rRH.m_aValue.m_pString; + break; + case DataType::DATE: + m_aValue.m_pValue = new Date(*static_cast<Date*>(_rRH.m_aValue.m_pValue)); + break; + case DataType::TIME: + m_aValue.m_pValue = new Time(*static_cast<Time*>(_rRH.m_aValue.m_pValue)); + break; + case DataType::TIMESTAMP: + m_aValue.m_pValue = new DateTime(*static_cast<DateTime*>(_rRH.m_aValue.m_pValue)); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + m_aValue.m_pValue = new Sequence<sal_Int8>(*static_cast<Sequence<sal_Int8>*>(_rRH.m_aValue.m_pValue)); + break; + case DataType::BIT: + case DataType::BOOLEAN: + m_aValue.m_bBool = _rRH.m_aValue.m_bBool; + break; + case DataType::TINYINT: + if ( _rRH.m_bSigned ) + m_aValue.m_nInt8 = _rRH.m_aValue.m_nInt8; + else + m_aValue.m_uInt8 = _rRH.m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( _rRH.m_bSigned ) + m_aValue.m_nInt16 = _rRH.m_aValue.m_nInt16; + else + m_aValue.m_uInt16 = _rRH.m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( _rRH.m_bSigned ) + m_aValue.m_nInt32 = _rRH.m_aValue.m_nInt32; + else + m_aValue.m_uInt32 = _rRH.m_aValue.m_uInt32; + break; + case DataType::BIGINT: + if ( _rRH.m_bSigned ) + m_aValue.m_nInt64 = _rRH.m_aValue.m_nInt64; + else + m_aValue.m_uInt64 = _rRH.m_aValue.m_uInt64; + break; + case DataType::FLOAT: + m_aValue.m_nFloat = _rRH.m_aValue.m_nFloat; + break; + case DataType::DOUBLE: + case DataType::REAL: + m_aValue.m_nDouble = _rRH.m_aValue.m_nDouble; + break; + default: + m_aValue.m_pValue = new Any(*static_cast<Any*>(_rRH.m_aValue.m_pValue)); + } + } + else if(!_rRH.m_bNull) + { + switch(_rRH.m_eTypeKind) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + (*this) = OUString(_rRH.m_aValue.m_pString); + break; + case DataType::DATE: + (*this) = *static_cast<Date*>(_rRH.m_aValue.m_pValue); + break; + case DataType::TIME: + (*this) = *static_cast<Time*>(_rRH.m_aValue.m_pValue); + break; + case DataType::TIMESTAMP: + (*this) = *static_cast<DateTime*>(_rRH.m_aValue.m_pValue); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + (*this) = *static_cast<Sequence<sal_Int8>*>(_rRH.m_aValue.m_pValue); + break; + case DataType::BIT: + case DataType::BOOLEAN: + m_aValue.m_bBool = _rRH.m_aValue.m_bBool; + break; + case DataType::TINYINT: + if ( _rRH.m_bSigned ) + m_aValue.m_nInt8 = _rRH.m_aValue.m_nInt8; + else + m_aValue.m_uInt8 = _rRH.m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( _rRH.m_bSigned ) + m_aValue.m_nInt16 = _rRH.m_aValue.m_nInt16; + else + m_aValue.m_uInt16 = _rRH.m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( _rRH.m_bSigned ) + m_aValue.m_nInt32 = _rRH.m_aValue.m_nInt32; + else + m_aValue.m_uInt32 = _rRH.m_aValue.m_uInt32; + break; + case DataType::BIGINT: + if ( _rRH.m_bSigned ) + m_aValue.m_nInt64 = _rRH.m_aValue.m_nInt64; + else + m_aValue.m_uInt64 = _rRH.m_aValue.m_uInt64; + break; + case DataType::FLOAT: + m_aValue.m_nFloat = _rRH.m_aValue.m_nFloat; + break; + case DataType::DOUBLE: + case DataType::REAL: + m_aValue.m_nDouble = _rRH.m_aValue.m_nDouble; + break; + default: + *static_cast<Any*>(m_aValue.m_pValue) = *static_cast<Any*>(_rRH.m_aValue.m_pValue); + } + } + + m_bNull = _rRH.m_bNull; + // OJ: BUGID: 96277 + m_eTypeKind = _rRH.m_eTypeKind; + + return *this; +} + +ORowSetValue& ORowSetValue::operator=(ORowSetValue&& _rRH) noexcept +{ + if ( m_eTypeKind != _rRH.m_eTypeKind || !m_bNull) + free(); + if(!_rRH.m_bNull) + { + m_aValue = _rRH.m_aValue; + memset(&_rRH.m_aValue, 0, sizeof(_rRH.m_aValue)); + } + m_bBound = _rRH.m_bBound; + m_eTypeKind = _rRH.m_eTypeKind; + m_bSigned = _rRH.m_bSigned; + m_bNull = _rRH.m_bNull; + _rRH.m_bNull = true; + return *this; +} + + +ORowSetValue& ORowSetValue::operator=(const Date& _rRH) +{ + if(m_eTypeKind != DataType::DATE) + free(); + + if(m_bNull) + { + m_aValue.m_pValue = new Date(_rRH); + m_eTypeKind = DataType::DATE; + m_bNull = false; + } + else + *static_cast<Date*>(m_aValue.m_pValue) = _rRH; + + return *this; +} + +ORowSetValue& ORowSetValue::operator=(const css::util::Time& _rRH) +{ + if(m_eTypeKind != DataType::TIME) + free(); + + if(m_bNull) + { + m_aValue.m_pValue = new Time(_rRH); + m_eTypeKind = DataType::TIME; + m_bNull = false; + } + else + *static_cast<Time*>(m_aValue.m_pValue) = _rRH; + + return *this; +} + +ORowSetValue& ORowSetValue::operator=(const DateTime& _rRH) +{ + if(m_eTypeKind != DataType::TIMESTAMP) + free(); + if(m_bNull) + { + m_aValue.m_pValue = new DateTime(_rRH); + m_eTypeKind = DataType::TIMESTAMP; + m_bNull = false; + } + else + *static_cast<DateTime*>(m_aValue.m_pValue) = _rRH; + + return *this; +} + + +ORowSetValue& ORowSetValue::operator=(const OUString& _rRH) +{ + if(m_eTypeKind != DataType::VARCHAR || m_aValue.m_pString != _rRH.pData) + { + free(); + m_bNull = false; + + m_aValue.m_pString = _rRH.pData; + rtl_uString_acquire(m_aValue.m_pString); + m_eTypeKind = DataType::VARCHAR; + } + + return *this; +} + + +ORowSetValue& ORowSetValue::operator=(double _rRH) +{ + if(m_eTypeKind != DataType::DOUBLE) + free(); + + m_aValue.m_nDouble = _rRH; + m_eTypeKind = DataType::DOUBLE; + m_bNull = false; + + return *this; +} + +ORowSetValue& ORowSetValue::operator=(float _rRH) +{ + if(m_eTypeKind != DataType::FLOAT) + free(); + + m_aValue.m_nFloat = _rRH; + m_eTypeKind = DataType::FLOAT; + m_bNull = false; + + return *this; +} + + +ORowSetValue& ORowSetValue::operator=(sal_Int8 _rRH) +{ + if(m_eTypeKind != DataType::TINYINT ) + free(); + + m_aValue.m_nInt8 = _rRH; + m_eTypeKind = DataType::TINYINT; + m_bNull = false; + m_bSigned = true; + return *this; +} + +ORowSetValue& ORowSetValue::operator=(sal_Int16 _rRH) +{ + if(m_eTypeKind != DataType::SMALLINT ) + free(); + + m_aValue.m_nInt16 = _rRH; + m_eTypeKind = DataType::SMALLINT; + m_bNull = false; + m_bSigned = true; + + return *this; +} + + +ORowSetValue& ORowSetValue::operator=(sal_uInt16 _rRH) +{ + if(m_eTypeKind != DataType::SMALLINT ) + free(); + + m_aValue.m_uInt16 = _rRH; + m_eTypeKind = DataType::SMALLINT; + m_bNull = false; + m_bSigned = false; + + return *this; +} + + +ORowSetValue& ORowSetValue::operator=(sal_Int32 _rRH) +{ + if(m_eTypeKind != DataType::INTEGER ) + free(); + + m_aValue.m_nInt32 = _rRH; + + m_eTypeKind = DataType::INTEGER; + m_bNull = false; + m_bSigned = true; + + return *this; +} + + +ORowSetValue& ORowSetValue::operator=(sal_uInt32 _rRH) +{ + if(m_eTypeKind != DataType::INTEGER ) + free(); + + m_aValue.m_uInt32 = _rRH; + + m_eTypeKind = DataType::INTEGER; + m_bNull = false; + m_bSigned = false; + + return *this; +} + + +ORowSetValue& ORowSetValue::operator=(const bool _rRH) +{ + if(m_eTypeKind != DataType::BIT && DataType::BOOLEAN != m_eTypeKind ) + free(); + + m_aValue.m_bBool = _rRH; + m_eTypeKind = DataType::BOOLEAN; + m_bNull = false; + + return *this; +} + +ORowSetValue& ORowSetValue::operator=(sal_Int64 _rRH) +{ + if ( DataType::BIGINT != m_eTypeKind) + free(); + + m_aValue.m_nInt64 = _rRH; + m_eTypeKind = DataType::BIGINT; + m_bNull = false; + m_bSigned = true; + + return *this; +} + +ORowSetValue& ORowSetValue::operator=(sal_uInt64 _rRH) +{ + if ( DataType::BIGINT != m_eTypeKind) + free(); + + m_aValue.m_uInt64 = _rRH; + m_eTypeKind = DataType::BIGINT; + m_bNull = false; + m_bSigned = false; + + return *this; +} + +ORowSetValue& ORowSetValue::operator=(const Sequence<sal_Int8>& _rRH) +{ + if (!isStorageCompatible(DataType::LONGVARBINARY,m_eTypeKind)) + free(); + + if (m_bNull) + { + m_aValue.m_pValue = new Sequence<sal_Int8>(_rRH); + } + else + *static_cast< Sequence< sal_Int8 >* >(m_aValue.m_pValue) = _rRH; + + m_eTypeKind = DataType::LONGVARBINARY; + m_bNull = false; + + return *this; +} + +ORowSetValue& ORowSetValue::operator=(const Any& _rAny) +{ + if (!isStorageCompatible(DataType::OBJECT,m_eTypeKind)) + free(); + + if ( m_bNull ) + { + m_aValue.m_pValue = new Any(_rAny); + } + else + *static_cast<Any*>(m_aValue.m_pValue) = _rAny; + + m_eTypeKind = DataType::OBJECT; + m_bNull = false; + + return *this; +} + + +bool ORowSetValue::operator==(const ORowSetValue& _rRH) const +{ + if ( m_bNull != _rRH.isNull() ) + return false; + + if(m_bNull && _rRH.isNull()) + return true; + + if ( !isStorageComparable(m_eTypeKind, _rRH.m_eTypeKind )) + { + switch(m_eTypeKind) + { + case DataType::FLOAT: + case DataType::DOUBLE: + case DataType::REAL: + return getDouble() == _rRH.getDouble(); + default: + switch(_rRH.m_eTypeKind) + { + case DataType::FLOAT: + case DataType::DOUBLE: + case DataType::REAL: + return getDouble() == _rRH.getDouble(); + default: + break; + } + break; + } + return false; + } + + bool bRet = false; + OSL_ENSURE(!m_bNull,"Should not be null!"); + switch(m_eTypeKind) + { + case DataType::VARCHAR: + case DataType::CHAR: + case DataType::LONGVARCHAR: + { + OUString aVal1(m_aValue.m_pString); + OUString aVal2(_rRH.m_aValue.m_pString); + return aVal1 == aVal2; + } + default: + if ( m_bSigned != _rRH.m_bSigned ) + return false; + break; + } + + switch(m_eTypeKind) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + { + OUString aVal1(m_aValue.m_pString); + OUString aVal2(_rRH.m_aValue.m_pString); + bRet = aVal1 == aVal2; + } + break; + case DataType::FLOAT: + bRet = m_aValue.m_nFloat == _rRH.m_aValue.m_nFloat; + break; + case DataType::DOUBLE: + case DataType::REAL: + bRet = m_aValue.m_nDouble == _rRH.m_aValue.m_nDouble; + break; + case DataType::TINYINT: + bRet = m_bSigned ? ( m_aValue.m_nInt8 == _rRH.m_aValue.m_nInt8 ) : (m_aValue.m_uInt8 == _rRH.m_aValue.m_uInt8); + break; + case DataType::SMALLINT: + bRet = m_bSigned ? ( m_aValue.m_nInt16 == _rRH.m_aValue.m_nInt16 ) : (m_aValue.m_uInt16 == _rRH.m_aValue.m_uInt16); + break; + case DataType::INTEGER: + bRet = m_bSigned ? ( m_aValue.m_nInt32 == _rRH.m_aValue.m_nInt32 ) : (m_aValue.m_uInt32 == _rRH.m_aValue.m_uInt32); + break; + case DataType::BIGINT: + bRet = m_bSigned ? ( m_aValue.m_nInt64 == _rRH.m_aValue.m_nInt64 ) : (m_aValue.m_uInt64 == _rRH.m_aValue.m_uInt64); + break; + case DataType::BIT: + case DataType::BOOLEAN: + bRet = m_aValue.m_bBool == _rRH.m_aValue.m_bBool; + break; + case DataType::DATE: + bRet = *static_cast<Date*>(m_aValue.m_pValue) == *static_cast<Date*>(_rRH.m_aValue.m_pValue); + break; + case DataType::TIME: + bRet = *static_cast<Time*>(m_aValue.m_pValue) == *static_cast<Time*>(_rRH.m_aValue.m_pValue); + break; + case DataType::TIMESTAMP: + bRet = *static_cast<DateTime*>(m_aValue.m_pValue) == *static_cast<DateTime*>(_rRH.m_aValue.m_pValue); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + bRet = false; + break; + case DataType::BLOB: + case DataType::CLOB: + case DataType::OBJECT: + case DataType::OTHER: + bRet = false; + break; + default: + bRet = false; + SAL_WARN( "connectivity.commontools","ORowSetValue::operator==(): UNSUPPORTED TYPE!"); + break; + } + return bRet; +} + +Any ORowSetValue::makeAny() const +{ + Any rValue; + if(isBound() && !isNull()) + { + switch(getTypeKind()) + { + case DataType::SQLNULL: + assert(rValue == Any()); + break; + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + OSL_ENSURE(m_aValue.m_pString,"Value is null!"); + rValue <<= OUString(m_aValue.m_pString); + break; + case DataType::FLOAT: + rValue <<= m_aValue.m_nFloat; + break; + case DataType::DOUBLE: + case DataType::REAL: + rValue <<= m_aValue.m_nDouble; + break; + case DataType::DATE: + OSL_ENSURE(m_aValue.m_pValue,"Value is null!"); + rValue <<= *static_cast<Date*>(m_aValue.m_pValue); + break; + case DataType::TIME: + OSL_ENSURE(m_aValue.m_pValue,"Value is null!"); + rValue <<= *static_cast<Time*>(m_aValue.m_pValue); + break; + case DataType::TIMESTAMP: + OSL_ENSURE(m_aValue.m_pValue,"Value is null!"); + rValue <<= *static_cast<DateTime*>(m_aValue.m_pValue); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + OSL_ENSURE(m_aValue.m_pValue,"Value is null!"); + rValue <<= *static_cast<Sequence<sal_Int8>*>(m_aValue.m_pValue); + break; + case DataType::BLOB: + case DataType::CLOB: + case DataType::OBJECT: + case DataType::OTHER: + rValue = getAny(); + break; + case DataType::BIT: + case DataType::BOOLEAN: + rValue <<= m_aValue.m_bBool; + break; + case DataType::TINYINT: + if ( m_bSigned ) + // TypeClass_BYTE + rValue <<= m_aValue.m_nInt8; + else + // There is no TypeClass_UNSIGNED_BYTE, + // so silently promote it to a 16-bit integer, + // that is TypeClass_UNSIGNED_SHORT + rValue <<= static_cast<sal_uInt16>(m_aValue.m_uInt8); + break; + case DataType::SMALLINT: + if ( m_bSigned ) + // TypeClass_SHORT + rValue <<= m_aValue.m_nInt16; + else + // TypeClass_UNSIGNED_SHORT + rValue <<= m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( m_bSigned ) + // TypeClass_LONG + rValue <<= m_aValue.m_nInt32; + else + // TypeClass_UNSIGNED_LONG + rValue <<= m_aValue.m_uInt32; + break; + case DataType::BIGINT: + if ( m_bSigned ) + // TypeClass_HYPER + rValue <<= m_aValue.m_nInt64; + else + // TypeClass_UNSIGNED_HYPER + rValue <<= m_aValue.m_uInt64; + break; + default: + SAL_WARN( "connectivity.commontools","ORowSetValue::makeAny(): UNSUPPORTED TYPE!"); + rValue = getAny(); + break; + } + } + return rValue; +} + +OUString ORowSetValue::getString( ) const +{ + OUString aRet; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + aRet = m_aValue.m_pString; + break; + case DataType::FLOAT: + aRet = OUString::number(getFloat()); + break; + case DataType::DOUBLE: + case DataType::REAL: + aRet = OUString::number(getDouble()); + break; + case DataType::DATE: + aRet = DBTypeConversion::toDateString(getDate()); + break; + case DataType::TIME: + aRet = DBTypeConversion::toTimeString(getTime()); + break; + case DataType::TIMESTAMP: + aRet = DBTypeConversion::toDateTimeString(getDateTime()); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + { + OUStringBuffer sVal("0x"); + Sequence<sal_Int8> aSeq(getSequence()); + const sal_Int8* pBegin = aSeq.getConstArray(); + const sal_Int8* pEnd = pBegin + aSeq.getLength(); + for(;pBegin != pEnd;++pBegin) + sVal.append(static_cast<sal_Int32>(*pBegin),16); + aRet = sVal.makeStringAndClear(); + } + break; + case DataType::BIT: + aRet = OUString::number(int(getBool())); + break; + case DataType::BOOLEAN: + aRet = OUString::boolean(getBool()); + break; + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + if ( m_bSigned ) + aRet = OUString::number(getInt32()); + else + aRet = OUString::number(getUInt32()); + break; + case DataType::BIGINT: + if ( m_bSigned ) + aRet = OUString::number(getLong()); + else + aRet = OUString::number(getULong()); + break; + case DataType::CLOB: + { + Any aValue( getAny() ); + Reference< XClob > xClob; + if ( (aValue >>= xClob) && xClob.is() ) + { + aRet = xClob->getSubString(1,static_cast<sal_Int32>(xClob->length()) ); + } + } + break; + default: + { + Any aValue = makeAny(); + aValue >>= aRet; + break; + } + } + } + return aRet; +} + +bool ORowSetValue::getBool() const +{ + bool bRet = false; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + { + const OUString sValue(m_aValue.m_pString); + if ( sValue.equalsIgnoreAsciiCase("true") || (sValue == "1") ) + { + bRet = true; + break; + } + else if ( sValue.equalsIgnoreAsciiCase("false") || (sValue == "0") ) + { + bRet = false; + break; + } + } + [[fallthrough]]; + case DataType::DECIMAL: + case DataType::NUMERIC: + + bRet = OUString::unacquired(&m_aValue.m_pString).toInt32() != 0; + break; + case DataType::FLOAT: + bRet = m_aValue.m_nFloat != 0.0; + break; + case DataType::DOUBLE: + case DataType::REAL: + bRet = m_aValue.m_nDouble != 0.0; + break; + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + OSL_FAIL("getBool() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + bRet = m_aValue.m_bBool; + break; + case DataType::TINYINT: + bRet = m_bSigned ? (m_aValue.m_nInt8 != 0) : (m_aValue.m_uInt8 != 0); + break; + case DataType::SMALLINT: + bRet = m_bSigned ? (m_aValue.m_nInt16 != 0) : (m_aValue.m_uInt16 != 0); + break; + case DataType::INTEGER: + bRet = m_bSigned ? (m_aValue.m_nInt32 != 0) : (m_aValue.m_uInt32 != 0); + break; + case DataType::BIGINT: + bRet = m_bSigned ? (m_aValue.m_nInt64 != 0) : (m_aValue.m_uInt64 != 0); + break; + default: + { + Any aValue = makeAny(); + aValue >>= bRet; + break; + } + } + } + return bRet; +} + + +sal_Int8 ORowSetValue::getInt8() const +{ + sal_Int8 nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = sal_Int8(OUString::unacquired(&m_aValue.m_pString).toInt32()); + break; + case DataType::FLOAT: + nRet = sal_Int8(m_aValue.m_nFloat); + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = sal_Int8(m_aValue.m_nDouble); + break; + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getInt8() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = sal_Int8(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = static_cast<sal_Int8>(m_aValue.m_uInt8); + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = static_cast<sal_Int8>(m_aValue.m_nInt16); + else + nRet = static_cast<sal_Int8>(m_aValue.m_uInt16); + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = static_cast<sal_Int8>(m_aValue.m_nInt32); + else + nRet = static_cast<sal_Int8>(m_aValue.m_uInt32); + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = static_cast<sal_Int8>(m_aValue.m_nInt64); + else + nRet = static_cast<sal_Int8>(m_aValue.m_uInt64); + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + + +sal_uInt8 ORowSetValue::getUInt8() const +{ + sal_uInt8 nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = sal_uInt8(OUString::unacquired(&m_aValue.m_pString).toInt32()); + break; + case DataType::FLOAT: + nRet = sal_uInt8(m_aValue.m_nFloat); + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = sal_uInt8(m_aValue.m_nDouble); + break; + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getuInt8() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = int(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = static_cast<sal_uInt8>(m_aValue.m_nInt16); + else + nRet = static_cast<sal_uInt8>(m_aValue.m_uInt16); + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = static_cast<sal_uInt8>(m_aValue.m_nInt32); + else + nRet = static_cast<sal_uInt8>(m_aValue.m_uInt32); + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = static_cast<sal_uInt8>(m_aValue.m_nInt64); + else + nRet = static_cast<sal_uInt8>(m_aValue.m_uInt64); + break; + default: + { + Any aValue = makeAny(); + // Cf. "There is no TypeClass_UNSIGNED_BYTE" in makeAny: + sal_uInt16 n; + if (aValue >>= n) { + nRet = static_cast<sal_uInt8>(n); + } + break; + } + } + } + return nRet; +} + + +sal_Int16 ORowSetValue::getInt16() const +{ + sal_Int16 nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = sal_Int16(OUString::unacquired(&m_aValue.m_pString).toInt32()); + break; + case DataType::FLOAT: + nRet = sal_Int16(m_aValue.m_nFloat); + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = sal_Int16(m_aValue.m_nDouble); + break; + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getInt16() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = sal_Int16(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt16; + else + nRet = static_cast<sal_Int16>(m_aValue.m_uInt16); + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = static_cast<sal_Int16>(m_aValue.m_nInt32); + else + nRet = static_cast<sal_Int16>(m_aValue.m_uInt32); + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = static_cast<sal_Int16>(m_aValue.m_nInt64); + else + nRet = static_cast<sal_Int16>(m_aValue.m_uInt64); + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + + +sal_uInt16 ORowSetValue::getUInt16() const +{ + sal_uInt16 nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = sal_uInt16(OUString::unacquired(&m_aValue.m_pString).toInt32()); + break; + case DataType::FLOAT: + nRet = sal_uInt16(m_aValue.m_nFloat); + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = sal_uInt16(m_aValue.m_nDouble); + break; + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getuInt16() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = sal_uInt16(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt16; + else + nRet = m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = static_cast<sal_uInt16>(m_aValue.m_nInt32); + else + nRet = static_cast<sal_uInt16>(m_aValue.m_uInt32); + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = static_cast<sal_uInt16>(m_aValue.m_nInt64); + else + nRet = static_cast<sal_uInt16>(m_aValue.m_uInt64); + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + + +sal_Int32 ORowSetValue::getInt32() const +{ + sal_Int32 nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = OUString::unacquired(&m_aValue.m_pString).toInt32(); + break; + case DataType::FLOAT: + nRet = sal_Int32(m_aValue.m_nFloat); + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = sal_Int32(m_aValue.m_nDouble); + break; + case DataType::DATE: + nRet = dbtools::DBTypeConversion::toDays(*static_cast<css::util::Date*>(m_aValue.m_pValue)); + break; + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getInt32() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = sal_Int32(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt16; + else + nRet = m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = m_aValue.m_nInt32; + else + nRet = static_cast<sal_Int32>(m_aValue.m_uInt32); + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = static_cast<sal_Int32>(m_aValue.m_nInt64); + else + nRet = static_cast<sal_Int32>(m_aValue.m_uInt64); + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + + +sal_uInt32 ORowSetValue::getUInt32() const +{ + sal_uInt32 nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = OUString::unacquired(&m_aValue.m_pString).toUInt32(); + break; + case DataType::FLOAT: + nRet = sal_uInt32(m_aValue.m_nFloat); + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = sal_uInt32(m_aValue.m_nDouble); + break; + case DataType::DATE: + nRet = dbtools::DBTypeConversion::toDays(*static_cast<css::util::Date*>(m_aValue.m_pValue)); + break; + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getuInt32() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = sal_uInt32(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt16; + else + nRet = m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = m_aValue.m_nInt32; + else + nRet = m_aValue.m_uInt32; + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = static_cast<sal_uInt32>(m_aValue.m_nInt64); + else + nRet = static_cast<sal_uInt32>(m_aValue.m_uInt64); + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + + +sal_Int64 ORowSetValue::getLong() const +{ + sal_Int64 nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = OUString::unacquired(&m_aValue.m_pString).toInt64(); + break; + case DataType::FLOAT: + nRet = sal_Int64(m_aValue.m_nFloat); + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = sal_Int64(m_aValue.m_nDouble); + break; + case DataType::DATE: + nRet = dbtools::DBTypeConversion::toDays(*static_cast<css::util::Date*>(m_aValue.m_pValue)); + break; + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getLong() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = sal_Int64(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt16; + else + nRet = m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = m_aValue.m_nInt32; + else + nRet = m_aValue.m_uInt32; + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt64; + else + nRet = static_cast<sal_Int64>(m_aValue.m_uInt64); + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + + +sal_uInt64 ORowSetValue::getULong() const +{ + sal_uInt64 nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = OUString::unacquired(&m_aValue.m_pString).toUInt64(); + break; + case DataType::FLOAT: + nRet = sal_uInt64(m_aValue.m_nFloat); + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = sal_uInt64(m_aValue.m_nDouble); + break; + case DataType::DATE: + nRet = dbtools::DBTypeConversion::toDays(*static_cast<css::util::Date*>(m_aValue.m_pValue)); + break; + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getULong() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = sal_uInt64(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt16; + else + nRet = m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = m_aValue.m_nInt32; + else + nRet = m_aValue.m_uInt32; + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt64; + else + nRet = m_aValue.m_uInt64; + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + + +float ORowSetValue::getFloat() const +{ + float nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = OUString::unacquired(&m_aValue.m_pString).toFloat(); + break; + case DataType::FLOAT: + nRet = m_aValue.m_nFloat; + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = static_cast<float>(m_aValue.m_nDouble); + break; + case DataType::DATE: + nRet = static_cast<float>(dbtools::DBTypeConversion::toDouble(*static_cast<css::util::Date*>(m_aValue.m_pValue))); + break; + case DataType::TIME: + nRet = static_cast<float>(dbtools::DBTypeConversion::toDouble(*static_cast<css::util::Time*>(m_aValue.m_pValue))); + break; + case DataType::TIMESTAMP: + nRet = static_cast<float>(dbtools::DBTypeConversion::toDouble(*static_cast<css::util::DateTime*>(m_aValue.m_pValue))); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getDouble() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = float(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt16; + else + nRet = static_cast<float>(m_aValue.m_uInt16); + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = static_cast<float>(m_aValue.m_nInt32); + else + nRet = static_cast<float>(m_aValue.m_uInt32); + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = static_cast<float>(m_aValue.m_nInt64); + else + nRet = static_cast<float>(m_aValue.m_uInt64); + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + +double ORowSetValue::getDouble() const +{ + double nRet = 0; + if(!m_bNull) + { + switch(getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + nRet = OUString::unacquired(&m_aValue.m_pString).toDouble(); + break; + case DataType::FLOAT: + nRet = m_aValue.m_nFloat; + break; + case DataType::DOUBLE: + case DataType::REAL: + nRet = m_aValue.m_nDouble; + break; + case DataType::DATE: + nRet = dbtools::DBTypeConversion::toDouble(*static_cast<css::util::Date*>(m_aValue.m_pValue)); + break; + case DataType::TIME: + nRet = dbtools::DBTypeConversion::toDouble(*static_cast<css::util::Time*>(m_aValue.m_pValue)); + break; + case DataType::TIMESTAMP: + nRet = dbtools::DBTypeConversion::toDouble(*static_cast<css::util::DateTime*>(m_aValue.m_pValue)); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + case DataType::CLOB: + OSL_FAIL("getDouble() for this type is not allowed!"); + break; + case DataType::BIT: + case DataType::BOOLEAN: + nRet = double(m_aValue.m_bBool); + break; + case DataType::TINYINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt8; + else + nRet = m_aValue.m_uInt8; + break; + case DataType::SMALLINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt16; + else + nRet = m_aValue.m_uInt16; + break; + case DataType::INTEGER: + if ( m_bSigned ) + nRet = m_aValue.m_nInt32; + else + nRet = m_aValue.m_uInt32; + break; + case DataType::BIGINT: + if ( m_bSigned ) + nRet = m_aValue.m_nInt64; + else + nRet = m_aValue.m_uInt64; + break; + default: + { + Any aValue = makeAny(); + aValue >>= nRet; + break; + } + } + } + return nRet; +} + +Sequence<sal_Int8> ORowSetValue::getSequence() const +{ + Sequence<sal_Int8> aSeq; + if (!m_bNull) + { + switch(m_eTypeKind) + { + case DataType::OBJECT: + case DataType::CLOB: + case DataType::BLOB: + { + Reference<XInputStream> xStream; + const Any aValue = makeAny(); + if(aValue.hasValue()) + { + Reference<XBlob> xBlob(aValue,UNO_QUERY); + if ( xBlob.is() ) + xStream = xBlob->getBinaryStream(); + else + { + Reference<XClob> xClob(aValue,UNO_QUERY); + if ( xClob.is() ) + xStream = xClob->getCharacterStream(); + } + if(xStream.is()) + { + const sal_uInt32 nBytesToRead = 65535; + sal_uInt32 nRead; + + do + { + css::uno::Sequence< sal_Int8 > aReadSeq; + + nRead = xStream->readSomeBytes( aReadSeq, nBytesToRead ); + + if( nRead ) + { + const sal_uInt32 nOldLength = aSeq.getLength(); + aSeq.realloc( nOldLength + nRead ); + memcpy( aSeq.getArray() + nOldLength, aReadSeq.getConstArray(), aReadSeq.getLength() ); + } + } + while( nBytesToRead == nRead ); + xStream->closeInput(); + } + } + } + break; + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + { + aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(m_aValue.m_pString->buffer), + sizeof(sal_Unicode) * m_aValue.m_pString->length); + } + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + aSeq = *static_cast< Sequence<sal_Int8>*>(m_aValue.m_pValue); + break; + default: + { + Any aValue = makeAny(); + aValue >>= aSeq; + break; + } + } + } + return aSeq; + +} + +css::util::Date ORowSetValue::getDate() const +{ + css::util::Date aValue; + if(!m_bNull) + { + switch(m_eTypeKind) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + aValue = DBTypeConversion::toDate(getString()); + break; + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::FLOAT: + case DataType::DOUBLE: + case DataType::REAL: + aValue = DBTypeConversion::toDate(getDouble()); + break; + + case DataType::DATE: + aValue = *static_cast< css::util::Date*>(m_aValue.m_pValue); + break; + case DataType::TIMESTAMP: + { + css::util::DateTime* pDateTime = static_cast< css::util::DateTime*>(m_aValue.m_pValue); + aValue.Day = pDateTime->Day; + aValue.Month = pDateTime->Month; + aValue.Year = pDateTime->Year; + } + break; + case DataType::BIT: + case DataType::BOOLEAN: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::BIGINT: + aValue = DBTypeConversion::toDate( double( getLong() ) ); + break; + + case DataType::BLOB: + case DataType::CLOB: + case DataType::OBJECT: + default: + OSL_ENSURE( false, "ORowSetValue::getDate: cannot retrieve the data!" ); + [[fallthrough]]; + + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::TIME: + aValue = DBTypeConversion::toDate( double(0) ); + break; + } + } + return aValue; +} + +css::util::Time ORowSetValue::getTime() const +{ + css::util::Time aValue; + if(!m_bNull) + { + switch(m_eTypeKind) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + aValue = DBTypeConversion::toTime(getString()); + break; + case DataType::DECIMAL: + case DataType::NUMERIC: + aValue = DBTypeConversion::toTime(getDouble()); + break; + case DataType::FLOAT: + case DataType::DOUBLE: + case DataType::REAL: + aValue = DBTypeConversion::toTime(getDouble()); + break; + case DataType::TIMESTAMP: + { + css::util::DateTime* pDateTime = static_cast< css::util::DateTime*>(m_aValue.m_pValue); + aValue.NanoSeconds = pDateTime->NanoSeconds; + aValue.Seconds = pDateTime->Seconds; + aValue.Minutes = pDateTime->Minutes; + aValue.Hours = pDateTime->Hours; + } + break; + case DataType::TIME: + aValue = *static_cast< css::util::Time*>(m_aValue.m_pValue); + break; + default: + { + Any aAnyValue = makeAny(); + aAnyValue >>= aValue; + break; + } + } + } + return aValue; +} + +css::util::DateTime ORowSetValue::getDateTime() const +{ + css::util::DateTime aValue; + if(!m_bNull) + { + switch(m_eTypeKind) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + aValue = DBTypeConversion::toDateTime(getString()); + break; + case DataType::DECIMAL: + case DataType::NUMERIC: + aValue = DBTypeConversion::toDateTime(getDouble()); + break; + case DataType::FLOAT: + case DataType::DOUBLE: + case DataType::REAL: + aValue = DBTypeConversion::toDateTime(getDouble()); + break; + case DataType::DATE: + { + css::util::Date* pDate = static_cast< css::util::Date*>(m_aValue.m_pValue); + aValue.Day = pDate->Day; + aValue.Month = pDate->Month; + aValue.Year = pDate->Year; + } + break; + case DataType::TIME: + { + css::util::Time* pTime = static_cast< css::util::Time*>(m_aValue.m_pValue); + aValue.NanoSeconds = pTime->NanoSeconds; + aValue.Seconds = pTime->Seconds; + aValue.Minutes = pTime->Minutes; + aValue.Hours = pTime->Hours; + } + break; + case DataType::TIMESTAMP: + aValue = *static_cast< css::util::DateTime*>(m_aValue.m_pValue); + break; + default: + { + Any aAnyValue = makeAny(); + aAnyValue >>= aValue; + break; + } + } + } + return aValue; +} + +void ORowSetValue::setSigned(bool _bMod) +{ + if ( m_bSigned == _bMod ) + return; + + m_bSigned = _bMod; + if ( m_bNull ) + return; + + sal_Int32 nType = m_eTypeKind; + switch(m_eTypeKind) + { + case DataType::TINYINT: + if ( m_bSigned ) + (*this) = getInt8(); + else + { + m_bSigned = !m_bSigned; + (*this) = getInt16(); + m_bSigned = !m_bSigned; + } + break; + case DataType::SMALLINT: + if ( m_bSigned ) + (*this) = getInt16(); + else + { + m_bSigned = !m_bSigned; + (*this) = getInt32(); + m_bSigned = !m_bSigned; + } + break; + case DataType::INTEGER: + if ( m_bSigned ) + (*this) = getInt32(); + else + { + m_bSigned = !m_bSigned; + (*this) = getLong(); + m_bSigned = !m_bSigned; + } + break; + case DataType::BIGINT: + { + if ( m_bSigned ) + { + auto nTmp = static_cast<sal_Int64>(m_aValue.m_uInt64); + m_aValue.m_nInt64 = nTmp; + } + else + { + auto nTmp = static_cast<sal_uInt64>(m_aValue.m_nInt64); + m_aValue.m_uInt64 = nTmp; + } + break; + } + } + m_eTypeKind = nType; +} + + +namespace detail +{ + class SAL_NO_VTABLE IValueSource + { + public: + virtual OUString getString() const = 0; + virtual bool getBoolean() const = 0; + virtual sal_Int8 getByte() const = 0; + virtual sal_Int16 getShort() const = 0; + virtual sal_Int32 getInt() const = 0; + virtual sal_Int64 getLong() const = 0; + virtual float getFloat() const = 0; + virtual double getDouble() const = 0; + virtual Date getDate() const = 0; + virtual css::util::Time getTime() const = 0; + virtual DateTime getTimestamp() const = 0; + virtual Sequence< sal_Int8 > getBytes() const = 0; + virtual Reference< XBlob > getBlob() const = 0; + virtual Reference< XClob > getClob() const = 0; + virtual Any getObject() const = 0; + virtual bool wasNull() const = 0; + + virtual ~IValueSource() { } + }; + + namespace { + + class RowValue : public IValueSource + { + public: + RowValue( const Reference< XRow >& _xRow, const sal_Int32 _nPos ) + :m_xRow( _xRow ) + ,m_nPos( _nPos ) + { + } + + // IValueSource + virtual OUString getString() const override { return m_xRow->getString( m_nPos ); }; + virtual bool getBoolean() const override { return m_xRow->getBoolean( m_nPos ); }; + virtual sal_Int8 getByte() const override { return m_xRow->getByte( m_nPos ); }; + virtual sal_Int16 getShort() const override { return m_xRow->getShort( m_nPos ); } + virtual sal_Int32 getInt() const override { return m_xRow->getInt( m_nPos ); } + virtual sal_Int64 getLong() const override { return m_xRow->getLong( m_nPos ); } + virtual float getFloat() const override { return m_xRow->getFloat( m_nPos ); }; + virtual double getDouble() const override { return m_xRow->getDouble( m_nPos ); }; + virtual Date getDate() const override { return m_xRow->getDate( m_nPos ); }; + virtual css::util::Time getTime() const override { return m_xRow->getTime( m_nPos ); }; + virtual DateTime getTimestamp() const override { return m_xRow->getTimestamp( m_nPos ); }; + virtual Sequence< sal_Int8 > getBytes() const override { return m_xRow->getBytes( m_nPos ); }; + virtual Reference< XBlob > getBlob() const override { return m_xRow->getBlob( m_nPos ); }; + virtual Reference< XClob > getClob() const override { return m_xRow->getClob( m_nPos ); }; + virtual Any getObject() const override { return m_xRow->getObject( m_nPos ,nullptr); }; + virtual bool wasNull() const override { return m_xRow->wasNull( ); }; + + private: + const Reference< XRow > m_xRow; + const sal_Int32 m_nPos; + }; + + class ColumnValue : public IValueSource + { + public: + explicit ColumnValue( const Reference< XColumn >& _rxColumn ) + :m_xColumn( _rxColumn ) + { + } + + // IValueSource + virtual OUString getString() const override { return m_xColumn->getString(); }; + virtual bool getBoolean() const override { return m_xColumn->getBoolean(); }; + virtual sal_Int8 getByte() const override { return m_xColumn->getByte(); }; + virtual sal_Int16 getShort() const override { return m_xColumn->getShort(); } + virtual sal_Int32 getInt() const override { return m_xColumn->getInt(); } + virtual sal_Int64 getLong() const override { return m_xColumn->getLong(); } + virtual float getFloat() const override { return m_xColumn->getFloat(); }; + virtual double getDouble() const override { return m_xColumn->getDouble(); }; + virtual Date getDate() const override { return m_xColumn->getDate(); }; + virtual css::util::Time getTime() const override { return m_xColumn->getTime(); }; + virtual DateTime getTimestamp() const override { return m_xColumn->getTimestamp(); }; + virtual Sequence< sal_Int8 > getBytes() const override { return m_xColumn->getBytes(); }; + virtual Reference< XBlob > getBlob() const override { return m_xColumn->getBlob(); }; + virtual Reference< XClob > getClob() const override { return m_xColumn->getClob(); }; + virtual Any getObject() const override { return m_xColumn->getObject( nullptr ); }; + virtual bool wasNull() const override { return m_xColumn->wasNull( ); }; + + private: + const Reference< XColumn > m_xColumn; + }; + + } +} + + +void ORowSetValue::fill( const sal_Int32 _nType, const Reference< XColumn >& _rxColumn ) +{ + detail::ColumnValue aColumnValue( _rxColumn ); + impl_fill( _nType, true, aColumnValue ); +} + + +void ORowSetValue::fill( sal_Int32 _nPos, sal_Int32 _nType, bool _bNullable, const Reference< XRow>& _xRow ) +{ + detail::RowValue aRowValue( _xRow, _nPos ); + impl_fill( _nType, _bNullable, aRowValue ); +} + + +void ORowSetValue::fill(sal_Int32 _nPos, + sal_Int32 _nType, + const css::uno::Reference< css::sdbc::XRow>& _xRow) +{ + fill(_nPos,_nType,true,_xRow); +} + + +void ORowSetValue::impl_fill( const sal_Int32 _nType, bool _bNullable, const detail::IValueSource& _rValueSource ) +{ + switch(_nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + (*this) = _rValueSource.getString(); + break; + case DataType::BIGINT: + if ( isSigned() ) + (*this) = _rValueSource.getLong(); + else + // TODO: this is rather horrible performance-wise + // but fixing it needs extending the css::sdbc::XRow API + // to have a getULong(), and needs updating all drivers :-| + // When doing that, add getUByte, getUShort, getUInt for symmetry/completeness + (*this) = _rValueSource.getString().toUInt64(); + break; + case DataType::FLOAT: + (*this) = _rValueSource.getFloat(); + break; + case DataType::DOUBLE: + case DataType::REAL: + (*this) = _rValueSource.getDouble(); + break; + case DataType::DATE: + (*this) = _rValueSource.getDate(); + break; + case DataType::TIME: + (*this) = _rValueSource.getTime(); + break; + case DataType::TIMESTAMP: + (*this) = _rValueSource.getTimestamp(); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + (*this) = _rValueSource.getBytes(); + break; + case DataType::BIT: + case DataType::BOOLEAN: + (*this) = _rValueSource.getBoolean(); + break; + case DataType::TINYINT: + if ( isSigned() ) + (*this) = _rValueSource.getByte(); + else + (*this) = _rValueSource.getShort(); + break; + case DataType::SMALLINT: + if ( isSigned() ) + (*this) = _rValueSource.getShort(); + else + (*this) = _rValueSource.getInt(); + break; + case DataType::INTEGER: + if ( isSigned() ) + (*this) = _rValueSource.getInt(); + else + (*this) = _rValueSource.getLong(); + break; + case DataType::CLOB: + (*this) = css::uno::Any(_rValueSource.getClob()); + setTypeKind(DataType::CLOB); + break; + case DataType::BLOB: + (*this) = css::uno::Any(_rValueSource.getBlob()); + setTypeKind(DataType::BLOB); + break; + case DataType::OTHER: + (*this) = _rValueSource.getObject(); + setTypeKind(DataType::OTHER); + break; + default: + SAL_WARN( "connectivity.commontools", "ORowSetValue::fill: unsupported type!" ); + (*this) = _rValueSource.getObject(); + break; + } + if ( _bNullable && _rValueSource.wasNull() ) + setNull(); + setTypeKind(_nType); +} + +void ORowSetValue::fill(const Any& _rValue) +{ + switch (_rValue.getValueType().getTypeClass()) + { + case TypeClass_VOID: + setNull(); break; + case TypeClass_BOOLEAN: + { + bool bValue( false ); + _rValue >>= bValue; + (*this) = bValue; + break; + } + case TypeClass_CHAR: + { + sal_Unicode aDummy(0); + _rValue >>= aDummy; + (*this) = OUString(aDummy); + break; + } + case TypeClass_STRING: + { + OUString sDummy; + _rValue >>= sDummy; + (*this) = sDummy; + break; + } + case TypeClass_FLOAT: + { + float aDummy(0.0); + _rValue >>= aDummy; + (*this) = aDummy; + break; + } + case TypeClass_DOUBLE: + { + double aDummy(0.0); + _rValue >>= aDummy; + (*this) = aDummy; + break; + } + case TypeClass_BYTE: + { + sal_Int8 aDummy(0); + _rValue >>= aDummy; + (*this) = aDummy; + break; + } + case TypeClass_SHORT: + { + sal_Int16 aDummy(0); + _rValue >>= aDummy; + (*this) = aDummy; + break; + } + case TypeClass_UNSIGNED_SHORT: + { + sal_uInt16 nValue(0); + _rValue >>= nValue; + (*this) = nValue; + break; + } + case TypeClass_LONG: + { + sal_Int32 aDummy(0); + _rValue >>= aDummy; + (*this) = aDummy; + break; + } + case TypeClass_UNSIGNED_LONG: + { + sal_uInt32 nValue(0); + _rValue >>= nValue; + (*this) = static_cast<sal_Int64>(nValue); + setSigned(false); + break; + } + case TypeClass_HYPER: + { + sal_Int64 nValue(0); + _rValue >>= nValue; + (*this) = nValue; + break; + } + case TypeClass_UNSIGNED_HYPER: + { + sal_uInt64 nValue(0); + _rValue >>= nValue; + (*this) = nValue; + setSigned(false); + break; + } + case TypeClass_ENUM: + { + sal_Int32 enumValue( 0 ); + ::cppu::enum2int( enumValue, _rValue ); + (*this) = enumValue; + } + break; + + case TypeClass_SEQUENCE: + { + Sequence<sal_Int8> aDummy; + if ( _rValue >>= aDummy ) + (*this) = aDummy; + else + SAL_WARN( "connectivity.commontools", "ORowSetValue::fill: unsupported sequence type!" ); + break; + } + + case TypeClass_STRUCT: + { + css::util::Date aDate; + css::util::Time aTime; + css::util::DateTime aDateTime; + if ( _rValue >>= aDate ) + { + (*this) = aDate; + } + else if ( _rValue >>= aTime ) + { + (*this) = aTime; + } + else if ( _rValue >>= aDateTime ) + { + (*this) = aDateTime; + } + else + SAL_WARN( "connectivity.commontools", "ORowSetValue::fill: unsupported structure!" ); + + break; + } + case TypeClass_INTERFACE: + { + Reference< XClob > xClob; + if ( _rValue >>= xClob ) + { + (*this) = _rValue; + setTypeKind(DataType::CLOB); + } + else + { + Reference< XBlob > xBlob; + if ( _rValue >>= xBlob ) + { + (*this) = _rValue; + setTypeKind(DataType::BLOB); + } + else + { + (*this) = _rValue; + } + } + } + break; + + default: + SAL_WARN( "connectivity.commontools","Unknown type"); + break; + } +} + +} // namespace connectivity + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/ParameterSubstitution.cxx b/connectivity/source/commontools/ParameterSubstitution.cxx new file mode 100644 index 0000000000..6fa5578080 --- /dev/null +++ b/connectivity/source/commontools/ParameterSubstitution.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 <ParameterSubstitution.hxx> +#include <connectivity/sqlparse.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <utility> + +namespace connectivity +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star; + + ParameterSubstitution::ParameterSubstitution(css::uno::Reference< css::uno::XComponentContext > _xContext ) : m_xContext(std::move(_xContext)) + { + } + void SAL_CALL ParameterSubstitution::initialize( const uno::Sequence< uno::Any >& _aArguments ) + { + ::osl::MutexGuard aGuard(m_aMutex); + comphelper::SequenceAsHashMap aArgs(_aArguments); + uno::Reference< sdbc::XConnection > xConnection; + xConnection = aArgs.getUnpackedValueOrDefault("ActiveConnection",xConnection); + m_xConnection = xConnection; + } + + OUString SAL_CALL ParameterSubstitution::getImplementationName( ) + { + return "org.openoffice.comp.helper.ParameterSubstitution"; + } + + sal_Bool SAL_CALL ParameterSubstitution::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + Sequence< OUString > SAL_CALL ParameterSubstitution::getSupportedServiceNames( ) + { + return { "com.sun.star.sdb.ParameterSubstitution" }; + } + + + OUString SAL_CALL ParameterSubstitution::substituteVariables( const OUString& _sText, sal_Bool /*bSubstRequired*/ ) + { + OUString sRet = _sText; + uno::Reference< sdbc::XConnection > xConnection = m_xConnection; + if ( xConnection.is() ) + { + try + { + OSQLParser aParser( m_xContext ); + OUString sErrorMessage; + std::unique_ptr<OSQLParseNode> pNode = aParser.parseTree(sErrorMessage,_sText); + if(pNode) + { // special handling for parameters + OSQLParseNode::substituteParameterNames(pNode.get()); + OUString sNewSql; + pNode->parseNodeToStr( sNewSql, xConnection ); + sRet = sNewSql; + } + } + catch(const Exception&) + { + } + } + return sRet; + } + + OUString SAL_CALL ParameterSubstitution::reSubstituteVariables( const OUString& _sText ) + { + return _sText; + } + + OUString SAL_CALL ParameterSubstitution::getSubstituteVariableValue( const OUString& /*variable*/ ) + { + throw container::NoSuchElementException(); + } + + +} // connectivity + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +connectivity_dbtools_ParameterSubstitution_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new connectivity::ParameterSubstitution(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/RowFunctionParser.cxx b/connectivity/source/commontools/RowFunctionParser.cxx new file mode 100644 index 0000000000..da4935dc61 --- /dev/null +++ b/connectivity/source/commontools/RowFunctionParser.cxx @@ -0,0 +1,442 @@ +/* -*- 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 . + */ + + +// Makes parser a static resource, +// we're synchronized externally. +// But watch out, the parser might have +// state not visible to this code! +#define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE + +#if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL) +#include <typeinfo> +#define BOOST_SPIRIT_DEBUG +#endif +#include <boost/spirit/include/classic_core.hpp> +#include <RowFunctionParser.hxx> +#include <rtl/ustring.hxx> + + +#if (OSL_DEBUG_LEVEL > 0) +#include <iostream> +#endif +#include <algorithm> +#include <stack> +#include <utility> + +namespace connectivity +{ +using namespace com::sun::star; + +namespace +{ + + +// EXPRESSION NODES + + +class ConstantValueExpression : public ExpressionNode +{ + ORowSetValueDecoratorRef maValue; + +public: + + explicit ConstantValueExpression( ORowSetValueDecoratorRef aValue ) : + maValue(std::move( aValue )) + { + } + virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const override + { + return maValue; + } + virtual void fill(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const override + { + } +}; + + +/** ExpressionNode implementation for unary + function over two ExpressionNodes + */ +class BinaryFunctionExpression : public ExpressionNode +{ + const ExpressionFunct meFunct; + std::shared_ptr<ExpressionNode> mpFirstArg; + std::shared_ptr<ExpressionNode> mpSecondArg; + +public: + + BinaryFunctionExpression( const ExpressionFunct eFunct, std::shared_ptr<ExpressionNode> xFirstArg, std::shared_ptr<ExpressionNode> xSecondArg ) : + meFunct( eFunct ), + mpFirstArg(std::move( xFirstArg )), + mpSecondArg(std::move( xSecondArg )) + { + } + virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& _aRow ) const override + { + ORowSetValueDecoratorRef aRet; + switch(meFunct) + { + case ExpressionFunct::Equation: + aRet = new ORowSetValueDecorator( ORowSetValue(mpFirstArg->evaluate(_aRow )->getValue() == mpSecondArg->evaluate(_aRow )->getValue()) ); + break; + case ExpressionFunct::And: + aRet = new ORowSetValueDecorator( ORowSetValue(mpFirstArg->evaluate(_aRow )->getValue().getBool() && mpSecondArg->evaluate(_aRow )->getValue().getBool()) ); + break; + case ExpressionFunct::Or: + aRet = new ORowSetValueDecorator( ORowSetValue(mpFirstArg->evaluate(_aRow )->getValue().getBool() || mpSecondArg->evaluate(_aRow )->getValue().getBool()) ); + break; + default: + break; + } + return aRet; + } + virtual void fill(const ODatabaseMetaDataResultSet::ORow& _aRow ) const override + { + switch(meFunct) + { + case ExpressionFunct::Equation: + (*mpFirstArg->evaluate(_aRow )) = mpSecondArg->evaluate(_aRow )->getValue(); + break; + default: + break; + } + } +}; + + +// FUNCTION PARSER + + +typedef const char* StringIteratorT; + +struct ParserContext +{ + typedef std::stack< std::shared_ptr<ExpressionNode> > OperandStack; + + // stores a stack of not-yet-evaluated operands. This is used + // by the operators (i.e. '+', '*', 'sin' etc.) to pop their + // arguments from. If all arguments to an operator are constant, + // the operator pushes a precalculated result on the stack, and + // a composite ExpressionNode otherwise. + OperandStack maOperandStack; +}; + +typedef std::shared_ptr< ParserContext > ParserContextSharedPtr; + +/** Generate apriori constant value + */ + +class ConstantFunctor +{ + ParserContextSharedPtr mpContext; + +public: + + explicit ConstantFunctor( ParserContextSharedPtr xContext ) : + mpContext(std::move( xContext )) + { + } + void operator()( StringIteratorT rFirst,StringIteratorT rSecond) const + { + OUString sVal( rFirst, rSecond - rFirst, RTL_TEXTENCODING_UTF8 ); + mpContext->maOperandStack.push( std::make_shared<ConstantValueExpression>( new ORowSetValueDecorator( sVal ) ) ); + } +}; + +/** Generate parse-dependent-but-then-constant value + */ +class IntConstantFunctor +{ + ParserContextSharedPtr mpContext; + +public: + explicit IntConstantFunctor( ParserContextSharedPtr xContext ) : + mpContext(std::move( xContext )) + { + } + void operator()( sal_Int32 n ) const + { + mpContext->maOperandStack.push( std::make_shared<ConstantValueExpression>( new ORowSetValueDecorator( n ) ) ); + } +}; + +/** Implements a binary function over two ExpressionNodes + + @tpl Generator + Generator functor, to generate an ExpressionNode of + appropriate type + + */ +class BinaryFunctionFunctor +{ + const ExpressionFunct meFunct; + ParserContextSharedPtr mpContext; + +public: + + BinaryFunctionFunctor( const ExpressionFunct eFunct, ParserContextSharedPtr xContext ) : + meFunct( eFunct ), + mpContext(std::move( xContext )) + { + } + + void operator()( StringIteratorT, StringIteratorT ) const + { + ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack ); + + if( rNodeStack.size() < 2 ) + throw ParseError( "Not enough arguments for binary operator" ); + + // retrieve arguments + std::shared_ptr<ExpressionNode> pSecondArg( std::move(rNodeStack.top()) ); + rNodeStack.pop(); + std::shared_ptr<ExpressionNode> pFirstArg( std::move(rNodeStack.top()) ); + rNodeStack.pop(); + + // create combined ExpressionNode + auto pNode = std::make_shared<BinaryFunctionExpression>( meFunct, pFirstArg, pSecondArg ); + // check for constness + rNodeStack.push( pNode ); + } +}; +/** ExpressionNode implementation for unary + function over one ExpressionNode + */ +class UnaryFunctionExpression : public ExpressionNode +{ + std::shared_ptr<ExpressionNode> mpArg; + +public: + explicit UnaryFunctionExpression( std::shared_ptr<ExpressionNode> xArg ) : + mpArg(std::move( xArg )) + { + } + virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& _aRow ) const override + { + return _aRow[mpArg->evaluate(_aRow )->getValue().getUInt32()]; + } + virtual void fill(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const override + { + } +}; + +class UnaryFunctionFunctor +{ + ParserContextSharedPtr mpContext; + +public: + + explicit UnaryFunctionFunctor(ParserContextSharedPtr xContext) + : mpContext(std::move(xContext)) + { + } + void operator()( StringIteratorT, StringIteratorT ) const + { + + ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack ); + + if( rNodeStack.empty() ) + throw ParseError( "Not enough arguments for unary operator" ); + + // retrieve arguments + std::shared_ptr<ExpressionNode> pArg( std::move(rNodeStack.top()) ); + rNodeStack.pop(); + + rNodeStack.push( std::make_shared<UnaryFunctionExpression>( pArg ) ); + } +}; + +/* This class implements the following grammar (more or + less literally written down below, only slightly + obfuscated by the parser actions): + + basic_expression = + number | + '(' additive_expression ')' + + unary_expression = + basic_expression + + multiplicative_expression = + unary_expression ( ( '*' unary_expression )* | + ( '/' unary_expression )* ) + + additive_expression = + multiplicative_expression ( ( '+' multiplicative_expression )* | + ( '-' multiplicative_expression )* ) + + */ +class ExpressionGrammar : public ::boost::spirit::classic::grammar< ExpressionGrammar > +{ +public: + /** Create an arithmetic expression grammar + + @param rParserContext + Contains context info for the parser + */ + explicit ExpressionGrammar( ParserContextSharedPtr xParserContext ) : + mpParserContext(std::move( xParserContext )) + { + } + + template< typename ScannerT > class definition + { + public: + // grammar definition + explicit definition( const ExpressionGrammar& self ) + { + using ::boost::spirit::classic::space_p; + using ::boost::spirit::classic::range_p; + using ::boost::spirit::classic::lexeme_d; + using ::boost::spirit::classic::ch_p; + using ::boost::spirit::classic::int_p; + using ::boost::spirit::classic::as_lower_d; + using ::boost::spirit::classic::strlit; + using ::boost::spirit::classic::inhibit_case; + + + typedef inhibit_case<strlit<> > token_t; + token_t COLUMN = as_lower_d[ "column" ]; + token_t OR_ = as_lower_d[ "or" ]; + token_t AND_ = as_lower_d[ "and" ]; + + integer = + int_p + [IntConstantFunctor(self.getContext())]; + + argument = + integer + | lexeme_d[ +( range_p('a','z') | range_p('A','Z') | range_p('0','9') ) ] + [ ConstantFunctor(self.getContext()) ] + ; + + unaryFunction = + (COLUMN >> '(' >> integer >> ')' ) + [ UnaryFunctionFunctor( self.getContext()) ] + ; + + assignment = + unaryFunction >> ch_p('=') >> argument + [ BinaryFunctionFunctor( ExpressionFunct::Equation, self.getContext()) ] + ; + + andExpression = + assignment + | ( '(' >> orExpression >> ')' ) + | ( assignment >> AND_ >> assignment ) [ BinaryFunctionFunctor( ExpressionFunct::And, self.getContext()) ] + ; + + orExpression = + andExpression + | ( orExpression >> OR_ >> andExpression ) [ BinaryFunctionFunctor( ExpressionFunct::Or, self.getContext()) ] + ; + + basicExpression = + orExpression + ; + + BOOST_SPIRIT_DEBUG_RULE(basicExpression); + BOOST_SPIRIT_DEBUG_RULE(unaryFunction); + BOOST_SPIRIT_DEBUG_RULE(assignment); + BOOST_SPIRIT_DEBUG_RULE(argument); + BOOST_SPIRIT_DEBUG_RULE(integer); + BOOST_SPIRIT_DEBUG_RULE(orExpression); + BOOST_SPIRIT_DEBUG_RULE(andExpression); + } + + const ::boost::spirit::classic::rule< ScannerT >& start() const + { + return basicExpression; + } + + private: + // the constituents of the Spirit arithmetic expression grammar. + // For the sake of readability, without 'ma' prefix. + ::boost::spirit::classic::rule< ScannerT > basicExpression; + ::boost::spirit::classic::rule< ScannerT > unaryFunction; + ::boost::spirit::classic::rule< ScannerT > assignment; + ::boost::spirit::classic::rule< ScannerT > integer,argument; + ::boost::spirit::classic::rule< ScannerT > orExpression,andExpression; + }; + + const ParserContextSharedPtr& getContext() const + { + return mpParserContext; + } + +private: + ParserContextSharedPtr mpParserContext; // might get modified during parsing +}; + +const ParserContextSharedPtr& getParserContext() +{ + static ParserContextSharedPtr lcl_parserContext = std::make_shared<ParserContext>(); + + // clear node stack (since we reuse the static object, that's + // the whole point here) + while( !lcl_parserContext->maOperandStack.empty() ) + lcl_parserContext->maOperandStack.pop(); + + return lcl_parserContext; +} + +} + +std::shared_ptr<ExpressionNode> const & FunctionParser::parseFunction( const OUString& _sFunction) +{ + // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_* + // gives better conversion robustness here (we might want to map space + // etc. to ASCII space here) + const OString& rAsciiFunction( + OUStringToOString( _sFunction, RTL_TEXTENCODING_ASCII_US ) ); + + StringIteratorT aStart( rAsciiFunction.getStr() ); + StringIteratorT aEnd( rAsciiFunction.getStr()+rAsciiFunction.getLength() ); + + // static parser context, because the actual + // Spirit parser is also a static object + ParserContextSharedPtr pContext = getParserContext(); + + ExpressionGrammar aExpressionGrammer( pContext ); + + const ::boost::spirit::classic::parse_info<StringIteratorT> aParseInfo( + ::boost::spirit::classic::parse( aStart, + aEnd, + aExpressionGrammer, + ::boost::spirit::classic::space_p ) ); + +#if (OSL_DEBUG_LEVEL > 0) + std::cout.flush(); // needed to keep stdout and cout in sync +#endif + + // input fully congested by the parser? + if( !aParseInfo.full ) + throw ParseError( "RowFunctionParser::parseFunction(): string not fully parseable" ); + + // parser's state stack now must contain exactly _one_ ExpressionNode, + // which represents our formula. + if( pContext->maOperandStack.size() != 1 ) + throw ParseError( "RowFunctionParser::parseFunction(): incomplete or empty expression" ); + + return pContext->maOperandStack.top(); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TColumnsHelper.cxx b/connectivity/source/commontools/TColumnsHelper.cxx new file mode 100644 index 0000000000..f81eca9f0a --- /dev/null +++ b/connectivity/source/commontools/TColumnsHelper.cxx @@ -0,0 +1,208 @@ +/* -*- 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 <connectivity/TColumnsHelper.hxx> +#include <connectivity/sdbcx/VColumn.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <TConnection.hxx> +#include <connectivity/TTableHelper.hxx> + +using namespace ::comphelper; + + +using namespace connectivity::sdbcx; +using namespace connectivity; +using namespace dbtools; +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; + +namespace connectivity +{ + class OColumnsHelperImpl + { + public: + explicit OColumnsHelperImpl(bool _bCase) + : m_aColumnInfo(_bCase) + { + } + ColumnInformationMap m_aColumnInfo; + }; +} + +OColumnsHelper::OColumnsHelper( ::cppu::OWeakObject& _rParent + ,bool _bCase + ,::osl::Mutex& _rMutex + ,const ::std::vector< OUString> &_rVector + ,bool _bUseHardRef + ) : OCollection(_rParent,_bCase,_rMutex,_rVector,false,_bUseHardRef) + ,m_pTable(nullptr) +{ +} + +OColumnsHelper::~OColumnsHelper() +{ +} + + +sdbcx::ObjectType OColumnsHelper::createObject(const OUString& _rName) +{ + OSL_ENSURE(m_pTable,"NO Table set. Error!"); + Reference<XConnection> xConnection = m_pTable->getConnection(); + + if ( !m_pImpl ) + m_pImpl.reset(new OColumnsHelperImpl(isCaseSensitive())); + + bool bQueryInfo = true; + bool bAutoIncrement = false; + bool bIsCurrency = false; + sal_Int32 nDataType = DataType::OTHER; + + ColumnInformationMap::const_iterator aFind = m_pImpl->m_aColumnInfo.find(_rName); + if ( aFind == m_pImpl->m_aColumnInfo.end() ) // we have to fill it + { + OUString sComposedName = ::dbtools::composeTableNameForSelect( xConnection, m_pTable ); + collectColumnInformation(xConnection,sComposedName,u"*" ,m_pImpl->m_aColumnInfo); + aFind = m_pImpl->m_aColumnInfo.find(_rName); + } + if ( aFind != m_pImpl->m_aColumnInfo.end() ) + { + bQueryInfo = false; + bAutoIncrement = aFind->second.first.first; + bIsCurrency = aFind->second.first.second; + nDataType = aFind->second.second; + } // if ( aFind != m_pImpl->m_aColumnInfo.end() ) + + sdbcx::ObjectType xRet; + const ColumnDesc* pColDesc = m_pTable->getColumnDescription(_rName); + if ( pColDesc ) + { + Reference<XPropertySet> xPr = m_pTable; + const Reference<XNameAccess> xPrimaryKeyColumns = getPrimaryKeyColumns_throw(xPr); + sal_Int32 nField11 = pColDesc->nField11; + if ( nField11 != ColumnValue::NO_NULLS && xPrimaryKeyColumns.is() && xPrimaryKeyColumns->hasByName(_rName) ) + { + nField11 = ColumnValue::NO_NULLS; + } // if ( xKeys.is() ) + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + OUString aCatalog, aSchema, aTable; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) >>= aCatalog; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= aTable; + rtl::Reference<connectivity::sdbcx::OColumn> pRet = new connectivity::sdbcx::OColumn(_rName, + pColDesc->aField6, + pColDesc->sField13, + pColDesc->sField12, + nField11, + pColDesc->nField7, + pColDesc->nField9, + pColDesc->nField5, + bAutoIncrement, + false, + bIsCurrency, + isCaseSensitive(), + aCatalog, + aSchema, + aTable); + + xRet = pRet; + } + else + { + + xRet = ::dbtools::createSDBCXColumn( m_pTable, + xConnection, + _rName, + isCaseSensitive(), + bQueryInfo, + bAutoIncrement, + bIsCurrency, + nDataType); + } + return xRet; +} + + +void OColumnsHelper::impl_refresh() +{ + if ( m_pTable ) + { + m_pImpl->m_aColumnInfo.clear(); + m_pTable->refreshColumns(); + } +} + +Reference< XPropertySet > OColumnsHelper::createDescriptor() +{ + return new OColumn(true); +} + +// XAppend +sdbcx::ObjectType OColumnsHelper::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_pTable,"OColumnsHelper::appendByDescriptor: Table is null!"); + if ( !m_pTable || m_pTable->isNew() ) + return cloneDescriptor( descriptor ); + + Reference<XDatabaseMetaData> xMetaData = m_pTable->getConnection()->getMetaData(); + OUString aSql = "ALTER TABLE " + + ::dbtools::composeTableName( xMetaData, m_pTable, ::dbtools::EComposeRule::InTableDefinitions, true ) + + " ADD " + + ::dbtools::createStandardColumnPart(descriptor,m_pTable->getConnection(),nullptr,m_pTable->getTypeCreatePattern()); + + Reference< XStatement > xStmt = m_pTable->getConnection()->createStatement( ); + if ( xStmt.is() ) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + return createObject( _rForName ); +} + +// XDrop +void OColumnsHelper::dropObject(sal_Int32 /*_nPos*/, const OUString& _sElementName) +{ + OSL_ENSURE(m_pTable,"OColumnsHelper::dropByName: Table is null!"); + if ( !m_pTable || m_pTable->isNew() ) + return; + + Reference<XDatabaseMetaData> xMetaData = m_pTable->getConnection()->getMetaData(); + OUString aQuote = xMetaData->getIdentifierQuoteString( ); + OUString aSql = "ALTER TABLE " + + ::dbtools::composeTableName( xMetaData, m_pTable, ::dbtools::EComposeRule::InTableDefinitions, true ) + + " DROP " + + ::dbtools::quoteName( aQuote,_sElementName); + + Reference< XStatement > xStmt = m_pTable->getConnection()->createStatement( ); + if ( xStmt.is() ) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TConnection.cxx b/connectivity/source/commontools/TConnection.cxx new file mode 100644 index 0000000000..f35e8ca194 --- /dev/null +++ b/connectivity/source/commontools/TConnection.cxx @@ -0,0 +1,85 @@ +/* -*- 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 <TConnection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> + +using namespace connectivity; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::beans; +using namespace ::osl; + + +OMetaConnection::OMetaConnection() + : OMetaConnection_BASE(m_aMutex) + , m_nTextEncoding(RTL_TEXTENCODING_MS_1252) +{ +} + +void OMetaConnection::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + m_xMetaData = WeakReference< XDatabaseMetaData>(); + for (auto const& statement : m_aStatements) + { + try + { + Reference< XInterface > xStatement( statement.get() ); + ::comphelper::disposeComponent( xStatement ); + } + catch (const DisposedException&) + { + } + } + m_aStatements.clear(); +} +//XUnoTunnel +sal_Int64 SAL_CALL OMetaConnection::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +const Sequence< sal_Int8 > & OMetaConnection::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +::dbtools::OPropertyMap& OMetaConnection::getPropMap() +{ + static ::dbtools::OPropertyMap s_aPropertyNameMap; + return s_aPropertyNameMap; +} + +void OMetaConnection::throwGenericSQLException(TranslateId pErrorResourceId, const Reference< XInterface>& _xContext ) +{ + OUString sErrorMessage; + if (pErrorResourceId) + sErrorMessage = m_aResources.getResourceString(pErrorResourceId); + Reference< XInterface> xContext = _xContext; + if ( !xContext.is() ) + xContext = *this; + ::dbtools::throwGenericSQLException( sErrorMessage, xContext); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TDatabaseMetaDataBase.cxx b/connectivity/source/commontools/TDatabaseMetaDataBase.cxx new file mode 100644 index 0000000000..bd2b7a4671 --- /dev/null +++ b/connectivity/source/commontools/TDatabaseMetaDataBase.cxx @@ -0,0 +1,329 @@ +/* -*- 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 <cstddef> + +#include <TDatabaseMetaDataBase.hxx> +#include <RowFunctionParser.hxx> + +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/evtlistenerhlp.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/lang/XComponent.hpp> +#include <resource/sharedresources.hxx> +#include <strings.hrc> +#include <connectivity/dbexception.hxx> +#include <sal/macros.h> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::beans; +using namespace comphelper; +using namespace connectivity; + + +ODatabaseMetaDataBase::ODatabaseMetaDataBase(const Reference< XConnection >& _rxConnection,const Sequence< PropertyValue >& _rInfo) + : m_aConnectionInfo(_rInfo) + ,m_isCatalogAtStart(false,false) + ,m_sCatalogSeparator(false,OUString()) + ,m_sIdentifierQuoteString(false,OUString()) + ,m_supportsCatalogsInTableDefinitions(false,false) + ,m_supportsSchemasInTableDefinitions(false,false) + ,m_supportsCatalogsInDataManipulation(false,false) + ,m_supportsSchemasInDataManipulation(false,false) + ,m_supportsMixedCaseQuotedIdentifiers(false,false) + ,m_supportsAlterTableWithAddColumn(false,false) + ,m_supportsAlterTableWithDropColumn(false,false) + ,m_MaxStatements(false,0) + ,m_MaxTablesInSelect(false,0) + ,m_storesMixedCaseQuotedIdentifiers(false,false) + , m_xConnection(_rxConnection) +{ + osl_atomic_increment( &m_refCount ); + { + m_xListenerHelper = new OEventListenerHelper(this); + Reference<XComponent> xCom(m_xConnection,UNO_QUERY); + if(xCom.is()) + xCom->addEventListener(m_xListenerHelper); + } + osl_atomic_decrement( &m_refCount ); +} + +ODatabaseMetaDataBase::~ODatabaseMetaDataBase() +{ +} + + +Sequence< PropertyValue > SAL_CALL ODatabaseMetaDataBase::getConnectionInfo( ) +{ + return m_aConnectionInfo; +} + + +void SAL_CALL ODatabaseMetaDataBase::disposing( const EventObject& /*Source*/ ) +{ + // cut off all references to the connection +m_xConnection.clear(); +m_xListenerHelper.clear(); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getTypeInfo( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_aTypeInfoRows.empty() ) + { + Reference< XResultSet > xRet = impl_getTypeInfo_throw(); + Reference< XRow > xRow(xRet,UNO_QUERY); + ::comphelper::SequenceAsHashMap aMap(m_aConnectionInfo); + Sequence< Any > aTypeInfoSettings; + aTypeInfoSettings = aMap.getUnpackedValueOrDefault("TypeInfoSettings",aTypeInfoSettings); + + if ( xRow.is() ) + { + static const sal_Int32 pTypes[] = { + DataType::VARCHAR + ,DataType::INTEGER + ,DataType::INTEGER + ,DataType::VARCHAR + ,DataType::VARCHAR + ,DataType::VARCHAR + ,DataType::INTEGER + ,DataType::BOOLEAN + ,DataType::INTEGER + ,DataType::BOOLEAN + ,DataType::BOOLEAN + ,DataType::BOOLEAN + ,DataType::VARCHAR + ,DataType::INTEGER + ,DataType::INTEGER + ,DataType::INTEGER + ,DataType::INTEGER + ,DataType::INTEGER + }; + std::vector<std::shared_ptr<ExpressionNode>> aConditions; + if ( aTypeInfoSettings.getLength() > 1 && ((aTypeInfoSettings.getLength() % 2) == 0) ) + { + const Any* pIter = aTypeInfoSettings.getConstArray(); + const Any* pEnd = pIter + aTypeInfoSettings.getLength(); + try + { + for(;pIter != pEnd;++pIter) + aConditions.push_back(FunctionParser::parseFunction(::comphelper::getString(*pIter))); + } + catch(ParseError&) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_FORMULA_WRONG)); + ::dbtools::throwGenericSQLException(sError,*this); + } + } + + ::connectivity::ODatabaseMetaDataResultSet::ORows aTypeInfoRows; + while( xRet->next() ) + { + ::connectivity::ODatabaseMetaDataResultSet::ORow aRow; + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + const sal_Int32* pType = pTypes; + for (std::size_t i = 1; i <= std::size(pTypes); ++i,++pType) + { + ORowSetValue aValue; + aValue.fill(i,*pType,xRow); + aRow.push_back(new ORowSetValueDecorator(std::move(aValue))); + } + + std::vector<std::shared_ptr<ExpressionNode>>::iterator aIter = aConditions.begin(); + std::vector<std::shared_ptr<ExpressionNode>>::const_iterator aEnd = aConditions.end(); + for (; aIter != aEnd; ++aIter) + { + if ( (*aIter)->evaluate(aRow)->getValue().getBool() ) + { + ++aIter; + (*aIter)->fill(aRow); + } + else + ++aIter; + } + aTypeInfoRows.push_back(aRow); + } + m_aTypeInfoRows = aTypeInfoRows; + } + } + rtl::Reference<::connectivity::ODatabaseMetaDataResultSet> pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTypeInfo); + pResult->setRows(std::vector(m_aTypeInfoRows)); + return pResult; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getExportedKeys( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& /*table*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eExportedKeys ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getImportedKeys( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& /*table*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eImportedKeys ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getPrimaryKeys( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& /*table*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::ePrimaryKeys ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getIndexInfo( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& /*table*/, + sal_Bool /*unique*/, sal_Bool /*approximate*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eIndexInfo ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getBestRowIdentifier( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& /*table*/, sal_Int32 /*scope*/, + sal_Bool /*nullable*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eBestRowIdentifier ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getCrossReference( + const Any& /*primaryCatalog*/, const OUString& /*primarySchema*/, + const OUString& /*primaryTable*/, const Any& /*foreignCatalog*/, + const OUString& /*foreignSchema*/, const OUString& /*foreignTable*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eCrossReference ); +} + +Reference< XConnection > SAL_CALL ODatabaseMetaDataBase::getConnection( ) +{ + return m_xConnection; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getProcedureColumns( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& /*procedureNamePattern*/, const OUString& /*columnNamePattern*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eProcedureColumns ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getProcedures( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& /*procedureNamePattern*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eProcedures ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getVersionColumns( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& /*table*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eVersionColumns ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getSchemas( ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eSchemas ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getColumnPrivileges( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& /*table*/, + const OUString& /*columnNamePattern*/ ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eColumnPrivileges ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getTablePrivileges( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& /*table*/) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTablePrivileges ); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaDataBase::getCatalogs( ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eCatalogs ); +} + +OUString SAL_CALL ODatabaseMetaDataBase::getIdentifierQuoteString( ) +{ + return callImplMethod(m_sIdentifierQuoteString,std::function<OUString(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_getIdentifierQuoteString_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::isCatalogAtStart( ) +{ + return callImplMethod(m_isCatalogAtStart,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_isCatalogAtStart_throw)); +} + +OUString SAL_CALL ODatabaseMetaDataBase::getCatalogSeparator( ) +{ + return callImplMethod(m_sCatalogSeparator,std::function<OUString(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_getCatalogSeparator_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::supportsCatalogsInTableDefinitions( ) +{ + return callImplMethod(m_supportsCatalogsInTableDefinitions,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_supportsCatalogsInTableDefinitions_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::supportsSchemasInTableDefinitions( ) +{ + return callImplMethod(m_supportsSchemasInTableDefinitions,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_supportsSchemasInTableDefinitions_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::supportsCatalogsInDataManipulation( ) +{ + return callImplMethod(m_supportsCatalogsInDataManipulation,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_supportsCatalogsInDataManipulation_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::supportsSchemasInDataManipulation( ) +{ + return callImplMethod(m_supportsSchemasInDataManipulation,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_supportsSchemasInDataManipulation_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::supportsMixedCaseQuotedIdentifiers( ) +{ + return callImplMethod(m_supportsMixedCaseQuotedIdentifiers,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_supportsMixedCaseQuotedIdentifiers_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::supportsAlterTableWithAddColumn( ) +{ + return callImplMethod(m_supportsAlterTableWithAddColumn,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_supportsAlterTableWithAddColumn_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::supportsAlterTableWithDropColumn( ) +{ + return callImplMethod(m_supportsAlterTableWithDropColumn,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_supportsAlterTableWithDropColumn_throw)); +} + +sal_Int32 SAL_CALL ODatabaseMetaDataBase::getMaxStatements( ) +{ + return callImplMethod(m_MaxStatements,std::function<sal_Int32(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_getMaxStatements_throw)); +} + +sal_Int32 SAL_CALL ODatabaseMetaDataBase::getMaxTablesInSelect( ) +{ + return callImplMethod(m_MaxTablesInSelect,std::function<sal_Int32(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_getMaxTablesInSelect_throw)); +} + +sal_Bool SAL_CALL ODatabaseMetaDataBase::storesMixedCaseQuotedIdentifiers( ) +{ + return callImplMethod(m_storesMixedCaseQuotedIdentifiers,std::function<bool(ODatabaseMetaDataBase *)>(&ODatabaseMetaDataBase::impl_storesMixedCaseQuotedIdentifiers_throw)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TIndex.cxx b/connectivity/source/commontools/TIndex.cxx new file mode 100644 index 0000000000..f7f891308e --- /dev/null +++ b/connectivity/source/commontools/TIndex.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 <TIndex.hxx> +#include <TIndexColumns.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <connectivity/TTableHelper.hxx> +#include <TConnection.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::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OIndexHelper::OIndexHelper( OTableHelper* _pTable) : connectivity::sdbcx::OIndex(true) + , m_pTable(_pTable) +{ + construct(); + std::vector< OUString> aVector; + m_pColumns.reset(new OIndexColumns(this,m_aMutex,aVector)); +} + +OIndexHelper::OIndexHelper( OTableHelper* _pTable, + const OUString& Name, + const OUString& Catalog, + bool _isUnique, + bool _isPrimaryKeyIndex, + bool _isClustered + ) : connectivity::sdbcx::OIndex(Name, + Catalog, + _isUnique, + _isPrimaryKeyIndex, + _isClustered,true) + ,m_pTable(_pTable) +{ + construct(); + refreshColumns(); +} + + +void OIndexHelper::refreshColumns() +{ + if ( !m_pTable ) + return; + + std::vector< OUString> aVector; + if ( !isNew() ) + { + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + OUString aSchema,aTable; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= aTable; + + Reference< XResultSet > xResult = m_pTable->getMetaData()->getIndexInfo( + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)), + aSchema,aTable,false,false); + + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + OUString aColName; + while( xResult->next() ) + { + if ( xRow->getString(6) == m_Name ) + { + aColName = xRow->getString(9); + if ( !xRow->wasNull() ) + aVector.push_back(aColName); + } + } + } + } + if(m_pColumns) + m_pColumns->reFill(aVector); + else + m_pColumns.reset(new OIndexColumns(this,m_aMutex,aVector)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TIndexColumns.cxx b/connectivity/source/commontools/TIndexColumns.cxx new file mode 100644 index 0000000000..1db4426c18 --- /dev/null +++ b/connectivity/source/commontools/TIndexColumns.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 <TIndexColumns.hxx> +#include <sdbcx/VIndexColumn.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <TIndex.hxx> +#include <connectivity/TTableHelper.hxx> +#include <TConnection.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::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OIndexColumns::OIndexColumns( OIndexHelper* _pIndex, + ::osl::Mutex& _rMutex, + const std::vector< OUString> &_rVector) + : connectivity::sdbcx::OCollection(*_pIndex,true,_rMutex,_rVector) + ,m_pIndex(_pIndex) +{ +} + +sdbcx::ObjectType OIndexColumns::createObject(const OUString& _rName) +{ + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + OUString aCatalog, aSchema, aTable; + css::uno::Any Catalog(m_pIndex->getTable()->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME))); + Catalog >>= aCatalog; + m_pIndex->getTable()->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema; + m_pIndex->getTable()->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= aTable; + + Reference< XResultSet > xResult = m_pIndex->getTable()->getConnection()->getMetaData()->getIndexInfo( + Catalog, aSchema, aTable, false, false); + + bool bAsc = true; + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while( xResult->next() ) + { + if(xRow->getString(9) == _rName) + bAsc = xRow->getString(10) != "D"; + } + } + + xResult = m_pIndex->getTable()->getConnection()->getMetaData()->getColumns( + Catalog, aSchema, aTable, _rName); + + sdbcx::ObjectType xRet; + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while( xResult->next() ) + { + if ( xRow->getString(4) == _rName ) + { + sal_Int32 nDataType = xRow->getInt(5); + OUString aTypeName(xRow->getString(6)); + sal_Int32 nSize = xRow->getInt(7); + sal_Int32 nDec = xRow->getInt(9); + sal_Int32 nNull = xRow->getInt(11); + OUString aColumnDef(xRow->getString(13)); + + xRet = new OIndexColumn(bAsc, + _rName, + aTypeName, + aColumnDef, + nNull, + nSize, + nDec, + nDataType, + true, + aCatalog, aSchema, aTable); + break; + } + } + } + + return xRet; +} + +Reference< XPropertySet > OIndexColumns::createDescriptor() +{ + return new OIndexColumn(true); +} + +void OIndexColumns::impl_refresh() +{ + m_pIndex->refreshColumns(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TIndexes.cxx b/connectivity/source/commontools/TIndexes.cxx new file mode 100644 index 0000000000..2ba9124a51 --- /dev/null +++ b/connectivity/source/commontools/TIndexes.cxx @@ -0,0 +1,247 @@ +/* -*- 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 <connectivity/TIndexes.hxx> +#include <TIndex.hxx> +#include <connectivity/TTableHelper.hxx> +#include <com/sun/star/sdb/tools/XIndexAlteration.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/IndexType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <connectivity/dbtools.hxx> +#include <TConnection.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/types.hxx> +#include <rtl/ustrbuf.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 cppu; + + +OIndexesHelper::OIndexesHelper(OTableHelper* _pTable, + ::osl::Mutex& _rMutex, + const std::vector< OUString> &_rVector + ) + : OCollection(*_pTable,true,_rMutex,_rVector) + ,m_pTable(_pTable) +{ +} + + +sdbcx::ObjectType OIndexesHelper::createObject(const OUString& _rName) +{ + Reference< XConnection> xConnection = m_pTable->getConnection(); + if ( !xConnection.is() ) + return nullptr; + + sdbcx::ObjectType xRet; + OUString aName,aQualifier; + sal_Int32 nLen = _rName.indexOf('.'); + if ( nLen != -1 ) + { + aQualifier = _rName.copy(0,nLen); + aName = _rName.copy(nLen+1); + } + else + aName = _rName; + + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + OUString aSchema,aTable; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= aTable; + + Any aCatalog = m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)); + Reference< XResultSet > xResult = m_pTable->getMetaData()->getIndexInfo(aCatalog,aSchema,aTable,false,false); + + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while( xResult->next() ) + { + bool bUnique = !xRow->getBoolean(4); + if((aQualifier.isEmpty() || xRow->getString(5) == aQualifier ) && xRow->getString(6) == aName) + { + sal_Int32 nClustered = xRow->getShort(7); + bool bPrimarKeyIndex = false; + xRow.clear(); + xResult.clear(); + try + { + xResult = m_pTable->getMetaData()->getPrimaryKeys(aCatalog,aSchema,aTable); + xRow.set(xResult,UNO_QUERY); + + if ( xRow.is() && xResult->next() ) // there can be only one primary key + { + bPrimarKeyIndex = xRow->getString(6) == aName; + } + } + catch(const Exception&) + { + } + xRet = new OIndexHelper(m_pTable,aName,aQualifier,bUnique, + bPrimarKeyIndex, + nClustered == IndexType::CLUSTERED); + break; + } + } + } + + return xRet; +} + +void OIndexesHelper::impl_refresh() +{ + m_pTable->refreshIndexes(); +} + +Reference< XPropertySet > OIndexesHelper::createDescriptor() +{ + return new OIndexHelper(m_pTable); +} + +// XAppend +sdbcx::ObjectType OIndexesHelper::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + Reference< XConnection> xConnection = m_pTable->getConnection(); + if ( !xConnection.is() ) + return nullptr; + if ( m_pTable->isNew() ) + return cloneDescriptor( descriptor ); + + if ( m_pTable->getIndexService().is() ) + { + m_pTable->getIndexService()->addIndex(m_pTable,descriptor); + } + else + { + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + OUStringBuffer aSql( "CREATE " ); + OUString aQuote = m_pTable->getMetaData()->getIdentifierQuoteString( ); + + if(comphelper::getBOOL(descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISUNIQUE)))) + aSql.append("UNIQUE "); + aSql.append("INDEX "); + + + OUString aCatalog,aSchema,aTable; + dbtools::qualifiedNameComponents(m_pTable->getMetaData(),m_pTable->getName(),aCatalog,aSchema,aTable,::dbtools::EComposeRule::InDataManipulation); + + OUString aComposedName = dbtools::composeTableName(m_pTable->getMetaData(),aCatalog,aSchema,aTable, true, ::dbtools::EComposeRule::InIndexDefinitions); + if (!_rForName.isEmpty() ) + { + aSql.append( ::dbtools::quoteName( aQuote, _rForName ) + + " ON " + + aComposedName + + " ( "); + + Reference<XColumnsSupplier> xColumnSup(descriptor,UNO_QUERY); + Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY); + Reference< XPropertySet > xColProp; + bool bAddIndexAppendix = ::dbtools::getBooleanDataSourceSetting( m_pTable->getConnection(), "AddIndexAppendix" ); + sal_Int32 nCount = xColumns->getCount(); + for(sal_Int32 i = 0 ; i < nCount; ++i) + { + xColProp.set(xColumns->getByIndex(i),UNO_QUERY); + aSql.append(::dbtools::quoteName( aQuote,comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))))); + + if ( bAddIndexAppendix ) + { + + aSql.appendAscii(any2bool(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISASCENDING))) + ? + " ASC" + : + " DESC"); + } + aSql.append(","); + } + aSql[aSql.getLength() - 1] = ')'; + } + else + { + aSql.append(aComposedName); + + Reference<XColumnsSupplier> xColumnSup(descriptor,UNO_QUERY); + Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY); + Reference< XPropertySet > xColProp; + if(xColumns->getCount() != 1) + throw SQLException(); + + xColumns->getByIndex(0) >>= xColProp; + + aSql.append("." + + ::dbtools::quoteName( aQuote,comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))))); + } + + Reference< XStatement > xStmt = m_pTable->getConnection()->createStatement( ); + if ( xStmt.is() ) + { + OUString sSql = aSql.makeStringAndClear(); + xStmt->execute(sSql); + ::comphelper::disposeComponent(xStmt); + } + } + + return createObject( _rForName ); +} + +// XDrop +void OIndexesHelper::dropObject(sal_Int32 /*_nPos*/,const OUString& _sElementName) +{ + Reference< XConnection> xConnection = m_pTable->getConnection(); + if( !xConnection.is() || m_pTable->isNew() ) + return; + + if ( m_pTable->getIndexService().is() ) + { + m_pTable->getIndexService()->dropIndex(m_pTable,_sElementName); + } + else + { + OUString aName,aSchema; + sal_Int32 nLen = _sElementName.indexOf('.'); + if(nLen != -1) + aSchema = _sElementName.copy(0,nLen); + aName = _sElementName.copy(nLen+1); + + OUString aSql( "DROP INDEX " ); + + OUString aComposedName = dbtools::composeTableName( m_pTable->getMetaData(), m_pTable, ::dbtools::EComposeRule::InIndexDefinitions, true ); + OUString sIndexName = dbtools::composeTableName( m_pTable->getMetaData(), OUString(), aSchema, aName, true, ::dbtools::EComposeRule::InIndexDefinitions ); + + aSql += sIndexName + " ON " + aComposedName; + + Reference< XStatement > xStmt = m_pTable->getConnection()->createStatement( ); + if ( xStmt.is() ) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TKey.cxx b/connectivity/source/commontools/TKey.cxx new file mode 100644 index 0000000000..1341291abc --- /dev/null +++ b/connectivity/source/commontools/TKey.cxx @@ -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 . + */ + +#include <TKey.hxx> +#include <TKeyColumns.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <TConnection.hxx> +#include <connectivity/TTableHelper.hxx> + +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; + +OTableKeyHelper::OTableKeyHelper(OTableHelper* _pTable) : connectivity::sdbcx::OKey(true) + ,m_pTable(_pTable) +{ + construct(); +} + +OTableKeyHelper::OTableKeyHelper( OTableHelper* _pTable + ,const OUString& Name + ,const std::shared_ptr<sdbcx::KeyProperties>& _rProps + ) : connectivity::sdbcx::OKey(Name,_rProps,true) + ,m_pTable(_pTable) +{ + construct(); + refreshColumns(); +} + +void OTableKeyHelper::refreshColumns() +{ + if ( !m_pTable ) + return; + + std::vector< OUString> aVector; + if ( !isNew() ) + { + aVector = m_aProps->m_aKeyColumnNames; + if ( aVector.empty() ) + { + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + OUString aSchema,aTable; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= aTable; + + if ( !m_Name.isEmpty() ) // foreign key + { + + Reference< XResultSet > xResult = m_pTable->getMetaData()->getImportedKeys(m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)), + aSchema,aTable); + + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while( xResult->next() ) + { + OUString aForeignKeyColumn = xRow->getString(8); + if(xRow->getString(12) == m_Name) + aVector.push_back(aForeignKeyColumn); + } + } + } + + if ( aVector.empty() ) + { + const Reference< XResultSet > xResult = m_pTable->getMetaData()->getPrimaryKeys(m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)), + aSchema,aTable); + + if ( xResult.is() ) + { + const Reference< XRow > xRow(xResult,UNO_QUERY); + while( xResult->next() ) + aVector.push_back(xRow->getString(4)); + } // if ( xResult.is() ) + } + } + } + + + if ( m_pColumns ) + m_pColumns->reFill(aVector); + else + m_pColumns.reset(new OKeyColumnsHelper(this,m_aMutex,aVector)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TKeyColumns.cxx b/connectivity/source/commontools/TKeyColumns.cxx new file mode 100644 index 0000000000..0a2c02bb22 --- /dev/null +++ b/connectivity/source/commontools/TKeyColumns.cxx @@ -0,0 +1,132 @@ +/* -*- 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 <TKeyColumns.hxx> +#include <TKey.hxx> +#include <sdbcx/VKeyColumn.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <TConnection.hxx> +#include <connectivity/TTableHelper.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::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +OKeyColumnsHelper::OKeyColumnsHelper( OTableKeyHelper* _pKey, + ::osl::Mutex& _rMutex, + const std::vector< OUString> &_rVector) + : connectivity::sdbcx::OCollection(*_pKey,true,_rMutex,_rVector) + ,m_pKey(_pKey) +{ +} + +sdbcx::ObjectType OKeyColumnsHelper::createObject(const OUString& _rName) +{ + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + OUString aCatalog, aSchema, aTable; + css::uno::Any Catalog(m_pKey->getTable()->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME))); + Catalog >>= aCatalog; + m_pKey->getTable()->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema; + m_pKey->getTable()->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= aTable; + + // first get the related column to _rName + Reference< XResultSet > xResult = m_pKey->getTable()->getMetaData()->getImportedKeys( + Catalog, aSchema, aTable); + + OUString aRefColumnName; + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + OUString aTemp; + while(xResult->next()) + { + aTemp = xRow->getString(4); + if(xRow->getString(8) == _rName && m_pKey->getName() == xRow->getString(12)) + { + aRefColumnName = aTemp; + break; + } + } + } + + sdbcx::ObjectType xRet; + + // now describe the column _rName and set his related column + xResult = m_pKey->getTable()->getMetaData()->getColumns(Catalog, aSchema, aTable, _rName); + + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + if ( xResult->next() ) + { + if ( xRow->getString(4) == _rName ) + { + sal_Int32 nDataType = xRow->getInt(5); + OUString aTypeName(xRow->getString(6)); + sal_Int32 nSize = xRow->getInt(7); + sal_Int32 nDec = xRow->getInt(9); + sal_Int32 nNull = xRow->getInt(11); + OUString sColumnDef; + try + { + sColumnDef = xRow->getString(13); + } + catch(const SQLException&) + { + // sometimes we get an error when asking for this param + } + + xRet = new OKeyColumn(aRefColumnName, + _rName, + aTypeName, + sColumnDef, + nNull, + nSize, + nDec, + nDataType, + isCaseSensitive(), + aCatalog, + aSchema, + aTable); + } + } + } + + return xRet; +} + +Reference< XPropertySet > OKeyColumnsHelper::createDescriptor() +{ + return new OKeyColumn(isCaseSensitive()); +} + +void OKeyColumnsHelper::impl_refresh() +{ + m_pKey->refreshColumns(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TKeys.cxx b/connectivity/source/commontools/TKeys.cxx new file mode 100644 index 0000000000..e6e50f20a4 --- /dev/null +++ b/connectivity/source/commontools/TKeys.cxx @@ -0,0 +1,307 @@ +/* -*- 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 <connectivity/TKeys.hxx> +#include <TKey.hxx> +#include <connectivity/TTableHelper.hxx> +#include <com/sun/star/sdb/tools/XKeyAlteration.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <connectivity/dbtools.hxx> +#include <comphelper/types.hxx> +#include <TConnection.hxx> + +namespace connectivity +{ +using namespace comphelper; +using namespace connectivity::sdbcx; +using namespace dbtools; +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; + + +OKeysHelper::OKeysHelper( OTableHelper* _pTable, + ::osl::Mutex& _rMutex, + const ::std::vector< OUString>& _rVector + ) : OKeys_BASE(*_pTable,true,_rMutex,_rVector,true) + ,m_pTable(_pTable) +{ +} + +sdbcx::ObjectType OKeysHelper::createObject(const OUString& _rName) +{ + sdbcx::ObjectType xRet; + + if(!_rName.isEmpty()) + { + xRet = new OTableKeyHelper(m_pTable,_rName,m_pTable->getKeyProperties(_rName)); + } + + if(!xRet.is()) // we have a primary key with a system name + { + xRet = new OTableKeyHelper(m_pTable,_rName,m_pTable->getKeyProperties(_rName)); + } + + return xRet; +} + +void OKeysHelper::impl_refresh() +{ + m_pTable->refreshKeys(); +} + +Reference< XPropertySet > OKeysHelper::createDescriptor() +{ + return new OTableKeyHelper(m_pTable); +} + +/** returns the keyrule string for the primary key +*/ +static OUString getKeyRuleString(bool _bUpdate,sal_Int32 _nKeyRule) +{ + const char* pKeyRule = nullptr; + switch ( _nKeyRule ) + { + case KeyRule::CASCADE: + pKeyRule = _bUpdate ? " ON UPDATE CASCADE " : " ON DELETE CASCADE "; + break; + case KeyRule::RESTRICT: + pKeyRule = _bUpdate ? " ON UPDATE RESTRICT " : " ON DELETE RESTRICT "; + break; + case KeyRule::SET_NULL: + pKeyRule = _bUpdate ? " ON UPDATE SET NULL " : " ON DELETE SET NULL "; + break; + case KeyRule::SET_DEFAULT: + pKeyRule = _bUpdate ? " ON UPDATE SET DEFAULT " : " ON DELETE SET DEFAULT "; + break; + default: + ; + } + OUString sRet; + if ( pKeyRule ) + sRet = OUString::createFromAscii(pKeyRule); + return sRet; +} + +void OKeysHelper::cloneDescriptorColumns( const sdbcx::ObjectType& _rSourceDescriptor, const sdbcx::ObjectType& _rDestDescriptor ) +{ + Reference< XColumnsSupplier > xColSupp( _rSourceDescriptor, UNO_QUERY_THROW ); + Reference< XIndexAccess > xSourceCols( xColSupp->getColumns(), UNO_QUERY_THROW ); + + xColSupp.set( _rDestDescriptor, UNO_QUERY_THROW ); + Reference< XAppend > xDestAppend( xColSupp->getColumns(), UNO_QUERY_THROW ); + + sal_Int32 nCount = xSourceCols->getCount(); + for ( sal_Int32 i=0; i< nCount; ++i ) + { + Reference< XPropertySet > xColProp( xSourceCols->getByIndex(i), UNO_QUERY ); + xDestAppend->appendByDescriptor( xColProp ); + } +} + +// XAppend +sdbcx::ObjectType OKeysHelper::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + Reference< XConnection> xConnection = m_pTable->getConnection(); + if ( !xConnection.is() ) + return nullptr; + if ( m_pTable->isNew() ) + { + Reference< XPropertySet > xNewDescriptor( cloneDescriptor( descriptor ) ); + cloneDescriptorColumns( descriptor, xNewDescriptor ); + return xNewDescriptor; + } + + const ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + sal_Int32 nKeyType = getINT32(descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE))); + sal_Int32 nUpdateRule = 0, nDeleteRule = 0; + OUString sReferencedName; + + if ( nKeyType == KeyType::FOREIGN ) + { + descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_REFERENCEDTABLE)) >>= sReferencedName; + descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_UPDATERULE)) >>= nUpdateRule; + descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_DELETERULE)) >>= nDeleteRule; + } + + if ( m_pTable->getKeyService().is() ) + { + m_pTable->getKeyService()->addKey(m_pTable,descriptor); + } + else + { + // if we're here, we belong to a table which is not new, i.e. already exists in the database. + // In this case, really append the new index. + OUStringBuffer aSql("ALTER TABLE "); + OUString aQuote = m_pTable->getConnection()->getMetaData()->getIdentifierQuoteString( ); + + aSql.append(composeTableName( m_pTable->getConnection()->getMetaData(), m_pTable, ::dbtools::EComposeRule::InTableDefinitions, true ) + + " ADD "); + + if ( nKeyType == KeyType::PRIMARY ) + { + aSql.append(" PRIMARY KEY ("); + } + else if ( nKeyType == KeyType::FOREIGN ) + { + aSql.append(" FOREIGN KEY ("); + } + else + throw SQLException(); + + Reference<XColumnsSupplier> xColumnSup(descriptor,UNO_QUERY); + Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY); + Reference< XPropertySet > xColProp; + for(sal_Int32 i = 0 ; i < xColumns->getCount() ; ++i) + { + if ( i > 0 ) + aSql.append(","); + xColProp.set(xColumns->getByIndex(i), css::uno::UNO_QUERY); + aSql.append( ::dbtools::quoteName( aQuote,getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)))) ); + + } + aSql.append(")"); + + if ( nKeyType == KeyType::FOREIGN ) + { + aSql.append(" REFERENCES " + + ::dbtools::quoteTableName(m_pTable->getConnection()->getMetaData(),sReferencedName,::dbtools::EComposeRule::InTableDefinitions) + + " ("); + + for(sal_Int32 i=0;i<xColumns->getCount();++i) + { + if ( i > 0 ) + aSql.append(","); + xColumns->getByIndex(i) >>= xColProp; + aSql.append(::dbtools::quoteName( aQuote,getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_RELATEDCOLUMN))))); + + } + aSql.append(")" + + getKeyRuleString(true ,nUpdateRule) + + getKeyRuleString(false ,nDeleteRule)); + } + + Reference< XStatement > xStmt = m_pTable->getConnection()->createStatement( ); + xStmt->execute(aSql.makeStringAndClear()); + } + // find the name which the database gave the new key + OUString sNewName( _rForName ); + try + { + OUString aSchema,aTable; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema; + m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= aTable; + Reference< XResultSet > xResult; + sal_Int32 nColumn = 12; + if ( nKeyType == KeyType::FOREIGN ) + xResult = m_pTable->getMetaData()->getImportedKeys( m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) + ,aSchema + ,aTable); + else + { + xResult = m_pTable->getMetaData()->getPrimaryKeys( m_pTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) + ,aSchema + ,aTable); + nColumn = 6; + } + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while( xResult->next() ) + { + OUString sName = xRow->getString(nColumn); + if ( !m_pElements->exists(sName) ) // this name wasn't inserted yet so it must be the new one + { + descriptor->setPropertyValue( rPropMap.getNameByIndex( PROPERTY_ID_NAME ), Any( sName ) ); + sNewName = sName; + break; + } + } + ::comphelper::disposeComponent(xResult); + } + } + catch(const SQLException&) + { + } + + m_pTable->addKey(sNewName,std::make_shared<sdbcx::KeyProperties>(sReferencedName,nKeyType,nUpdateRule,nDeleteRule)); + + return createObject( sNewName ); +} + +OUString OKeysHelper::getDropForeignKey() const +{ + return " DROP CONSTRAINT "; +} + +// XDrop +void OKeysHelper::dropObject(sal_Int32 _nPos, const OUString& _sElementName) +{ + Reference< XConnection> xConnection = m_pTable->getConnection(); + if ( !xConnection.is() || m_pTable->isNew() ) + return; + + Reference<XPropertySet> xKey(getObject(_nPos),UNO_QUERY); + if ( m_pTable->getKeyService().is() ) + { + m_pTable->getKeyService()->dropKey(m_pTable,xKey); + } + else + { + OUStringBuffer aSql( + "ALTER TABLE " + + composeTableName( m_pTable->getConnection()->getMetaData(), m_pTable,::dbtools::EComposeRule::InTableDefinitions, true )); + + sal_Int32 nKeyType = KeyType::PRIMARY; + if ( xKey.is() ) + { + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + xKey->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE)) >>= nKeyType; + } + if ( KeyType::PRIMARY == nKeyType ) + { + aSql.append(" DROP PRIMARY KEY"); + } + else + { + aSql.append(getDropForeignKey()); + const OUString aQuote = m_pTable->getConnection()->getMetaData()->getIdentifierQuoteString(); + aSql.append( ::dbtools::quoteName( aQuote,_sElementName) ); + } + + Reference< XStatement > xStmt = m_pTable->getConnection()->createStatement( ); + if ( xStmt.is() ) + { + xStmt->execute(aSql.makeStringAndClear()); + ::comphelper::disposeComponent(xStmt); + } + } +} + +} // namespace connectivity + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TPrivilegesResultSet.cxx b/connectivity/source/commontools/TPrivilegesResultSet.cxx new file mode 100644 index 0000000000..928e9c016a --- /dev/null +++ b/connectivity/source/commontools/TPrivilegesResultSet.cxx @@ -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 . + */ + +#include <TPrivilegesResultSet.hxx> + +using namespace connectivity; + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OResultSetPrivileges::OResultSetPrivileges( const Reference< XDatabaseMetaData>& _rxMeta + , const Any& catalog + , const OUString& schemaPattern + , const OUString& tableNamePattern) + : ODatabaseMetaDataResultSet(eTablePrivileges) + , m_bResetValues(true) +{ + osl_atomic_increment( &m_refCount ); + { + OUString sUserWorkingFor; + // we want all catalogues, all schemas, all tables + Sequence< OUString > sTableTypes {"VIEW", "TABLE", "%"}; // this last one is just to be sure to include anything else... + try + { + m_xTables = _rxMeta->getTables(catalog,schemaPattern,tableNamePattern,sTableTypes); + m_xRow.set(m_xTables,UNO_QUERY); + + sUserWorkingFor = _rxMeta->getUserName(); + } + catch(Exception&) + { + } + + ODatabaseMetaDataResultSet::ORows aRows; + ODatabaseMetaDataResultSet::ORow aRow(8); + aRow[5] = new ORowSetValueDecorator(sUserWorkingFor); + aRow[6] = ODatabaseMetaDataResultSet::getSelectValue(); + aRow[7] = new ORowSetValueDecorator(OUString("YES")); + aRows.push_back(aRow); + aRow[6] = ODatabaseMetaDataResultSet::getInsertValue(); + aRows.push_back(aRow); + aRow[6] = ODatabaseMetaDataResultSet::getDeleteValue(); + aRows.push_back(aRow); + aRow[6] = ODatabaseMetaDataResultSet::getUpdateValue(); + aRows.push_back(aRow); + aRow[6] = ODatabaseMetaDataResultSet::getCreateValue(); + aRows.push_back(aRow); + aRow[6] = ODatabaseMetaDataResultSet::getReadValue(); + aRows.push_back(aRow); + aRow[6] = ODatabaseMetaDataResultSet::getAlterValue(); + aRows.push_back(aRow); + aRow[6] = ODatabaseMetaDataResultSet::getDropValue(); + aRows.push_back(aRow); + aRow[6] = new ORowSetValueDecorator(OUString("REFERENCE")); + aRows.push_back(aRow); + + setRows(std::move(aRows)); + } + osl_atomic_decrement( &m_refCount ); +} + +const ORowSetValue& OResultSetPrivileges::getValue(sal_Int32 columnIndex) +{ + switch(columnIndex) + { + case 1: + case 2: + case 3: + if ( m_xRow.is() && m_bResetValues ) + { + (*m_aRowsIter)[1] = new ORowSetValueDecorator(m_xRow->getString(1)); + if ( m_xRow->wasNull() ) + (*m_aRowsIter)[1]->setNull(); + (*m_aRowsIter)[2] = new ORowSetValueDecorator(m_xRow->getString(2)); + if ( m_xRow->wasNull() ) + (*m_aRowsIter)[2]->setNull(); + (*m_aRowsIter)[3] = new ORowSetValueDecorator(m_xRow->getString(3)); + if ( m_xRow->wasNull() ) + (*m_aRowsIter)[3]->setNull(); + + m_bResetValues = false; + } + } + return ODatabaseMetaDataResultSet::getValue(columnIndex); +} + +void SAL_CALL OResultSetPrivileges::disposing() +{ + ODatabaseMetaDataResultSet::disposing(); + m_xTables.clear(); + m_xRow.clear(); +} + +sal_Bool SAL_CALL OResultSetPrivileges::next( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + bool bReturn = false; + if ( m_xTables.is() ) + { + if ( m_bBOF ) + { + m_bResetValues = true; + if ( !m_xTables->next() ) + return false; + } + + bReturn = ODatabaseMetaDataResultSet::next(); + if ( !bReturn ) + { + m_bBOF = false; + m_bResetValues = bReturn = m_xTables->next(); + } + } + return bReturn; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TSkipDeletedSet.cxx b/connectivity/source/commontools/TSkipDeletedSet.cxx new file mode 100644 index 0000000000..701bd743f6 --- /dev/null +++ b/connectivity/source/commontools/TSkipDeletedSet.cxx @@ -0,0 +1,255 @@ +/* -*- 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 <TSkipDeletedSet.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <algorithm> + +using namespace connectivity; + +OSkipDeletedSet::OSkipDeletedSet(IResultSetHelper* _pHelper) + : m_pHelper(_pHelper) + ,m_bDeletedVisible(false) +{ + m_aBookmarksPositions.reserve(256); +} + +OSkipDeletedSet::~OSkipDeletedSet() +{ + m_aBookmarksPositions.clear(); + //m_aBookmarks.clear(); +} + +bool OSkipDeletedSet::skipDeleted(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset, bool _bRetrieveData) +{ + OSL_ENSURE(_eCursorPosition != IResultSetHelper::BOOKMARK,"OSkipDeletedSet::SkipDeleted can't be called for BOOKMARK"); + + IResultSetHelper::Movement eDelPosition = _eCursorPosition; + sal_Int32 nDelOffset = abs(_nOffset); + + switch (_eCursorPosition) + { + case IResultSetHelper::ABSOLUTE1: + return moveAbsolute(_nOffset,_bRetrieveData); + case IResultSetHelper::FIRST: // set the movement when positioning failed + eDelPosition = IResultSetHelper::NEXT; + nDelOffset = 1; + break; + case IResultSetHelper::LAST: + eDelPosition = IResultSetHelper::PRIOR; // last row is invalid so position before + nDelOffset = 1; + break; + case IResultSetHelper::RELATIVE1: + eDelPosition = (_nOffset >= 0) ? IResultSetHelper::NEXT : IResultSetHelper::PRIOR; + break; + default: + break; + } + + bool bDone = true; + bool bDataFound = false; + + if (_eCursorPosition == IResultSetHelper::LAST) + { + SAL_INFO( "connectivity.commontools", "OSkipDeletedSet::skipDeleted: last" ); + sal_Int32 nBookmark = 0; + // first position on the last known row + if ( m_aBookmarksPositions.empty() ) + { + bDataFound = m_pHelper->move(IResultSetHelper::FIRST, 0, _bRetrieveData); + if(bDataFound && (m_bDeletedVisible || !m_pHelper->isRowDeleted())) + //m_aBookmarksPositions.push_back(m_aBookmarks.emplace( m_pHelper->getDriverPos(),m_aBookmarksPositions.size()+1)).first); + m_aBookmarksPositions.push_back(m_pHelper->getDriverPos()); + } + else + { + // I already have a bookmark so we can positioned on that and look if it is the last one + nBookmark = (*m_aBookmarksPositions.rbegin())/*->first*/; + + bDataFound = m_pHelper->move(IResultSetHelper::BOOKMARK, nBookmark, _bRetrieveData); + OSL_ENSURE((m_bDeletedVisible || !m_pHelper->isRowDeleted()),"A bookmark should not be deleted!"); + } + + + // and then move forward until we are after the last row + while(bDataFound) + { + bDataFound = m_pHelper->move(IResultSetHelper::NEXT, 1, false); // we don't need the data here + if( bDataFound && ( m_bDeletedVisible || !m_pHelper->isRowDeleted()) ) + { // we weren't on the last row we remember it and move on + m_aBookmarksPositions.push_back(m_pHelper->getDriverPos()); + //m_aBookmarksPositions.push_back(m_aBookmarks.emplace( m_pHelper->getDriverPos(),m_aBookmarksPositions.size()+1)).first); + } + else if(!bDataFound && !m_aBookmarksPositions.empty() ) + { + // i already know the last bookmark :-) + // now we only have to repositioning us to the last row + nBookmark = (*m_aBookmarksPositions.rbegin())/*->first*/; + bDataFound = m_pHelper->move(IResultSetHelper::BOOKMARK, nBookmark, _bRetrieveData); + break; + } + } + return bDataFound; + } + else if (_eCursorPosition != IResultSetHelper::RELATIVE1) + { + bDataFound = m_pHelper->move(_eCursorPosition, _nOffset, _bRetrieveData); + bDone = bDataFound && (m_bDeletedVisible || !m_pHelper->isRowDeleted()); + } + else + { + bDataFound = m_pHelper->move(eDelPosition, 1, _bRetrieveData); + if (bDataFound && (m_bDeletedVisible || !m_pHelper->isRowDeleted())) + { + bDone = (--nDelOffset) == 0; + if ( !bDone ) + m_aBookmarksPositions.push_back(m_pHelper->getDriverPos()); + //m_aBookmarksPositions.push_back(m_aBookmarks.emplace( m_pHelper->getDriverPos(),m_aBookmarksPositions.size()+1)).first); + } + else + bDone = false; + } + + while (bDataFound && !bDone) // Iterate until we are at the valid set + { + bDataFound = m_pHelper->move(eDelPosition, 1, _bRetrieveData); + if (_eCursorPosition != IResultSetHelper::RELATIVE1) + bDone = bDataFound && (m_bDeletedVisible || !m_pHelper->isRowDeleted()); + else if (bDataFound && (m_bDeletedVisible || !m_pHelper->isRowDeleted())) + { + bDone = (--nDelOffset) == 0; + if ( !bDone ) + m_aBookmarksPositions.push_back(m_pHelper->getDriverPos()); + //m_aBookmarksPositions.push_back(m_aBookmarks.emplace( m_pHelper->getDriverPos(),m_aBookmarksPositions.size()+1)).first); + } + else + bDone = false; + } + + if(bDataFound && bDone) + { + const sal_Int32 nDriverPos = m_pHelper->getDriverPos(); + if ( m_bDeletedVisible ) + { + if ( nDriverPos > static_cast<sal_Int32>(m_aBookmarksPositions.size()) ) + m_aBookmarksPositions.push_back(nDriverPos); + } + else if ( std::find(m_aBookmarksPositions.begin(),m_aBookmarksPositions.end(),nDriverPos) == m_aBookmarksPositions.end() ) + m_aBookmarksPositions.push_back(nDriverPos); + /*sal_Int32 nDriverPos = m_pHelper->getDriverPos(); + if(m_aBookmarks.find(nDriverPos) == m_aBookmarks.end()) + m_aBookmarksPositions.push_back(m_aBookmarks.emplace( nDriverPos,m_aBookmarksPositions.size()+1)).first);*/ + } + + return bDataFound; +} + +bool OSkipDeletedSet::moveAbsolute(sal_Int32 _nPos,bool _bRetrieveData) +{ + bool bDataFound = false; + sal_Int32 nNewPos = _nPos; + if(nNewPos > 0) + { + if(static_cast<sal_Int32>(m_aBookmarksPositions.size()) < nNewPos) + { + // bookmark isn't known yet + // start at the last known position + sal_Int32 nCurPos = 0; + if ( m_aBookmarksPositions.empty() ) + { + bDataFound = m_pHelper->move(IResultSetHelper::FIRST, 0, _bRetrieveData ); + if(bDataFound && (m_bDeletedVisible || !m_pHelper->isRowDeleted())) + { + ++nCurPos; + m_aBookmarksPositions.push_back(m_pHelper->getDriverPos()); + //m_aBookmarksPositions.push_back(m_aBookmarks.emplace( m_pHelper->getDriverPos(),m_aBookmarksPositions.size()+1)).first); + --nNewPos; + } + } // if ( m_aBookmarksPositions.empty() ) + else + { + sal_Int32 nLastBookmark = *m_aBookmarksPositions.rbegin()/*->first*/; + nCurPos = /*(**/m_aBookmarksPositions.size()/*->second*/; + nNewPos = nNewPos - nCurPos; + bDataFound = m_pHelper->move(IResultSetHelper::BOOKMARK, nLastBookmark, _bRetrieveData); + } + + // now move to that row we need and don't count deleted rows + while (bDataFound && nNewPos) + { + bDataFound = m_pHelper->move(IResultSetHelper::NEXT, 1, _bRetrieveData); + if(bDataFound && (m_bDeletedVisible || !m_pHelper->isRowDeleted())) + { + ++nCurPos; + m_aBookmarksPositions.push_back(m_pHelper->getDriverPos()); + //m_aBookmarksPositions.push_back(m_aBookmarks.emplace( m_pHelper->getDriverPos(),m_aBookmarksPositions.size()+1)).first); + --nNewPos; + } + } + } + else + { + const sal_Int32 nBookmark = m_aBookmarksPositions[nNewPos-1]/*->first*/; + bDataFound = m_pHelper->move(IResultSetHelper::BOOKMARK,nBookmark, _bRetrieveData); + OSL_ENSURE((m_bDeletedVisible || !m_pHelper->isRowDeleted()),"moveAbsolute: row can't be deleted!"); + } + } + else + { + ++nNewPos; + bDataFound = skipDeleted(IResultSetHelper::LAST,0,nNewPos == 0); + + for(sal_Int32 i=nNewPos+1;bDataFound && i <= 0;++i) + bDataFound = skipDeleted(IResultSetHelper::PRIOR,1,i == 0); + + } + return bDataFound; +} + +void OSkipDeletedSet::clear() +{ + std::vector<sal_Int32>().swap(m_aBookmarksPositions); +} + +sal_Int32 OSkipDeletedSet::getMappedPosition(sal_Int32 _nPos) const +{ + std::vector<sal_Int32>::const_iterator aFind = std::find(m_aBookmarksPositions.begin(),m_aBookmarksPositions.end(),_nPos); + if ( aFind != m_aBookmarksPositions.end() ) + return (aFind - m_aBookmarksPositions.begin()) + 1; + OSL_FAIL("Why!"); + return -1; +} + +void OSkipDeletedSet::insertNewPosition(sal_Int32 _nPos) +{ + m_aBookmarksPositions.push_back(_nPos); +} + +void OSkipDeletedSet::deletePosition(sal_Int32 _nBookmark) +{ + std::vector<sal_Int32>::iterator aFind = std::find(m_aBookmarksPositions.begin(),m_aBookmarksPositions.end(),_nBookmark); + if ( aFind != m_aBookmarksPositions.end() ) + { + m_aBookmarksPositions.erase(aFind); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TSortIndex.cxx b/connectivity/source/commontools/TSortIndex.cxx new file mode 100644 index 0000000000..44a883dc86 --- /dev/null +++ b/connectivity/source/commontools/TSortIndex.cxx @@ -0,0 +1,152 @@ +/* -*- 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 <TSortIndex.hxx> +#include <algorithm> +#include <iterator> +#include <o3tl/functional.hxx> + +using namespace connectivity; + +namespace { + +/// Functor object for class OSortIndex::TIntValuePairVector::value_type returntype is bool +struct TKeyValueFunc +{ + OSortIndex* pIndex; + + explicit TKeyValueFunc(OSortIndex* _pIndex) : pIndex(_pIndex) + { + } + // return false if compared values are equal otherwise true + bool operator()(const OSortIndex::TIntValuePairVector::value_type& lhs,const OSortIndex::TIntValuePairVector::value_type& rhs) const + { + const std::vector<OKeyType>& aKeyType = pIndex->getKeyType(); + size_t i = 0; + for (auto const& elem : aKeyType) + { + const bool bGreater = pIndex->getAscending(i) != TAscendingOrder::ASC; + const bool bLess = !bGreater; + + // compare depending for type + switch (elem) + { + case OKeyType::String: + { + sal_Int32 nRes = lhs.second->getKeyString(i).compareTo(rhs.second->getKeyString(i)); + if (nRes < 0) + return bLess; + else if (nRes > 0) + return bGreater; + } + break; + case OKeyType::Double: + { + double d1 = lhs.second->getKeyDouble(i); + double d2 = rhs.second->getKeyDouble(i); + + if (d1 < d2) + return bLess; + else if (d1 > d2) + return bGreater; + } + break; + case OKeyType::NONE: + break; + } + ++i; + } + + // know we know that the values are equal + return false; + } +}; + +} + +::rtl::Reference<OKeySet> OSortIndex::CreateKeySet() +{ + Freeze(); + + ::rtl::Reference<OKeySet> pKeySet = new OKeySet(); + pKeySet->reserve(m_aKeyValues.size()); + std::transform(m_aKeyValues.begin() + ,m_aKeyValues.end() + ,std::back_inserter(*pKeySet) + ,::o3tl::select1st<TIntValuePairVector::value_type>()); + pKeySet->setFrozen(); + return pKeySet; +} + +OSortIndex::OSortIndex( std::vector<OKeyType>&& _aKeyType, + std::vector<TAscendingOrder>&& _aAscending) + :m_aKeyType(std::move(_aKeyType)) + ,m_aAscending(std::move(_aAscending)) + ,m_bFrozen(false) +{ +} + +OSortIndex::~OSortIndex() +{ +} + +void OSortIndex::AddKeyValue(std::unique_ptr<OKeyValue> pKeyValue) +{ + assert(pKeyValue && "Can not be null here!"); + if(m_bFrozen) + { + m_aKeyValues.push_back({pKeyValue->getValue(),nullptr}); + } + else + m_aKeyValues.push_back({pKeyValue->getValue(),std::move(pKeyValue)}); +} + +void OSortIndex::Freeze() +{ + OSL_ENSURE(! m_bFrozen,"OSortIndex::Freeze: already frozen!"); + // sorting: + if (m_aKeyType[0] != OKeyType::NONE) + // we will sort ourself when the first keyType say so + std::sort(m_aKeyValues.begin(),m_aKeyValues.end(),TKeyValueFunc(this)); + + for (auto & keyValue : m_aKeyValues) + { + keyValue.second.reset(); + } + + m_bFrozen = true; +} + + +OKeyValue::OKeyValue(sal_Int32 nVal) +: m_nValue(nVal) +{ +} + +OKeyValue::~OKeyValue() +{ +} + +std::unique_ptr<OKeyValue> OKeyValue::createKeyValue(sal_Int32 _nVal) +{ + return std::unique_ptr<OKeyValue>(new OKeyValue(_nVal)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/TTableHelper.cxx b/connectivity/source/commontools/TTableHelper.cxx new file mode 100644 index 0000000000..31e2404694 --- /dev/null +++ b/connectivity/source/commontools/TTableHelper.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 <sal/config.h> +#include <sal/log.hxx> + +#include <connectivity/TTableHelper.hxx> +#include <com/sun/star/sdb/tools/XTableRename.hpp> +#include <com/sun/star/sdb/tools/XTableAlteration.hpp> +#include <com/sun/star/sdb/tools/XKeyAlteration.hpp> +#include <com/sun/star/sdb/tools/XIndexAlteration.hpp> +#include <sdbcx/VKey.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/sdbcx/VCollection.hxx> +#include <unotools/sharedunocomponent.hxx> +#include <TConnection.hxx> + +#include <o3tl/functional.hxx> + +#include <iterator> +#include <set> + +using namespace ::comphelper; +using namespace connectivity; +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; + +namespace +{ + /// helper class for column property change events which holds the OComponentDefinition weak +class OTableContainerListener: + public ::cppu::WeakImplHelper< XContainerListener > +{ + OTableHelper* m_pComponent; + std::map< OUString,bool> m_aRefNames; + +protected: + virtual ~OTableContainerListener() override {} +public: + explicit OTableContainerListener(OTableHelper* _pComponent) : m_pComponent(_pComponent){} + // noncopyable + OTableContainerListener(const OTableContainerListener&) = delete; + const OTableContainerListener& operator=(const OTableContainerListener&) = delete; + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& /*Event*/ ) override + { + } + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override + { + // tdf#137745, perhaps connectivity::OTableHelper::disposing() has been called + // which called OTableContainerListener::clear(), so m_pComponent may be null + if (m_pComponent == nullptr) + return; + + OUString sName; + Event.Accessor >>= sName; + if ( m_aRefNames.find(sName) != m_aRefNames.end() ) + m_pComponent->refreshKeys(); + } + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override + { + OUString sOldComposedName,sNewComposedName; + Event.ReplacedElement >>= sOldComposedName; + Event.Accessor >>= sNewComposedName; + if ( sOldComposedName != sNewComposedName && m_aRefNames.find(sOldComposedName) != m_aRefNames.end() ) + m_pComponent->refreshKeys(); + } + // XEventListener + virtual void SAL_CALL disposing( const EventObject& /*_rSource*/ ) override + { + } + void clear() { m_pComponent = nullptr; } + void add(const OUString& _sRefName) { m_aRefNames.emplace(_sRefName,true); } +}; +} +namespace connectivity +{ + 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; + } + struct OTableHelperImpl + { + TKeyMap m_aKeys; + // helper services which can be provided by extensions + Reference< css::sdb::tools::XTableRename> m_xRename; + Reference< css::sdb::tools::XTableAlteration> m_xAlter; + Reference< css::sdb::tools::XKeyAlteration> m_xKeyAlter; + Reference< css::sdb::tools::XIndexAlteration> m_xIndexAlter; + + Reference< css::sdbc::XDatabaseMetaData > m_xMetaData; + Reference< css::sdbc::XConnection > m_xConnection; + rtl::Reference<OTableContainerListener> m_xTablePropertyListener; + std::vector< ColumnDesc > m_aColumnDesc; + explicit OTableHelperImpl(const Reference< css::sdbc::XConnection >& _xConnection) + : m_xConnection(_xConnection) + { + try + { + m_xMetaData = m_xConnection->getMetaData(); + Reference<XMultiServiceFactory> xFac(_xConnection,UNO_QUERY); + if ( xFac.is() ) + { + m_xRename.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,"TableRenameServiceName")),UNO_QUERY); + m_xAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,"TableAlterationServiceName")),UNO_QUERY); + m_xKeyAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,"KeyAlterationServiceName")),UNO_QUERY); + m_xIndexAlter.set(xFac->createInstance(lcl_getServiceNameForSetting(m_xConnection,"IndexAlterationServiceName")),UNO_QUERY); + } + } + catch(const Exception&) + { + } + } + }; +} + +OTableHelper::OTableHelper( sdbcx::OCollection* _pTables, + const Reference< XConnection >& _xConnection, + bool _bCase) + :OTable_TYPEDEF(_pTables,_bCase) + ,m_pImpl(new OTableHelperImpl(_xConnection)) +{ +} + +OTableHelper::OTableHelper( sdbcx::OCollection* _pTables, + const Reference< XConnection >& _xConnection, + bool _bCase, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName + ) : OTable_TYPEDEF(_pTables, + _bCase, + Name, + Type, + Description, + SchemaName, + CatalogName) + ,m_pImpl(new OTableHelperImpl(_xConnection)) +{ +} + +OTableHelper::~OTableHelper() +{ +} + +void SAL_CALL OTableHelper::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + if ( m_pImpl->m_xTablePropertyListener.is() ) + { + m_pTables->removeContainerListener(m_pImpl->m_xTablePropertyListener); + m_pImpl->m_xTablePropertyListener->clear(); + m_pImpl->m_xTablePropertyListener.clear(); + } + OTable_TYPEDEF::disposing(); + + m_pImpl->m_xConnection = nullptr; + m_pImpl->m_xMetaData = nullptr; + +} + + +namespace +{ + /** collects ColumnDesc's from a resultset produced by XDatabaseMetaData::getColumns + */ + void lcl_collectColumnDescs_throw( const Reference< XResultSet >& _rxResult, std::vector< ColumnDesc >& _out_rColumns ) + { + Reference< XRow > xRow( _rxResult, UNO_QUERY_THROW ); + OUString sName; + OrdinalPosition nOrdinalPosition( 0 ); + while ( _rxResult->next() ) + { + sName = xRow->getString( 4 ); // COLUMN_NAME + sal_Int32 nField5 = xRow->getInt(5); + OUString aField6 = xRow->getString(6); + sal_Int32 nField7 = xRow->getInt(7) + , nField9 = xRow->getInt(9) + , nField11= xRow->getInt(11); + OUString sField12 = xRow->getString(12) + ,sField13 = xRow->getString(13); + nOrdinalPosition = xRow->getInt( 17 ); // ORDINAL_POSITION + _out_rColumns.push_back( ColumnDesc( sName,nField5,aField6,nField7,nField9,nField11,sField12,sField13, nOrdinalPosition ) ); + } + } + + /** checks a given array of ColumnDesc's whether it has reasonable ordinal positions. If not, + they will be normalized to be the array index. + */ + void lcl_sanitizeColumnDescs( std::vector< ColumnDesc >& _rColumns ) + { + if ( _rColumns.empty() ) + return; + + // collect all used ordinals + std::set< OrdinalPosition > aUsedOrdinals; + for ( const auto& collect : _rColumns ) + aUsedOrdinals.insert( collect.nOrdinalPosition ); + + // we need to have as much different ordinals as we have different columns + bool bDuplicates = aUsedOrdinals.size() != _rColumns.size(); + // and it needs to be a continuous range + size_t nOrdinalsRange = *aUsedOrdinals.rbegin() - *aUsedOrdinals.begin() + 1; + bool bGaps = nOrdinalsRange != _rColumns.size(); + + // if that's not the case, normalize it + if ( bGaps || bDuplicates ) + { + OSL_FAIL( "lcl_sanitizeColumnDescs: database did provide invalid ORDINAL_POSITION values!" ); + + OrdinalPosition nNormalizedPosition = 1; + for ( auto& normalize : _rColumns ) + normalize.nOrdinalPosition = nNormalizedPosition++; + return; + } + + // what's left is that the range might not be from 1 to <column count>, but for instance + // 0 to <column count>-1. + size_t nOffset = *aUsedOrdinals.begin() - 1; + for ( auto& offset : _rColumns ) + offset.nOrdinalPosition -= nOffset; + } +} + + +void OTableHelper::refreshColumns() +{ + ::std::vector< OUString> aVector; + if(!isNew()) + { + Any aCatalog; + if ( !m_CatalogName.isEmpty() ) + aCatalog <<= m_CatalogName; + + ::utl::SharedUNOComponent< XResultSet > xResult( getMetaData()->getColumns( + aCatalog, + m_SchemaName, + m_Name, + "%" + ) ); + + // collect the column names, together with their ordinal position + m_pImpl->m_aColumnDesc.clear(); + lcl_collectColumnDescs_throw( xResult, m_pImpl->m_aColumnDesc ); + + // ensure that the ordinal positions as obtained from the meta data do make sense + lcl_sanitizeColumnDescs( m_pImpl->m_aColumnDesc ); + + // sort by ordinal position + std::map< OrdinalPosition, OUString > aSortedColumns; + for (const auto& copy : m_pImpl->m_aColumnDesc) + aSortedColumns[ copy.nOrdinalPosition ] = copy.sName; + + // copy them to aVector, now that we have the proper ordering + std::transform( + aSortedColumns.begin(), + aSortedColumns.end(), + std::insert_iterator< ::std::vector< OUString> >( aVector, aVector.begin() ), + ::o3tl::select2nd< std::map< OrdinalPosition, OUString >::value_type >() + ); + } + + if(m_xColumns) + m_xColumns->reFill(aVector); + else + m_xColumns.reset(createColumns(aVector)); +} + +const ColumnDesc* OTableHelper::getColumnDescription(const OUString& _sName) const +{ + const ColumnDesc* pRet = nullptr; + auto aIter = std::find_if(m_pImpl->m_aColumnDesc.begin(), m_pImpl->m_aColumnDesc.end(), + [&_sName](const ColumnDesc& rColumnDesc) { return rColumnDesc.sName == _sName; }); + if (aIter != m_pImpl->m_aColumnDesc.end()) + pRet = &*aIter; + return pRet; +} + +void OTableHelper::refreshPrimaryKeys(::std::vector< OUString>& _rNames) +{ + Any aCatalog; + if ( !m_CatalogName.isEmpty() ) + aCatalog <<= m_CatalogName; + Reference< XResultSet > xResult = getMetaData()->getPrimaryKeys(aCatalog,m_SchemaName,m_Name); + + if ( xResult.is() ) + { + auto pKeyProps = std::make_shared<sdbcx::KeyProperties>(OUString(),KeyType::PRIMARY,0,0); + OUString aPkName; + bool bAlreadyFetched = false; + const Reference< XRow > xRow(xResult,UNO_QUERY); + while ( xResult->next() ) + { + pKeyProps->m_aKeyColumnNames.push_back(xRow->getString(4)); + if ( !bAlreadyFetched ) + { + aPkName = xRow->getString(6); + SAL_WARN_IF(xRow->wasNull(),"connectivity.commontools", "NULL Primary Key name"); + SAL_WARN_IF(aPkName.isEmpty(),"connectivity.commontools", "empty Primary Key name"); + bAlreadyFetched = true; + } + } + + if(bAlreadyFetched) + { + SAL_WARN_IF(aPkName.isEmpty(),"connectivity.commontools", "empty Primary Key name"); + SAL_WARN_IF(pKeyProps->m_aKeyColumnNames.empty(),"connectivity.commontools", "Primary Key has no columns"); + m_pImpl->m_aKeys.emplace(aPkName,pKeyProps); + _rNames.push_back(aPkName); + } + } // if ( xResult.is() && xResult->next() ) + ::comphelper::disposeComponent(xResult); +} + +void OTableHelper::refreshForeignKeys(::std::vector< OUString>& _rNames) +{ + Any aCatalog; + if ( !m_CatalogName.isEmpty() ) + aCatalog <<= m_CatalogName; + Reference< XResultSet > xResult = getMetaData()->getImportedKeys(aCatalog,m_SchemaName,m_Name); + Reference< XRow > xRow(xResult,UNO_QUERY); + + if ( !xRow.is() ) + return; + + std::shared_ptr<sdbcx::KeyProperties> pKeyProps; + OUString aName,sCatalog,aSchema,sOldFKName; + while( xResult->next() ) + { + // this must be outside the "if" because we have to call in a right order + sCatalog = xRow->getString(1); + if ( xRow->wasNull() ) + sCatalog.clear(); + aSchema = xRow->getString(2); + aName = xRow->getString(3); + + const OUString sForeignKeyColumn = xRow->getString(8); + const sal_Int32 nUpdateRule = xRow->getInt(10); + const sal_Int32 nDeleteRule = xRow->getInt(11); + const OUString sFkName = xRow->getString(12); + + if ( !sFkName.isEmpty() && !xRow->wasNull() ) + { + if ( sOldFKName != sFkName ) + { + if ( pKeyProps ) + m_pImpl->m_aKeys.emplace(sOldFKName,pKeyProps); + + const OUString sReferencedName = ::dbtools::composeTableName(getMetaData(),sCatalog,aSchema,aName,false,::dbtools::EComposeRule::InDataManipulation); + pKeyProps = std::make_shared<sdbcx::KeyProperties>(sReferencedName,KeyType::FOREIGN,nUpdateRule,nDeleteRule); + pKeyProps->m_aKeyColumnNames.push_back(sForeignKeyColumn); + _rNames.push_back(sFkName); + if ( m_pTables->hasByName(sReferencedName) ) + { + if ( !m_pImpl->m_xTablePropertyListener.is() ) + m_pImpl->m_xTablePropertyListener = new OTableContainerListener(this); + m_pTables->addContainerListener(m_pImpl->m_xTablePropertyListener); + m_pImpl->m_xTablePropertyListener->add(sReferencedName); + } // if ( m_pTables->hasByName(sReferencedName) ) + sOldFKName = sFkName; + } // if ( sOldFKName != sFkName ) + else if ( pKeyProps ) + { + pKeyProps->m_aKeyColumnNames.push_back(sForeignKeyColumn); + } + } + } // while( xResult->next() ) + if ( pKeyProps ) + m_pImpl->m_aKeys.emplace(sOldFKName,pKeyProps); + ::comphelper::disposeComponent(xResult); +} + +void OTableHelper::refreshKeys() +{ + m_pImpl->m_aKeys.clear(); + + ::std::vector< OUString> aNames; + + if(!isNew()) + { + refreshPrimaryKeys(aNames); + refreshForeignKeys(aNames); + m_xKeys.reset(createKeys(aNames)); + } // if(!isNew()) + else if (!m_xKeys ) + m_xKeys.reset(createKeys(aNames)); + /*if(m_pKeys) + m_pKeys->reFill(aVector); + else*/ + +} + +void OTableHelper::refreshIndexes() +{ + ::std::vector< OUString> aVector; + if(!isNew()) + { + // fill indexes + Any aCatalog; + if ( !m_CatalogName.isEmpty() ) + aCatalog <<= m_CatalogName; + Reference< XResultSet > xResult = getMetaData()->getIndexInfo(aCatalog,m_SchemaName,m_Name,false,false); + + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + OUString sCatalogSep = getMetaData()->getCatalogSeparator(); + OUString sPreviousRoundName; + while( xResult->next() ) + { + OUString aName = xRow->getString(5); + if(!aName.isEmpty()) + aName += sCatalogSep; + aName += xRow->getString(6); + if ( !aName.isEmpty() ) + { + // don't insert the name if the last one we inserted was the same + if (sPreviousRoundName != aName) + aVector.push_back(aName); + } + sPreviousRoundName = aName; + } + ::comphelper::disposeComponent(xResult); + } + } + + if(m_xIndexes) + m_xIndexes->reFill(aVector); + else + m_xIndexes.reset(createIndexes(aVector)); +} + +OUString OTableHelper::getRenameStart() const +{ + OUString sSql("RENAME "); + if ( m_Type == "VIEW" ) + sSql += " VIEW "; + else + sSql += " TABLE "; + + return sSql; +} + +// XRename +void SAL_CALL OTableHelper::rename( const OUString& newName ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed( +#ifdef __GNUC__ + ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed +#else + rBHelper.bDisposed +#endif + ); + + if(!isNew()) + { + if ( m_pImpl->m_xRename.is() ) + { + m_pImpl->m_xRename->rename(this,newName); + } + else + { + OUString sSql = getRenameStart(); + + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(getMetaData(),newName,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + + OUString sComposedName; + sComposedName = ::dbtools::composeTableName(getMetaData(),m_CatalogName,m_SchemaName,m_Name,true,::dbtools::EComposeRule::InDataManipulation); + sSql += sComposedName + + " TO "; + sComposedName = ::dbtools::composeTableName(getMetaData(),sCatalog,sSchema,sTable,true,::dbtools::EComposeRule::InDataManipulation); + sSql += sComposedName; + + Reference< XStatement > xStmt = m_pImpl->m_xConnection->createStatement( ); + if ( xStmt.is() ) + { + xStmt->execute(sSql); + ::comphelper::disposeComponent(xStmt); + } + } + + OTable_TYPEDEF::rename(newName); + } + else + ::dbtools::qualifiedNameComponents(getMetaData(),newName,m_CatalogName,m_SchemaName,m_Name,::dbtools::EComposeRule::InTableDefinitions); +} + +Reference< XDatabaseMetaData> OTableHelper::getMetaData() const +{ + return m_pImpl->m_xMetaData; +} + +void SAL_CALL OTableHelper::alterColumnByIndex( sal_Int32 index, const Reference< XPropertySet >& descriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed( +#ifdef __GNUC__ + ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed +#else + rBHelper.bDisposed +#endif + ); + + Reference< XPropertySet > xOld( + m_xColumns->getByIndex(index), css::uno::UNO_QUERY); + if(xOld.is()) + alterColumnByName(getString(xOld->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),descriptor); +} + + +OUString SAL_CALL OTableHelper::getName() +{ + OUString sComposedName = ::dbtools::composeTableName(getMetaData(),m_CatalogName,m_SchemaName,m_Name,false,::dbtools::EComposeRule::InDataManipulation); + return sComposedName; +} + +const OUString & OTableHelper::getTableName() +{ + return m_Name; +} + +std::shared_ptr<sdbcx::KeyProperties> OTableHelper::getKeyProperties(const OUString& _sName) const +{ + std::shared_ptr<sdbcx::KeyProperties> pKeyProps; + TKeyMap::const_iterator aFind = m_pImpl->m_aKeys.find(_sName); + if ( aFind != m_pImpl->m_aKeys.end() ) + { + pKeyProps = aFind->second; + } + else // only a fall back + { + OSL_FAIL("No key with the given name found"); + pKeyProps = std::make_shared<sdbcx::KeyProperties>(); + } + + return pKeyProps; +} + +void OTableHelper::addKey(const OUString& _sName,const std::shared_ptr<sdbcx::KeyProperties>& _aKeyProperties) +{ + m_pImpl->m_aKeys.emplace(_sName,_aKeyProperties); +} + +OUString OTableHelper::getTypeCreatePattern() const +{ + return OUString(); +} + +Reference< XConnection> const & OTableHelper::getConnection() const +{ + return m_pImpl->m_xConnection; +} + +Reference< css::sdb::tools::XTableRename> const & OTableHelper::getRenameService() const +{ + return m_pImpl->m_xRename; +} + +Reference< css::sdb::tools::XTableAlteration> const & OTableHelper::getAlterService() const +{ + return m_pImpl->m_xAlter; +} + +Reference< css::sdb::tools::XKeyAlteration> const & OTableHelper::getKeyService() const +{ + return m_pImpl->m_xKeyAlter; +} + +Reference< css::sdb::tools::XIndexAlteration> const & OTableHelper::getIndexService() const +{ + return m_pImpl->m_xIndexAlter; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/conncleanup.cxx b/connectivity/source/commontools/conncleanup.cxx new file mode 100644 index 0000000000..7b703f093c --- /dev/null +++ b/connectivity/source/commontools/conncleanup.cxx @@ -0,0 +1,230 @@ +/* -*- 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 <connectivity/conncleanup.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <osl/diagnose.h> +#include <comphelper/diagnose_ex.hxx> + + +namespace dbtools +{ + + + using namespace css::uno; + using namespace css::beans; + using namespace css::sdbc; + using namespace css::lang; + + constexpr OUString ACTIVE_CONNECTION_PROPERTY_NAME = u"ActiveConnection"_ustr; + + OAutoConnectionDisposer::OAutoConnectionDisposer(const Reference< XRowSet >& _rxRowSet, const Reference< XConnection >& _rxConnection) + :m_xRowSet( _rxRowSet ) + ,m_bRSListening( false ) + ,m_bPropertyListening( false ) + { + Reference< XPropertySet > xProps(_rxRowSet, UNO_QUERY); + OSL_ENSURE(xProps.is(), "OAutoConnectionDisposer::OAutoConnectionDisposer: invalid rowset (no XPropertySet)!"); + + if (!xProps.is()) + return; + + try + { + xProps->setPropertyValue( ACTIVE_CONNECTION_PROPERTY_NAME, Any( _rxConnection ) ); + m_xOriginalConnection = _rxConnection; + startPropertyListening( xProps ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "OAutoConnectionDisposer::OAutoConnectionDisposer" ); + } + } + + + void OAutoConnectionDisposer::startPropertyListening( const Reference< XPropertySet >& _rxRowSet ) + { + try + { + _rxRowSet->addPropertyChangeListener( ACTIVE_CONNECTION_PROPERTY_NAME, this ); + m_bPropertyListening = true; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "OAutoConnectionDisposer::startPropertyListening" ); + } + } + + + void OAutoConnectionDisposer::stopPropertyListening( const Reference< XPropertySet >& _rxEventSource ) + { + // prevent deletion of ourself while we're herein + Reference< XInterface > xKeepAlive(getXWeak()); + + try + { // remove ourself as property change listener + OSL_ENSURE( _rxEventSource.is(), "OAutoConnectionDisposer::stopPropertyListening: invalid event source (no XPropertySet)!" ); + if ( _rxEventSource.is() ) + { + _rxEventSource->removePropertyChangeListener( ACTIVE_CONNECTION_PROPERTY_NAME, this ); + m_bPropertyListening = false; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "OAutoConnectionDisposer::stopPropertyListening" ); + } + } + + + void OAutoConnectionDisposer::startRowSetListening() + { + OSL_ENSURE( !m_bRSListening, "OAutoConnectionDisposer::startRowSetListening: already listening!" ); + try + { + if ( !m_bRSListening ) + m_xRowSet->addRowSetListener( this ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "OAutoConnectionDisposer::startRowSetListening" ); + } + m_bRSListening = true; + } + + + void OAutoConnectionDisposer::stopRowSetListening() + { + OSL_ENSURE( m_bRSListening, "OAutoConnectionDisposer::stopRowSetListening: not listening!" ); + try + { + m_xRowSet->removeRowSetListener( this ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "OAutoConnectionDisposer::stopRowSetListening" ); + } + m_bRSListening = false; + } + + + void SAL_CALL OAutoConnectionDisposer::propertyChange( const PropertyChangeEvent& _rEvent ) + { + if ( _rEvent.PropertyName != ACTIVE_CONNECTION_PROPERTY_NAME ) + return; + +// somebody set a new ActiveConnection + + Reference< XConnection > xNewConnection; + _rEvent.NewValue >>= xNewConnection; + + if ( isRowSetListening() ) + { + // we're listening at the row set, this means that the row set does not have our + // m_xOriginalConnection as active connection anymore + // So there are two possibilities + // a. somebody sets a new connection which is not our original one + // b. somebody sets a new connection, which is exactly the original one + // a. we're not interested in a, but in b: In this case, we simply need to move to the state + // we had originally: listen for property changes, do not listen for row set changes, and + // do not dispose the connection until the row set does not need it anymore + if ( xNewConnection.get() == m_xOriginalConnection.get() ) + { + stopRowSetListening(); + } + } + else + { + // start listening at the row set. We're allowed to dispose the old connection as soon + // as the RowSet changed + + // Unfortunately, the our database form implementations sometimes fire the change of their + // ActiveConnection twice. This is an error in forms/source/component/DatabaseForm.cxx, but + // changing this would require incompatible changes we can't do for a while. + // So for the moment, we have to live with it here. + // + // The only scenario where this doubled notification causes problems is when the connection + // of the form is reset to the one we're responsible for (m_xOriginalConnection), so we + // check this here. + // + // Yes, this is a HACK :( + if ( xNewConnection.get() != m_xOriginalConnection.get() ) + { +#if OSL_DEBUG_LEVEL > 0 + Reference< XConnection > xOldConnection; + _rEvent.OldValue >>= xOldConnection; + OSL_ENSURE( xOldConnection.get() == m_xOriginalConnection.get(), "OAutoConnectionDisposer::propertyChange: unexpected (original) property value!" ); +#endif + startRowSetListening(); + } + } + } + + + void SAL_CALL OAutoConnectionDisposer::disposing( const EventObject& _rSource ) + { + // the rowset is being disposed, and nobody has set a new ActiveConnection in the meantime + if ( isRowSetListening() ) + stopRowSetListening(); + + clearConnection(); + + if ( m_bPropertyListening ) + stopPropertyListening( Reference< XPropertySet >( _rSource.Source, UNO_QUERY ) ); + } + + void OAutoConnectionDisposer::clearConnection() + { + try + { + // dispose the old connection + Reference< XComponent > xComp(m_xOriginalConnection, UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + m_xOriginalConnection.clear(); + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("connectivity.commontools", "OAutoConnectionDisposer::clearConnection"); + } + } + + void SAL_CALL OAutoConnectionDisposer::cursorMoved( const css::lang::EventObject& /*event*/ ) + { + } + + void SAL_CALL OAutoConnectionDisposer::rowChanged( const css::lang::EventObject& /*event*/ ) + { + } + + void SAL_CALL OAutoConnectionDisposer::rowSetChanged( const css::lang::EventObject& /*event*/ ) + { + stopRowSetListening(); + clearConnection(); + + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/dbcharset.cxx b/connectivity/source/commontools/dbcharset.cxx new file mode 100644 index 0000000000..381afa606e --- /dev/null +++ b/connectivity/source/commontools/dbcharset.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 <connectivity/dbcharset.hxx> +#include <utility> +#include <osl/diagnose.h> +#include <rtl/tencinfo.h> + + +namespace dbtools +{ + + OCharsetMap::OCharsetMap() + { + } + + + void OCharsetMap::lateConstruct() + { + const rtl_TextEncoding eFirstEncoding = RTL_TEXTENCODING_DONTKNOW; + const rtl_TextEncoding eLastEncoding = 100; // TODO: a define in rtl/textenc.h would be fine here ... + OSL_ENSURE( 0 == eFirstEncoding, "OCharsetMap::OCharsetMap: somebody changed the numbers!" ); + + rtl_TextEncodingInfo aInfo; aInfo.StructSize = sizeof( rtl_TextEncodingInfo ); + for ( rtl_TextEncoding eEncoding = eFirstEncoding; eEncoding < eLastEncoding; ++eEncoding ) + { + if ( ( RTL_TEXTENCODING_DONTKNOW == eEncoding ) // this is always allowed - it has the special meaning "system encoding" + || ( rtl_getTextEncodingInfo( eEncoding, &aInfo ) + && approveEncoding( eEncoding, aInfo ) + ) + ) + { + m_aEncodings.insert( eEncoding ); + } + } + + OSL_ENSURE( find( RTL_TEXTENCODING_MS_1252 ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding ANSI!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_APPLE_ROMAN ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding macintosh!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_IBM_437 ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding IBM437!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_IBM_850) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding IBM850!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_IBM_860 ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding IBM860!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_IBM_861 ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding IBM861!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_IBM_863 ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding IBM863!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_IBM_865 ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding IBM865!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_IBM_866 ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding IBM866!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_DONTKNOW ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding SYSTEM!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_UTF8 ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding UTF-8!" ); + OSL_ENSURE( find( RTL_TEXTENCODING_BIG5_HKSCS ) != end(), "OCharsetMap::lateConstruct: missing compatibility encoding Big5-HKSCS!" ); + } + + + bool OCharsetMap::approveEncoding( const rtl_TextEncoding _eEncoding, const rtl_TextEncodingInfo& _rInfo ) const + { + bool bIsMimeEncoding = 0 != ( _rInfo.Flags & RTL_TEXTENCODING_INFO_MIME ); + OSL_ENSURE( !bIsMimeEncoding || rtl_getMimeCharsetFromTextEncoding( _eEncoding ), + "OCharsetMap::OCharsetMap: inconsistence in rtl!" ); + return bIsMimeEncoding; + } + + + OCharsetMap::~OCharsetMap() + { + } + + + OCharsetMap::CharsetIterator OCharsetMap::begin() const + { + ensureConstructed( ); + return CharsetIterator(this, m_aEncodings.begin() ); + } + + + OCharsetMap::CharsetIterator OCharsetMap::find(const rtl_TextEncoding _eEncoding) const + { + ensureConstructed( ); + return CharsetIterator( this, m_aEncodings.find( _eEncoding ) ); + } + + + OCharsetMap::CharsetIterator OCharsetMap::findIanaName(std::u16string_view _rIanaName) const + { + ensureConstructed( ); + + rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW; + if ( !_rIanaName.empty() ) + { + // byte string conversion + OString sMimeByteString = OUStringToOString( _rIanaName, RTL_TEXTENCODING_ASCII_US ); + // look up + eEncoding = rtl_getTextEncodingFromMimeCharset( sMimeByteString.getStr() ); + + if ( RTL_TEXTENCODING_DONTKNOW == eEncoding ) + { // if we're here, the name is not empty, but unknown -> this is an invalid name + return end(); + } + } + + return find( eEncoding ); + } + + + OCharsetMap::CharsetIterator OCharsetMap::end() const + { + ensureConstructed( ); + + return CharsetIterator( this, m_aEncodings.end() ); + } + + + CharsetIteratorDerefHelper::CharsetIteratorDerefHelper( const CharsetIteratorDerefHelper& _rSource ) + :m_eEncoding( _rSource.m_eEncoding ) + ,m_aIanaName( _rSource.m_aIanaName ) + { + } + + + CharsetIteratorDerefHelper:: CharsetIteratorDerefHelper(const rtl_TextEncoding _eEncoding, OUString _aIanaName ) + :m_eEncoding( _eEncoding ) + ,m_aIanaName(std::move( _aIanaName )) + { + } + + OCharsetMap::CharsetIterator::CharsetIterator(const OCharsetMap* _pContainer, OCharsetMap::TextEncBag::const_iterator _aPos ) + :m_pContainer( _pContainer ) + ,m_aPos(std::move( _aPos )) + { + OSL_ENSURE( m_pContainer, "OCharsetMap::CharsetIterator::CharsetIterator : invalid container!" ); + } + + CharsetIteratorDerefHelper OCharsetMap::CharsetIterator::operator*() const + { + OSL_ENSURE( m_aPos != m_pContainer->m_aEncodings.end(), "OCharsetMap::CharsetIterator::operator*: invalid position!"); + + rtl_TextEncoding eEncoding = *m_aPos; + OUString sIanaName; + + if ( RTL_TEXTENCODING_DONTKNOW != eEncoding ) + { // it's not the virtual "system charset" + const char* pIanaName = rtl_getMimeCharsetFromTextEncoding( eEncoding ); + OSL_ENSURE( pIanaName, "OCharsetMap::CharsetIterator: invalid mime name!" ); + if ( pIanaName ) + sIanaName = OUString::createFromAscii( pIanaName ); + } + return CharsetIteratorDerefHelper( eEncoding, sIanaName ); + } + + + const OCharsetMap::CharsetIterator& OCharsetMap::CharsetIterator::operator++() + { + OSL_ENSURE( m_aPos != m_pContainer->m_aEncodings.end(), "OCharsetMap::CharsetIterator::operator++ : invalid position!" ); + if ( m_aPos != m_pContainer->m_aEncodings.end()) + ++m_aPos; + return *this; + } + + + const OCharsetMap::CharsetIterator& OCharsetMap::CharsetIterator::operator--() + { + OSL_ENSURE( m_aPos != m_pContainer->m_aEncodings.begin(), "OCharsetMap::CharsetIterator::operator-- : invalid position!" ); + if ( m_aPos != m_pContainer->m_aEncodings.begin() ) + --m_aPos; + return *this; + } + + + bool operator==(const OCharsetMap::CharsetIterator& lhs, const OCharsetMap::CharsetIterator& rhs) + { + return ( lhs.m_pContainer == rhs.m_pContainer ) && ( lhs.m_aPos == rhs.m_aPos ); + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/dbconversion.cxx b/connectivity/source/commontools/dbconversion.cxx new file mode 100644 index 0000000000..704e168ad7 --- /dev/null +++ b/connectivity/source/commontools/dbconversion.cxx @@ -0,0 +1,392 @@ +/* -*- 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 <connectivity/dbconversion.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <rtl/character.hxx> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <unotools/datetime.hxx> +#include <comphelper/date.hxx> +#include <o3tl/string_view.hxx> +#include <sstream> +#include <iomanip> + +namespace +{ + const sal_Int64 nanoSecInSec = 1000000000; + const sal_Int16 secInMin = 60; + const sal_Int16 minInHour = 60; + + const sal_Int64 secMask = 1000000000; + const sal_Int64 minMask = 100000000000LL; + const sal_Int64 hourMask = 10000000000000LL; + + const double fNanoSecondsPerDay = nanoSecInSec * secInMin * minInHour * 24.0; + + // 32767-12-31 in "(days since 0001-01-01) + 1" format + const sal_Int32 maxDays = 11967896; + // -32768-01-01 in "(days since 0001-01-01) + 1" format + // Yes, I know it is currently unused. Will have to be used + // when we implement negative years. Writing down the correct + // value for future reference. + // *** Please don't remove just because it is unused *** + // Lionel Élie Mamane 2017-08-02 + // const sal_Int32 minDays = -11968270; +} + + +namespace dbtools +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + + css::util::Date const & DBTypeConversion::getStandardDate() + { + static css::util::Date STANDARD_DB_DATE(1,1,1900); + return STANDARD_DB_DATE; + } + + OUString DBTypeConversion::toDateString(const css::util::Date& rDate) + { + std::ostringstream ostr; + using std::setw; + ostr.fill('0'); + ostr << setw(4) << rDate.Year << "-" + << setw(2) << rDate.Month << "-" + << setw(2) << rDate.Day; + return OUString::createFromAscii(ostr.str()); + } + + OUString DBTypeConversion::toTimeStringS(const css::util::Time& rTime) + { + std::ostringstream ostr; + using std::setw; + ostr.fill('0'); + ostr << setw(2) << rTime.Hours << ":" + << setw(2) << rTime.Minutes << ":" + << setw(2) << rTime.Seconds; + return OUString::createFromAscii(ostr.str()); + } + + OUString DBTypeConversion::toTimeString(const css::util::Time& rTime) + { + std::ostringstream ostr; + using std::setw; + ostr.fill('0'); + ostr << setw(2) << rTime.Hours << ":" + << setw(2) << rTime.Minutes << ":" + << setw(2) << rTime.Seconds << "." + << setw(9) << rTime.NanoSeconds; + return OUString::createFromAscii(ostr.str()); + } + + OUString DBTypeConversion::toDateTimeString(const css::util::DateTime& _rDateTime) + { + css::util::Date aDate(_rDateTime.Day,_rDateTime.Month,_rDateTime.Year); + css::util::Time const aTime(_rDateTime.NanoSeconds, _rDateTime.Seconds, + _rDateTime.Minutes, _rDateTime.Hours, _rDateTime.IsUTC); + return toDateString(aDate) + " " + toTimeString(aTime); + } + + css::util::Date DBTypeConversion::toDate(const sal_Int32 _nVal) + { + css::util::Date aReturn; + aReturn.Day = static_cast<sal_uInt16>(_nVal % 100); + aReturn.Month = static_cast<sal_uInt16>((_nVal / 100) % 100); + aReturn.Year = static_cast<sal_uInt16>(_nVal / 10000); + return aReturn; + } + + + css::util::Time DBTypeConversion::toTime(const sal_Int64 _nVal) + { + css::util::Time aReturn; + sal_uInt64 unVal = static_cast<sal_uInt64>(_nVal >= 0 ? _nVal : -_nVal); + aReturn.Hours = unVal / hourMask; + aReturn.Minutes = (unVal / minMask) % 100; + aReturn.Seconds = (unVal / secMask) % 100; + aReturn.NanoSeconds = unVal % secMask; + return aReturn; + } + + sal_Int64 DBTypeConversion::getNsFromTime(const css::util::Time& rVal) + { + sal_Int32 nHour = rVal.Hours; + sal_Int32 nMin = rVal.Minutes; + sal_Int32 nSec = rVal.Seconds; + sal_Int32 nNanoSec = rVal.NanoSeconds; + + return nNanoSec + + nSec * nanoSecInSec + + nMin * (secInMin * nanoSecInSec) + + nHour * (minInHour * secInMin * nanoSecInSec); + } + + static sal_Int32 implRelativeToAbsoluteNull(const css::util::Date& _rDate) + { + if (_rDate.Day == 0 && _rDate.Month == 0 && _rDate.Year == 0) + { + // 0000-00-00 is *NOT* a valid date and passing it to the date + // conversion even when normalizing rightly asserts. Whatever we + // return here, it will be wrong. The old before commit + // 52ff16771ac160d27fd7beb78a4cfba22ad84f06 wrong implementation + // calculated -365 for that, effectively that would be a date of + // -0001-01-01 now but it was likely assumed that would be + // 0000-00-01 or even 0000-00-00 instead. Try if we get away with 0 + // for -0001-12-31, the same that + // comphelper::date::convertDateToDaysNormalizing() + // would return if comphelper::date::normalize() wouldn't ignore + // such "empty" date. + + return 0; + } + return comphelper::date::convertDateToDaysNormalizing( _rDate.Day, _rDate.Month, _rDate.Year); + } + + sal_Int32 DBTypeConversion::toDays(const css::util::Date& _rVal, const css::util::Date& _rNullDate) + { + return implRelativeToAbsoluteNull(_rVal) - implRelativeToAbsoluteNull(_rNullDate); + } + + + double DBTypeConversion::toDouble(const css::util::Date& rVal, const css::util::Date& _rNullDate) + { + return static_cast<double>(toDays(rVal, _rNullDate)); + } + + + double DBTypeConversion::toDouble(const css::util::Time& rVal) + { + return static_cast<double>(getNsFromTime(rVal)) / fNanoSecondsPerDay; + } + + + double DBTypeConversion::toDouble(const css::util::DateTime& _rVal, const css::util::Date& _rNullDate) + { + sal_Int64 nTime = toDays(css::util::Date(_rVal.Day, _rVal.Month, _rVal.Year), _rNullDate); + css::util::Time aTimePart; + + aTimePart.Hours = _rVal.Hours; + aTimePart.Minutes = _rVal.Minutes; + aTimePart.Seconds = _rVal.Seconds; + aTimePart.NanoSeconds = _rVal.NanoSeconds; + + return static_cast<double>(nTime) + toDouble(aTimePart); + } + + static void addDays(const sal_Int32 nDays, css::util::Date& _rDate) + { + sal_Int64 nTempDays = implRelativeToAbsoluteNull(_rDate); + + nTempDays += nDays; + if ( nTempDays > maxDays ) + { + _rDate.Day = 31; + _rDate.Month = 12; + _rDate.Year = 9999; + } + // TODO: can we replace that check by minDays? Would allow dates BCE + else if ( nTempDays <= 0 ) + { + _rDate.Day = 1; + _rDate.Month = 1; + _rDate.Year = 1; + } + else + comphelper::date::convertDaysToDate( nTempDays, _rDate.Day, _rDate.Month, _rDate.Year ); + } + + static void subDays(const sal_Int32 nDays, css::util::Date& _rDate ) + { + sal_Int64 nTempDays = implRelativeToAbsoluteNull(_rDate); + + nTempDays -= nDays; + if ( nTempDays > maxDays ) + { + _rDate.Day = 31; + _rDate.Month = 12; + _rDate.Year = 9999; + } + // TODO: can we replace that check by minDays? Would allow dates BCE + else if ( nTempDays <= 0 ) + { + _rDate.Day = 1; + _rDate.Month = 1; + _rDate.Year = 1; + } + else + comphelper::date::convertDaysToDate( nTempDays, _rDate.Day, _rDate.Month, _rDate.Year ); + } + + css::util::Date DBTypeConversion::toDate(const double dVal, const css::util::Date& _rNullDate) + { + css::util::Date aRet = _rNullDate; + + if (dVal >= 0) + addDays(static_cast<sal_Int32>(dVal),aRet); + else + subDays(static_cast<sal_uInt32>(-dVal),aRet); + // x -= (sal_uInt32)(-nDays); + + return aRet; + } + + css::util::Time DBTypeConversion::toTime(const double dVal, short nDigits) + { + const double nDays = std::trunc(dVal); + double fSeconds((dVal - nDays) * (fNanoSecondsPerDay / nanoSecInSec)); + fSeconds = ::rtl::math::round(fSeconds, nDigits); + sal_Int64 nNS = fSeconds * nanoSecInSec; + + sal_Int16 nSign; + if ( nNS < 0 ) + { + nNS *= -1; + nSign = -1; + } + else + nSign = 1; + + css::util::Time aRet; + // normalize time + // we have to sal_Int32 here because otherwise we get an overflow + sal_Int64 nNanoSeconds = nNS; + sal_Int32 nSeconds = nNanoSeconds / nanoSecInSec; + sal_Int32 nMinutes = nSeconds / secInMin; + + aRet.NanoSeconds = nNanoSeconds % nanoSecInSec; + aRet.Seconds = nSeconds % secInMin; + aRet.Hours = nMinutes / minInHour; + aRet.Minutes = nMinutes % minInHour; + + // assemble time + sal_Int64 nTime = nSign * + (aRet.NanoSeconds + + aRet.Seconds * secMask + + aRet.Minutes * minMask + + aRet.Hours * hourMask); + + if(nTime < 0) + { + aRet.NanoSeconds = nanoSecInSec-1; + aRet.Seconds = secInMin-1; + aRet.Minutes = minInHour-1; + aRet.Hours = 23; + } + return aRet; + } + + css::util::DateTime DBTypeConversion::toDateTime(const double dVal, const css::util::Date& _rNullDate) + { + css::util::DateTime aRet; + + if (!std::isfinite(dVal)) + { + SAL_WARN("connectivity.commontools", "DateTime has invalid value: " << dVal); + return aRet; + } + + css::util::Date aDate = toDate(dVal, _rNullDate); + // there is not enough precision in a double to have both a date + // and a time up to nanoseconds -> limit to microseconds to have + // correct rounding, that is e.g. 13:00:00.000000000 instead of + // 12:59:59.999999790 + css::util::Time aTime = toTime(dVal, 6); + + aRet.Day = aDate.Day; + aRet.Month = aDate.Month; + aRet.Year = aDate.Year; + + aRet.NanoSeconds = aTime.NanoSeconds; + aRet.Minutes = aTime.Minutes; + aRet.Seconds = aTime.Seconds; + aRet.Hours = aTime.Hours; + + + return aRet; + } + + css::util::Date DBTypeConversion::toDate(std::u16string_view _sSQLString) + { + // get the token out of a string + static const sal_Unicode sDateSep = '-'; + + sal_Int32 nIndex = 0; + sal_uInt16 nYear = 0, + nMonth = 0, + nDay = 0; + nYear = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(_sSQLString, 0,sDateSep,nIndex))); + if(nIndex != -1) + { + nMonth = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(_sSQLString, 0,sDateSep,nIndex))); + if(nIndex != -1) + nDay = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(_sSQLString, 0,sDateSep,nIndex))); + } + + return css::util::Date(nDay,nMonth,nYear); + } + + + css::util::DateTime DBTypeConversion::toDateTime(const OUString& _sSQLString) + { + //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Timestamp.html#valueOf(java.lang.String) + //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Date.html#valueOf(java.lang.String) + //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Time.html#valueOf(java.lang.String) + + // the date part + css::util::Date aDate = toDate(_sSQLString); + css::util::Time aTime; + sal_Int32 nSeparation = _sSQLString.indexOf( ' ' ); + if ( -1 != nSeparation ) + { + const sal_Unicode *p = _sSQLString.getStr() + nSeparation; + const sal_Unicode *const begin = p; + while (rtl::isAsciiWhiteSpace(*p)) { ++p; } + nSeparation += p - begin; + aTime = toTime( _sSQLString.subView( nSeparation ) ); + } + + return css::util::DateTime(aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours, + aDate.Day, aDate.Month, aDate.Year, false); + } + + + css::util::Time DBTypeConversion::toTime(std::u16string_view _sSQLString) + { + css::util::Time aTime; + ::utl::ISO8601parseTime(_sSQLString, aTime); + return aTime; + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/dbexception.cxx b/connectivity/source/commontools/dbexception.cxx new file mode 100644 index 0000000000..bc5a9be808 --- /dev/null +++ b/connectivity/source/commontools/dbexception.cxx @@ -0,0 +1,485 @@ +/* -*- 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 <connectivity/dbexception.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/sdb/SQLErrorEvent.hpp> +#include <strings.hrc> +#include <resource/sharedresources.hxx> +#include <comphelper/diagnose_ex.hxx> + +namespace dbtools +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbc; + using namespace ::comphelper; + using namespace ::connectivity; + +SQLExceptionInfo::SQLExceptionInfo() + :m_eType(TYPE::Undefined) +{ +} + + +SQLExceptionInfo::SQLExceptionInfo(const css::sdbc::SQLException& _rError) +{ + m_aContent <<= _rError; + implDetermineType(); +} + + +SQLExceptionInfo::SQLExceptionInfo(const css::sdbc::SQLWarning& _rError) +{ + m_aContent <<= _rError; + implDetermineType(); +} + + +SQLExceptionInfo::SQLExceptionInfo(const css::sdb::SQLContext& _rError) +{ + m_aContent <<= _rError; + implDetermineType(); +} + + +SQLExceptionInfo::SQLExceptionInfo( const OUString& _rSimpleErrorMessage ) +{ + SQLException aError(_rSimpleErrorMessage, {}, {}, 0, {}); + m_aContent <<= aError; + implDetermineType(); +} + +SQLExceptionInfo& SQLExceptionInfo::operator=(const css::sdbc::SQLException& _rError) +{ + m_aContent <<= _rError; + implDetermineType(); + return *this; +} + + +SQLExceptionInfo& SQLExceptionInfo::operator=(const css::sdbc::SQLWarning& _rError) +{ + m_aContent <<= _rError; + implDetermineType(); + return *this; +} + + +SQLExceptionInfo& SQLExceptionInfo::operator=(const css::sdb::SQLContext& _rError) +{ + m_aContent <<= _rError; + implDetermineType(); + return *this; +} + + +SQLExceptionInfo& SQLExceptionInfo::operator=(const css::sdb::SQLErrorEvent& _rErrorEvent) +{ + m_aContent = _rErrorEvent.Reason; + implDetermineType(); + return *this; +} + + +SQLExceptionInfo& SQLExceptionInfo::operator=(const css::uno::Any& _rCaughtSQLException) +{ + m_aContent = _rCaughtSQLException; + implDetermineType(); + return *this; +} + + +SQLExceptionInfo::SQLExceptionInfo(const css::uno::Any& _rError) +{ + const css::uno::Type& aSQLExceptionType = cppu::UnoType<css::sdbc::SQLException>::get(); + bool bValid = isAssignableFrom(aSQLExceptionType, _rError.getValueType()); + if (bValid) + m_aContent = _rError; + // no assertion here : if used with the NextException member of an SQLException bValid==sal_False is allowed. + + implDetermineType(); +} + + +void SQLExceptionInfo::implDetermineType() +{ + const Type& aSQLExceptionType = ::cppu::UnoType<SQLException>::get(); + const Type& aSQLWarningType = ::cppu::UnoType<SQLWarning>::get(); + const Type& aSQLContextType = ::cppu::UnoType<SQLContext>::get(); + + if ( isAssignableFrom( aSQLContextType, m_aContent.getValueType() ) ) + m_eType = TYPE::SQLContext; + else if ( isAssignableFrom( aSQLWarningType, m_aContent.getValueType() ) ) + m_eType = TYPE::SQLWarning; + else if ( isAssignableFrom( aSQLExceptionType, m_aContent.getValueType() ) ) + m_eType = TYPE::SQLException; + else + { + m_eType = TYPE::Undefined; + m_aContent.clear(); + } +} + + +bool SQLExceptionInfo::isKindOf(TYPE _eType) const +{ + switch (_eType) + { + case TYPE::SQLContext: + return (m_eType == TYPE::SQLContext); + case TYPE::SQLWarning: + return (m_eType == TYPE::SQLContext) || (m_eType == TYPE::SQLWarning); + case TYPE::SQLException: + return (m_eType == TYPE::SQLContext) || (m_eType == TYPE::SQLWarning) || (m_eType == TYPE::SQLException); + case TYPE::Undefined: + return (m_eType == TYPE::Undefined); + } + return false; +} + + +SQLExceptionInfo::operator const css::sdbc::SQLException*() const +{ + OSL_ENSURE(isKindOf(TYPE::SQLException), "SQLExceptionInfo::operator SQLException* : invalid call !"); + return o3tl::doAccess<css::sdbc::SQLException>(m_aContent); +} + + +SQLExceptionInfo::operator const css::sdb::SQLContext*() const +{ + OSL_ENSURE(isKindOf(TYPE::SQLContext), "SQLExceptionInfo::operator SQLException* : invalid call !"); + return o3tl::doAccess<css::sdb::SQLContext>(m_aContent); +} + + +void SQLExceptionInfo::prepend( const OUString& _rErrorMessage ) +{ + SQLException aException(_rErrorMessage, {}, "S1000", 0, m_aContent); + m_aContent <<= aException; + + m_eType = TYPE::SQLException; +} + +// create the to-be-appended exception +Any SQLExceptionInfo::createException(TYPE eType, const OUString& rErrorMessage, const OUString& rSQLState, const sal_Int32 nErrorCode) +{ + Any aAppend; + switch (eType) + { + case TYPE::SQLException: + aAppend <<= SQLException(rErrorMessage, {}, rSQLState, nErrorCode, {}); + break; + case TYPE::SQLWarning: + aAppend <<= SQLWarning(rErrorMessage, {}, rSQLState, nErrorCode, {}); + break; + case TYPE::SQLContext: + aAppend <<= SQLContext(rErrorMessage, {}, rSQLState, nErrorCode, {}, {}); + break; + default: + TOOLS_WARN_EXCEPTION("connectivity.commontools", "SQLExceptionInfo::createException: invalid exception type: this will crash!"); + break; + } + + return aAppend; +} + +// find the end of the exception chain +SQLException* SQLExceptionInfo::getLastException(SQLException* pLastException) +{ + SQLException* pException = pLastException; + while (pException) + { + pException = const_cast<SQLException*>(o3tl::tryAccess<SQLException>(pException->NextException)); + if (!pException) + break; + pLastException = pException; + } + return pLastException; +} + +void SQLExceptionInfo::append( TYPE _eType, const OUString& _rErrorMessage, const OUString& _rSQLState, const sal_Int32 _nErrorCode ) +{ + // create the to-be-appended exception + Any aAppend = createException(_eType, _rErrorMessage, _rSQLState, _nErrorCode); + + // find the end of the current chain + SQLException* pLastException = getLastException(const_cast<SQLException*>(o3tl::tryAccess<SQLException>(m_aContent))); + + // append + if (pLastException) + pLastException->NextException = aAppend; + else + { + m_aContent = aAppend; + m_eType = _eType; + } +} + +void SQLExceptionInfo::doThrow() +{ + if ( m_aContent.getValueTypeClass() == TypeClass_EXCEPTION ) + ::cppu::throwException( m_aContent ); + throw RuntimeException(); +} + +SQLExceptionIteratorHelper::SQLExceptionIteratorHelper( const SQLExceptionInfo& _rChainStart ) + :m_pCurrent( nullptr ) + ,m_eCurrentType( SQLExceptionInfo::TYPE::Undefined ) +{ + if ( _rChainStart.isValid() ) + { + m_pCurrent = _rChainStart; + m_eCurrentType = _rChainStart.getType(); + } +} + + +SQLExceptionIteratorHelper::SQLExceptionIteratorHelper( const css::sdbc::SQLException& _rChainStart ) + :m_pCurrent( &_rChainStart ) + ,m_eCurrentType( SQLExceptionInfo::TYPE::SQLException ) +{ +} + + +void SQLExceptionIteratorHelper::current( SQLExceptionInfo& _out_rInfo ) const +{ + switch ( m_eCurrentType ) + { + case SQLExceptionInfo::TYPE::SQLException: + _out_rInfo = *m_pCurrent; + break; + + case SQLExceptionInfo::TYPE::SQLWarning: + _out_rInfo = *static_cast< const SQLWarning* >( m_pCurrent ); + break; + + case SQLExceptionInfo::TYPE::SQLContext: + _out_rInfo = *static_cast< const SQLContext* >( m_pCurrent ); + break; + + default: + _out_rInfo = Any(); + break; + } +} + + +const css::sdbc::SQLException* SQLExceptionIteratorHelper::next() +{ + OSL_ENSURE( hasMoreElements(), "SQLExceptionIteratorHelper::next : invalid call (please use hasMoreElements)!" ); + + const css::sdbc::SQLException* pReturn = m_pCurrent; + if ( !m_pCurrent ) + return pReturn; + + // check for the next element within the chain + const Type aTypeException( ::cppu::UnoType< SQLException >::get() ); + + Type aNextElementType = m_pCurrent->NextException.getValueType(); + if ( !isAssignableFrom( aTypeException, aNextElementType ) ) + { + // no SQLException at all in the next chain element + m_pCurrent = nullptr; + m_eCurrentType = SQLExceptionInfo::TYPE::Undefined; + return pReturn; + } + + m_pCurrent = o3tl::doAccess< SQLException >( m_pCurrent->NextException ); + + // no finally determine the proper type of the exception + const Type aTypeContext( ::cppu::UnoType< SQLContext >::get() ); + if ( isAssignableFrom( aTypeContext, aNextElementType ) ) + { + m_eCurrentType = SQLExceptionInfo::TYPE::SQLContext; + return pReturn; + } + + const Type aTypeWarning( ::cppu::UnoType< SQLWarning >::get() ); + if ( isAssignableFrom( aTypeWarning, aNextElementType ) ) + { + m_eCurrentType = SQLExceptionInfo::TYPE::SQLWarning; + return pReturn; + } + + // a simple SQLException + m_eCurrentType = SQLExceptionInfo::TYPE::SQLException; + return pReturn; +} + + +void SQLExceptionIteratorHelper::next( SQLExceptionInfo& _out_rInfo ) +{ + current( _out_rInfo ); + next(); +} + + +void throwFunctionSequenceException(const Reference< XInterface >& Context, const Any& Next) +{ + ::connectivity::SharedResources aResources; + throw SQLException( + aResources.getResourceString(STR_ERRORMSG_SEQUENCE), + Context, + getStandardSQLState( StandardSQLState::FUNCTION_SEQUENCE_ERROR ), + 0, + Next + ); +} + +void throwInvalidIndexException(const css::uno::Reference< css::uno::XInterface >& Context, + const css::uno::Any& Next) +{ + ::connectivity::SharedResources aResources; + throw SQLException( + aResources.getResourceString(STR_INVALID_INDEX), + Context, + getStandardSQLState( StandardSQLState::INVALID_DESCRIPTOR_INDEX ), + 0, + Next + ); +} + +void throwFunctionNotSupportedSQLException(const OUString& _rFunctionName, + const css::uno::Reference<css::uno::XInterface>& _rxContext) +{ + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceStringWithSubstitution( + STR_UNSUPPORTED_FUNCTION, + "$functionname$", _rFunctionName + ) ); + throw SQLException( + sError, + _rxContext, + getStandardSQLState( StandardSQLState::FUNCTION_NOT_SUPPORTED ), + 0, + css::uno::Any() + ); +} + +void throwFunctionNotSupportedRuntimeException(const OUString& _rFunctionName, + const css::uno::Reference<css::uno::XInterface>& _rxContext) +{ + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceStringWithSubstitution( + STR_UNSUPPORTED_FUNCTION, + "$functionname$", _rFunctionName + ) ); + throw RuntimeException( + sError, + _rxContext + ); +} + +void throwGenericSQLException(const OUString& _rMsg, const css::uno::Reference< css::uno::XInterface >& _rxSource) +{ + throwGenericSQLException(_rMsg, _rxSource, Any()); +} + + +void throwGenericSQLException(const OUString& _rMsg, const Reference< XInterface >& _rxSource, const Any& _rNextException) +{ + throw SQLException( _rMsg, _rxSource, getStandardSQLState( StandardSQLState::GENERAL_ERROR ), 0, _rNextException); +} + +void throwFeatureNotImplementedSQLException( const OUString& _rFeatureName, const Reference< XInterface >& _rxContext, const Any& _rNextException ) +{ + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceStringWithSubstitution( + STR_UNSUPPORTED_FEATURE, + "$featurename$", _rFeatureName + ) ); + + throw SQLException( + sError, + _rxContext, + getStandardSQLState( StandardSQLState::FEATURE_NOT_IMPLEMENTED ), + 0, + _rNextException + ); +} + +void throwFeatureNotImplementedRuntimeException(const OUString& _rFeatureName, const Reference< XInterface >& _rxContext) +{ + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceStringWithSubstitution( + STR_UNSUPPORTED_FEATURE, + "$featurename$", _rFeatureName + ) ); + + throw RuntimeException(sError, _rxContext); +} + +void throwInvalidColumnException( const OUString& _rColumnName, const Reference< XInterface >& _rxContext) +{ + ::connectivity::SharedResources aResources; + OUString sErrorMessage( aResources.getResourceStringWithSubstitution( + STR_INVALID_COLUMNNAME, + "$columnname$",_rColumnName) ); + throwSQLException( sErrorMessage, StandardSQLState::COLUMN_NOT_FOUND, _rxContext ); +} + +void throwSQLException( const OUString& _rMessage, const OUString& _rSQLState, + const Reference< XInterface >& _rxContext, const sal_Int32 _nErrorCode ) +{ + throw SQLException( + _rMessage, + _rxContext, + _rSQLState, + _nErrorCode, + Any() + ); +} + + +void throwSQLException( const OUString& _rMessage, StandardSQLState _eSQLState, + const Reference< XInterface >& _rxContext, const sal_Int32 _nErrorCode ) +{ + throwSQLException( _rMessage, getStandardSQLState( _eSQLState ), _rxContext, _nErrorCode ); +} + + +OUString getStandardSQLState( StandardSQLState _eState ) +{ + switch ( _eState ) + { + case StandardSQLState::INVALID_DESCRIPTOR_INDEX: return "07009"; + case StandardSQLState::INVALID_CURSOR_STATE: return "24000"; + case StandardSQLState::COLUMN_NOT_FOUND: return "42S22"; + case StandardSQLState::GENERAL_ERROR: return "HY000"; + case StandardSQLState::INVALID_SQL_DATA_TYPE: return "HY004"; + case StandardSQLState::FUNCTION_SEQUENCE_ERROR: return "HY010"; + case StandardSQLState::INVALID_CURSOR_POSITION: return "HY109"; + case StandardSQLState::FEATURE_NOT_IMPLEMENTED: return "HYC00"; + case StandardSQLState::FUNCTION_NOT_SUPPORTED: return "IM001"; + case StandardSQLState::CONNECTION_DOES_NOT_EXIST: return "08003"; + default: return "HY001"; // General Error + } +} + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/dbmetadata.cxx b/connectivity/source/commontools/dbmetadata.cxx new file mode 100644 index 0000000000..8983ee25ba --- /dev/null +++ b/connectivity/source/commontools/dbmetadata.cxx @@ -0,0 +1,443 @@ +/* -*- 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 <connectivity/dbmetadata.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/DriversConfig.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdb/BooleanComparisonMode.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData2.hpp> +#include <com/sun/star/sdbcx/XUsersSupplier.hpp> +#include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp> +#include <com/sun/star/sdbc/DriverManager.hpp> + +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <sal/log.hxx> + +#include <optional> + + +namespace dbtools +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::sdbc::XDatabaseMetaData; + using ::com::sun::star::sdbc::XDatabaseMetaData2; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::container::XChild; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::sdbcx::XUsersSupplier; + using ::com::sun::star::sdbcx::XDataDefinitionSupplier; + using ::com::sun::star::sdbc::DriverManager; + using ::com::sun::star::sdbc::XDriverManager2; + using ::com::sun::star::uno::UNO_SET_THROW; + + namespace BooleanComparisonMode = ::com::sun::star::sdb::BooleanComparisonMode; + + struct DatabaseMetaData_Impl + { + Reference< XConnection > xConnection; + Reference< XDatabaseMetaData > xConnectionMetaData; + ::connectivity::DriversConfig aDriverConfig; + + ::std::optional< OUString > sCachedIdentifierQuoteString; + ::std::optional< OUString > sCachedCatalogSeparator; + + DatabaseMetaData_Impl() + : aDriverConfig( ::comphelper::getProcessComponentContext() ) + { + } + }; + + + namespace + { + + void lcl_construct( DatabaseMetaData_Impl& _metaDataImpl, const Reference< XConnection >& _connection ) + { + _metaDataImpl.xConnection = _connection; + if ( !_metaDataImpl.xConnection.is() ) + return; + + _metaDataImpl.xConnectionMetaData = _connection->getMetaData(); + if ( !_metaDataImpl.xConnectionMetaData.is() ) + throw IllegalArgumentException(); + } + + + void lcl_checkConnected( const DatabaseMetaData_Impl& _metaDataImpl ) + { + if ( !_metaDataImpl.xConnection.is() || !_metaDataImpl.xConnectionMetaData.is() ) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_NO_CONNECTION_GIVEN)); + throwSQLException( sError, StandardSQLState::CONNECTION_DOES_NOT_EXIST, nullptr ); + } + } + + + bool lcl_getDriverSetting( const OUString& _asciiName, const DatabaseMetaData_Impl& _metaData, Any& _out_setting ) + { + lcl_checkConnected( _metaData ); + const ::comphelper::NamedValueCollection& rDriverMetaData = _metaData.aDriverConfig.getMetaData( _metaData.xConnectionMetaData->getURL() ); + if ( !rDriverMetaData.has( _asciiName ) ) + return false; + _out_setting = rDriverMetaData.get( _asciiName ); + return true; + } + + + bool lcl_getConnectionSetting(const OUString& _asciiName, const DatabaseMetaData_Impl& _metaData, Any& _out_setting ) + { + try + { + Reference< XChild > xConnectionAsChild( _metaData.xConnection, UNO_QUERY ); + if ( xConnectionAsChild.is() ) + { + Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY_THROW ); + Reference< XPropertySet > xDataSourceSettings( + xDataSource->getPropertyValue("Settings"), + UNO_QUERY_THROW ); + + _out_setting = xDataSourceSettings->getPropertyValue( _asciiName ); + } + else + { + Reference< XDatabaseMetaData2 > xExtendedMetaData( _metaData.xConnectionMetaData, UNO_QUERY_THROW ); + _out_setting = ::comphelper::NamedValueCollection::get( xExtendedMetaData->getConnectionInfo(), _asciiName ); + return _out_setting.hasValue(); + } + return true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return false; + } + + + const OUString& lcl_getConnectionStringSetting( + const DatabaseMetaData_Impl& _metaData, ::std::optional< OUString >& _cachedSetting, + OUString (SAL_CALL XDatabaseMetaData::*_getter)() ) + { + if ( !_cachedSetting ) + { + lcl_checkConnected( _metaData ); + try + { + _cachedSetting = (_metaData.xConnectionMetaData.get()->*_getter)(); + } + catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); } + } + return *_cachedSetting; + } + } + + DatabaseMetaData::DatabaseMetaData() + :m_pImpl( new DatabaseMetaData_Impl ) + { + } + + DatabaseMetaData::DatabaseMetaData( const Reference< XConnection >& _connection ) + :m_pImpl( new DatabaseMetaData_Impl ) + { + lcl_construct( *m_pImpl, _connection ); + } + + + DatabaseMetaData::DatabaseMetaData( const DatabaseMetaData& _copyFrom ) + :m_pImpl( new DatabaseMetaData_Impl( *_copyFrom.m_pImpl ) ) + { + } + + DatabaseMetaData::DatabaseMetaData(DatabaseMetaData&& _copyFrom) noexcept + :m_pImpl(std::move(_copyFrom.m_pImpl)) + { + } + + DatabaseMetaData& DatabaseMetaData::operator=( const DatabaseMetaData& _copyFrom ) + { + if ( this == &_copyFrom ) + return *this; + + m_pImpl.reset( new DatabaseMetaData_Impl( *_copyFrom.m_pImpl ) ); + return *this; + } + + DatabaseMetaData& DatabaseMetaData::operator=(DatabaseMetaData&& _copyFrom) noexcept + { + m_pImpl = std::move(_copyFrom.m_pImpl); + return *this; + } + + DatabaseMetaData::~DatabaseMetaData() + { + } + + bool DatabaseMetaData::isConnected() const + { + return m_pImpl->xConnection.is(); + } + + + bool DatabaseMetaData::supportsSubqueriesInFrom() const + { + lcl_checkConnected( *m_pImpl ); + + bool bSupportsSubQueries = false; + try + { + sal_Int32 maxTablesInselect = m_pImpl->xConnectionMetaData->getMaxTablesInSelect(); + bSupportsSubQueries = ( maxTablesInselect > 1 ) || ( maxTablesInselect == 0 ); + // TODO: is there a better way to determine this? The above is not really true. More precise, + // it's a *very* generous heuristics ... + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return bSupportsSubQueries; + } + + + bool DatabaseMetaData::supportsPrimaryKeys() const + { + lcl_checkConnected( *m_pImpl ); + + bool bDoesSupportPrimaryKeys = false; + try + { + Any setting; + if ( !( lcl_getConnectionSetting( "PrimaryKeySupport", *m_pImpl, setting ) ) + || !( setting >>= bDoesSupportPrimaryKeys ) + ) + bDoesSupportPrimaryKeys = m_pImpl->xConnectionMetaData->supportsCoreSQLGrammar() + || m_pImpl->xConnectionMetaData->supportsANSI92EntryLevelSQL(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return bDoesSupportPrimaryKeys; + } + + + const OUString& DatabaseMetaData::getIdentifierQuoteString() const + { + return lcl_getConnectionStringSetting( *m_pImpl, m_pImpl->sCachedIdentifierQuoteString, &XDatabaseMetaData::getIdentifierQuoteString ); + } + + + const OUString& DatabaseMetaData::getCatalogSeparator() const + { + return lcl_getConnectionStringSetting( *m_pImpl, m_pImpl->sCachedCatalogSeparator, &XDatabaseMetaData::getCatalogSeparator ); + } + + + bool DatabaseMetaData::restrictIdentifiersToSQL92() const + { + lcl_checkConnected( *m_pImpl ); + + bool restrict( false ); + Any setting; + if ( lcl_getConnectionSetting( "EnableSQL92Check", *m_pImpl, setting ) ) + if( ! (setting >>= restrict) ) + SAL_WARN("connectivity.commontools", "restrictIdentifiersToSQL92: unable to assign EnableSQL92Check"); + return restrict; + } + + + bool DatabaseMetaData::generateASBeforeCorrelationName() const + { + bool doGenerate( false ); + Any setting; + if ( lcl_getConnectionSetting( "GenerateASBeforeCorrelationName", *m_pImpl, setting ) ) + if( ! (setting >>= doGenerate) ) + SAL_WARN("connectivity.commontools", "generateASBeforeCorrelationName: unable to assign GenerateASBeforeCorrelationName"); + return doGenerate; + } + + bool DatabaseMetaData::shouldEscapeDateTime() const + { + bool doGenerate( true ); + Any setting; + if ( lcl_getConnectionSetting( "EscapeDateTime", *m_pImpl, setting ) ) + if( ! (setting >>= doGenerate) ) + SAL_WARN("connectivity.commontools", "shouldEscapeDateTime: unable to assign EscapeDateTime"); + return doGenerate; + } + + bool DatabaseMetaData::shouldSubstituteParameterNames() const + { + bool doSubstitute( true ); + Any setting; + if ( lcl_getConnectionSetting( "ParameterNameSubstitution", *m_pImpl, setting ) ) + if( ! (setting >>= doSubstitute) ) + SAL_WARN("connectivity.commontools", "shouldSubstituteParameterNames: unable to assign ParameterNameSubstitution"); + return doSubstitute; + } + + bool DatabaseMetaData::isAutoIncrementPrimaryKey() const + { + bool is( true ); + Any setting; + if ( lcl_getDriverSetting( "AutoIncrementIsPrimaryKey", *m_pImpl, setting ) ) + if( ! (setting >>= is) ) + SAL_WARN("connectivity.commontools", "isAutoIncrementPrimaryKey: unable to assign AutoIncrementIsPrimaryKey"); + return is; + } + + sal_Int32 DatabaseMetaData::getBooleanComparisonMode() const + { + sal_Int32 mode( BooleanComparisonMode::EQUAL_INTEGER ); + Any setting; + if ( lcl_getConnectionSetting( "BooleanComparisonMode", *m_pImpl, setting ) ) + if( ! (setting >>= mode) ) + SAL_WARN("connectivity.commontools", "getBooleanComparisonMode: unable to assign BooleanComparisonMode"); + return mode; + } + + bool DatabaseMetaData::supportsRelations() const + { + lcl_checkConnected( *m_pImpl ); + bool bSupport = false; + try + { + bSupport = m_pImpl->xConnectionMetaData->supportsIntegrityEnhancementFacility(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + try + { + if ( !bSupport ) + { + const OUString url = m_pImpl->xConnectionMetaData->getURL(); + bSupport = url.startsWith("sdbc:mysql"); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return bSupport; + } + + + bool DatabaseMetaData::supportsColumnAliasInOrderBy() const + { + bool doGenerate( true ); + Any setting; + if ( lcl_getConnectionSetting( "ColumnAliasInOrderBy", *m_pImpl, setting ) ) + if( ! (setting >>= doGenerate) ) + SAL_WARN("connectivity.commontools", "supportsColumnAliasInOrderBy: unable to assign ColumnAliasInOrderBy"); + return doGenerate; + } + + + bool DatabaseMetaData::supportsUserAdministration( const Reference<XComponentContext>& _rContext ) const + { + lcl_checkConnected( *m_pImpl ); + + bool isSupported( false ); + try + { + // find the XUsersSupplier interface + // - either directly at the connection + Reference< XUsersSupplier > xUsersSupp( m_pImpl->xConnection, UNO_QUERY ); + if ( !xUsersSupp.is() ) + { + // - or at the driver manager + Reference< XDriverManager2 > xDriverManager = DriverManager::create( _rContext ); + Reference< XDataDefinitionSupplier > xDriver( xDriverManager->getDriverByURL( m_pImpl->xConnectionMetaData->getURL() ), UNO_QUERY ); + if ( xDriver.is() ) + xUsersSupp.set( xDriver->getDataDefinitionByConnection( m_pImpl->xConnection ), UNO_QUERY ); + } + + isSupported = ( xUsersSupp.is() && xUsersSupp->getUsers().is() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return isSupported; + } + + + bool DatabaseMetaData::displayEmptyTableFolders() const + { + bool doDisplay( true ); +#ifdef IMPLEMENTED_LATER + Any setting; + if ( lcl_getConnectionSetting( "DisplayEmptyTableFolders", *m_pImpl, setting ) ) + if( ! (setting >>= doDisplay) ) + SAL_WARN("connectivity.commontools", "displayEmptyTableFolders: unable to assign DisplayEmptyTableFolders"); +#else + try + { + Reference< XDatabaseMetaData > xMeta( m_pImpl->xConnectionMetaData, UNO_SET_THROW ); + OUString sConnectionURL( xMeta->getURL() ); + doDisplay = sConnectionURL.startsWith( "sdbc:mysql:mysqlc" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } +#endif + return doDisplay; + } + + bool DatabaseMetaData::supportsThreads() const + { + bool bSupported( true ); + try + { + Reference< XDatabaseMetaData > xMeta( m_pImpl->xConnectionMetaData, UNO_SET_THROW ); + OUString sConnectionURL( xMeta->getURL() ); + bSupported = !sConnectionURL.startsWith( "sdbc:mysql:mysqlc" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return bSupported; + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/dbtools.cxx b/connectivity/source/commontools/dbtools.cxx new file mode 100644 index 0000000000..885e28c751 --- /dev/null +++ b/connectivity/source/commontools/dbtools.cxx @@ -0,0 +1,2074 @@ +/* -*- 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 <connectivity/CommonTools.hxx> +#include <TConnection.hxx> +#include <ParameterCont.hxx> + +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/BooleanComparisonMode.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/ErrorMessageDialog.hpp> +#include <com/sun/star/sdb/ParametersRequest.hpp> +#include <com/sun/star/sdb/RowSetVetoException.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdb/XCompletedConnection.hpp> +#include <com/sun/star/sdb/XInteractionSupplyParameters.hpp> +#include <com/sun/star/sdb/XOfficeDatabaseDocument.hpp> +#include <com/sun/star/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdbc/ConnectionPool.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XKeysSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/util/NumberFormatsSupplier.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> + +#include <comphelper/extract.hxx> +#include <comphelper/interaction.hxx> +#include <comphelper/property.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/types.hxx> +#include <connectivity/conncleanup.hxx> +#include <connectivity/dbconversion.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/statementcomposer.hxx> +#include <o3tl/any.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/stream.hxx> +#include <cppuhelper/implbase.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + +#include <algorithm> +#include <iterator> +#include <set> + +using namespace ::comphelper; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::form; +using namespace connectivity; + +namespace dbtools +{ + +namespace +{ + typedef sal_Bool (SAL_CALL XDatabaseMetaData::*FMetaDataSupport)(); +} + +sal_Int32 getDefaultNumberFormat(const Reference< XPropertySet >& _xColumn, + const Reference< XNumberFormatTypes >& _xTypes, + const Locale& _rLocale) +{ + OSL_ENSURE(_xTypes.is() && _xColumn.is(), "dbtools::getDefaultNumberFormat: invalid arg !"); + if (!_xTypes.is() || !_xColumn.is()) + return NumberFormat::UNDEFINED; + + sal_Int32 nDataType = 0; + sal_Int32 nScale = 0; + try + { + // determine the datatype of the column + _xColumn->getPropertyValue("Type") >>= nDataType; + + if (DataType::NUMERIC == nDataType || DataType::DECIMAL == nDataType) + _xColumn->getPropertyValue("Scale") >>= nScale; + } + catch (Exception&) + { + return NumberFormat::UNDEFINED; + } + return getDefaultNumberFormat(nDataType, + nScale, + ::cppu::any2bool(_xColumn->getPropertyValue("IsCurrency")), + _xTypes, + _rLocale); +} + +sal_Int32 getDefaultNumberFormat(sal_Int32 _nDataType, + sal_Int32 _nScale, + bool _bIsCurrency, + const Reference< XNumberFormatTypes >& _xTypes, + const Locale& _rLocale) +{ + OSL_ENSURE(_xTypes.is() , "dbtools::getDefaultNumberFormat: invalid arg !"); + if (!_xTypes.is()) + return NumberFormat::UNDEFINED; + + sal_Int32 nFormat = 0; + sal_Int32 nNumberType = _bIsCurrency ? NumberFormat::CURRENCY : NumberFormat::NUMBER; + switch (_nDataType) + { + case DataType::BIT: + case DataType::BOOLEAN: + nFormat = _xTypes->getStandardFormat(NumberFormat::LOGICAL, _rLocale); + break; + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::BIGINT: + case DataType::FLOAT: + case DataType::REAL: + case DataType::DOUBLE: + case DataType::NUMERIC: + case DataType::DECIMAL: + { + try + { + nFormat = _xTypes->getStandardFormat(static_cast<sal_Int16>(nNumberType), _rLocale); + if(_nScale > 0) + { + // generate a new format if necessary + Reference< XNumberFormats > xFormats(_xTypes, UNO_QUERY); + OUString sNewFormat = xFormats->generateFormat( 0, _rLocale, false, false, static_cast<sal_Int16>(_nScale), 1); + + // and add it to the formatter if necessary + nFormat = xFormats->queryKey(sNewFormat, _rLocale, false); + if (nFormat == sal_Int32(-1)) + nFormat = xFormats->addNew(sNewFormat, _rLocale); + } + } + catch (Exception&) + { + nFormat = _xTypes->getStandardFormat(static_cast<sal_Int16>(nNumberType), _rLocale); + } + } break; + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + case DataType::CLOB: + nFormat = _xTypes->getStandardFormat(NumberFormat::TEXT, _rLocale); + break; + case DataType::DATE: + nFormat = _xTypes->getStandardFormat(NumberFormat::DATE, _rLocale); + break; + case DataType::TIME: + nFormat = _xTypes->getStandardFormat(NumberFormat::TIME, _rLocale); + break; + case DataType::TIMESTAMP: + nFormat = _xTypes->getStandardFormat(NumberFormat::DATETIME, _rLocale); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::SQLNULL: + case DataType::OTHER: + case DataType::OBJECT: + case DataType::DISTINCT: + case DataType::STRUCT: + case DataType::ARRAY: + case DataType::BLOB: + case DataType::REF: + default: + nFormat = _xTypes->getStandardFormat(NumberFormat::UNDEFINED, _rLocale); + } + return nFormat; +} + +static Reference< XConnection> findConnection(const Reference< XInterface >& xParent) +{ + Reference< XConnection> xConnection(xParent, UNO_QUERY); + if (!xConnection.is()) + { + Reference< XChild> xChild(xParent, UNO_QUERY); + if (xChild.is()) + xConnection = findConnection(xChild->getParent()); + } + return xConnection; +} + +static Reference< XDataSource> getDataSource_allowException( + const OUString& _rsTitleOrPath, + const Reference< XComponentContext >& _rxContext ) +{ + ENSURE_OR_RETURN( !_rsTitleOrPath.isEmpty(), "getDataSource_allowException: invalid arg !", nullptr ); + + Reference< XDatabaseContext> xDatabaseContext = DatabaseContext::create(_rxContext); + + return Reference< XDataSource >( xDatabaseContext->getByName( _rsTitleOrPath ), UNO_QUERY ); +} + +Reference< XDataSource > getDataSource( + const OUString& _rsTitleOrPath, + const Reference< XComponentContext >& _rxContext ) +{ + Reference< XDataSource > xDS; + try + { + xDS = getDataSource_allowException( _rsTitleOrPath, _rxContext ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + + return xDS; +} + +static Reference< XConnection > getConnection_allowException( + const OUString& _rsTitleOrPath, + const OUString& _rsUser, + const OUString& _rsPwd, + const Reference< XComponentContext>& _rxContext, + const Reference< XWindow >& _rxParent) +{ + Reference< XDataSource> xDataSource( getDataSource_allowException(_rsTitleOrPath, _rxContext) ); + Reference<XConnection> xConnection; + if (xDataSource.is()) + { + + //set ParentWindow for dialog, but just for the duration of this + //call, undo at end of scope + Reference<XInitialization> xIni(xDataSource, UNO_QUERY); + if (xIni.is()) + { + Sequence< Any > aArgs{ Any(NamedValue( "ParentWindow", Any(_rxParent) )) }; + xIni->initialize(aArgs); + } + + // do it with interaction handler + if(_rsUser.isEmpty() || _rsPwd.isEmpty()) + { + Reference<XPropertySet> xProp(xDataSource,UNO_QUERY); + OUString sPwd, sUser; + bool bPwdReq = false; + try + { + xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD)) >>= sPwd; + bPwdReq = ::cppu::any2bool(xProp->getPropertyValue("IsPasswordRequired")); + xProp->getPropertyValue("User") >>= sUser; + } + catch(Exception&) + { + OSL_FAIL("dbtools::getConnection: error while retrieving data source properties!"); + } + if(bPwdReq && sPwd.isEmpty()) + { // password required, but empty -> connect using an interaction handler + Reference<XCompletedConnection> xConnectionCompletion(xProp, UNO_QUERY); + if (xConnectionCompletion.is()) + { // instantiate the default SDB interaction handler + Reference< XInteractionHandler > xHandler = + InteractionHandler::createWithParent(_rxContext, _rxParent); + xConnection = xConnectionCompletion->connectWithCompletion(xHandler); + } + } + else + xConnection = xDataSource->getConnection(sUser, sPwd); + } + if(!xConnection.is()) // try to get one if not already have one, just to make sure + xConnection = xDataSource->getConnection(_rsUser, _rsPwd); + + if (xIni.is()) + { + Sequence< Any > aArgs{ Any(NamedValue( "ParentWindow", Any(Reference<XWindow>()) )) }; + xIni->initialize(aArgs); + } + + } + return xConnection; +} + +Reference< XConnection> getConnection_withFeedback(const OUString& _rDataSourceName, + const OUString& _rUser, const OUString& _rPwd, const Reference< XComponentContext>& _rxContext, + const Reference< XWindow >& _rxParent) +{ + Reference< XConnection > xReturn; + try + { + xReturn = getConnection_allowException(_rDataSourceName, _rUser, _rPwd, _rxContext, _rxParent); + } + catch(SQLException&) + { + // allowed to pass + throw; + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "::dbtools::getConnection_withFeedback: unexpected (non-SQL) exception caught!"); + } + return xReturn; +} + +Reference< XConnection> getConnection(const Reference< XRowSet>& _rxRowSet) +{ + Reference< XConnection> xReturn; + Reference< XPropertySet> xRowSetProps(_rxRowSet, UNO_QUERY); + if (xRowSetProps.is()) + xRowSetProps->getPropertyValue("ActiveConnection") >>= xReturn; + return xReturn; +} + +// helper function which allows to implement both the connectRowset and the ensureRowSetConnection semantics +// if connectRowset (which is deprecated) is removed, this function and one of its parameters are +// not needed anymore, the whole implementation can be moved into ensureRowSetConnection then) +static SharedConnection lcl_connectRowSet(const Reference< XRowSet>& _rxRowSet, const Reference< XComponentContext >& _rxContext, + bool _bAttachAutoDisposer, const Reference< XWindow >& _rxParent) +{ + SharedConnection xConnection; + + do + { + Reference< XPropertySet> xRowSetProps(_rxRowSet, UNO_QUERY); + if ( !xRowSetProps.is() ) + break; + + // 1. already connected? + Reference< XConnection > xExistingConn( + xRowSetProps->getPropertyValue("ActiveConnection"), + UNO_QUERY ); + + if ( xExistingConn.is() + // 2. embedded in a database? + || isEmbeddedInDatabase( _rxRowSet, xExistingConn ) + // 3. is there a connection in the parent hierarchy? + || ( xExistingConn = findConnection( _rxRowSet ) ).is() + ) + { + xRowSetProps->setPropertyValue("ActiveConnection", Any( xExistingConn ) ); + // no auto disposer needed, since we did not create the connection + + xConnection.reset( xExistingConn, SharedConnection::NoTakeOwnership ); + break; + } + + // build a connection with its current settings (4. data source name, or 5. URL) + + static constexpr OUString sUserProp( u"User"_ustr ); + OUString sDataSourceName; + xRowSetProps->getPropertyValue("DataSourceName") >>= sDataSourceName; + OUString sURL; + xRowSetProps->getPropertyValue("URL") >>= sURL; + + Reference< XConnection > xPureConnection; + if (!sDataSourceName.isEmpty()) + { // the row set's data source property is set + // -> try to connect, get user and pwd setting for that + OUString sUser, sPwd; + + if (hasProperty(sUserProp, xRowSetProps)) + xRowSetProps->getPropertyValue(sUserProp) >>= sUser; + if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD), xRowSetProps)) + xRowSetProps->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD)) >>= sPwd; + + xPureConnection = getConnection_allowException( sDataSourceName, sUser, sPwd, _rxContext, _rxParent ); + } + else if (!sURL.isEmpty()) + { // the row set has no data source, but a connection url set + // -> try to connection with that url + Reference< XConnectionPool > xDriverManager; + try { + xDriverManager = ConnectionPool::create( _rxContext ); + } catch( const Exception& ) { } + if (xDriverManager.is()) + { + OUString sUser, sPwd; + if (hasProperty(sUserProp, xRowSetProps)) + xRowSetProps->getPropertyValue(sUserProp) >>= sUser; + if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD), xRowSetProps)) + xRowSetProps->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD)) >>= sPwd; + if (!sUser.isEmpty()) + { // use user and pwd together with the url + auto aInfo(::comphelper::InitPropertySequence({ + { "user", Any(sUser) }, + { "password", Any(sPwd) } + })); + xPureConnection = xDriverManager->getConnectionWithInfo( sURL, aInfo ); + } + else + // just use the url + xPureConnection = xDriverManager->getConnection( sURL ); + } + } + xConnection.reset( + xPureConnection, + _bAttachAutoDisposer ? SharedConnection::NoTakeOwnership : SharedConnection::TakeOwnership + /* take ownership if and only if we're *not* going to auto-dispose the connection */ + ); + + // now if we created a connection, forward it to the row set + if ( xConnection.is() ) + { + try + { + if ( _bAttachAutoDisposer ) + { + new OAutoConnectionDisposer( _rxRowSet, xConnection ); + } + else + xRowSetProps->setPropertyValue( + "ActiveConnection", + Any( xConnection.getTyped() ) + ); + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "EXception when we set the new active connection!"); + } + } + } + while ( false ); + + return xConnection; +} + +Reference< XConnection> connectRowset(const Reference< XRowSet>& _rxRowSet, const Reference< XComponentContext >& _rxContext, const Reference< XWindow >& _rxParent) +{ + SharedConnection xConnection = lcl_connectRowSet( _rxRowSet, _rxContext, true, _rxParent ); + return xConnection.getTyped(); +} + +SharedConnection ensureRowSetConnection(const Reference< XRowSet>& _rxRowSet, const Reference< XComponentContext>& _rxContext, const Reference< XWindow >& _rxParent) +{ + return lcl_connectRowSet( _rxRowSet, _rxContext, false/*bUseAutoConnectionDisposer*/, _rxParent ); +} + +Reference< XNameAccess> getTableFields(const Reference< XConnection>& _rxConn,const OUString& _rName) +{ + Reference< XComponent > xDummy; + return getFieldsByCommandDescriptor( _rxConn, CommandType::TABLE, _rName, xDummy ); +} + +Reference< XNameAccess> getPrimaryKeyColumns_throw(const Any& i_aTable) +{ + const Reference< XPropertySet > xTable(i_aTable,UNO_QUERY_THROW); + return getPrimaryKeyColumns_throw(xTable); +} + +Reference< XNameAccess> getPrimaryKeyColumns_throw(const Reference< XPropertySet >& i_xTable) +{ + Reference<XNameAccess> xKeyColumns; + const Reference<XKeysSupplier> xKeySup(i_xTable,UNO_QUERY); + if ( xKeySup.is() ) + { + const Reference<XIndexAccess> xKeys = xKeySup->getKeys(); + if ( xKeys.is() ) + { + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + const OUString& sPropName = rPropMap.getNameByIndex(PROPERTY_ID_TYPE); + Reference<XPropertySet> xProp; + const sal_Int32 nCount = xKeys->getCount(); + for(sal_Int32 i = 0;i< nCount;++i) + { + xProp.set(xKeys->getByIndex(i),UNO_QUERY_THROW); + sal_Int32 nKeyType = 0; + xProp->getPropertyValue(sPropName) >>= nKeyType; + if(KeyType::PRIMARY == nKeyType) + { + const Reference<XColumnsSupplier> xKeyColsSup(xProp,UNO_QUERY_THROW); + xKeyColumns = xKeyColsSup->getColumns(); + break; + } + } + } + } + + return xKeyColumns; +} + +namespace +{ + enum FieldLookupState + { + HANDLE_TABLE, HANDLE_QUERY, HANDLE_SQL, RETRIEVE_OBJECT, RETRIEVE_COLUMNS, DONE, FAILED + }; +} + +Reference< XNameAccess > getFieldsByCommandDescriptor( const Reference< XConnection >& _rxConnection, + const sal_Int32 _nCommandType, const OUString& _rCommand, + Reference< XComponent >& _rxKeepFieldsAlive, SQLExceptionInfo* _pErrorInfo ) +{ + OSL_PRECOND( _rxConnection.is(), "::dbtools::getFieldsByCommandDescriptor: invalid connection!" ); + OSL_PRECOND( ( CommandType::TABLE == _nCommandType ) || ( CommandType::QUERY == _nCommandType ) || ( CommandType::COMMAND == _nCommandType ), + "::dbtools::getFieldsByCommandDescriptor: invalid command type!" ); + OSL_PRECOND( !_rCommand.isEmpty(), "::dbtools::getFieldsByCommandDescriptor: invalid command (empty)!" ); + + Reference< XNameAccess > xFields; + + // reset the error + if ( _pErrorInfo ) + *_pErrorInfo = SQLExceptionInfo(); + // reset the ownership holder + _rxKeepFieldsAlive.clear(); + + // go for the fields + try + { + // some kind of state machine to ease the sharing of code + FieldLookupState eState = FAILED; + switch ( _nCommandType ) + { + case CommandType::TABLE: + eState = HANDLE_TABLE; + break; + case CommandType::QUERY: + eState = HANDLE_QUERY; + break; + case CommandType::COMMAND: + eState = HANDLE_SQL; + break; + } + + // needed in various states: + Reference< XNameAccess > xObjectCollection; + Reference< XColumnsSupplier > xSupplyColumns; + + // go! + while ( ( DONE != eState ) && ( FAILED != eState ) ) + { + switch ( eState ) + { + case HANDLE_TABLE: + { + // initial state for handling the tables + + // get the table objects + Reference< XTablesSupplier > xSupplyTables( _rxConnection, UNO_QUERY ); + if ( xSupplyTables.is() ) + xObjectCollection = xSupplyTables->getTables(); + // if something went wrong 'til here, then this will be handled in the next state + + // next state: get the object + eState = RETRIEVE_OBJECT; + } + break; + + case HANDLE_QUERY: + { + // initial state for handling the tables + + // get the table objects + Reference< XQueriesSupplier > xSupplyQueries( _rxConnection, UNO_QUERY ); + if ( xSupplyQueries.is() ) + xObjectCollection = xSupplyQueries->getQueries(); + // if something went wrong 'til here, then this will be handled in the next state + + // next state: get the object + eState = RETRIEVE_OBJECT; + } + break; + + case RETRIEVE_OBJECT: + // here we should have an object (aka query or table) collection, and are going + // to retrieve the desired object + + // next state: default to FAILED + eState = FAILED; + + OSL_ENSURE( xObjectCollection.is(), "::dbtools::getFieldsByCommandDescriptor: invalid connection (no sdb.Connection, or no Tables-/QueriesSupplier)!"); + if ( xObjectCollection.is() && xObjectCollection->hasByName( _rCommand ) ) + { + xObjectCollection->getByName( _rCommand ) >>= xSupplyColumns; + // (xSupplyColumns being NULL will be handled in the next state) + + // next: go for the columns + eState = RETRIEVE_COLUMNS; + } + break; + + case RETRIEVE_COLUMNS: + OSL_ENSURE( xSupplyColumns.is(), "::dbtools::getFieldsByCommandDescriptor: could not retrieve the columns supplier!" ); + + // next state: default to FAILED + eState = FAILED; + + if ( xSupplyColumns.is() ) + { + xFields = xSupplyColumns->getColumns(); + // that's it + eState = DONE; + } + break; + + case HANDLE_SQL: + { + OUString sStatementToExecute( _rCommand ); + + // well, the main problem here is to handle statements which contain a parameter + // If we would simply execute a parametrized statement, then this will fail because + // we cannot supply any parameter values. + // Thus, we try to analyze the statement, and to append a WHERE 0=1 filter criterion + // This should cause every driver to not really execute the statement, but to return + // an empty result set with the proper structure. We then can use this result set + // to retrieve the columns. + + try + { + Reference< XMultiServiceFactory > xComposerFac( _rxConnection, UNO_QUERY ); + + if ( xComposerFac.is() ) + { + Reference< XSingleSelectQueryComposer > xComposer(xComposerFac->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"),UNO_QUERY); + if ( xComposer.is() ) + { + xComposer->setQuery( sStatementToExecute ); + + // Now set the filter to a dummy restriction which will result in an empty + // result set. + xComposer->setFilter( "0=1" ); + sStatementToExecute = xComposer->getQuery( ); + } + } + } + catch( const Exception& ) + { + // silent this error, this was just a try. If we're here, we did not change sStatementToExecute, + // so it will still be _rCommand, which then will be executed without being touched + } + + // now execute + Reference< XPreparedStatement > xStatement = _rxConnection->prepareStatement( sStatementToExecute ); + // transfer ownership of this temporary object to the caller + _rxKeepFieldsAlive.set(xStatement, css::uno::UNO_QUERY); + + // set the "MaxRows" to 0. This is just in case our attempt to append a 0=1 filter + // failed - in this case, the MaxRows restriction should at least ensure that there + // is no data returned (which would be potentially expensive) + Reference< XPropertySet > xStatementProps( xStatement,UNO_QUERY ); + try + { + if ( xStatementProps.is() ) + xStatementProps->setPropertyValue( "MaxRows", Any( sal_Int32( 0 ) ) ); + } + catch( const Exception& ) + { + OSL_FAIL( "::dbtools::getFieldsByCommandDescriptor: could not set the MaxRows!" ); + // oh damn. Not much of a chance to recover, we will no retrieve the complete + // full blown result set + } + + xSupplyColumns.set(xStatement->executeQuery(), css::uno::UNO_QUERY); + // this should have given us a result set which does not contain any data, but + // the structural information we need + + // so the next state is to get the columns + eState = RETRIEVE_COLUMNS; + } + break; + + default: + OSL_FAIL( "::dbtools::getFieldsByCommandDescriptor: oops! unhandled state here!" ); + eState = FAILED; + } + } + } + catch( const SQLContext& e ) { if ( _pErrorInfo ) *_pErrorInfo = SQLExceptionInfo( e ); } + catch( const SQLWarning& e ) { if ( _pErrorInfo ) *_pErrorInfo = SQLExceptionInfo( e ); } + catch( const SQLException& e ) { if ( _pErrorInfo ) *_pErrorInfo = SQLExceptionInfo( e ); } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "::dbtools::getFieldsByCommandDescriptor: caught an exception while retrieving the fields!" ); + } + + return xFields; +} + +Sequence< OUString > getFieldNamesByCommandDescriptor( const Reference< XConnection >& _rxConnection, + const sal_Int32 _nCommandType, const OUString& _rCommand, + SQLExceptionInfo* _pErrorInfo ) +{ + // get the container for the fields + Reference< XComponent > xKeepFieldsAlive; + Reference< XNameAccess > xFieldContainer = getFieldsByCommandDescriptor( _rxConnection, _nCommandType, _rCommand, xKeepFieldsAlive, _pErrorInfo ); + + // get the names of the fields + Sequence< OUString > aNames; + if ( xFieldContainer.is() ) + aNames = xFieldContainer->getElementNames(); + + // clean up any temporary objects which have been created + disposeComponent( xKeepFieldsAlive ); + + // outta here + return aNames; +} + +SQLException prependErrorInfo( const SQLException& _rChainedException, const Reference< XInterface >& _rxContext, + const OUString& _rAdditionalError, const StandardSQLState _eSQLState ) +{ + return SQLException( _rAdditionalError, _rxContext, + _eSQLState == StandardSQLState::ERROR_UNSPECIFIED ? OUString() : getStandardSQLState( _eSQLState ), + 0, Any( _rChainedException ) ); +} + +namespace +{ + struct NameComponentSupport + { + const bool bCatalogs; + const bool bSchemas; + + NameComponentSupport( const bool _bCatalogs, const bool _bSchemas ) + :bCatalogs( _bCatalogs ) + ,bSchemas( _bSchemas ) + { + } + }; + + NameComponentSupport lcl_getNameComponentSupport( const Reference< XDatabaseMetaData >& _rxMetaData, EComposeRule _eComposeRule ) + { + OSL_PRECOND( _rxMetaData.is(), "lcl_getNameComponentSupport: invalid meta data!" ); + + FMetaDataSupport pCatalogCall = &XDatabaseMetaData::supportsCatalogsInDataManipulation; + FMetaDataSupport pSchemaCall = &XDatabaseMetaData::supportsSchemasInDataManipulation; + bool bIgnoreMetaData = false; + + switch ( _eComposeRule ) + { + case EComposeRule::InTableDefinitions: + pCatalogCall = &XDatabaseMetaData::supportsCatalogsInTableDefinitions; + pSchemaCall = &XDatabaseMetaData::supportsSchemasInTableDefinitions; + break; + case EComposeRule::InIndexDefinitions: + pCatalogCall = &XDatabaseMetaData::supportsCatalogsInIndexDefinitions; + pSchemaCall = &XDatabaseMetaData::supportsSchemasInIndexDefinitions; + break; + case EComposeRule::InProcedureCalls: + pCatalogCall = &XDatabaseMetaData::supportsCatalogsInProcedureCalls; + pSchemaCall = &XDatabaseMetaData::supportsSchemasInProcedureCalls; + break; + case EComposeRule::InPrivilegeDefinitions: + pCatalogCall = &XDatabaseMetaData::supportsCatalogsInPrivilegeDefinitions; + pSchemaCall = &XDatabaseMetaData::supportsSchemasInPrivilegeDefinitions; + break; + case EComposeRule::Complete: + bIgnoreMetaData = true; + break; + case EComposeRule::InDataManipulation: + // already properly set above + break; + } + return NameComponentSupport( + bIgnoreMetaData || (_rxMetaData.get()->*pCatalogCall)(), + bIgnoreMetaData || (_rxMetaData.get()->*pSchemaCall)() + ); + } +} + +static OUString impl_doComposeTableName( const Reference< XDatabaseMetaData >& _rxMetaData, + const OUString& _rCatalog, const OUString& _rSchema, const OUString& _rName, + bool _bQuote, EComposeRule _eComposeRule ) +{ + OSL_ENSURE(_rxMetaData.is(), "impl_doComposeTableName : invalid meta data !"); + if ( !_rxMetaData.is() ) + return OUString(); + OSL_ENSURE(!_rName.isEmpty(), "impl_doComposeTableName : at least the name should be non-empty !"); + + const OUString sQuoteString = _rxMetaData->getIdentifierQuoteString(); + const NameComponentSupport aNameComps( lcl_getNameComponentSupport( _rxMetaData, _eComposeRule ) ); + + OUStringBuffer aComposedName; + + OUString sCatalogSep; + bool bCatalogAtStart = true; + if ( !_rCatalog.isEmpty() && aNameComps.bCatalogs ) + { + sCatalogSep = _rxMetaData->getCatalogSeparator(); + bCatalogAtStart = _rxMetaData->isCatalogAtStart(); + + if ( bCatalogAtStart && !sCatalogSep.isEmpty()) + { + aComposedName.append( _bQuote ? quoteName( sQuoteString, _rCatalog ) : _rCatalog ); + aComposedName.append( sCatalogSep ); + } + } + + if ( !_rSchema.isEmpty() && aNameComps.bSchemas ) + { + aComposedName.append( + (_bQuote ? quoteName( sQuoteString, _rSchema ) : _rSchema ) + + "." ); + } + + aComposedName.append( _bQuote ? quoteName( sQuoteString, _rName ) : _rName ); + + if ( !_rCatalog.isEmpty() + && !bCatalogAtStart + && !sCatalogSep.isEmpty() + && aNameComps.bCatalogs + ) + { + aComposedName.append( sCatalogSep ); + aComposedName.append( _bQuote ? quoteName( sQuoteString, _rCatalog ) : _rCatalog ); + } + + return aComposedName.makeStringAndClear(); +} + +OUString quoteTableName(const Reference< XDatabaseMetaData>& _rxMeta + , const OUString& _rName + , EComposeRule _eComposeRule) +{ + OUString sCatalog, sSchema, sTable; + qualifiedNameComponents(_rxMeta,_rName,sCatalog,sSchema,sTable,_eComposeRule); + return impl_doComposeTableName( _rxMeta, sCatalog, sSchema, sTable, true, _eComposeRule ); +} + +void qualifiedNameComponents(const Reference< XDatabaseMetaData >& _rxConnMetaData, const OUString& _rQualifiedName, OUString& _rCatalog, OUString& _rSchema, OUString& _rName,EComposeRule _eComposeRule) +{ + OSL_ENSURE(_rxConnMetaData.is(), "QualifiedNameComponents : invalid meta data!"); + + NameComponentSupport aNameComps( lcl_getNameComponentSupport( _rxConnMetaData, _eComposeRule ) ); + + OUString sSeparator = _rxConnMetaData->getCatalogSeparator(); + + OUString sName(_rQualifiedName); + // do we have catalogs? + if ( aNameComps.bCatalogs ) + { + if (_rxConnMetaData->isCatalogAtStart()) + { + // search for the catalog name at the beginning + sal_Int32 nIndex = sName.indexOf(sSeparator); + if (-1 != nIndex) + { + _rCatalog = sName.copy(0, nIndex); + sName = sName.copy(nIndex + 1); + } + } + else + { + // Catalog name at the end + sal_Int32 nIndex = sName.lastIndexOf(sSeparator); + if (-1 != nIndex) + { + _rCatalog = sName.copy(nIndex + 1); + sName = sName.copy(0, nIndex); + } + } + } + + if ( aNameComps.bSchemas ) + { + sal_Int32 nIndex = sName.indexOf('.'); + // OSL_ENSURE(-1 != nIndex, "QualifiedNameComponents: no schema separator!"); + if ( nIndex != -1 ) + _rSchema = sName.copy(0, nIndex); + sName = sName.copy(nIndex + 1); + } + + _rName = sName; +} + +Reference< XNumberFormatsSupplier> getNumberFormats( + const Reference< XConnection>& _rxConn, + bool _bAlloweDefault, + const Reference< XComponentContext>& _rxContext) +{ + // ask the parent of the connection (should be a DatabaseAccess) + Reference< XNumberFormatsSupplier> xReturn; + Reference< XChild> xConnAsChild(_rxConn, UNO_QUERY); + static constexpr OUString sPropFormatsSupplier( u"NumberFormatsSupplier"_ustr ); + if (xConnAsChild.is()) + { + Reference< XPropertySet> xConnParentProps(xConnAsChild->getParent(), UNO_QUERY); + if (xConnParentProps.is() && hasProperty(sPropFormatsSupplier, xConnParentProps)) + xConnParentProps->getPropertyValue(sPropFormatsSupplier) >>= xReturn; + } + else if(_bAlloweDefault && _rxContext.is()) + { + xReturn = NumberFormatsSupplier::createWithDefaultLocale( _rxContext ); + } + return xReturn; +} + +void TransferFormComponentProperties( + const Reference< XPropertySet>& xOldProps, + const Reference< XPropertySet>& xNewProps, + const Locale& _rLocale) +{ +try +{ + OSL_ENSURE( xOldProps.is() && xNewProps.is(), "TransferFormComponentProperties: invalid source/dest!" ); + if ( !xOldProps.is() || !xNewProps.is() ) + return; + + // First we copy all the Props, that are available in source and target and have the same description + Reference< XPropertySetInfo> xOldInfo( xOldProps->getPropertySetInfo()); + Reference< XPropertySetInfo> xNewInfo( xNewProps->getPropertySetInfo()); + + const Sequence< Property> aOldProperties = xOldInfo->getProperties(); + const Sequence< Property> aNewProperties = xNewInfo->getProperties(); + + static constexpr OUString sPropFormatsSupplier(u"FormatsSupplier"_ustr); + static constexpr OUString sPropCurrencySymbol(u"CurrencySymbol"_ustr); + static constexpr OUString sPropDecimals(u"Decimals"_ustr); + static constexpr OUString sPropEffectiveMin(u"EffectiveMin"_ustr); + static constexpr OUString sPropEffectiveMax(u"EffectiveMax"_ustr); + static constexpr OUString sPropEffectiveDefault(u"EffectiveDefault"_ustr); + static constexpr OUString sPropDefaultText(u"DefaultText"_ustr); + static constexpr OUString sPropDefaultDate(u"DefaultDate"_ustr); + static constexpr OUString sPropDefaultTime(u"DefaultTime"_ustr); + static constexpr OUString sPropValueMin(u"ValueMin"_ustr); + static constexpr OUString sPropValueMax(u"ValueMax"_ustr); + static constexpr OUString sPropDecimalAccuracy(u"DecimalAccuracy"_ustr); + static constexpr OUString sPropClassId(u"ClassId"_ustr); + static constexpr OUString sFormattedServiceName( u"com.sun.star.form.component.FormattedField"_ustr ); + + for (const Property& rOldProp : aOldProperties) + { + if ( rOldProp.Name != "DefaultControl" && rOldProp.Name != "LabelControl" ) + { + // binary search + const Property* pResult = std::lower_bound( + aNewProperties.begin(), aNewProperties.end(), rOldProp, ::comphelper::PropertyCompareByName()); + + if ( ( pResult != aNewProperties.end() ) + && ( pResult->Name == rOldProp.Name ) + && ( (pResult->Attributes & PropertyAttribute::READONLY) == 0 ) + && ( pResult->Type.equals(rOldProp.Type)) ) + { // Attributes match and the property is not read-only + try + { + xNewProps->setPropertyValue(pResult->Name, xOldProps->getPropertyValue(pResult->Name)); + } + catch(IllegalArgumentException const &) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "TransferFormComponentProperties : could not transfer the value for property \"" + << pResult->Name << "\""); + } + } + } + } + + // for formatted fields (either old or new) we have some special treatments + Reference< XServiceInfo > xSI( xOldProps, UNO_QUERY ); + bool bOldIsFormatted = xSI.is() && xSI->supportsService( sFormattedServiceName ); + xSI.set( xNewProps, UNO_QUERY ); + bool bNewIsFormatted = xSI.is() && xSI->supportsService( sFormattedServiceName ); + + if (!bOldIsFormatted && !bNewIsFormatted) + return; // nothing to do + + if (bOldIsFormatted && bNewIsFormatted) + // if both fields are formatted we do no conversions + return; + + if (bOldIsFormatted) + { + // get some properties from the selected format and put them in the new Set + Any aFormatKey( xOldProps->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY)) ); + if (aFormatKey.hasValue()) + { + Reference< XNumberFormatsSupplier> xSupplier; + xOldProps->getPropertyValue(sPropFormatsSupplier) >>= xSupplier; + if (xSupplier.is()) + { + Reference< XNumberFormats> xFormats(xSupplier->getNumberFormats()); + Reference< XPropertySet> xFormat(xFormats->getByKey(getINT32(aFormatKey))); + if (hasProperty(sPropCurrencySymbol, xFormat)) + { + Any aVal( xFormat->getPropertyValue(sPropCurrencySymbol) ); + if (aVal.hasValue() && hasProperty(sPropCurrencySymbol, xNewProps)) + // If the source value hasn't been set then don't copy it + // so we don't overwrite the default value + xNewProps->setPropertyValue(sPropCurrencySymbol, aVal); + } + if (hasProperty(sPropDecimals, xFormat) && hasProperty(sPropDecimals, xNewProps)) + xNewProps->setPropertyValue(sPropDecimals, xFormat->getPropertyValue(sPropDecimals)); + } + } + + // a potential Min-Max-Conversion + Any aEffectiveMin( xOldProps->getPropertyValue(sPropEffectiveMin) ); + if (aEffectiveMin.hasValue()) + { // Unlike the ValueMin the EffectiveMin can be void + if (hasProperty(sPropValueMin, xNewProps)) + { + OSL_ENSURE(aEffectiveMin.getValueType().getTypeClass() == TypeClass_DOUBLE, + "TransferFormComponentProperties : invalid property type !"); + xNewProps->setPropertyValue(sPropValueMin, aEffectiveMin); + } + } + Any aEffectiveMax( xOldProps->getPropertyValue(sPropEffectiveMax) ); + if (aEffectiveMax.hasValue()) + { // analog + if (hasProperty(sPropValueMax, xNewProps)) + { + OSL_ENSURE(aEffectiveMax.getValueType().getTypeClass() == TypeClass_DOUBLE, + "TransferFormComponentProperties : invalid property type !"); + xNewProps->setPropertyValue(sPropValueMax, aEffectiveMax); + } + } + + // then we can still convert and copy the default values + Any aEffectiveDefault( xOldProps->getPropertyValue(sPropEffectiveDefault) ); + if (aEffectiveDefault.hasValue()) + { + bool bIsString = aEffectiveDefault.getValueType().getTypeClass() == TypeClass_STRING; + OSL_ENSURE(bIsString || aEffectiveDefault.getValueType().getTypeClass() == TypeClass_DOUBLE, + "TransferFormComponentProperties : invalid property type !"); + // The Effective-Properties should always be void or string or double... + + if (hasProperty(sPropDefaultDate, xNewProps) && !bIsString) + { // (to convert an OUString into a date will not always succeed, because it might be bound to a text-column, + // but we can work with a double) + Date aDate = DBTypeConversion::toDate(getDouble(aEffectiveDefault)); + xNewProps->setPropertyValue(sPropDefaultDate, Any(aDate)); + } + + if (hasProperty(sPropDefaultTime, xNewProps) && !bIsString) + { // Completely analogous to time + css::util::Time aTime = DBTypeConversion::toTime(getDouble(aEffectiveDefault)); + xNewProps->setPropertyValue(sPropDefaultTime, Any(aTime)); + } + + if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE), xNewProps) && !bIsString) + { // Here we can simply pass the double + xNewProps->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE), aEffectiveDefault); + } + + if (hasProperty(sPropDefaultText, xNewProps) && bIsString) + { // and here the OUString + xNewProps->setPropertyValue(sPropDefaultText, aEffectiveDefault); + } + + // nyi: The translation between doubles and OUString would offer more alternatives + } + } + + // The other direction: the new Control shall be formatted + if (bNewIsFormatted) + { + // first the formatting + // we can't set a Supplier, so the new Set must bring one in + Reference< XNumberFormatsSupplier> xSupplier; + xNewProps->getPropertyValue(sPropFormatsSupplier) >>= xSupplier; + if (xSupplier.is()) + { + Reference< XNumberFormats> xFormats(xSupplier->getNumberFormats()); + + // Set number of decimals + sal_Int16 nDecimals = 2; + if (hasProperty(sPropDecimalAccuracy, xOldProps)) + xOldProps->getPropertyValue(sPropDecimalAccuracy) >>= nDecimals; + + // base format (depending on the ClassId of the old Set) + sal_Int32 nBaseKey = 0; + if (hasProperty(sPropClassId, xOldProps)) + { + Reference< XNumberFormatTypes> xTypeList(xFormats, UNO_QUERY); + if (xTypeList.is()) + { + sal_Int16 nClassId = 0; + xOldProps->getPropertyValue(sPropClassId) >>= nClassId; + switch (nClassId) + { + case FormComponentType::DATEFIELD : + nBaseKey = xTypeList->getStandardFormat(NumberFormat::DATE, _rLocale); + break; + + case FormComponentType::TIMEFIELD : + nBaseKey = xTypeList->getStandardFormat(NumberFormat::TIME, _rLocale); + break; + + case FormComponentType::CURRENCYFIELD : + nBaseKey = xTypeList->getStandardFormat(NumberFormat::CURRENCY, _rLocale); + break; + } + } + } + + // With this we can generate a new format ... + OUString sNewFormat = xFormats->generateFormat(nBaseKey, _rLocale, false, false, nDecimals, 0); + // No thousands separator, negative numbers are not in red, no leading zeros + + // ... and add at FormatsSupplier (if needed) + sal_Int32 nKey = xFormats->queryKey(sNewFormat, _rLocale, false); + if (nKey == sal_Int32(-1)) + { // not added yet in my formatter ... + nKey = xFormats->addNew(sNewFormat, _rLocale); + } + + xNewProps->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY), Any(nKey)); + } + + // min-/max-Value + Any aNewMin, aNewMax; + if (hasProperty(sPropValueMin, xOldProps)) + aNewMin = xOldProps->getPropertyValue(sPropValueMin); + if (hasProperty(sPropValueMax, xOldProps)) + aNewMax = xOldProps->getPropertyValue(sPropValueMax); + xNewProps->setPropertyValue(sPropEffectiveMin, aNewMin); + xNewProps->setPropertyValue(sPropEffectiveMax, aNewMax); + + // Default-Value + Any aNewDefault; + if (hasProperty(sPropDefaultDate, xOldProps)) + { + Any aDate( xOldProps->getPropertyValue(sPropDefaultDate) ); + if (aDate.hasValue()) + aNewDefault <<= DBTypeConversion::toDouble(*o3tl::doAccess<Date>(aDate)); + } + + if (hasProperty(sPropDefaultTime, xOldProps)) + { + Any aTime( xOldProps->getPropertyValue(sPropDefaultTime) ); + if (aTime.hasValue()) + aNewDefault <<= DBTypeConversion::toDouble(*o3tl::doAccess<Time>(aTime)); + } + + // double or OUString will be copied directly + if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE), xOldProps)) + aNewDefault = xOldProps->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE)); + if (hasProperty(sPropDefaultText, xOldProps)) + aNewDefault = xOldProps->getPropertyValue(sPropDefaultText); + + if (aNewDefault.hasValue()) + xNewProps->setPropertyValue(sPropEffectiveDefault, aNewDefault); + } +} +catch(const Exception&) +{ + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "TransferFormComponentProperties" ); +} +} + +bool canInsert(const Reference< XPropertySet>& _rxCursorSet) +{ + return (_rxCursorSet.is() && (getINT32(_rxCursorSet->getPropertyValue("Privileges")) & Privilege::INSERT) != 0); +} + +bool canUpdate(const Reference< XPropertySet>& _rxCursorSet) +{ + return (_rxCursorSet.is() && (getINT32(_rxCursorSet->getPropertyValue("Privileges")) & Privilege::UPDATE) != 0); +} + +bool canDelete(const Reference< XPropertySet>& _rxCursorSet) +{ + return (_rxCursorSet.is() && (getINT32(_rxCursorSet->getPropertyValue("Privileges")) & Privilege::DELETE) != 0); +} + +Reference< XDataSource> findDataSource(const Reference< XInterface >& _xParent) +{ + Reference< XOfficeDatabaseDocument> xDatabaseDocument(_xParent, UNO_QUERY); + Reference< XDataSource> xDataSource; + if ( xDatabaseDocument.is() ) + xDataSource = xDatabaseDocument->getDataSource(); + if ( !xDataSource.is() ) + xDataSource.set(_xParent, UNO_QUERY); + if (!xDataSource.is()) + { + Reference< XChild> xChild(_xParent, UNO_QUERY); + if ( xChild.is() ) + xDataSource = findDataSource(xChild->getParent()); + } + return xDataSource; +} + +static Reference< XSingleSelectQueryComposer > getComposedRowSetStatement( const Reference< XPropertySet >& _rxRowSet, const Reference< XComponentContext >& _rxContext, const Reference< XWindow >& _rxParent ) +{ + Reference< XSingleSelectQueryComposer > xComposer; + try + { + Reference< XConnection> xConn = connectRowset( Reference< XRowSet >( _rxRowSet, UNO_QUERY ), _rxContext, _rxParent ); + if ( xConn.is() ) // implies _rxRowSet.is() + { + // build the statement the row set is based on (can't use the ActiveCommand property of the set + // as this reflects the status after the last execute, not the currently set properties) + + sal_Int32 nCommandType = CommandType::COMMAND; + OUString sCommand; + bool bEscapeProcessing = false; + + OSL_VERIFY( _rxRowSet->getPropertyValue("CommandType") >>= nCommandType ); + OSL_VERIFY( _rxRowSet->getPropertyValue("Command") >>= sCommand ); + OSL_VERIFY( _rxRowSet->getPropertyValue("EscapeProcessing") >>= bEscapeProcessing ); + + StatementComposer aComposer( xConn, sCommand, nCommandType, bEscapeProcessing ); + // append sort + aComposer.setOrder( getString( _rxRowSet->getPropertyValue("Order") ) ); + + // append filter + bool bApplyFilter = true; + _rxRowSet->getPropertyValue("ApplyFilter") >>= bApplyFilter; + if ( bApplyFilter ) + { + aComposer.setFilter( getString( _rxRowSet->getPropertyValue("Filter") ) ); + aComposer.setHavingClause( getString( _rxRowSet->getPropertyValue("HavingClause") ) ); + } + + aComposer.getQuery(); + + xComposer = aComposer.getComposer(); + aComposer.setDisposeComposer( false ); + } + } + catch( const SQLException& ) + { + throw; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + + return xComposer; +} + +Reference< XSingleSelectQueryComposer > getCurrentSettingsComposer( + const Reference< XPropertySet>& _rxRowSetProps, + const Reference< XComponentContext>& _rxContext, + const Reference< XWindow >& _rxParent) +{ + Reference< XSingleSelectQueryComposer > xReturn; + try + { + xReturn = getComposedRowSetStatement( _rxRowSetProps, _rxContext, _rxParent ); + } + catch( const SQLException& ) + { + throw; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "::getCurrentSettingsComposer : caught an exception !" ); + } + + return xReturn; +} + +OUString composeTableName( const Reference< XDatabaseMetaData >& _rxMetaData, + const OUString& _rCatalog, + const OUString& _rSchema, + const OUString& _rName, + bool _bQuote, + EComposeRule _eComposeRule) +{ + return impl_doComposeTableName( _rxMetaData, _rCatalog, _rSchema, _rName, _bQuote, _eComposeRule ); +} + +OUString composeTableNameForSelect( const Reference< XConnection >& _rxConnection, + const OUString& _rCatalog, const OUString& _rSchema, const OUString& _rName ) +{ + bool bUseCatalogInSelect = isDataSourcePropertyEnabled( _rxConnection, "UseCatalogInSelect", true ); + bool bUseSchemaInSelect = isDataSourcePropertyEnabled( _rxConnection, "UseSchemaInSelect", true ); + + return impl_doComposeTableName( + _rxConnection->getMetaData(), + bUseCatalogInSelect ? _rCatalog : OUString(), + bUseSchemaInSelect ? _rSchema : OUString(), + _rName, + true, + EComposeRule::InDataManipulation + ); +} + +namespace +{ + void lcl_getTableNameComponents( const Reference<XPropertySet>& _xTable, + OUString& _out_rCatalog, OUString& _out_rSchema, OUString& _out_rName ) + { + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + Reference< XPropertySetInfo > xInfo; + if (_xTable.is()) + xInfo = _xTable->getPropertySetInfo(); + if ( xInfo.is() + && xInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) ) + { + if ( xInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) + && xInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) ) + { + _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) >>= _out_rCatalog; + _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= _out_rSchema; + } + _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= _out_rName; + } + else + OSL_FAIL( "::dbtools::lcl_getTableNameComponents: this is no table object!" ); + } +} + +OUString composeTableNameForSelect( const Reference< XConnection >& _rxConnection, const Reference<XPropertySet>& _xTable ) +{ + OUString sCatalog, sSchema, sName; + lcl_getTableNameComponents( _xTable, sCatalog, sSchema, sName ); + + return composeTableNameForSelect( _rxConnection, sCatalog, sSchema, sName ); +} + +OUString composeTableName(const Reference<XDatabaseMetaData>& _xMetaData, + const Reference<XPropertySet>& _xTable, + EComposeRule _eComposeRule, + bool _bQuote ) +{ + OUString sCatalog, sSchema, sName; + lcl_getTableNameComponents( _xTable, sCatalog, sSchema, sName ); + + return impl_doComposeTableName( + _xMetaData, + sCatalog, + sSchema, + sName, + _bQuote, + _eComposeRule + ); +} + +sal_Int32 getSearchColumnFlag( const Reference< XConnection>& _rxConn,sal_Int32 _nDataType) +{ + sal_Int32 nSearchFlag = 0; + Reference<XResultSet> xSet = _rxConn->getMetaData()->getTypeInfo(); + if(xSet.is()) + { + Reference<XRow> xRow(xSet,UNO_QUERY); + while(xSet->next()) + { + if(xRow->getInt(2) == _nDataType) + { + nSearchFlag = xRow->getInt(9); + break; + } + } + } + return nSearchFlag; +} + +OUString createUniqueName( const Sequence< OUString >& _rNames, const OUString& _rBaseName, bool _bStartWithNumber ) +{ + std::set< OUString > aUsedNames(_rNames.begin(), _rNames.end()); + + OUString sName( _rBaseName ); + sal_Int32 nPos = 1; + if ( _bStartWithNumber ) + sName += OUString::number( nPos ); + + while ( aUsedNames.find( sName ) != aUsedNames.end() ) + { + sName = _rBaseName + OUString::number( ++nPos ); + } + return sName; +} + +OUString createUniqueName(const Reference<XNameAccess>& _rxContainer,const OUString& _rBaseName, bool _bStartWithNumber) +{ + Sequence< OUString > aElementNames; + + OSL_ENSURE( _rxContainer.is(), "createUniqueName: invalid container!" ); + if ( _rxContainer.is() ) + aElementNames = _rxContainer->getElementNames(); + + return createUniqueName( aElementNames, _rBaseName, _bStartWithNumber ); +} + +void showError(const SQLExceptionInfo& _rInfo, + const Reference< XWindow>& _xParent, + const Reference< XComponentContext >& _rxContext) +{ + if (_rInfo.isValid()) + { + try + { + Reference< XExecutableDialog > xErrorDialog = ErrorMessageDialog::create( _rxContext, "", _xParent, _rInfo.get() ); + xErrorDialog->execute(); + } + catch(const Exception&) + { + OSL_FAIL("showError: could not display the error message!"); + } + } +} + +bool implUpdateObject(const Reference< XRowUpdate >& _rxUpdatedObject, + const sal_Int32 _nColumnIndex, const Any& _rValue) +{ + bool bSuccessfullyReRouted = true; + switch (_rValue.getValueTypeClass()) + { + case TypeClass_ANY: + { + bSuccessfullyReRouted = implUpdateObject(_rxUpdatedObject, _nColumnIndex, _rValue); + } + break; + + case TypeClass_VOID: + _rxUpdatedObject->updateNull(_nColumnIndex); + break; + + case TypeClass_STRING: + _rxUpdatedObject->updateString(_nColumnIndex, *o3tl::forceAccess<OUString>(_rValue)); + break; + + case TypeClass_BOOLEAN: + _rxUpdatedObject->updateBoolean(_nColumnIndex, *o3tl::forceAccess<bool>(_rValue)); + break; + + case TypeClass_BYTE: + _rxUpdatedObject->updateByte(_nColumnIndex, *o3tl::forceAccess<sal_Int8>(_rValue)); + break; + + case TypeClass_UNSIGNED_SHORT: + case TypeClass_SHORT: + _rxUpdatedObject->updateShort(_nColumnIndex, *o3tl::forceAccess<sal_Int16>(_rValue)); + break; + + case TypeClass_CHAR: + _rxUpdatedObject->updateString(_nColumnIndex,OUString(*o3tl::forceAccess<sal_Unicode>(_rValue))); + break; + + case TypeClass_UNSIGNED_LONG: + case TypeClass_LONG: + _rxUpdatedObject->updateInt(_nColumnIndex, *o3tl::forceAccess<sal_Int32>(_rValue)); + break; + + case TypeClass_HYPER: + { + sal_Int64 nValue = 0; + OSL_VERIFY( _rValue >>= nValue ); + _rxUpdatedObject->updateLong( _nColumnIndex, nValue ); + } + break; + + case TypeClass_FLOAT: + _rxUpdatedObject->updateFloat(_nColumnIndex, *o3tl::forceAccess<float>(_rValue)); + break; + + case TypeClass_DOUBLE: + _rxUpdatedObject->updateDouble(_nColumnIndex, *o3tl::forceAccess<double>(_rValue)); + break; + + case TypeClass_SEQUENCE: + if (auto s = o3tl::tryAccess<Sequence< sal_Int8 >>(_rValue)) + _rxUpdatedObject->updateBytes(_nColumnIndex, *s); + else + bSuccessfullyReRouted = false; + break; + case TypeClass_STRUCT: + if (auto s1 = o3tl::tryAccess<DateTime>(_rValue)) + _rxUpdatedObject->updateTimestamp(_nColumnIndex, *s1); + else if (auto s2 = o3tl::tryAccess<Date>(_rValue)) + _rxUpdatedObject->updateDate(_nColumnIndex, *s2); + else if (auto s3 = o3tl::tryAccess<Time>(_rValue)) + _rxUpdatedObject->updateTime(_nColumnIndex, *s3); + else + bSuccessfullyReRouted = false; + break; + + case TypeClass_INTERFACE: + if (auto xStream = o3tl::tryAccess<Reference<XInputStream>>(_rValue)) + { + _rxUpdatedObject->updateBinaryStream(_nColumnIndex, *xStream, (*xStream)->available()); + break; + } + [[fallthrough]]; + default: + bSuccessfullyReRouted = false; + } + + return bSuccessfullyReRouted; +} + +bool implSetObject( const Reference< XParameters >& _rxParameters, + const sal_Int32 _nColumnIndex, const Any& _rValue) +{ + bool bSuccessfullyReRouted = true; + switch (_rValue.getValueTypeClass()) + { + case TypeClass_UNSIGNED_HYPER: + { + sal_uInt64 nValue = 0; + OSL_VERIFY( _rValue >>= nValue ); + _rxParameters->setString(_nColumnIndex, OUString::number(nValue)); + } + break; + + case TypeClass_UNSIGNED_LONG: + case TypeClass_HYPER: + { + sal_Int64 nValue = 0; + OSL_VERIFY( _rValue >>= nValue ); + _rxParameters->setLong( _nColumnIndex, nValue ); + } + break; + + case TypeClass_ANY: + { + bSuccessfullyReRouted = implSetObject(_rxParameters, _nColumnIndex, _rValue); + } + break; + + case TypeClass_VOID: + _rxParameters->setNull(_nColumnIndex,DataType::VARCHAR); + break; + + case TypeClass_STRING: + _rxParameters->setString(_nColumnIndex, *o3tl::forceAccess<OUString>(_rValue)); + break; + + case TypeClass_BOOLEAN: + _rxParameters->setBoolean(_nColumnIndex, *o3tl::forceAccess<bool>(_rValue)); + break; + + case TypeClass_BYTE: + _rxParameters->setByte(_nColumnIndex, *o3tl::forceAccess<sal_Int8>(_rValue)); + break; + + case TypeClass_SHORT: + _rxParameters->setShort(_nColumnIndex, *o3tl::forceAccess<sal_Int16>(_rValue)); + break; + + case TypeClass_CHAR: + _rxParameters->setString(_nColumnIndex, OUString(*o3tl::forceAccess<sal_Unicode>(_rValue))); + break; + + case TypeClass_UNSIGNED_SHORT: + case TypeClass_LONG: + { + sal_Int32 nValue = 0; + OSL_VERIFY( _rValue >>= nValue ); + _rxParameters->setInt(_nColumnIndex, nValue); + break; + } + + case TypeClass_FLOAT: + _rxParameters->setFloat(_nColumnIndex, *o3tl::forceAccess<float>(_rValue)); + break; + + case TypeClass_DOUBLE: + _rxParameters->setDouble(_nColumnIndex, *o3tl::forceAccess<double>(_rValue)); + break; + + case TypeClass_SEQUENCE: + if (auto s = o3tl::tryAccess<Sequence< sal_Int8 >>(_rValue)) + { + _rxParameters->setBytes(_nColumnIndex, *s); + } + else + bSuccessfullyReRouted = false; + break; + case TypeClass_STRUCT: + if (auto s1 = o3tl::tryAccess<DateTime>(_rValue)) + _rxParameters->setTimestamp(_nColumnIndex, *s1); + else if (auto s2 = o3tl::tryAccess<Date>(_rValue)) + _rxParameters->setDate(_nColumnIndex, *s2); + else if (auto s3 = o3tl::tryAccess<Time>(_rValue)) + _rxParameters->setTime(_nColumnIndex, *s3); + else + bSuccessfullyReRouted = false; + break; + + case TypeClass_INTERFACE: + if (_rValue.getValueType() == cppu::UnoType<XInputStream>::get()) + { + Reference< XInputStream > xStream; + _rValue >>= xStream; + _rxParameters->setBinaryStream(_nColumnIndex, xStream, xStream->available()); + break; + } + [[fallthrough]]; + default: + bSuccessfullyReRouted = false; + + } + + return bSuccessfullyReRouted; +} + +namespace +{ + class OParameterWrapper : public ::cppu::WeakImplHelper< XIndexAccess > + { + std::vector<bool, std::allocator<bool> > m_aSet; + Reference<XIndexAccess> m_xSource; + public: + OParameterWrapper(std::vector<bool, std::allocator<bool> >&& _aSet,const Reference<XIndexAccess>& _xSource) + : m_aSet(std::move(_aSet)), m_xSource(_xSource) {} + private: + // css::container::XElementAccess + virtual Type SAL_CALL getElementType() override + { + return m_xSource->getElementType(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + if ( m_aSet.empty() ) + return m_xSource->hasElements(); + return std::count(m_aSet.begin(),m_aSet.end(),false) != 0; + } + // css::container::XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override + { + if ( m_aSet.empty() ) + return m_xSource->getCount(); + return std::count(m_aSet.begin(),m_aSet.end(),false); + } + virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override + { + if ( m_aSet.empty() ) + return m_xSource->getByIndex(Index); + if ( Index < 0 || m_aSet.size() < o3tl::make_unsigned(Index) ) + throw IndexOutOfBoundsException(); + + std::vector<bool, std::allocator<bool> >::const_iterator aIter = m_aSet.begin(); + std::vector<bool, std::allocator<bool> >::const_iterator aEnd = m_aSet.end(); + sal_Int32 i = 0; + for(; aIter != aEnd && i <= Index; ++aIter) + { + if ( !*aIter ) + { + ++i; + } + } + auto nParamPos = static_cast<sal_Int32>(std::distance(m_aSet.cbegin(), aIter)) - 1; + return m_xSource->getByIndex(nParamPos); + } + }; +} + +void askForParameters(const Reference< XSingleSelectQueryComposer >& _xComposer, + const Reference<XParameters>& _xParameters, + const Reference< XConnection>& _xConnection, + const Reference< XInteractionHandler >& _rxHandler, + const std::vector<bool, std::allocator<bool> >& _aParametersSet) +{ + OSL_ENSURE(_xComposer.is(),"dbtools::askForParameters XSQLQueryComposer is null!"); + OSL_ENSURE(_xParameters.is(),"dbtools::askForParameters XParameters is null!"); + OSL_ENSURE(_xConnection.is(),"dbtools::askForParameters XConnection is null!"); + OSL_ENSURE(_rxHandler.is(),"dbtools::askForParameters XInteractionHandler is null!"); + + // we have to set this here again because getCurrentSettingsComposer can force a setpropertyvalue + Reference<XParametersSupplier> xParameters(_xComposer, UNO_QUERY); + + Reference<XIndexAccess> xParamsAsIndicies = xParameters.is() ? xParameters->getParameters() : Reference<XIndexAccess>(); + sal_Int32 nParamCount = xParamsAsIndicies.is() ? xParamsAsIndicies->getCount() : 0; + std::vector<bool, std::allocator<bool> > aNewParameterSet( _aParametersSet ); + if ( !(nParamCount && std::count(aNewParameterSet.begin(),aNewParameterSet.end(),true) != nParamCount) ) + return; + + static const OUString PROPERTY_NAME(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)); + aNewParameterSet.resize(nParamCount ,false); + typedef std::map< OUString, std::vector<sal_Int32> > TParameterPositions; + TParameterPositions aParameterNames; + for(sal_Int32 i = 0; i < nParamCount; ++i) + { + Reference<XPropertySet> xParam(xParamsAsIndicies->getByIndex(i),UNO_QUERY); + OUString sName; + xParam->getPropertyValue(PROPERTY_NAME) >>= sName; + + TParameterPositions::const_iterator aFind = aParameterNames.find(sName); + if ( aFind != aParameterNames.end() ) + aNewParameterSet[i] = true; + aParameterNames[sName].push_back(i+1); + } + // build an interaction request + // two continuations (Ok and Cancel) + rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort; + rtl::Reference<OParameterContinuation> pParams = new OParameterContinuation; + // the request + ParametersRequest aRequest; + Reference<XIndexAccess> xWrappedParameters = new OParameterWrapper(std::move(aNewParameterSet),xParamsAsIndicies); + aRequest.Parameters = xWrappedParameters; + aRequest.Connection = _xConnection; + rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest(Any(aRequest)); + // some knittings + pRequest->addContinuation(pAbort); + pRequest->addContinuation(pParams); + + // execute the request + _rxHandler->handle(pRequest); + + if (!pParams->wasSelected()) + { + // canceled by the user (i.e. (s)he canceled the dialog) + RowSetVetoException e; + e.ErrorCode = ParameterInteractionCancelled; + throw e; + } + + // now transfer the values from the continuation object to the parameter columns + Sequence< PropertyValue > aFinalValues = pParams->getValues(); + const PropertyValue* pFinalValues = aFinalValues.getConstArray(); + for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues) + { + Reference< XPropertySet > xParamColumn(xWrappedParameters->getByIndex(i),UNO_QUERY); + if (xParamColumn.is()) + { + OUString sName; + xParamColumn->getPropertyValue(PROPERTY_NAME) >>= sName; + OSL_ENSURE(sName == pFinalValues->Name, "::dbaui::askForParameters: inconsistent parameter names!"); + + // determine the field type and ... + sal_Int32 nParamType = 0; + xParamColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nParamType; + // ... the scale of the parameter column + sal_Int32 nScale = 0; + if (hasProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE), xParamColumn)) + xParamColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE)) >>= nScale; + // (the index of the parameters is one-based) + TParameterPositions::const_iterator aFind = aParameterNames.find(pFinalValues->Name); + for(const auto& rItem : aFind->second) + { + if ( _aParametersSet.empty() || !_aParametersSet[rItem-1] ) + { + _xParameters->setObjectWithInfo(rItem, pFinalValues->Value, nParamType, nScale); + } + } + } + } +} + +void setObjectWithInfo(const Reference<XParameters>& _xParams, + sal_Int32 parameterIndex, + const Any& x, + sal_Int32 sqlType, + sal_Int32 scale) +{ + ORowSetValue aVal; + aVal.fill(x); + setObjectWithInfo(_xParams,parameterIndex,aVal,sqlType,scale); +} + +void setObjectWithInfo(const Reference<XParameters>& _xParams, + sal_Int32 parameterIndex, + const ::connectivity::ORowSetValue& _rValue, + sal_Int32 sqlType, + sal_Int32 scale) +{ + if ( _rValue.isNull() ) + _xParams->setNull(parameterIndex,sqlType); + else + { + switch(sqlType) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + _xParams->setObjectWithInfo(parameterIndex,_rValue.makeAny(),sqlType,scale); + break; + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + _xParams->setString(parameterIndex,_rValue.getString()); + break; + case DataType::CLOB: + { + Any x(_rValue.makeAny()); + OUString sValue; + if ( x >>= sValue ) + _xParams->setString(parameterIndex,sValue); + else + { + Reference< XClob > xClob; + if(x >>= xClob) + _xParams->setClob(parameterIndex,xClob); + else + { + Reference< css::io::XInputStream > xStream; + if(x >>= xStream) + _xParams->setCharacterStream(parameterIndex,xStream,xStream->available()); + } + } + } + break; + case DataType::BIGINT: + if ( _rValue.isSigned() ) + _xParams->setLong(parameterIndex,_rValue.getLong()); + else + _xParams->setString(parameterIndex,_rValue.getString()); + break; + + case DataType::FLOAT: + _xParams->setFloat(parameterIndex,_rValue.getFloat()); + break; + case DataType::REAL: + case DataType::DOUBLE: + _xParams->setDouble(parameterIndex,_rValue.getDouble()); + break; + case DataType::DATE: + _xParams->setDate(parameterIndex,_rValue.getDate()); + break; + case DataType::TIME: + _xParams->setTime(parameterIndex,_rValue.getTime()); + break; + case DataType::TIMESTAMP: + _xParams->setTimestamp(parameterIndex,_rValue.getDateTime()); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + { + Any x(_rValue.makeAny()); + Sequence< sal_Int8> aBytes; + if(x >>= aBytes) + _xParams->setBytes(parameterIndex,aBytes); + else + { + Reference< XBlob > xBlob; + if(x >>= xBlob) + _xParams->setBlob(parameterIndex,xBlob); + else + { + Reference< XClob > xClob; + if(x >>= xClob) + _xParams->setClob(parameterIndex,xClob); + else + { + Reference< css::io::XInputStream > xBinStream; + if(x >>= xBinStream) + _xParams->setBinaryStream(parameterIndex,xBinStream,xBinStream->available()); + } + } + } + } + break; + case DataType::BIT: + case DataType::BOOLEAN: + _xParams->setBoolean(parameterIndex,_rValue.getBool()); + break; + case DataType::TINYINT: + if ( _rValue.isSigned() ) + _xParams->setByte(parameterIndex,_rValue.getInt8()); + else + _xParams->setShort(parameterIndex,_rValue.getInt16()); + break; + case DataType::SMALLINT: + if ( _rValue.isSigned() ) + _xParams->setShort(parameterIndex,_rValue.getInt16()); + else + _xParams->setInt(parameterIndex,_rValue.getInt32()); + break; + case DataType::INTEGER: + if ( _rValue.isSigned() ) + _xParams->setInt(parameterIndex,_rValue.getULong()); + else + _xParams->setLong(parameterIndex,_rValue.getLong()); + break; + default: + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceStringWithSubstitution( + STR_UNKNOWN_PARA_TYPE, + "$position$", OUString::number(parameterIndex) + ) ); + ::dbtools::throwGenericSQLException(sError,nullptr); + } + } + } +} + +void getBooleanComparisonPredicate( std::u16string_view _rExpression, const bool _bValue, const sal_Int32 _nBooleanComparisonMode, + OUStringBuffer& _out_rSQLPredicate ) +{ + switch ( _nBooleanComparisonMode ) + { + case BooleanComparisonMode::IS_LITERAL: + _out_rSQLPredicate.append( _rExpression ); + if ( _bValue ) + _out_rSQLPredicate.append( " IS TRUE" ); + else + _out_rSQLPredicate.append( " IS FALSE" ); + break; + + case BooleanComparisonMode::EQUAL_LITERAL: + _out_rSQLPredicate.append( _rExpression ); + _out_rSQLPredicate.appendAscii( _bValue ? " = TRUE" : " = FALSE" ); + break; + + case BooleanComparisonMode::ACCESS_COMPAT: + if ( _bValue ) + { + _out_rSQLPredicate.append( " NOT ( ( " ); + _out_rSQLPredicate.append( _rExpression ); + _out_rSQLPredicate.append( " = 0 ) OR ( " ); + _out_rSQLPredicate.append( _rExpression ); + _out_rSQLPredicate.append( " IS NULL ) )" ); + } + else + { + _out_rSQLPredicate.append( _rExpression ); + _out_rSQLPredicate.append( " = 0" ); + } + break; + + case BooleanComparisonMode::EQUAL_INTEGER: + // fall through + default: + _out_rSQLPredicate.append( _rExpression ); + _out_rSQLPredicate.appendAscii( _bValue ? " = 1" : " = 0" ); + break; + } +} + +} // namespace dbtools + +namespace connectivity +{ +void checkDisposed(bool _bThrow) +{ + if (_bThrow) + throw DisposedException(); + +} + +OSQLColumns::const_iterator find(const OSQLColumns::const_iterator& first, + const OSQLColumns::const_iterator& last, + std::u16string_view _rVal, + const ::comphelper::UStringMixEqual& _rCase) +{ + OUString sName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME); + return find(first,last,sName,_rVal,_rCase); +} + +OSQLColumns::const_iterator findRealName(const OSQLColumns::const_iterator& first, + const OSQLColumns::const_iterator& last, + std::u16string_view _rVal, + const ::comphelper::UStringMixEqual& _rCase) +{ + OUString sRealName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME); + return find(first,last,sRealName,_rVal,_rCase); +} + +OSQLColumns::const_iterator find(OSQLColumns::const_iterator first, + const OSQLColumns::const_iterator& last, + const OUString& _rProp, + std::u16string_view _rVal, + const ::comphelper::UStringMixEqual& _rCase) +{ + while (first != last && !_rCase(getString((*first)->getPropertyValue(_rProp)),_rVal)) + ++first; + return first; +} + +namespace dbase +{ + bool dbfDecodeCharset(rtl_TextEncoding &_out_encoding, sal_uInt8 nType, sal_uInt8 nCodepage) + { + switch (nType) + { + // dBaseIII header doesn't contain language driver ID + // See http://dbase.free.fr/tlcharge/structure%20tables.pdf + case dBaseIII: + case dBaseIIIMemo: + break; + case dBaseIV: + case dBaseV: + case VisualFoxPro: + case VisualFoxProAuto: + case dBaseFS: + case dBaseFSMemo: + case dBaseIVMemoSQL: + case FoxProMemo: + { + if (nCodepage != 0x00) + { + auto eEncoding(RTL_TEXTENCODING_DONTKNOW); + switch(nCodepage) + { + case 0x01: eEncoding = RTL_TEXTENCODING_IBM_437; break; // DOS USA code page 437 + case 0x02: eEncoding = RTL_TEXTENCODING_IBM_850; break; // DOS Multilingual code page 850 + case 0x03: eEncoding = RTL_TEXTENCODING_MS_1252; break; // Windows ANSI code page 1252 + case 0x04: eEncoding = RTL_TEXTENCODING_APPLE_ROMAN; break; // Standard Macintosh + case 0x64: eEncoding = RTL_TEXTENCODING_IBM_852; break; // EE MS-DOS code page 852 + case 0x65: eEncoding = RTL_TEXTENCODING_IBM_866; break; // Russian MS-DOS code page 866 + case 0x66: eEncoding = RTL_TEXTENCODING_IBM_865; break; // Nordic MS-DOS code page 865 + case 0x67: eEncoding = RTL_TEXTENCODING_IBM_861; break; // Icelandic MS-DOS + case 0x68: eEncoding = RTL_TEXTENCODING_KAMENICKY; break; // Kamenicky (Czech) MS-DOS + case 0x69: eEncoding = RTL_TEXTENCODING_MAZOVIA; break; // Mazovia (Polish) MS-DOS + case 0x6A: eEncoding = RTL_TEXTENCODING_IBM_737; break; // Greek MS-DOS (437G) + case 0x6B: eEncoding = RTL_TEXTENCODING_IBM_857; break; // Turkish MS-DOS + case 0x6C: eEncoding = RTL_TEXTENCODING_IBM_863; break; // MS-DOS, Canada + case 0x78: eEncoding = RTL_TEXTENCODING_MS_950; break; // Windows, Traditional Chinese + case 0x79: eEncoding = RTL_TEXTENCODING_MS_949; break; // Windows, Korean (Hangul) + case 0x7A: eEncoding = RTL_TEXTENCODING_MS_936; break; // Windows, Simplified Chinese + case 0x7B: eEncoding = RTL_TEXTENCODING_MS_932; break; // Windows, Japanese (Shift-jis) + case 0x7C: eEncoding = RTL_TEXTENCODING_MS_874; break; // Windows, Thai + case 0x7D: eEncoding = RTL_TEXTENCODING_MS_1255; break; // Windows, Hebrew + case 0x7E: eEncoding = RTL_TEXTENCODING_MS_1256; break; // Windows, Arabic + case 0x96: eEncoding = RTL_TEXTENCODING_APPLE_CYRILLIC; break; // Russian Macintosh + case 0x97: eEncoding = RTL_TEXTENCODING_APPLE_CENTEURO; break; // Eastern European Macintosh + case 0x98: eEncoding = RTL_TEXTENCODING_APPLE_GREEK; break; // Greek Macintosh + case 0xC8: eEncoding = RTL_TEXTENCODING_MS_1250; break; // Windows EE code page 1250 + case 0xC9: eEncoding = RTL_TEXTENCODING_MS_1251; break; // Russian Windows + case 0xCA: eEncoding = RTL_TEXTENCODING_MS_1254; break; // Turkish Windows + case 0xCB: eEncoding = RTL_TEXTENCODING_MS_1253; break; // Greek Windows + case 0xCC: eEncoding = RTL_TEXTENCODING_MS_1257; break; // Windows, Baltic + } + if(eEncoding != RTL_TEXTENCODING_DONTKNOW) + { + _out_encoding = eEncoding; + return true; + } + } + } + } + return false; + } + + bool dbfReadCharset(rtl_TextEncoding &nCharSet, SvStream* dbf_Stream) + { + sal_uInt8 nType=0; + dbf_Stream->ReadUChar( nType ); + + dbf_Stream->Seek(STREAM_SEEK_TO_BEGIN + 29); + if (dbf_Stream->eof()) + { + return false; + } + else + { + sal_uInt8 nEncoding=0; + dbf_Stream->ReadUChar( nEncoding ); + return dbfDecodeCharset(nCharSet, nType, nEncoding); + } + } + +} + +} //namespace connectivity + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/dbtools2.cxx b/connectivity/source/commontools/dbtools2.cxx new file mode 100644 index 0000000000..8148b25ddc --- /dev/null +++ b/connectivity/source/commontools/dbtools2.cxx @@ -0,0 +1,1018 @@ +/* -*- 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 <connectivity/dbtools.hxx> +#include <connectivity/dbconversion.hxx> +#include <connectivity/dbcharset.hxx> +#include <SQLStatementHelper.hxx> +#include <unotools/confignode.hxx> +#include <resource/sharedresources.hxx> +#include <strings.hrc> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/DriverManager.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbcx/XKeysSupplier.hpp> +#include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <TConnection.hxx> +#include <connectivity/sdbcx/VColumn.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/container/XChild.hpp> + +#include <comphelper/types.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <unotools/sharedunocomponent.hxx> +#include <algorithm> +#include <string_view> + +namespace dbtools +{ + + using namespace ::com::sun::star::uno; + 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::lang; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::frame; + using namespace connectivity; + using namespace comphelper; + +OUString createStandardTypePart(const Reference< XPropertySet >& xColProp,const Reference< XConnection>& _xConnection,std::u16string_view _sCreatePattern) +{ + + Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData(); + + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + + OUString sTypeName; + sal_Int32 nDataType = 0; + sal_Int32 nPrecision = 0; + sal_Int32 nScale = 0; + + nDataType = nPrecision = nScale = 0; + + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPENAME)) >>= sTypeName; + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE)) >>= nDataType; + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_PRECISION)) >>= nPrecision; + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCALE)) >>= nScale; + + OUStringBuffer aSql; + + // check if the user enter a specific string to create autoincrement values + OUString sAutoIncrementValue; + Reference<XPropertySetInfo> xPropInfo = xColProp->getPropertySetInfo(); + if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) ) + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) >>= sAutoIncrementValue; + // look if we have to use precisions + bool bUseLiteral = false; + OUString sPrefix,sPostfix,sCreateParams; + { + Reference<XResultSet> xRes = xMetaData->getTypeInfo(); + if(xRes.is()) + { + Reference<XRow> xRow(xRes,UNO_QUERY); + while(xRes->next()) + { + OUString sTypeName2Cmp = xRow->getString(1); + sal_Int32 nType = xRow->getShort(2); + sPrefix = xRow->getString (4); + sPostfix = xRow->getString (5); + sCreateParams = xRow->getString(6); + // first identical type will be used if typename is empty + if ( sTypeName.isEmpty() && nType == nDataType ) + sTypeName = sTypeName2Cmp; + + if( sTypeName.equalsIgnoreAsciiCase(sTypeName2Cmp) && nType == nDataType && !sCreateParams.isEmpty() && !xRow->wasNull()) + { + bUseLiteral = true; + break; + } + } + } + } + + if ( !sAutoIncrementValue.isEmpty() ) + { + sal_Int32 nIndex = sTypeName.indexOf(sAutoIncrementValue); + if (nIndex != -1) + sTypeName = sTypeName.replaceAt(nIndex,sTypeName.getLength() - nIndex, u""); + } + + if ( (nPrecision > 0 || nScale > 0) && bUseLiteral ) + { + bool bTimed = (nDataType == DataType::TIME || + nDataType == DataType::TIME_WITH_TIMEZONE || + nDataType == DataType::TIMESTAMP || + nDataType == DataType::TIMESTAMP_WITH_TIMEZONE); + + sal_Int32 nParenPos = (nDataType == DataType::TIME_WITH_TIMEZONE || + nDataType == DataType::TIMESTAMP_WITH_TIMEZONE) ? + sTypeName.indexOf(' ') : + sTypeName.indexOf('('); + + if ( nParenPos == -1 ) + aSql.append(sTypeName); + else + aSql.append(sTypeName.subView(0, nParenPos)); + aSql.append("("); + + if ( nPrecision > 0 && !bTimed ) + { + aSql.append(nPrecision); + if ( (nScale > 0) || (!_sCreatePattern.empty() && sCreateParams.indexOf(_sCreatePattern) != -1) ) + aSql.append(","); + } + if ( (nScale > 0) || ( !_sCreatePattern.empty() && sCreateParams.indexOf(_sCreatePattern) != -1 ) || bTimed ) + aSql.append(nScale); + + if ( nParenPos == -1 ) + aSql.append(")"); + else + { + if ( bTimed ) + aSql.append(")"); + else + nParenPos = sTypeName.indexOf(')',nParenPos); + aSql.append(sTypeName.subView(nParenPos)); + } + } + else + aSql.append(sTypeName); // simply add the type name + + OUString aDefault = ::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_DEFAULTVALUE))); + if ( !aDefault.isEmpty() ) + { + aSql.append(" DEFAULT " + sPrefix + aDefault + sPostfix); + } // if ( aDefault.getLength() ) + + return aSql.makeStringAndClear(); +} + +OUString createStandardColumnPart(const Reference< XPropertySet >& xColProp,const Reference< XConnection>& _xConnection,ISQLStatementHelper* _pHelper,std::u16string_view _sCreatePattern) +{ + Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData(); + + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + + bool bIsAutoIncrement = false; + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT)) >>= bIsAutoIncrement; + + const OUString sQuoteString = xMetaData->getIdentifierQuoteString(); + OUStringBuffer aSql(::dbtools::quoteName(sQuoteString,::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))))); + + // check if the user enter a specific string to create autoincrement values + OUString sAutoIncrementValue; + Reference<XPropertySetInfo> xPropInfo = xColProp->getPropertySetInfo(); + if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) ) + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) >>= sAutoIncrementValue; + + aSql.append(" " + createStandardTypePart(xColProp, _xConnection, _sCreatePattern)); + + if(::comphelper::getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISNULLABLE))) == ColumnValue::NO_NULLS) + aSql.append(" NOT NULL"); + + if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty()) + { + aSql.append(" " + sAutoIncrementValue); + } + + if ( _pHelper ) + _pHelper->addComment(xColProp,aSql); + + return aSql.makeStringAndClear(); +} + + +OUString createStandardCreateStatement(const Reference< XPropertySet >& descriptor,const Reference< XConnection>& _xConnection,ISQLStatementHelper* _pHelper,std::u16string_view _sCreatePattern) +{ + OUStringBuffer aSql("CREATE TABLE "); + OUString sCatalog,sSchema,sTable,sComposedName; + + Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData(); + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + + descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) >>= sCatalog; + descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= sSchema; + descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= sTable; + + sComposedName = ::dbtools::composeTableName( xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions ); + if ( sComposedName.isEmpty() ) + ::dbtools::throwFunctionSequenceException(_xConnection); + + aSql.append(sComposedName + " ("); + + // columns + Reference<XColumnsSupplier> xColumnSup(descriptor,UNO_QUERY); + Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY); + // check if there are columns + if(!xColumns.is() || !xColumns->getCount()) + ::dbtools::throwFunctionSequenceException(_xConnection); + + Reference< XPropertySet > xColProp; + + sal_Int32 nCount = xColumns->getCount(); + for(sal_Int32 i=0;i<nCount;++i) + { + if ( (xColumns->getByIndex(i) >>= xColProp) && xColProp.is() ) + { + aSql.append( + createStandardColumnPart(xColProp,_xConnection,_pHelper,_sCreatePattern) + + ","); + } + } + return aSql.makeStringAndClear(); +} +namespace +{ + OUString generateColumnNames(const Reference<XIndexAccess>& _xColumns,const Reference<XDatabaseMetaData>& _xMetaData) + { + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + + const OUString sQuote(_xMetaData->getIdentifierQuoteString()); + OUStringBuffer sSql( " (" ); + Reference< XPropertySet > xColProp; + + sal_Int32 nColCount = _xColumns->getCount(); + for(sal_Int32 i=0;i<nColCount;++i) + { + if ( (_xColumns->getByIndex(i) >>= xColProp) && xColProp.is() ) + sSql.append( ::dbtools::quoteName(sQuote,::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)))) + + ","); + } + + if ( nColCount ) + sSql[sSql.getLength()-1] = ')'; + return sSql.makeStringAndClear(); + } +} + +OUString createStandardKeyStatement(const Reference< XPropertySet >& descriptor,const Reference< XConnection>& _xConnection) +{ + Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData(); + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + + OUStringBuffer aSql; + // keys + Reference<XKeysSupplier> xKeySup(descriptor,UNO_QUERY); + Reference<XIndexAccess> xKeys = xKeySup->getKeys(); + if ( xKeys.is() ) + { + Reference< XPropertySet > xColProp; + Reference<XIndexAccess> xColumns; + Reference<XColumnsSupplier> xColumnSup; + OUString sCatalog,sSchema,sTable,sComposedName; + bool bPKey = false; + for(sal_Int32 i=0;i<xKeys->getCount();++i) + { + if ( (xKeys->getByIndex(i) >>= xColProp) && xColProp.is() ) + { + + sal_Int32 nKeyType = ::comphelper::getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE))); + + if ( nKeyType == KeyType::PRIMARY ) + { + if(bPKey) + ::dbtools::throwFunctionSequenceException(_xConnection); + + bPKey = true; + xColumnSup.set(xColProp,UNO_QUERY); + xColumns.set(xColumnSup->getColumns(),UNO_QUERY); + if(!xColumns.is() || !xColumns->getCount()) + ::dbtools::throwFunctionSequenceException(_xConnection); + + aSql.append(" PRIMARY KEY " + generateColumnNames(xColumns,xMetaData)); + } + else if(nKeyType == KeyType::UNIQUE) + { + xColumnSup.set(xColProp,UNO_QUERY); + xColumns.set(xColumnSup->getColumns(),UNO_QUERY); + if(!xColumns.is() || !xColumns->getCount()) + ::dbtools::throwFunctionSequenceException(_xConnection); + + aSql.append(" UNIQUE " + generateColumnNames(xColumns,xMetaData)); + } + else if(nKeyType == KeyType::FOREIGN) + { + sal_Int32 nDeleteRule = getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_DELETERULE))); + + xColumnSup.set(xColProp,UNO_QUERY); + xColumns.set(xColumnSup->getColumns(),UNO_QUERY); + if(!xColumns.is() || !xColumns->getCount()) + ::dbtools::throwFunctionSequenceException(_xConnection); + + aSql.append(" FOREIGN KEY "); + OUString sRefTable = getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_REFERENCEDTABLE))); + ::dbtools::qualifiedNameComponents(xMetaData, + sRefTable, + sCatalog, + sSchema, + sTable, + ::dbtools::EComposeRule::InDataManipulation); + sComposedName = ::dbtools::composeTableName( xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions ); + + + if ( sComposedName.isEmpty() ) + ::dbtools::throwFunctionSequenceException(_xConnection); + + aSql.append(generateColumnNames(xColumns,xMetaData)); + + switch(nDeleteRule) + { + case KeyRule::CASCADE: + aSql.append(" ON DELETE CASCADE "); + break; + case KeyRule::RESTRICT: + aSql.append(" ON DELETE RESTRICT "); + break; + case KeyRule::SET_NULL: + aSql.append(" ON DELETE SET NULL "); + break; + case KeyRule::SET_DEFAULT: + aSql.append(" ON DELETE SET DEFAULT "); + break; + default: + ; + } + } + } + } + } + + if ( !aSql.isEmpty() ) + { + if ( aSql[aSql.getLength() - 1] == ',' ) + aSql[aSql.getLength() - 1] = ')'; + else + aSql.append(")"); + } + + return aSql.makeStringAndClear(); + +} + +OUString createSqlCreateTableStatement( const Reference< XPropertySet >& descriptor, + const Reference< XConnection>& _xConnection) +{ + OUString aSql = ::dbtools::createStandardCreateStatement(descriptor,_xConnection,nullptr,{}); + const OUString sKeyStmt = ::dbtools::createStandardKeyStatement(descriptor,_xConnection); + if ( !sKeyStmt.isEmpty() ) + aSql += sKeyStmt; + else + { + if ( aSql.endsWith(",") ) + aSql = aSql.replaceAt(aSql.getLength()-1, 1, u")"); + else + aSql += ")"; + } + return aSql; +} +namespace +{ + Reference<XPropertySet> lcl_createSDBCXColumn(const Reference<XNameAccess>& _xPrimaryKeyColumns, + const Reference<XConnection>& _xConnection, + const Any& _aCatalog, + const OUString& _aSchema, + const OUString& _aTable, + const OUString& _rQueryName, + const OUString& _rName, + bool _bCase, + bool _bQueryForInfo, + bool _bIsAutoIncrement, + bool _bIsCurrency, + sal_Int32 _nDataType) + { + Reference<XPropertySet> xProp; + Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData(); + Reference< XResultSet > xResult = xMetaData->getColumns(_aCatalog, _aSchema, _aTable, _rQueryName); + OUString sCatalog; + _aCatalog >>= sCatalog; + + if ( xResult.is() ) + { + UStringMixEqual aMixCompare(_bCase); + Reference< XRow > xRow(xResult,UNO_QUERY); + while( xResult->next() ) + { + if ( aMixCompare(xRow->getString(4),_rName) ) + { + sal_Int32 nField5 = xRow->getInt(5); + OUString aField6 = xRow->getString(6); + sal_Int32 nField7 = xRow->getInt(7) + , nField9 = xRow->getInt(9) + , nField11= xRow->getInt(11); + OUString sField12 = xRow->getString(12), + sField13 = xRow->getString(13); + ::comphelper::disposeComponent(xRow); + + bool bAutoIncrement = _bIsAutoIncrement + ,bIsCurrency = _bIsCurrency; + if ( _bQueryForInfo ) + { + const OUString sQuote = xMetaData->getIdentifierQuoteString(); + OUString sQuotedName = ::dbtools::quoteName(sQuote,_rName); + OUString sComposedName = composeTableNameForSelect(_xConnection, getString( _aCatalog ), _aSchema, _aTable ); + + ColumnInformationMap aInfo(_bCase); + collectColumnInformation(_xConnection,sComposedName,sQuotedName,aInfo); + ColumnInformationMap::const_iterator aIter = aInfo.begin(); + if ( aIter != aInfo.end() ) + { + bAutoIncrement = aIter->second.first.first; + bIsCurrency = aIter->second.first.second; + if ( DataType::OTHER == nField5 ) + nField5 = aIter->second.second; + } + } + else if ( DataType::OTHER == nField5 ) + nField5 = _nDataType; + + if ( nField11 != ColumnValue::NO_NULLS ) + { + try + { + if ( _xPrimaryKeyColumns.is() ) + { + if ( _xPrimaryKeyColumns->hasByName(_rName) ) + nField11 = ColumnValue::NO_NULLS; + + } + else + { + Reference< XResultSet > xPKeys = xMetaData->getPrimaryKeys( _aCatalog, _aSchema, _aTable ); + Reference< XRow > xPKeyRow( xPKeys, UNO_QUERY_THROW ); + while( xPKeys->next() ) // there can be only one primary key + { + OUString sKeyColumn = xPKeyRow->getString(4); + if ( aMixCompare(_rName,sKeyColumn) ) + { + nField11 = ColumnValue::NO_NULLS; + break; + } + } + } + } + catch(SQLException&) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "lcl_createSDBCXColumn" ); + } + } + + xProp = new connectivity::sdbcx::OColumn(_rName, + aField6, + sField13, + sField12, + nField11, + nField7, + nField9, + nField5, + bAutoIncrement, + false, + bIsCurrency, + _bCase, + sCatalog, + _aSchema, + _aTable); + + break; + } + } + } + + return xProp; + } + + Reference< XModel> lcl_getXModel(const Reference< XInterface>& _xIface) + { + Reference< XInterface > xParent = _xIface; + Reference< XModel > xModel(xParent,UNO_QUERY); + while( xParent.is() && !xModel.is() ) + { + Reference<XChild> xChild(xParent,UNO_QUERY); + xParent.set(xChild.is() ? xChild->getParent() : Reference< XInterface >(),UNO_QUERY); + xModel.set(xParent,UNO_QUERY); + } + return xModel; + } +} + +Reference<XPropertySet> createSDBCXColumn(const Reference<XPropertySet>& _xTable, + const Reference<XConnection>& _xConnection, + const OUString& _rName, + bool _bCase, + bool _bQueryForInfo, + bool _bIsAutoIncrement, + bool _bIsCurrency, + sal_Int32 _nDataType) +{ + Reference<XPropertySet> xProp; + OSL_ENSURE(_xTable.is(),"Table is NULL!"); + if ( !_xTable.is() ) + return xProp; + + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + Any aCatalog = _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)); + OUString sCatalog; + aCatalog >>= sCatalog; + + OUString aSchema, aTable; + _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= aSchema; + _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= aTable; + + Reference<XNameAccess> xPrimaryKeyColumns = getPrimaryKeyColumns_throw(_xTable); + + xProp = lcl_createSDBCXColumn(xPrimaryKeyColumns,_xConnection,aCatalog, aSchema, aTable, _rName,_rName,_bCase,_bQueryForInfo,_bIsAutoIncrement,_bIsCurrency,_nDataType); + if ( !xProp.is() ) + { + xProp = lcl_createSDBCXColumn(xPrimaryKeyColumns,_xConnection,aCatalog, aSchema, aTable, "%",_rName,_bCase,_bQueryForInfo,_bIsAutoIncrement,_bIsCurrency,_nDataType); + if ( !xProp.is() ) + xProp = new connectivity::sdbcx::OColumn(_rName, + OUString(),OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + DataType::VARCHAR, + _bIsAutoIncrement, + false, + _bIsCurrency, + _bCase, + sCatalog, + aSchema, + aTable); + + } + + return xProp; +} + + +bool getBooleanDataSourceSetting( const Reference< XConnection >& _rxConnection, const char* _pAsciiSettingName ) +{ + return getBooleanDataSourceSetting(_rxConnection, OUString::createFromAscii( _pAsciiSettingName )); +} + +bool getBooleanDataSourceSetting( const Reference< XConnection >& _rxConnection, const OUString & rSettingName ) +{ + bool bValue( false ); + try + { + Reference< XPropertySet> xDataSourceProperties( findDataSource( _rxConnection ), UNO_QUERY ); + OSL_ENSURE( xDataSourceProperties.is(), "::dbtools::getBooleanDataSourceSetting: somebody is using this with a non-SDB-level connection!" ); + if ( xDataSourceProperties.is() ) + { + Reference< XPropertySet > xSettings( + xDataSourceProperties->getPropertyValue("Settings"), + UNO_QUERY_THROW + ); + OSL_VERIFY( xSettings->getPropertyValue( rSettingName ) >>= bValue ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return bValue; +} + +bool getDataSourceSetting( const Reference< XInterface >& _xChild, const OUString& _sAsciiSettingsName, + Any& /* [out] */ _rSettingsValue ) +{ + bool bIsPresent = false; + try + { + const Reference< XPropertySet> xDataSourceProperties( findDataSource( _xChild ), UNO_QUERY ); + if ( !xDataSourceProperties.is() ) + return false; + + const Reference< XPropertySet > xSettings( + xDataSourceProperties->getPropertyValue("Settings"), + UNO_QUERY_THROW + ); + + _rSettingsValue = xSettings->getPropertyValue( _sAsciiSettingsName ); + bIsPresent = true; + } + catch( const Exception& ) + { + bIsPresent = false; + } + return bIsPresent; +} + +bool getDataSourceSetting( const Reference< XInterface >& _rxDataSource, const char* _pAsciiSettingsName, + Any& /* [out] */ _rSettingsValue ) +{ + OUString sAsciiSettingsName = OUString::createFromAscii(_pAsciiSettingsName); + return getDataSourceSetting( _rxDataSource, sAsciiSettingsName,_rSettingsValue ); +} + +bool isDataSourcePropertyEnabled(const Reference<XInterface>& _xProp, const OUString& _sProperty, bool _bDefault) +{ + bool bEnabled = _bDefault; + try + { + Reference< XPropertySet> xProp(findDataSource(_xProp),UNO_QUERY); + if ( xProp.is() ) + { + Sequence< PropertyValue > aInfo; + xProp->getPropertyValue("Info") >>= aInfo; + const PropertyValue* pValue =std::find_if(std::cbegin(aInfo), + std::cend(aInfo), + [&_sProperty](const PropertyValue& lhs) + { return lhs.Name == _sProperty; }); + if ( pValue != std::cend(aInfo) ) + pValue->Value >>= bEnabled; + } + } + catch(SQLException&) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return bEnabled; +} + +Reference< XTablesSupplier> getDataDefinitionByURLAndConnection( + const OUString& _rsUrl, + const Reference< XConnection>& _xConnection, + const Reference< XComponentContext >& _rxContext) +{ + Reference< XTablesSupplier> xTablesSup; + try + { + Reference< XDriverManager2 > xManager = DriverManager::create( _rxContext ); + Reference< XDataDefinitionSupplier > xSupp( xManager->getDriverByURL( _rsUrl ), UNO_QUERY ); + + if ( xSupp.is() ) + { + xTablesSup = xSupp->getDataDefinitionByConnection( _xConnection ); + OSL_ENSURE(xTablesSup.is(),"No table supplier!"); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + return xTablesSup; +} + + +sal_Int32 getTablePrivileges(const Reference< XDatabaseMetaData>& _xMetaData, + const OUString& _sCatalog, + const OUString& _sSchema, + const OUString& _sTable) +{ + OSL_ENSURE(_xMetaData.is(),"Invalid metadata!"); + sal_Int32 nPrivileges = 0; + try + { + Any aVal; + if(!_sCatalog.isEmpty()) + aVal <<= _sCatalog; + Reference< XResultSet > xPrivileges = _xMetaData->getTablePrivileges(aVal, _sSchema, _sTable); + Reference< XRow > xCurrentRow(xPrivileges, UNO_QUERY); + + const OUString sUserWorkingFor = _xMetaData->getUserName(); + static const char sSELECT[] = "SELECT"; + static const char sINSERT[] = "INSERT"; + static const char sUPDATE[] = "UPDATE"; + static const char sDELETE[] = "DELETE"; + static const char sREAD[] = "READ"; + static const char sCREATE[] = "CREATE"; + static const char sALTER[] = "ALTER"; + static const char sREFERENCE[] = "REFERENCE"; + static const char sDROP[] = "DROP"; + + if ( xCurrentRow.is() ) + { + // after creation the set is positioned before the first record, per definition + OUString sPrivilege, sGrantee; + while ( xPrivileges->next() ) + { + sGrantee = xCurrentRow->getString(5); + sPrivilege = xCurrentRow->getString(6); + + if (!sUserWorkingFor.equalsIgnoreAsciiCase(sGrantee)) + continue; + + if (sPrivilege.equalsIgnoreAsciiCase(sSELECT)) + nPrivileges |= Privilege::SELECT; + else if (sPrivilege.equalsIgnoreAsciiCase(sINSERT)) + nPrivileges |= Privilege::INSERT; + else if (sPrivilege.equalsIgnoreAsciiCase(sUPDATE)) + nPrivileges |= Privilege::UPDATE; + else if (sPrivilege.equalsIgnoreAsciiCase(sDELETE)) + nPrivileges |= Privilege::DELETE; + else if (sPrivilege.equalsIgnoreAsciiCase(sREAD)) + nPrivileges |= Privilege::READ; + else if (sPrivilege.equalsIgnoreAsciiCase(sCREATE)) + nPrivileges |= Privilege::CREATE; + else if (sPrivilege.equalsIgnoreAsciiCase(sALTER)) + nPrivileges |= Privilege::ALTER; + else if (sPrivilege.equalsIgnoreAsciiCase(sREFERENCE)) + nPrivileges |= Privilege::REFERENCE; + else if (sPrivilege.equalsIgnoreAsciiCase(sDROP)) + nPrivileges |= Privilege::DROP; + } + } + disposeComponent(xPrivileges); + + // Some drivers put a table privilege as soon as any column has the privilege, + // some drivers only if all columns have the privilege. + // To unify the situation, collect column privileges here, too. + Reference< XResultSet > xColumnPrivileges = _xMetaData->getColumnPrivileges(aVal, _sSchema, _sTable, "%"); + Reference< XRow > xColumnCurrentRow(xColumnPrivileges, UNO_QUERY); + if ( xColumnCurrentRow.is() ) + { + // after creation the set is positioned before the first record, per definition + OUString sPrivilege, sGrantee; + while ( xColumnPrivileges->next() ) + { + sGrantee = xColumnCurrentRow->getString(6); + sPrivilege = xColumnCurrentRow->getString(7); + + if (!sUserWorkingFor.equalsIgnoreAsciiCase(sGrantee)) + continue; + + if (sPrivilege.equalsIgnoreAsciiCase(sSELECT)) + nPrivileges |= Privilege::SELECT; + else if (sPrivilege.equalsIgnoreAsciiCase(sINSERT)) + nPrivileges |= Privilege::INSERT; + else if (sPrivilege.equalsIgnoreAsciiCase(sUPDATE)) + nPrivileges |= Privilege::UPDATE; + else if (sPrivilege.equalsIgnoreAsciiCase(sDELETE)) + nPrivileges |= Privilege::DELETE; + else if (sPrivilege.equalsIgnoreAsciiCase(sREAD)) + nPrivileges |= Privilege::READ; + else if (sPrivilege.equalsIgnoreAsciiCase(sCREATE)) + nPrivileges |= Privilege::CREATE; + else if (sPrivilege.equalsIgnoreAsciiCase(sALTER)) + nPrivileges |= Privilege::ALTER; + else if (sPrivilege.equalsIgnoreAsciiCase(sREFERENCE)) + nPrivileges |= Privilege::REFERENCE; + else if (sPrivilege.equalsIgnoreAsciiCase(sDROP)) + nPrivileges |= Privilege::DROP; + } + } + disposeComponent(xColumnPrivileges); + } + catch(const SQLException& e) + { + // some drivers don't support any privileges so we assume that we are allowed to do all we want :-) + if(e.SQLState == "IM001") + nPrivileges |= Privilege::DROP | + Privilege::REFERENCE | + Privilege::ALTER | + Privilege::CREATE | + Privilege::READ | + Privilege::DELETE | + Privilege::UPDATE | + Privilege::INSERT | + Privilege::SELECT; + else + OSL_FAIL("Could not collect the privileges !"); + } + return nPrivileges; +} + +// we need some more information about the column +void collectColumnInformation(const Reference< XConnection>& _xConnection, + std::u16string_view _sComposedName, + std::u16string_view _rName, + ColumnInformationMap& _rInfo) +{ + OUString sSelect = OUString::Concat("SELECT ") + _rName + + " FROM " + _sComposedName + + " WHERE 0 = 1"; + + try + { + ::utl::SharedUNOComponent< XStatement > xStmt( _xConnection->createStatement() ); + Reference< XPropertySet > xStatementProps( xStmt, UNO_QUERY_THROW ); + xStatementProps->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ESCAPEPROCESSING ), Any( false ) ); + Reference< XResultSet > xResult( xStmt->executeQuery( sSelect ), UNO_SET_THROW ); + Reference< XResultSetMetaDataSupplier > xSuppMeta( xResult, UNO_QUERY_THROW ); + Reference< XResultSetMetaData > xMeta( xSuppMeta->getMetaData(), UNO_SET_THROW ); + + sal_Int32 nCount = xMeta->getColumnCount(); + OSL_ENSURE( nCount != 0, "::dbtools::collectColumnInformation: result set has empty (column-less) meta data!" ); + for (sal_Int32 i=1; i <= nCount ; ++i) + { + _rInfo.emplace( xMeta->getColumnName(i), + ColumnInformation(TBoolPair(xMeta->isAutoIncrement(i),xMeta->isCurrency(i)),xMeta->getColumnType(i))); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } +} + + +bool isEmbeddedInDatabase( const Reference< XInterface >& _rxComponent, Reference< XConnection >& _rxActualConnection ) +{ + bool bIsEmbedded = false; + try + { + Reference< XModel > xModel = lcl_getXModel( _rxComponent ); + + if ( xModel.is() ) + { + Sequence< PropertyValue > aArgs = xModel->getArgs(); + const PropertyValue* pIter = aArgs.getConstArray(); + const PropertyValue* pEnd = pIter + aArgs.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( pIter->Name == "ComponentData" ) + { + Sequence<PropertyValue> aDocumentContext; + pIter->Value >>= aDocumentContext; + const PropertyValue* pContextIter = aDocumentContext.getConstArray(); + const PropertyValue* pContextEnd = pContextIter + aDocumentContext.getLength(); + for(;pContextIter != pContextEnd;++pContextIter) + { + if ( pContextIter->Name == "ActiveConnection" + && ( pContextIter->Value >>= _rxActualConnection ) + ) + { + bIsEmbedded = true; + break; + } + } + break; + } + } + } + } + catch(Exception&) + { + // not interested in + } + return bIsEmbedded; +} + +namespace +{ + OUString lcl_getEncodingName( rtl_TextEncoding _eEncoding ) + { + OUString sEncodingName; + + OCharsetMap aCharsets; + OCharsetMap::CharsetIterator aEncodingPos = aCharsets.find( _eEncoding ); + OSL_ENSURE( aEncodingPos != aCharsets.end(), "lcl_getEncodingName: *which* encoding?" ); + if ( aEncodingPos != aCharsets.end() ) + sEncodingName = (*aEncodingPos).getIanaName(); + + return sEncodingName; + } +} + + +sal_Int32 DBTypeConversion::convertUnicodeString( const OUString& _rSource, OString& _rDest, rtl_TextEncoding _eEncoding ) +{ + if ( !rtl_convertUStringToString( &_rDest.pData, _rSource.getStr(), _rSource.getLength(), + _eEncoding, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE | + RTL_UNICODETOTEXT_FLAGS_PRIVATE_MAPTO0 ) + ) + { + SharedResources aResources; + OUString sMessage = aResources.getResourceStringWithSubstitution( STR_CANNOT_CONVERT_STRING, + "$string$", _rSource, + "$charset$", lcl_getEncodingName( _eEncoding ) + ); + + throw SQLException( + sMessage, + nullptr, + "22018", + 22018, + Any() + ); + } + + return _rDest.getLength(); +} + + +sal_Int32 DBTypeConversion::convertUnicodeStringToLength( const OUString& _rSource, OString& _rDest, + sal_Int32 _nMaxLen, rtl_TextEncoding _eEncoding ) +{ + sal_Int32 nLen = convertUnicodeString( _rSource, _rDest, _eEncoding ); + if ( nLen > _nMaxLen ) + { + SharedResources aResources; + OUString sMessage = aResources.getResourceStringWithSubstitution( STR_STRING_LENGTH_EXCEEDED, + "$string$", _rSource, + "$maxlen$", OUString::number( _nMaxLen ), + "$charset$", lcl_getEncodingName( _eEncoding ) + ); + + throw SQLException( + sMessage, + nullptr, + "22001", + 22001, + Any() + ); + } + + return nLen; +} + +OUString getDefaultReportEngineServiceName(const Reference< XComponentContext >& _rxORB) +{ + ::utl::OConfigurationTreeRoot aReportEngines = ::utl::OConfigurationTreeRoot::createWithComponentContext( + _rxORB, "org.openoffice.Office.DataAccess/ReportEngines", -1, ::utl::OConfigurationTreeRoot::CM_READONLY); + + if ( aReportEngines.isValid() ) + { + OUString sDefaultReportEngineName; + aReportEngines.getNodeValue("DefaultReportEngine") >>= sDefaultReportEngineName; + if ( !sDefaultReportEngineName.isEmpty() ) + { + ::utl::OConfigurationNode aReportEngineNames = aReportEngines.openNode("ReportEngineNames"); + if ( aReportEngineNames.isValid() ) + { + ::utl::OConfigurationNode aReportEngine = aReportEngineNames.openNode(sDefaultReportEngineName); + if ( aReportEngine.isValid() ) + { + OUString sRet; + aReportEngine.getNodeValue("ServiceName") >>= sRet; + return sRet; + } + } + } + else + return "org.libreoffice.report.pentaho.SOReportJobFactory"; + } + else + return "org.libreoffice.report.pentaho.SOReportJobFactory"; + return OUString(); +} + +bool isAggregateColumn(const Reference< XSingleSelectQueryComposer > &_xParser, const Reference< XPropertySet > &_xField) +{ + OUString sName; + _xField->getPropertyValue("Name") >>= sName; + Reference< XColumnsSupplier > xColumnsSupplier(_xParser, UNO_QUERY); + Reference< css::container::XNameAccess > xCols; + if (xColumnsSupplier.is()) + xCols = xColumnsSupplier->getColumns(); + + return isAggregateColumn(xCols, sName); +} + +bool isAggregateColumn(const Reference< XNameAccess > &_xColumns, const OUString &_sName) +{ + if ( _xColumns.is() && _xColumns->hasByName(_sName) ) + { + Reference<XPropertySet> xProp(_xColumns->getByName(_sName),UNO_QUERY); + assert(xProp.is()); + return isAggregateColumn( xProp ); + } + return false; +} + +bool isAggregateColumn( const Reference< XPropertySet > &_xColumn ) +{ + bool bAgg(false); + + static constexpr OUString sAgg = u"AggregateFunction"_ustr; + if ( _xColumn->getPropertySetInfo()->hasPropertyByName(sAgg) ) + _xColumn->getPropertyValue(sAgg) >>= bAgg; + + return bAgg; +} + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/filtermanager.cxx b/connectivity/source/commontools/filtermanager.cxx new file mode 100644 index 0000000000..0a6ac9018f --- /dev/null +++ b/connectivity/source/commontools/filtermanager.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 <connectivity/filtermanager.hxx> + +#include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp> +#include <TConnection.hxx> +#include <osl/diagnose.h> +#include <comphelper/diagnose_ex.hxx> +#include <rtl/ustrbuf.hxx> + + +namespace dbtools +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace connectivity; + + FilterManager::FilterManager( ) + :m_bApplyPublicFilter( true ) + { + } + + + void FilterManager::initialize( const Reference< XPropertySet >& _rxComponentAggregate ) + { + m_xComponentAggregate = _rxComponentAggregate; + OSL_ENSURE( m_xComponentAggregate.is(), "FilterManager::initialize: invalid arguments!" ); + + if ( m_xComponentAggregate.is() ) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_APPLYFILTER), Any( true ) ); + } + + + void FilterManager::dispose( ) + { + m_xComponentAggregate.clear(); + } + + + const OUString& FilterManager::getFilterComponent( FilterComponent _eWhich ) const + { + switch (_eWhich) + { + case FilterComponent::PublicFilter: + return m_aPublicFilterComponent; + case FilterComponent::PublicHaving: + return m_aPublicHavingComponent; + case FilterComponent::LinkFilter: + return m_aLinkFilterComponent; + case FilterComponent::LinkHaving: + return m_aLinkHavingComponent; + } + assert(false); + + static constexpr OUString sErr(u"#FilterManager::getFilterComponent unknown component#"_ustr); + return sErr; + } + + + void FilterManager::setFilterComponent( FilterComponent _eWhich, const OUString& _rComponent ) + { + switch (_eWhich) + { + case FilterComponent::PublicFilter: + m_aPublicFilterComponent = _rComponent; + break; + case FilterComponent::PublicHaving: + m_aPublicHavingComponent = _rComponent; + break; + case FilterComponent::LinkFilter: + m_aLinkFilterComponent = _rComponent; + break; + case FilterComponent::LinkHaving: + m_aLinkHavingComponent = _rComponent; + break; + } + try + { + if ( m_xComponentAggregate.is() ) + { + bool propagate(true); + switch (_eWhich) + { + case FilterComponent::PublicFilter: + propagate = propagate && m_bApplyPublicFilter; + [[fallthrough]]; + case FilterComponent::LinkFilter: + if (propagate) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FILTER), Any( getComposedFilter() ) ); + break; + case FilterComponent::PublicHaving: + propagate = propagate && m_bApplyPublicFilter; + [[fallthrough]]; + case FilterComponent::LinkHaving: + if (propagate) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_HAVINGCLAUSE), Any( getComposedHaving() ) ); + break; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + } + + + void FilterManager::setApplyPublicFilter( bool _bApply ) + { + if ( m_bApplyPublicFilter == _bApply ) + return; + + m_bApplyPublicFilter = _bApply; + + try + { + if ( m_xComponentAggregate.is()) + { + // only where/if something changed + if (!getFilterComponent( FilterComponent::PublicFilter ).isEmpty()) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FILTER), Any( getComposedFilter() ) ); + if (!getFilterComponent( FilterComponent::PublicHaving ).isEmpty()) + m_xComponentAggregate->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_HAVINGCLAUSE), Any( getComposedHaving() ) ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + } + + + void FilterManager::appendFilterComponent( OUStringBuffer& io_appendTo, std::u16string_view i_component ) + { + if ( !io_appendTo.isEmpty() ) + { + io_appendTo.insert( 0, '(' ); + io_appendTo.insert( 1, ' ' ); + io_appendTo.append( " ) AND " ); + } + + io_appendTo.append( "( " ); + io_appendTo.append( i_component ); + io_appendTo.append( " )" ); + } + + + bool FilterManager::isThereAtMostOneFilterComponent( OUString& o_singleComponent ) const + { + if (m_bApplyPublicFilter) { + if (!m_aPublicFilterComponent.isEmpty() && !m_aLinkFilterComponent.isEmpty()) + return false; + if (!m_aPublicFilterComponent.isEmpty()) + o_singleComponent = m_aPublicFilterComponent; + else if (!m_aLinkFilterComponent.isEmpty()) + o_singleComponent = m_aLinkFilterComponent; + else + o_singleComponent.clear(); + return true; + } + else + { + if (m_aLinkFilterComponent.isEmpty()) + o_singleComponent.clear(); + else + o_singleComponent = m_aLinkFilterComponent; + return true; + } + } + + bool FilterManager::isThereAtMostOneHavingComponent( OUString& o_singleComponent ) const + { + if (m_bApplyPublicFilter) { + if (!m_aPublicHavingComponent.isEmpty() && !m_aLinkHavingComponent.isEmpty()) + return false; + if (!m_aPublicHavingComponent.isEmpty()) + o_singleComponent = m_aPublicHavingComponent; + else if (!m_aLinkHavingComponent.isEmpty()) + o_singleComponent = m_aLinkHavingComponent; + else + o_singleComponent.clear(); + return true; + } + else + { + if (m_aLinkHavingComponent.isEmpty()) + o_singleComponent.clear(); + else + o_singleComponent = m_aLinkHavingComponent; + return true; + } + } + + + OUString FilterManager::getComposedFilter( ) const + { + // if we have only one non-empty component, then there's no need to compose anything + OUString singleComponent; + if ( isThereAtMostOneFilterComponent( singleComponent ) ) + { + return singleComponent; + } + // append the single components + OUStringBuffer aComposedFilter(singleComponent); + if (m_bApplyPublicFilter) + appendFilterComponent( aComposedFilter, m_aPublicFilterComponent ); + appendFilterComponent( aComposedFilter, m_aLinkFilterComponent ); + return aComposedFilter.makeStringAndClear(); + } + + + OUString FilterManager::getComposedHaving( ) const + { + // if we have only one non-empty component, then there's no need to compose anything + OUString singleComponent; + if ( isThereAtMostOneHavingComponent( singleComponent ) ) + { + return singleComponent; + } + // append the single components + OUStringBuffer aComposedFilter(singleComponent); + if (m_bApplyPublicFilter) + appendFilterComponent( aComposedFilter, m_aPublicHavingComponent ); + appendFilterComponent( aComposedFilter, m_aLinkHavingComponent ); + return aComposedFilter.makeStringAndClear(); + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/formattedcolumnvalue.cxx b/connectivity/source/commontools/formattedcolumnvalue.cxx new file mode 100644 index 0000000000..32db3d7177 --- /dev/null +++ b/connectivity/source/commontools/formattedcolumnvalue.cxx @@ -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 . + */ + + +#include <connectivity/formattedcolumnvalue.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbconversion.hxx> + +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/sdb/XColumnUpdate.hpp> + +#include <comphelper/diagnose_ex.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <comphelper/numbers.hxx> + + +namespace dbtools +{ + + + 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::XComponentContext; + using ::com::sun::star::sdbc::XRowSet; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::util::NumberFormatter; + using ::com::sun::star::util::XNumberFormatter; + using ::com::sun::star::util::Date; + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::util::XNumberFormatsSupplier; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::lang::Locale; + using ::com::sun::star::util::XNumberFormatTypes; + using ::com::sun::star::sdb::XColumn; + using ::com::sun::star::sdb::XColumnUpdate; + + namespace DataType = ::com::sun::star::sdbc::DataType; + namespace NumberFormat = ::com::sun::star::util::NumberFormat; + + struct FormattedColumnValue_Data + { + Reference< XNumberFormatter > m_xFormatter; + Date m_aNullDate; + sal_Int32 m_nFormatKey; + sal_Int32 m_nFieldType; + sal_Int16 m_nKeyType; + bool m_bNumericField; + + Reference< XColumn > m_xColumn; + Reference< XColumnUpdate > m_xColumnUpdate; + + FormattedColumnValue_Data() + :m_aNullDate( DBTypeConversion::getStandardDate() ) + ,m_nFormatKey( 0 ) + ,m_nFieldType( DataType::OTHER ) + ,m_nKeyType( NumberFormat::UNDEFINED ) + ,m_bNumericField( false ) + { + } + }; + + + namespace + { + + void lcl_clear_nothrow( FormattedColumnValue_Data& _rData ) + { + _rData.m_xFormatter.clear(); + _rData.m_nFormatKey = 0; + _rData.m_nFieldType = DataType::OTHER; + _rData.m_nKeyType = NumberFormat::UNDEFINED; + _rData.m_bNumericField = false; + + _rData.m_xColumn.clear(); + _rData.m_xColumnUpdate.clear(); + } + + + void lcl_initColumnDataValue_nothrow( FormattedColumnValue_Data& _rData, + const Reference< XNumberFormatter >& i_rNumberFormatter, const Reference< XPropertySet >& _rxColumn ) + { + lcl_clear_nothrow( _rData ); + + OSL_PRECOND( i_rNumberFormatter.is(), "lcl_initColumnDataValue_nothrow: no number formats -> no formatted values!" ); + if ( !i_rNumberFormatter.is() ) + return; + + try + { + Reference< XNumberFormatsSupplier > xNumberFormatsSupp( i_rNumberFormatter->getNumberFormatsSupplier(), UNO_SET_THROW ); + + // remember the column + _rData.m_xColumn.set( _rxColumn, UNO_QUERY_THROW ); + _rData.m_xColumnUpdate.set( _rxColumn, UNO_QUERY ); + + // determine the field type, and whether it's a numeric field + OSL_VERIFY( _rxColumn->getPropertyValue("Type") >>= _rData.m_nFieldType ); + + switch ( _rData.m_nFieldType ) + { + case DataType::DATE: + case DataType::TIME: + case DataType::TIME_WITH_TIMEZONE: + case DataType::TIMESTAMP: + case DataType::TIMESTAMP_WITH_TIMEZONE: + case DataType::BIT: + case DataType::BOOLEAN: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::REAL: + case DataType::BIGINT: + case DataType::DOUBLE: + case DataType::NUMERIC: + case DataType::DECIMAL: + _rData.m_bNumericField = true; + break; + default: + _rData.m_bNumericField = false; + break; + } + + // get the format key of our bound field + Reference< XPropertySetInfo > xPSI( _rxColumn->getPropertySetInfo(), UNO_SET_THROW ); + bool bHaveFieldFormat = false; + static constexpr OUString sFormatKeyProperty( u"FormatKey"_ustr ); + if ( xPSI->hasPropertyByName( sFormatKeyProperty ) ) + { + bHaveFieldFormat = ( _rxColumn->getPropertyValue( sFormatKeyProperty ) >>= _rData.m_nFormatKey ); + } + if ( !bHaveFieldFormat ) + { + // fall back to a format key as indicated by the field type + Locale aSystemLocale( LanguageTag( MsLangId::getConfiguredSystemLanguage() ).getLocale() ); + Reference< XNumberFormatTypes > xNumTypes( xNumberFormatsSupp->getNumberFormats(), UNO_QUERY_THROW ); + _rData.m_nFormatKey = getDefaultNumberFormat( _rxColumn, xNumTypes, aSystemLocale ); + } + + // some more formatter settings + _rData.m_nKeyType = ::comphelper::getNumberFormatType( xNumberFormatsSupp->getNumberFormats(), _rData.m_nFormatKey ); + Reference< XPropertySet > xFormatSettings( xNumberFormatsSupp->getNumberFormatSettings(), UNO_SET_THROW ); + OSL_VERIFY( xFormatSettings->getPropertyValue("NullDate") >>= _rData.m_aNullDate ); + + // remember the formatter + _rData.m_xFormatter = i_rNumberFormatter; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + } + + + void lcl_initColumnDataValue_nothrow( const Reference<XComponentContext>& i_rContext, FormattedColumnValue_Data& i_rData, + const Reference< XRowSet >& i_rRowSet, const Reference< XPropertySet >& i_rColumn ) + { + OSL_PRECOND( i_rRowSet.is(), "lcl_initColumnDataValue_nothrow: no row set!" ); + if ( !i_rRowSet.is() ) + return; + + Reference< XNumberFormatter > xNumberFormatter; + try + { + // get the number formats supplier of the connection of the form + Reference< XConnection > xConnection( getConnection( i_rRowSet ), UNO_SET_THROW ); + Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( xConnection, true, i_rContext ), UNO_SET_THROW ); + + // create a number formatter for it + xNumberFormatter.set( NumberFormatter::create( i_rContext ), UNO_QUERY_THROW ); + xNumberFormatter->attachNumberFormatsSupplier( xSupplier ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + + lcl_initColumnDataValue_nothrow( i_rData, xNumberFormatter, i_rColumn ); + } + } + + FormattedColumnValue::FormattedColumnValue( const Reference< XComponentContext >& _rxContext, + const Reference< XRowSet >& _rxRowSet, const Reference< XPropertySet >& i_rColumn ) + :m_pData( new FormattedColumnValue_Data ) + { + lcl_initColumnDataValue_nothrow( _rxContext, *m_pData, _rxRowSet, i_rColumn ); + } + + + FormattedColumnValue::FormattedColumnValue( const Reference< XNumberFormatter >& i_rNumberFormatter, + const Reference< XPropertySet >& _rxColumn ) + :m_pData( new FormattedColumnValue_Data ) + { + lcl_initColumnDataValue_nothrow( *m_pData, i_rNumberFormatter, _rxColumn ); + } + + + FormattedColumnValue::~FormattedColumnValue() + { + lcl_clear_nothrow( *m_pData ); + } + + sal_Int16 FormattedColumnValue::getKeyType() const + { + return m_pData->m_nKeyType; + } + + + const Reference< XColumn >& FormattedColumnValue::getColumn() const + { + return m_pData->m_xColumn; + } + + bool FormattedColumnValue::setFormattedValue( const OUString& _rFormattedStringValue ) const + { + OSL_PRECOND( m_pData->m_xColumnUpdate.is(), "FormattedColumnValue::setFormattedValue: no column!" ); + if ( !m_pData->m_xColumnUpdate.is() ) + return false; + + try + { + if ( m_pData->m_bNumericField ) + { + ::dbtools::DBTypeConversion::setValue( m_pData->m_xColumnUpdate, m_pData->m_xFormatter, m_pData->m_aNullDate, + _rFormattedStringValue, m_pData->m_nFormatKey, ::sal::static_int_cast< sal_Int16 >( m_pData->m_nFieldType ), + m_pData->m_nKeyType ); + } + else + { + m_pData->m_xColumnUpdate->updateString( _rFormattedStringValue ); + } + } + catch( const Exception& ) + { + return false; + } + return true; + } + + + OUString FormattedColumnValue::getFormattedValue() const + { + OSL_PRECOND( m_pData->m_xColumn.is(), "FormattedColumnValue::setFormattedValue: no column!" ); + + OUString sStringValue; + if ( m_pData->m_xColumn.is() ) + { + if ( m_pData->m_bNumericField ) + { + sStringValue = DBTypeConversion::getFormattedValue( + m_pData->m_xColumn, m_pData->m_xFormatter, m_pData->m_aNullDate, m_pData->m_nFormatKey, m_pData->m_nKeyType + ); + } + else + { + sStringValue = m_pData->m_xColumn->getString(); + } + } + return sStringValue; + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/parameters.cxx b/connectivity/source/commontools/parameters.cxx new file mode 100644 index 0000000000..990085c87f --- /dev/null +++ b/connectivity/source/commontools/parameters.cxx @@ -0,0 +1,1212 @@ +/* -*- 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 <connectivity/parameters.hxx> + +#include <com/sun/star/form/DatabaseParameterEvent.hpp> +#include <com/sun/star/form/XDatabaseParameterListener.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdb/ParametersRequest.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> + +#include <connectivity/dbtools.hxx> +#include <connectivity/filtermanager.hxx> +#include <TConnection.hxx> + +#include <comphelper/diagnose_ex.hxx> + +#include <ParameterCont.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +namespace dbtools +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdbcx; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::task; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::container; + + using namespace ::comphelper; + using namespace ::connectivity; + + ParameterManager::ParameterManager( ::osl::Mutex& _rMutex, const Reference< XComponentContext >& _rxContext ) + :m_rMutex ( _rMutex ) + ,m_aParameterListeners( _rMutex ) + ,m_xContext ( _rxContext ) + ,m_nInnerCount ( 0 ) + ,m_bUpToDate ( false ) + { + OSL_ENSURE( m_xContext.is(), "ParameterManager::ParameterManager: no service factory!" ); + } + + + void ParameterManager::initialize( const Reference< XPropertySet >& _rxComponent, const Reference< XAggregation >& _rxComponentAggregate ) + { + OSL_ENSURE( !m_xComponent.get().is(), "ParameterManager::initialize: already initialized!" ); + + m_xComponent = _rxComponent; + m_xAggregatedRowSet = _rxComponentAggregate; + if ( m_xAggregatedRowSet.is() ) + m_xAggregatedRowSet->queryAggregation( cppu::UnoType<decltype(m_xInnerParamUpdate)>::get() ) >>= m_xInnerParamUpdate; + OSL_ENSURE( m_xComponent.get().is() && m_xInnerParamUpdate.is(), "ParameterManager::initialize: invalid arguments!" ); + if ( !m_xComponent.get().is() || !m_xInnerParamUpdate.is() ) + return; + } + + + void ParameterManager::dispose( ) + { + clearAllParameterInformation(); + + m_xComposer.clear(); + m_xParentComposer.clear(); + //m_xComponent.clear(); + m_xInnerParamUpdate.clear(); + m_xAggregatedRowSet.clear(); + } + + + void ParameterManager::clearAllParameterInformation() + { + m_xInnerParamColumns.clear(); + if ( m_pOuterParameters.is() ) + m_pOuterParameters->dispose(); + m_pOuterParameters = nullptr; + m_nInnerCount = 0; + ParameterInformation().swap(m_aParameterInformation); + m_aMasterFields.clear(); + m_aDetailFields.clear(); + m_sIdentifierQuoteString.clear(); + m_sSpecialCharacters.clear(); + m_xConnectionMetadata.clear(); + std::vector< bool >().swap(m_aParametersVisited); + m_bUpToDate = false; + } + + + void ParameterManager::setAllParametersNull() + { + OSL_PRECOND( isAlive(), "ParameterManager::setAllParametersNull: not initialized, or already disposed!" ); + if ( !isAlive() ) + return; + + for ( sal_Int32 i = 1; i <= m_nInnerCount; ++i ) + m_xInnerParamUpdate->setNull( i, DataType::VARCHAR ); + } + + + bool ParameterManager::initializeComposerByComponent( const Reference< XPropertySet >& _rxComponent ) + { + OSL_PRECOND( _rxComponent.is(), "ParameterManager::initializeComposerByComponent: invalid !" ); + + m_xComposer.clear(); + m_xInnerParamColumns.clear(); + m_nInnerCount = 0; + + // create and fill a composer + try + { + // get a query composer for the 's settings + m_xComposer.reset( getCurrentSettingsComposer( _rxComponent, m_xContext, nullptr ), SharedQueryComposer::TakeOwnership ); + + // see if the composer found parameters + Reference< XParametersSupplier > xParamSupp( m_xComposer, UNO_QUERY ); + if ( xParamSupp.is() ) + m_xInnerParamColumns = xParamSupp->getParameters(); + + if ( m_xInnerParamColumns.is() ) + m_nInnerCount = m_xInnerParamColumns->getCount(); + } + catch( const SQLException& ) + { + } + + return m_xInnerParamColumns.is(); + } + + + void ParameterManager::collectInnerParameters( bool _bSecondRun ) + { + OSL_PRECOND( m_xInnerParamColumns.is(), "ParameterManager::collectInnerParameters: missing some internal data!" ); + if ( !m_xInnerParamColumns.is() ) + return; + + // strip previous index information + if ( _bSecondRun ) + { + for (auto & paramInfo : m_aParameterInformation) + { + paramInfo.second.aInnerIndexes.clear(); + } + } + + // we need to map the parameter names (which is all we get from the 's + // MasterFields property) to indices, which are needed by the XParameters + // interface of the row set) + Reference<XPropertySet> xParam; + for ( sal_Int32 i = 0; i < m_nInnerCount; ++i ) + { + try + { + xParam.clear(); + m_xInnerParamColumns->getByIndex( i ) >>= xParam; + + OUString sName; + xParam->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME) ) >>= sName; + + // only append additional parameters when they are not already in the list + ParameterInformation::iterator aExistentPos = m_aParameterInformation.find( sName ); + OSL_ENSURE( !_bSecondRun || ( aExistentPos != m_aParameterInformation.end() ), + "ParameterManager::collectInnerParameters: the parameter information should already exist in the second run!" ); + + if ( aExistentPos == m_aParameterInformation.end() ) + { + aExistentPos = m_aParameterInformation.emplace( + sName, xParam ).first; + } + else + aExistentPos->second.xComposerColumn = xParam; + + aExistentPos->second.aInnerIndexes.push_back( i ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "ParameterManager::collectInnerParameters" ); + } + } + } + + + OUString ParameterManager::createFilterConditionFromColumnLink( + const OUString &_rMasterColumn, const Reference < XPropertySet > &xDetailField, OUString &o_rNewParamName ) + { + OUString sFilter; + // format is: + // <detail_column> = :<new_param_name> + { + OUString tblName; + xDetailField->getPropertyValue("TableName") >>= tblName; + if (!tblName.isEmpty()) + sFilter = ::dbtools::quoteTableName( m_xConnectionMetadata, tblName, ::dbtools::EComposeRule::InDataManipulation ) + "."; + } + { + OUString colName; + xDetailField->getPropertyValue("RealName") >>= colName; + bool isFunction(false); + xDetailField->getPropertyValue("Function") >>= isFunction; + if (isFunction) + sFilter += colName; + else + sFilter += quoteName( m_sIdentifierQuoteString, colName ); + } + + // generate a parameter name which is not already used + o_rNewParamName = "link_from_"; + o_rNewParamName += convertName2SQLName( _rMasterColumn, m_sSpecialCharacters ); + while ( m_aParameterInformation.find( o_rNewParamName ) != m_aParameterInformation.end() ) + { + o_rNewParamName += "_"; + } + + return sFilter + " =:" + o_rNewParamName; + } + + + void ParameterManager::classifyLinks( const Reference< XNameAccess >& _rxParentColumns, + const Reference< XNameAccess >& _rxColumns, + std::vector< OUString >& _out_rAdditionalFilterComponents, + std::vector< OUString >& _out_rAdditionalHavingComponents ) + { + OSL_PRECOND( m_aMasterFields.size() == m_aDetailFields.size(), + "ParameterManager::classifyLinks: master and detail fields should have the same length!" ); + OSL_ENSURE( _rxColumns.is(), "ParameterManager::classifyLinks: invalid columns!" ); + + if ( !_rxColumns.is() ) + return; + + // we may need to strip any links which are invalid, so here go the containers + // for temporarily holding the new pairs + std::vector< OUString > aStrippedMasterFields; + std::vector< OUString > aStrippedDetailFields; + + bool bNeedExchangeLinks = false; + + // classify the links + auto pMasterFields = m_aMasterFields.begin(); + auto pDetailFields = m_aDetailFields.begin(); + auto pDetailFieldsEnd = m_aDetailFields.end(); + for ( ; pDetailFields != pDetailFieldsEnd; ++pDetailFields, ++pMasterFields ) + { + if ( pMasterFields->isEmpty() || pDetailFields->isEmpty() ) + continue; + + // if not even the master part of the relationship exists in the parent, the + // link is invalid as a whole #i63674# + if ( !_rxParentColumns->hasByName( *pMasterFields ) ) + { + bNeedExchangeLinks = true; + continue; + } + + bool bValidLink = true; + + // is there an inner parameter with this name? That is, a parameter which is already part of + // the very original statement (not the one we create ourselves, with the additional parameters) + ParameterInformation::iterator aPos = m_aParameterInformation.find( *pDetailFields ); + if ( aPos != m_aParameterInformation.end() ) + { // there is an inner parameter with this name + aPos->second.eType = ParameterClassification::LinkedByParamName; + aStrippedDetailFields.push_back( *pDetailFields ); + } + else + { + // does the detail name denote a column? + if ( _rxColumns->hasByName( *pDetailFields ) ) + { + Reference< XPropertySet > xDetailField(_rxColumns->getByName( *pDetailFields ), UNO_QUERY); + assert(xDetailField.is()); + + OUString sNewParamName; + const OUString sFilterCondition = createFilterConditionFromColumnLink( *pMasterFields, xDetailField, sNewParamName ); + OSL_PRECOND( !sNewParamName.isEmpty(), "ParameterManager::classifyLinks: createFilterConditionFromColumnLink returned nonsense!" ); + + // remember meta information about this new parameter + std::pair< ParameterInformation::iterator, bool > aInsertionPos = + m_aParameterInformation.emplace( + sNewParamName, ParameterMetaData( nullptr ) + ); + OSL_ENSURE( aInsertionPos.second, "ParameterManager::classifyLinks: there already was a parameter with this name!" ); + aInsertionPos.first->second.eType = ParameterClassification::LinkedByColumnName; + + // remember the filter component + if (isAggregateColumn(xDetailField)) + _out_rAdditionalHavingComponents.push_back( sFilterCondition ); + else + _out_rAdditionalFilterComponents.push_back( sFilterCondition ); + + // remember the new "detail field" for this link + aStrippedDetailFields.push_back( sNewParamName ); + bNeedExchangeLinks = true; + } + else + { + // the detail field neither denotes a column name, nor a parameter name + bValidLink = false; + bNeedExchangeLinks = true; + } + } + + if ( bValidLink ) + aStrippedMasterFields.push_back( *pMasterFields ); + } + SAL_WARN_IF( aStrippedMasterFields.size() != aStrippedDetailFields.size(), + "connectivity.commontools", + "ParameterManager::classifyLinks: inconsistency in new link pairs!" ); + + if ( bNeedExchangeLinks ) + { + m_aMasterFields.swap(aStrippedMasterFields); + m_aDetailFields.swap(aStrippedDetailFields); + } + } + + + void ParameterManager::analyzeFieldLinks( FilterManager& _rFilterManager, bool& /* [out] */ _rColumnsInLinkDetails ) + { + OSL_PRECOND( isAlive(), "ParameterManager::analyzeFieldLinks: not initialized, or already disposed!" ); + if ( !isAlive() ) + return; + + _rColumnsInLinkDetails = false; + try + { + // the links as determined by the properties + Reference< XPropertySet > xProp = m_xComponent; + OSL_ENSURE(xProp.is(),"Someone already released my component!"); + if ( xProp.is() ) + { + Sequence<OUString> aTmp; + if (xProp->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MASTERFIELDS) ) >>= aTmp) + comphelper::sequenceToContainer(m_aMasterFields, aTmp); + if (xProp->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DETAILFIELDS) ) >>= aTmp) + comphelper::sequenceToContainer(m_aDetailFields, aTmp); + } + + { + // normalize to equal length + sal_Int32 nMasterLength = m_aMasterFields.size(); + sal_Int32 nDetailLength = m_aDetailFields.size(); + + if ( nMasterLength > nDetailLength ) + m_aMasterFields.resize( nDetailLength ); + else if ( nDetailLength > nMasterLength ) + m_aDetailFields.resize( nMasterLength ); + } + + Reference< XNameAccess > xColumns; + if ( !getColumns( xColumns, true ) ) + // already asserted in getColumns + return; + + Reference< XNameAccess > xParentColumns; + if ( !getParentColumns( xParentColumns, true ) ) + return; + + // classify the links - depending on what the detail fields in each link pair denotes + std::vector< OUString > aAdditionalFilterComponents; + std::vector< OUString > aAdditionalHavingComponents; + classifyLinks( xParentColumns, xColumns, aAdditionalFilterComponents, aAdditionalHavingComponents ); + + // did we find links where the detail field refers to a detail column (instead of a parameter name)? + if ( !aAdditionalFilterComponents.empty() ) + { + // build a conjunction of all the filter components + OUStringBuffer sAdditionalFilter; + for (auto const& elem : aAdditionalFilterComponents) + { + if ( !sAdditionalFilter.isEmpty() ) + sAdditionalFilter.append(" AND "); + + sAdditionalFilter.append("( " + elem + " )"); + } + + // now set this filter at the filter manager + _rFilterManager.setFilterComponent( FilterManager::FilterComponent::LinkFilter, sAdditionalFilter.makeStringAndClear() ); + + _rColumnsInLinkDetails = true; + } + + if ( !aAdditionalHavingComponents.empty() ) + { + // build a conjunction of all the filter components + OUStringBuffer sAdditionalHaving; + for (auto const& elem : aAdditionalHavingComponents) + { + if ( !sAdditionalHaving.isEmpty() ) + sAdditionalHaving.append(" AND "); + + sAdditionalHaving.append("( " + elem + " )"); + } + + // now set this having clause at the filter manager + _rFilterManager.setFilterComponent( FilterManager::FilterComponent::LinkHaving, sAdditionalHaving.makeStringAndClear() ); + + _rColumnsInLinkDetails = true; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "ParameterManager::analyzeFieldLinks" ); + } + } + + + void ParameterManager::createOuterParameters() + { + OSL_PRECOND( !m_pOuterParameters.is(), "ParameterManager::createOuterParameters: outer parameters not initialized!" ); + OSL_PRECOND( m_xInnerParamUpdate.is(), "ParameterManager::createOuterParameters: no write access to the inner parameters!" ); + if ( !m_xInnerParamUpdate.is() ) + return; + + m_pOuterParameters = new param::ParameterWrapperContainer; + +#if OSL_DEBUG_LEVEL > 0 + sal_Int32 nSmallestIndexLinkedByColumnName = -1; + sal_Int32 nLargestIndexNotLinkedByColumnName = -1; +#endif + for (auto & aParam : m_aParameterInformation) + { +#if OSL_DEBUG_LEVEL > 0 + if ( aParam.second.aInnerIndexes.size() ) + { + if ( aParam.second.eType == ParameterClassification::LinkedByColumnName ) + { + if ( nSmallestIndexLinkedByColumnName == -1 ) + nSmallestIndexLinkedByColumnName = aParam.second.aInnerIndexes[ 0 ]; + } + else + { + nLargestIndexNotLinkedByColumnName = aParam.second.aInnerIndexes[ aParam.second.aInnerIndexes.size() - 1 ]; + } + } +#endif + if ( aParam.second.eType != ParameterClassification::FilledExternally ) + continue; + + // check which of the parameters have already been visited (e.g. filled via XParameters) + size_t nAlreadyVisited = 0; + for (auto & aIndex : aParam.second.aInnerIndexes) + { + if ( ( m_aParametersVisited.size() > o3tl::make_unsigned(aIndex) ) && m_aParametersVisited[ aIndex ] ) + { // exclude this index + aIndex = -1; + ++nAlreadyVisited; + } + } + if ( nAlreadyVisited == aParam.second.aInnerIndexes.size() ) + continue; + + // need a wrapper for this... the "inner parameters" as supplied by a result set don't have a "Value" + // property, but the parameter listeners expect such a property. So we need an object "aggregating" + // xParam and supplying an additional property ("Value") + // (it's no real aggregation of course...) + m_pOuterParameters->push_back( new param::ParameterWrapper( aParam.second.xComposerColumn, m_xInnerParamUpdate, std::vector(aParam.second.aInnerIndexes) ) ); + } + +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( ( nSmallestIndexLinkedByColumnName == -1 ) || ( nLargestIndexNotLinkedByColumnName == -1 ) || + ( nSmallestIndexLinkedByColumnName > nLargestIndexNotLinkedByColumnName ), + "ParameterManager::createOuterParameters: inconsistency!" ); + + // for the master-detail links, where the detail field denoted a column name, we created an additional ("artificial") + // filter, and *appended* it to all other (potentially) existing filters of the row set. This means that the indexes + // for the parameters resulting from the artificial filter should be larger than any other parameter index, and this + // is what the assertion checks. + // If the assertion fails, then we would need another handling for the "parameters visited" flags, since they're based + // on parameter indexes *without* the artificial filter (because this filter is not visible from the outside). +#endif + } + + + void ParameterManager::updateParameterInfo( FilterManager& _rFilterManager ) + { + OSL_PRECOND( isAlive(), "ParameterManager::updateParameterInfo: not initialized, or already disposed!" ); + if ( !isAlive() ) + return; + + clearAllParameterInformation(); + cacheConnectionInfo(); + + // check whether the is based on a statement/query which requires parameters + Reference< XPropertySet > xProp = m_xComponent; + OSL_ENSURE(xProp.is(),"Some already released my component!"); + if ( xProp.is() ) + { + if ( !initializeComposerByComponent( xProp ) ) + { // okay, nothing to do + m_bUpToDate = true; + return; + } // if ( !initializeComposerByComponent( m_xComponent ) ) + } + SAL_WARN_IF( !m_xInnerParamColumns.is(), + "connectivity.commontools", + "ParameterManager::updateParameterInfo: initializeComposerByComponent did nonsense (1)!" ); + + // collect all parameters which are defined by the "inner parameters" + collectInnerParameters( false ); + + // analyze the master-detail relationships + bool bColumnsInLinkDetails = false; + analyzeFieldLinks( _rFilterManager, bColumnsInLinkDetails ); + + if ( bColumnsInLinkDetails ) + { + // okay, in this case, analyzeFieldLinks modified the "real" filter at the RowSet, to contain + // an additional restriction (which we created ourself) + // So we need to update all information about our inner parameter columns + Reference< XPropertySet > xDirectRowSetProps; + m_xAggregatedRowSet->queryAggregation( cppu::UnoType<decltype(xDirectRowSetProps)>::get() ) >>= xDirectRowSetProps; + OSL_VERIFY( initializeComposerByComponent( xDirectRowSetProps ) ); + collectInnerParameters( true ); + } + + if ( !m_nInnerCount ) + { // no parameters at all + m_bUpToDate = true; + return; + } + + // for what now remains as outer parameters, create the wrappers for the single + // parameter columns + createOuterParameters(); + + m_bUpToDate = true; + } + + + void ParameterManager::fillLinkedParameters( const Reference< XNameAccess >& _rxParentColumns ) + { + OSL_PRECOND( isAlive(), "ParameterManager::fillLinkedParameters: not initialized, or already disposed!" ); + if ( !isAlive() ) + return; + OSL_PRECOND( m_xInnerParamColumns.is(), "ParameterManager::fillLinkedParameters: no inner parameters found!" ); + OSL_ENSURE ( _rxParentColumns.is(), "ParameterManager::fillLinkedParameters: invalid parent columns!" ); + + try + { + // the master and detail field( name)s of the + auto pMasterFields = m_aMasterFields.begin(); + auto pDetailFields = m_aDetailFields.begin(); + + sal_Int32 nMasterLen = m_aMasterFields.size(); + + // loop through all master fields. For each of them, get the respective column from the + // parent , and forward its current value as parameter value to the (inner) row set + for ( sal_Int32 i = 0; i < nMasterLen; ++i, ++pMasterFields, ++pDetailFields ) + { + // does the name denote a valid column in the parent? + if ( !_rxParentColumns->hasByName( *pMasterFields ) ) + { + SAL_WARN( "connectivity.commontools", "ParameterManager::fillLinkedParameters: invalid master names should have been stripped long before!" ); + continue; + } + + // do we, for this name, know where to place the values? + ParameterInformation::const_iterator aParamInfo = m_aParameterInformation.find( *pDetailFields ); + if ( ( aParamInfo == m_aParameterInformation.end() ) + || ( aParamInfo->second.aInnerIndexes.empty() ) + ) + { + SAL_WARN( "connectivity.commontools", "ParameterManager::fillLinkedParameters: nothing known about this detail field!" ); + continue; + } + + // the concrete master field + Reference< XPropertySet > xMasterField(_rxParentColumns->getByName( *pMasterFields ),UNO_QUERY); + + // the positions where we have to fill in values for the current parameter name + for (auto const& aPosition : aParamInfo->second.aInnerIndexes) + { + // the concrete detail field + Reference< XPropertySet > xDetailField(m_xInnerParamColumns->getByIndex(aPosition),UNO_QUERY); + OSL_ENSURE( xDetailField.is(), "ParameterManager::fillLinkedParameters: invalid detail field!" ); + if ( !xDetailField.is() ) + continue; + + // type and scale of the parameter field + sal_Int32 nParamType = DataType::VARCHAR; + OSL_VERIFY( xDetailField->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE) ) >>= nParamType ); + + sal_Int32 nScale = 0; + if ( xDetailField->getPropertySetInfo()->hasPropertyByName( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE) ) ) + OSL_VERIFY( xDetailField->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE) ) >>= nScale ); + + // transfer the param value + try + { + m_xInnerParamUpdate->setObjectWithInfo( + aPosition + 1, // parameters are based at 1 + xMasterField->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_VALUE) ), + nParamType, + nScale + ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + SAL_WARN( "connectivity.commontools", "ParameterManager::fillLinkedParameters: master-detail parameter number " << + sal_Int32( aPosition + 1 ) << " could not be filled!" ); + } + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + } + + + bool ParameterManager::completeParameters( const Reference< XInteractionHandler >& _rxCompletionHandler, const Reference< XConnection >& _rxConnection ) + { + OSL_PRECOND( isAlive(), "ParameterManager::completeParameters: not initialized, or already disposed!" ); + OSL_ENSURE ( _rxCompletionHandler.is(), "ParameterManager::completeParameters: invalid interaction handler!" ); + + // two continuations (Ok and Cancel) + rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort; + rtl::Reference<OParameterContinuation> pParams = new OParameterContinuation; + + // the request + ParametersRequest aRequest; + aRequest.Parameters = m_pOuterParameters.get(); + aRequest.Connection = _rxConnection; + rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest( Any( aRequest ) ); + + // some knittings + pRequest->addContinuation( pAbort ); + pRequest->addContinuation( pParams ); + + // execute the request + try + { + _rxCompletionHandler->handle( pRequest ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "ParameterManager::completeParameters: caught an exception while calling the handler" ); + } + + if ( !pParams->wasSelected() ) + // canceled by the user (i.e. (s)he canceled the dialog) + return false; + + try + { + // transfer the values from the continuation object to the parameter columns + const Sequence< PropertyValue >& aFinalValues = pParams->getValues(); + const PropertyValue* pFinalValues = aFinalValues.getConstArray(); + for ( sal_Int32 i = 0; i < aFinalValues.getLength(); ++i, ++pFinalValues ) + { + Reference< XPropertySet > xParamColumn(aRequest.Parameters->getByIndex( i ),UNO_QUERY); + if ( xParamColumn.is() ) + { + #ifdef DBG_UTIL + OUString sName; + xParamColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME) ) >>= sName; + OSL_ENSURE( sName == pFinalValues->Name, "ParameterManager::completeParameters: inconsistent parameter names!" ); + #endif + xParamColumn->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_VALUE), pFinalValues->Value ); + // the property sets are wrapper classes, translating the Value property into a call to + // the appropriate XParameters interface + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "ParameterManager::completeParameters: caught an exception while propagating the values" ); + } + return true; + } + + + bool ParameterManager::consultParameterListeners( ::osl::ResettableMutexGuard& _rClearForNotifies ) + { + bool bCanceled = false; + + sal_Int32 nParamsLeft = m_pOuterParameters->getParameters().size(); + // TODO: shouldn't we subtract all the parameters which were already visited? + if ( nParamsLeft ) + { + ::comphelper::OInterfaceIteratorHelper3 aIter( m_aParameterListeners ); + Reference< XPropertySet > xProp = m_xComponent; + OSL_ENSURE(xProp.is(),"Some already released my component!"); + DatabaseParameterEvent aEvent( xProp, m_pOuterParameters ); + + _rClearForNotifies.clear(); + while ( aIter.hasMoreElements() && !bCanceled ) + bCanceled = !aIter.next()->approveParameter( aEvent ); + _rClearForNotifies.reset(); + } + + return !bCanceled; + } + + + bool ParameterManager::fillParameterValues( const Reference< XInteractionHandler >& _rxCompletionHandler, ::osl::ResettableMutexGuard& _rClearForNotifies ) + { + OSL_PRECOND( isAlive(), "ParameterManager::fillParameterValues: not initialized, or already disposed!" ); + if ( !isAlive() ) + return true; + + if ( m_nInnerCount == 0 ) + // no parameters at all + return true; + + // fill the parameters from the master-detail relationship + Reference< XNameAccess > xParentColumns; + if ( getParentColumns( xParentColumns, false ) && xParentColumns->hasElements() && !m_aMasterFields.empty() ) + fillLinkedParameters( xParentColumns ); + + // let the user (via the interaction handler) fill all remaining parameters + Reference< XConnection > xConnection; + getConnection( xConnection ); + + if ( _rxCompletionHandler.is() ) + return completeParameters( _rxCompletionHandler, xConnection ); + + return consultParameterListeners( _rClearForNotifies ); + } + + + void ParameterManager::getConnection( Reference< XConnection >& /* [out] */ _rxConnection ) + { + OSL_PRECOND( isAlive(), "ParameterManager::getConnection: not initialized, or already disposed!" ); + if ( !isAlive() ) + return; + + _rxConnection.clear(); + try + { + Reference< XPropertySet > xProp = m_xComponent; + OSL_ENSURE(xProp.is(),"Some already released my component!"); + if ( xProp.is() ) + xProp->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ACTIVE_CONNECTION) ) >>= _rxConnection; + } + catch( const Exception& ) + { + SAL_WARN( "connectivity.commontools", "ParameterManager::getConnection: could not retrieve the connection of the !" ); + } + } + + + void ParameterManager::cacheConnectionInfo() + { + try + { + Reference< XConnection > xConnection; + getConnection( xConnection ); + Reference< XDatabaseMetaData > xMeta; + if ( xConnection.is() ) + xMeta = xConnection->getMetaData(); + if ( xMeta.is() ) + { + m_xConnectionMetadata = xMeta; + m_sIdentifierQuoteString = xMeta->getIdentifierQuoteString(); + m_sSpecialCharacters = xMeta->getExtraNameCharacters(); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "ParameterManager::cacheConnectionInfo: caught an exception" ); + } + } + + + bool ParameterManager::getColumns( Reference< XNameAccess >& /* [out] */ _rxColumns, bool _bFromComposer ) + { + _rxColumns.clear(); + + Reference< XColumnsSupplier > xColumnSupp; + if ( _bFromComposer ) + xColumnSupp.set(m_xComposer, css::uno::UNO_QUERY); + else + xColumnSupp.set( m_xComponent.get(),UNO_QUERY); + if ( xColumnSupp.is() ) + _rxColumns = xColumnSupp->getColumns(); + OSL_ENSURE( _rxColumns.is(), "ParameterManager::getColumns: could not retrieve the columns for the detail !" ); + + return _rxColumns.is(); + } + + + bool ParameterManager::getParentColumns( Reference< XNameAccess >& /* [out] */ _out_rxParentColumns, bool _bFromComposer ) + { + OSL_PRECOND( isAlive(), "ParameterManager::getParentColumns: not initialized, or already disposed!" ); + + _out_rxParentColumns.clear(); + try + { + // get the parent of the component we're working for + Reference< XChild > xAsChild( m_xComponent.get(), UNO_QUERY_THROW ); + Reference< XPropertySet > xParent( xAsChild->getParent(), UNO_QUERY ); + if ( !xParent.is() ) + return false; + + // the columns supplier: either from a composer, or directly from the + Reference< XColumnsSupplier > xParentColSupp; + if ( _bFromComposer ) + { + // re-create the parent composer all the time. Else, we'd have to bother with + // being a listener at its properties, its loaded state, and event the parent-relationship. + m_xParentComposer.reset( + getCurrentSettingsComposer( xParent, m_xContext, nullptr ), + SharedQueryComposer::TakeOwnership + ); + xParentColSupp.set(m_xParentComposer, css::uno::UNO_QUERY); + } + else + xParentColSupp.set(xParent, css::uno::UNO_QUERY); + + // get the columns of the parent + if ( xParentColSupp.is() ) + _out_rxParentColumns = xParentColSupp->getColumns(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "ParameterManager::getParentColumns" ); + } + return _out_rxParentColumns.is(); + } + + + void ParameterManager::addParameterListener( const Reference< XDatabaseParameterListener >& _rxListener ) + { + if ( _rxListener.is() ) + m_aParameterListeners.addInterface( _rxListener ); + } + + + void ParameterManager::removeParameterListener( const Reference< XDatabaseParameterListener >& _rxListener ) + { + m_aParameterListeners.removeInterface( _rxListener ); + } + + + void ParameterManager::resetParameterValues( ) + { + OSL_PRECOND( isAlive(), "ParameterManager::resetParameterValues: not initialized, or already disposed!" ); + if ( !isAlive() ) + return; + + if ( !m_nInnerCount ) + // no parameters at all + return; + + try + { + Reference< XNameAccess > xColumns; + if ( !getColumns( xColumns, false ) ) + // already asserted in getColumns + return; + + Reference< XNameAccess > xParentColumns; + if ( !getParentColumns( xParentColumns, false ) ) + return; + + // loop through all links pairs + auto pMasterFields = m_aMasterFields.begin(); + auto pDetailFields = m_aDetailFields.begin(); + + Reference< XPropertySet > xMasterField; + Reference< XPropertySet > xDetailField; + + // now really .... + auto pDetailFieldsEnd = m_aDetailFields.end(); + for ( ; pDetailFields != pDetailFieldsEnd; ++pDetailFields, ++pMasterFields ) + { + if ( !xParentColumns->hasByName( *pMasterFields ) ) + { + // if this name is unknown in the parent columns, then we don't have a source + // for copying the value to the detail columns + SAL_WARN( "connectivity.commontools", "ParameterManager::resetParameterValues: this should have been stripped long before!" ); + continue; + } + + // for all inner parameters which are bound to the name as specified by the + // slave element of the link, propagate the value from the master column to this + // parameter column + ParameterInformation::const_iterator aParamInfo = m_aParameterInformation.find( *pDetailFields ); + if ( ( aParamInfo == m_aParameterInformation.end() ) + || ( aParamInfo->second.aInnerIndexes.empty() ) + ) + { + SAL_WARN( "connectivity.commontools", "ParameterManager::resetParameterValues: nothing known about this detail field!" ); + continue; + } + + xParentColumns->getByName( *pMasterFields ) >>= xMasterField; + if ( !xMasterField.is() ) + continue; + + for (auto const& aPosition : aParamInfo->second.aInnerIndexes) + { + Reference< XPropertySet > xInnerParameter; + m_xInnerParamColumns->getByIndex(aPosition) >>= xInnerParameter; + if ( !xInnerParameter.is() ) + continue; + + OUString sParamColumnRealName; + xInnerParameter->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME) ) >>= sParamColumnRealName; + if ( xColumns->hasByName( sParamColumnRealName ) ) + { // our own columns have a column which's name equals the real name of the param column + // -> transfer the value property + xColumns->getByName( sParamColumnRealName ) >>= xDetailField; + if ( xDetailField.is() ) + xDetailField->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_VALUE), xMasterField->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_VALUE) ) ); + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "ParameterManager::resetParameterValues" ); + } + + } + + + void ParameterManager::externalParameterVisited( sal_Int32 _nIndex ) + { + if ( m_aParametersVisited.size() < o3tl::make_unsigned(_nIndex) ) + { + m_aParametersVisited.reserve( _nIndex ); + for ( sal_Int32 i = m_aParametersVisited.size(); i < _nIndex; ++i ) + m_aParametersVisited.push_back( false ); + } + m_aParametersVisited[ _nIndex - 1 ] = true; + } + + void ParameterManager::setNull( sal_Int32 _nIndex, sal_Int32 sqlType ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setNull(_nIndex, sqlType); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setObjectNull( sal_Int32 _nIndex, sal_Int32 sqlType, const OUString& typeName ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setObjectNull(_nIndex, sqlType, typeName); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setBoolean( sal_Int32 _nIndex, bool x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setBoolean(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setByte( sal_Int32 _nIndex, sal_Int8 x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setByte(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setShort( sal_Int32 _nIndex, sal_Int16 x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setShort(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setInt( sal_Int32 _nIndex, sal_Int32 x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setInt(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setLong( sal_Int32 _nIndex, sal_Int64 x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setLong(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setFloat( sal_Int32 _nIndex, float x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setFloat(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setDouble( sal_Int32 _nIndex, double x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setDouble(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setString( sal_Int32 _nIndex, const OUString& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setString(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setBytes( sal_Int32 _nIndex, const css::uno::Sequence< sal_Int8 >& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setBytes(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setDate( sal_Int32 _nIndex, const css::util::Date& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setDate(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setTime( sal_Int32 _nIndex, const css::util::Time& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setTime(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setTimestamp( sal_Int32 _nIndex, const css::util::DateTime& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setTimestamp(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setBinaryStream( sal_Int32 _nIndex, const css::uno::Reference< css::io::XInputStream>& x, sal_Int32 length ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setBinaryStream(_nIndex, x, length); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setCharacterStream( sal_Int32 _nIndex, const css::uno::Reference< css::io::XInputStream>& x, sal_Int32 length ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setCharacterStream(_nIndex, x, length); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setObject( sal_Int32 _nIndex, const css::uno::Any& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setObject(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setObjectWithInfo( sal_Int32 _nIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setObjectWithInfo(_nIndex, x, targetSqlType, scale); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setRef( sal_Int32 _nIndex, const css::uno::Reference< css::sdbc::XRef>& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setRef(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setBlob( sal_Int32 _nIndex, const css::uno::Reference< css::sdbc::XBlob>& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setBlob(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setClob( sal_Int32 _nIndex, const css::uno::Reference< css::sdbc::XClob>& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setClob(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::setArray( sal_Int32 _nIndex, const css::uno::Reference< css::sdbc::XArray>& x ) + { + ::osl::MutexGuard aGuard(m_rMutex); + OSL_ENSURE(m_xInnerParamUpdate.is(), "ParameterManager::XParameters::setXXX: no XParameters access to the RowSet!"); + if (!m_xInnerParamUpdate.is()) + return; + m_xInnerParamUpdate->setArray(_nIndex, x); + externalParameterVisited(_nIndex); + } + + + void ParameterManager::clearParameters( ) + { + if ( m_xInnerParamUpdate.is() ) + m_xInnerParamUpdate->clearParameters( ); + } + + void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& _rValues ) + { + m_aValues = _rValues; + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/paramwrapper.cxx b/connectivity/source/commontools/paramwrapper.cxx new file mode 100644 index 0000000000..e25a3e7b49 --- /dev/null +++ b/connectivity/source/commontools/paramwrapper.cxx @@ -0,0 +1,345 @@ +/* -*- 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 <connectivity/paramwrapper.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetException.hpp> +#include <com/sun/star/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryAnalyzer.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <o3tl/safeint.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/enumhelper.hxx> + +#define PROPERTY_ID_VALUE 1000 + + +namespace dbtools::param +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::sdbc::XParameters; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::Type; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::XWeak; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::XFastPropertySet; + using ::com::sun::star::beans::XMultiPropertySet; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::beans::Property; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Any; + using ::com::sun::star::sdbc::SQLException; + using ::com::sun::star::lang::WrappedTargetException; + using ::com::sun::star::lang::IndexOutOfBoundsException; + using ::com::sun::star::container::XEnumeration; + using ::com::sun::star::sdb::XSingleSelectQueryAnalyzer; + using ::com::sun::star::sdb::XParametersSupplier; + using ::com::sun::star::lang::DisposedException; + + namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute; + namespace DataType = ::com::sun::star::sdbc::DataType; + + ParameterWrapper::ParameterWrapper( const Reference< XPropertySet >& _rxColumn ) + :PropertyBase( m_aBHelper ) + ,m_xDelegator( _rxColumn ) + { + if ( m_xDelegator.is() ) + m_xDelegatorPSI = m_xDelegator->getPropertySetInfo(); + if ( !m_xDelegatorPSI.is() ) + throw RuntimeException(); + } + + + ParameterWrapper::ParameterWrapper( const Reference< XPropertySet >& _rxColumn, + const Reference< XParameters >& _rxAllParameters, std::vector< sal_Int32 >&& _rIndexes ) + :PropertyBase( m_aBHelper ) + ,m_aIndexes( std::move(_rIndexes) ) + ,m_xDelegator( _rxColumn ) + ,m_xValueDestination( _rxAllParameters ) + { + if ( m_xDelegator.is() ) + m_xDelegatorPSI = m_xDelegator->getPropertySetInfo(); + if ( !m_xDelegatorPSI.is() ) + throw RuntimeException(); + + OSL_ENSURE( !m_aIndexes.empty(), "ParameterWrapper::ParameterWrapper: sure about the indexes?" ); + } + + + ParameterWrapper::~ParameterWrapper() + { + } + + + IMPLEMENT_FORWARD_REFCOUNT( ParameterWrapper, UnoBase ) + + css::uno::Any ParameterWrapper::queryInterface(css::uno::Type const & aType) + { + css::uno::Any a(UnoBase::queryInterface(aType)); + if (!a.hasValue()) { + a = PropertyBase::queryInterface(aType); + if (!a.hasValue() + && aType == cppu::UnoType<css::lang::XTypeProvider>::get()) + { + a <<= css::uno::Reference<css::lang::XTypeProvider>(this); + } + } + return a; + } + + + Sequence< Type > SAL_CALL ParameterWrapper::getTypes( ) + { + return Sequence< Type > { + cppu::UnoType<XWeak>::get(), + cppu::UnoType<XTypeProvider>::get(), + cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XMultiPropertySet>::get() + }; + } + + + IMPLEMENT_GET_IMPLEMENTATION_ID( ParameterWrapper ) + + + OUString ParameterWrapper::impl_getPseudoAggregatePropertyName( sal_Int32 _nHandle ) const + { + Reference< XPropertySetInfo > xInfo = const_cast<ParameterWrapper*>( this )->getPropertySetInfo(); + const css::uno::Sequence<Property> aProperties = xInfo->getProperties(); + for ( const Property& rProperty : aProperties ) + { + if ( rProperty.Handle == _nHandle ) + return rProperty.Name; + } + + OSL_FAIL( "ParameterWrapper::impl_getPseudoAggregatePropertyName: invalid argument!" ); + return OUString(); + } + + + Reference< XPropertySetInfo > ParameterWrapper::getPropertySetInfo() + { + return createPropertySetInfo( getInfoHelper() ); + } + + + ::cppu::IPropertyArrayHelper& ParameterWrapper::getInfoHelper() + { + if (!m_pInfoHelper) + { + Sequence< Property > aProperties; + try + { + aProperties = m_xDelegatorPSI->getProperties(); + sal_Int32 nProperties( aProperties.getLength() ); + aProperties.realloc( nProperties + 1 ); + aProperties.getArray()[ nProperties ] = Property( + "Value", + PROPERTY_ID_VALUE, + ::cppu::UnoType< Any >::get(), + PropertyAttribute::TRANSIENT | PropertyAttribute::MAYBEVOID + ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + + m_pInfoHelper.reset( new ::cppu::OPropertyArrayHelper( aProperties, false ) ); + } + return *m_pInfoHelper; + } + + + sal_Bool ParameterWrapper::convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue) + { + OSL_ENSURE( PROPERTY_ID_VALUE == nHandle, "ParameterWrapper::convertFastPropertyValue: the only non-readonly prop should be our PROPERTY_VALUE!" ); + + // we're lazy here ... + rOldValue = m_aValue.makeAny(); + rConvertedValue = rValue; + return true; // assume "modified" ... + } + + + void ParameterWrapper::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) + { + if ( nHandle == PROPERTY_ID_VALUE ) + { + try + { + // TODO : aParamType & nScale can be obtained within the constructor... + sal_Int32 nParamType = DataType::VARCHAR; + OSL_VERIFY( m_xDelegator->getPropertyValue("Type") >>= nParamType ); + + sal_Int32 nScale = 0; + if ( m_xDelegatorPSI->hasPropertyByName("Scale") ) + OSL_VERIFY( m_xDelegator->getPropertyValue("Scale") >>= nScale ); + + if ( m_xValueDestination.is() ) + { + for ( const auto& rIndex : m_aIndexes ) + { + m_xValueDestination->setObjectWithInfo( rIndex + 1, rValue, nParamType, nScale ); + // (the index of the parameters is one-based) + } + } + + m_aValue = rValue; + } + catch( SQLException& e ) + { + throw WrappedTargetException(e.Message, e.Context, css::uno::Any(e)); + } + } + else + { + OUString aName = impl_getPseudoAggregatePropertyName( nHandle ); + m_xDelegator->setPropertyValue( aName, rValue ); + } + } + + + void ParameterWrapper::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const + { + if ( nHandle == PROPERTY_ID_VALUE ) + { + rValue = m_aValue.makeAny(); + } + else + { + OUString aName = impl_getPseudoAggregatePropertyName( nHandle ); + rValue = m_xDelegator->getPropertyValue( aName ); + } + } + + + void ParameterWrapper::dispose() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + m_aValue.setNull(); + m_aIndexes.resize(0); + m_xDelegator.clear(); + m_xDelegatorPSI.clear(); + m_xValueDestination.clear(); + + m_aBHelper.bDisposed = true; + } + + ParameterWrapperContainer::ParameterWrapperContainer() + { + } + + + ParameterWrapperContainer::ParameterWrapperContainer( const Reference< XSingleSelectQueryAnalyzer >& _rxComposer ) + { + Reference< XParametersSupplier > xSuppParams( _rxComposer, UNO_QUERY_THROW ); + Reference< XIndexAccess > xParameters( xSuppParams->getParameters(), css::uno::UNO_SET_THROW ); + sal_Int32 nParamCount( xParameters->getCount() ); + m_aParameters.reserve( nParamCount ); + for ( sal_Int32 i=0; i<nParamCount; ++i ) + { + m_aParameters.push_back( new ParameterWrapper( Reference< XPropertySet >( xParameters->getByIndex( i ), UNO_QUERY_THROW ) ) ); + } + } + + + ParameterWrapperContainer::~ParameterWrapperContainer() + { + } + + + Type SAL_CALL ParameterWrapperContainer::getElementType() + { + std::unique_lock aGuard( m_aMutex ); + impl_checkDisposed_throw(); + return cppu::UnoType<XPropertySet>::get(); + } + + + sal_Bool SAL_CALL ParameterWrapperContainer::hasElements() + { + std::unique_lock aGuard( m_aMutex ); + impl_checkDisposed_throw(); + return !m_aParameters.empty(); + } + + + sal_Int32 SAL_CALL ParameterWrapperContainer::getCount() + { + std::unique_lock aGuard( m_aMutex ); + impl_checkDisposed_throw(); + return m_aParameters.size(); + } + + + Any SAL_CALL ParameterWrapperContainer::getByIndex( sal_Int32 _nIndex ) + { + std::unique_lock aGuard( m_aMutex ); + impl_checkDisposed_throw(); + + if ( ( _nIndex < 0 ) || ( o3tl::make_unsigned(_nIndex) >= m_aParameters.size() ) ) + throw IndexOutOfBoundsException(); + + return Any( Reference< XPropertySet >( m_aParameters[ _nIndex ] ) ); + } + + + Reference< XEnumeration > ParameterWrapperContainer::createEnumeration() + { + std::unique_lock aGuard( m_aMutex ); + impl_checkDisposed_throw(); + + return new ::comphelper::OEnumerationByIndex( static_cast< XIndexAccess* >( this ) ); + } + + + void ParameterWrapperContainer::impl_checkDisposed_throw() + { + if ( m_bDisposed ) + throw DisposedException( OUString(), *this ); + } + + + void ParameterWrapperContainer::disposing(std::unique_lock<std::mutex>& /*rGuard*/) + { + for (const auto& rxParam : m_aParameters) + { + rxParam->dispose(); + } + + Parameters().swap(m_aParameters); + } + + +} // namespace + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/predicateinput.cxx b/connectivity/source/commontools/predicateinput.cxx new file mode 100644 index 0000000000..cbeaf511a9 --- /dev/null +++ b/connectivity/source/commontools/predicateinput.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 <connectivity/predicateinput.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <com/sun/star/i18n/LocaleData.hpp> +#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/util/NumberFormatter.hpp> +#include <osl/diagnose.h> +#include <connectivity/sqlnode.hxx> +#include <connectivity/PColumn.hxx> +#include <comphelper/numbers.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <memory> +#include <string_view> + +namespace dbtools +{ + + + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::util::XNumberFormatsSupplier; + using ::com::sun::star::util::NumberFormatter; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::lang::Locale; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::i18n::LocaleData; + using ::com::sun::star::i18n::LocaleDataItem; + using ::com::sun::star::uno::Any; + + using namespace ::com::sun::star::sdbc; + using namespace ::connectivity; + + using ::connectivity::OSQLParseNode; + + + static sal_Unicode lcl_getSeparatorChar( + std::u16string_view _rSeparator, sal_Unicode _nFallback ) + { + OSL_ENSURE( !_rSeparator.empty(), "::lcl_getSeparatorChar: invalid separator string!" ); + + sal_Unicode nReturn( _nFallback ); + if ( !_rSeparator.empty() ) + nReturn = _rSeparator[0]; + return nReturn; + } + + bool OPredicateInputController::getSeparatorChars( const Locale& _rLocale, sal_Unicode& _rDecSep, sal_Unicode& _rThdSep ) const + { + _rDecSep = '.'; + _rThdSep = ','; + try + { + LocaleDataItem aLocaleData; + if ( m_xLocaleData.is() ) + { + aLocaleData = m_xLocaleData->getLocaleItem( _rLocale ); + _rDecSep = lcl_getSeparatorChar( aLocaleData.decimalSeparator, _rDecSep ); + _rThdSep = lcl_getSeparatorChar( aLocaleData.thousandSeparator, _rThdSep ); + return true; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "OPredicateInputController::getSeparatorChars" ); + } + return false; + } + + + OPredicateInputController::OPredicateInputController( + const Reference< XComponentContext >& rxContext, const Reference< XConnection >& _rxConnection, const IParseContext* _pParseContext ) + : m_xConnection( _rxConnection ) + ,m_aParser( rxContext, _pParseContext ) + { + try + { + // create a number formatter / number formats supplier pair + OSL_ENSURE( rxContext.is(), "OPredicateInputController::OPredicateInputController: need a service factory!" ); + if ( rxContext.is() ) + { + m_xFormatter.set( NumberFormatter::create(rxContext), UNO_QUERY_THROW ); + } + + Reference< XNumberFormatsSupplier > xNumberFormats = ::dbtools::getNumberFormats( m_xConnection, true ); + if ( !xNumberFormats.is() ) + ::comphelper::disposeComponent( m_xFormatter ); + else + m_xFormatter->attachNumberFormatsSupplier( xNumberFormats ); + + // create the locale data + if ( rxContext.is() ) + { + m_xLocaleData = LocaleData::create( rxContext ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "OPredicateInputController::OPredicateInputController" ); + } + } + + + std::unique_ptr<OSQLParseNode> OPredicateInputController::implPredicateTree(OUString& _rErrorMessage, const OUString& _rStatement, const Reference< XPropertySet > & _rxField) const + { + std::unique_ptr<OSQLParseNode> pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, _rStatement, m_xFormatter, _rxField ); + if ( !pReturn ) + { // is it a text field ? + sal_Int32 nType = DataType::OTHER; + _rxField->getPropertyValue("Type") >>= nType; + + if ( ( DataType::CHAR == nType ) + || ( DataType::VARCHAR == nType ) + || ( DataType::LONGVARCHAR == nType ) + || ( DataType::CLOB == nType ) + ) + { // yes -> force a quoted text and try again + OUString sQuoted( _rStatement ); + if ( !sQuoted.isEmpty() + && ( !sQuoted.startsWith("'") + || !sQuoted.endsWith("'") + ) + ) + { + sQuoted = u"'" + sQuoted.replaceAll(u"'", u"''") + u"'"; + } + pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, sQuoted, m_xFormatter, _rxField ); + } + + // one more fallback: for numeric fields, and value strings containing a decimal/thousands separator + // problem which is to be solved with this: + // * a system locale "german" + // * a column formatted with an english number format + // => the output is german (as we use the system locale for this), i.e. "3,4" + // => the input does not recognize the german text, as predicateTree uses the number format + // of the column to determine the main locale - the locale on the context is only a fallback + if ( ( DataType::FLOAT == nType ) + || ( DataType::REAL == nType ) + || ( DataType::DOUBLE == nType ) + || ( DataType::NUMERIC == nType ) + || ( DataType::DECIMAL == nType ) + ) + { + const IParseContext& rParseContext = m_aParser.getContext(); + // get the separators for the locale of our parse context + sal_Unicode nCtxDecSep; + sal_Unicode nCtxThdSep; + getSeparatorChars( rParseContext.getPreferredLocale(), nCtxDecSep, nCtxThdSep ); + + // determine the locale of the column we're building a predicate string for + sal_Unicode nFmtDecSep( nCtxDecSep ); + sal_Unicode nFmtThdSep( nCtxThdSep ); + try + { + Reference< XPropertySetInfo > xPSI( _rxField->getPropertySetInfo() ); + if ( xPSI.is() && xPSI->hasPropertyByName("FormatKey") ) + { + sal_Int32 nFormatKey = 0; + _rxField->getPropertyValue("FormatKey") >>= nFormatKey; + if ( nFormatKey && m_xFormatter.is() ) + { + Locale aFormatLocale; + ::comphelper::getNumberFormatProperty( + m_xFormatter, + nFormatKey, + "Locale" + ) >>= aFormatLocale; + + // valid locale + if ( !aFormatLocale.Language.isEmpty() ) + { + getSeparatorChars( aFormatLocale, nFmtDecSep, nCtxThdSep ); + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.commontools", "OPredicateInputController::implPredicateTree: caught an exception while dealing with the formats!" ); + } + + bool bDecDiffers = ( nCtxDecSep != nFmtDecSep ); + bool bFmtDiffers = ( nCtxThdSep != nFmtThdSep ); + if ( bDecDiffers || bFmtDiffers ) + { // okay, at least one differs + // "translate" the value into the "format locale" + OUString sTranslated( _rStatement ); + const sal_Unicode nIntermediate( '_' ); + sTranslated = sTranslated.replace( nCtxDecSep, nIntermediate ); + sTranslated = sTranslated.replace( nCtxThdSep, nFmtThdSep ); + sTranslated = sTranslated.replace( nIntermediate, nFmtDecSep ); + + pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, sTranslated, m_xFormatter, _rxField ); + } + } + } + return pReturn; + } + + + bool OPredicateInputController::normalizePredicateString( + OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField, OUString* _pErrorMessage ) const + { + OSL_ENSURE( m_xConnection.is() && m_xFormatter.is() && _rxField.is(), + "OPredicateInputController::normalizePredicateString: invalid state or params!" ); + + bool bSuccess = false; + if ( m_xConnection.is() && m_xFormatter.is() && _rxField.is() ) + { + // parse the string + OUString sError; + OUString sTransformedText( _rPredicateValue ); + std::unique_ptr<OSQLParseNode> pParseNode = implPredicateTree( sError, sTransformedText, _rxField ); + if ( _pErrorMessage ) *_pErrorMessage = sError; + + if ( pParseNode ) + { + const IParseContext& rParseContext = m_aParser.getContext(); + sal_Unicode nDecSeparator, nThousandSeparator; + getSeparatorChars( rParseContext.getPreferredLocale(), nDecSeparator, nThousandSeparator ); + + // translate it back into a string + sTransformedText.clear(); + pParseNode->parseNodeToPredicateStr( + sTransformedText, m_xConnection, m_xFormatter, _rxField, OUString(), + rParseContext.getPreferredLocale(), OUString(nDecSeparator), &rParseContext + ); + _rPredicateValue = sTransformedText; + + bSuccess = true; + } + } + + return bSuccess; + } + + + OUString OPredicateInputController::getPredicateValueStr( + const OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField ) const + { + OSL_ENSURE( _rxField.is(), "OPredicateInputController::getPredicateValue: invalid params!" ); + OUString sReturn; + if ( _rxField.is() ) + { + // The following is mostly stolen from the former implementation in the parameter dialog + // (dbaccess/source/ui/dlg/paramdialog.cxx). I do not fully understand this... + + OUString sError; + std::unique_ptr<OSQLParseNode> pParseNode = implPredicateTree( sError, _rPredicateValue, _rxField ); + + implParseNode(std::move(pParseNode), true) >>= sReturn; + } + + return sReturn; + } + + OUString OPredicateInputController::getPredicateValueStr( + const OUString& _sField, const OUString& _rPredicateValue ) const + { + OUString sReturn = _rPredicateValue; + OUString sError; + sal_Int32 nIndex = 0; + OUString sField = _sField.getToken(0, '(', nIndex); + if(nIndex == -1) + sField = _sField; + sal_Int32 nType = ::connectivity::OSQLParser::getFunctionReturnType(sField,&m_aParser.getContext()); + if ( nType == DataType::OTHER || sField.isEmpty() ) + { + // first try the international version + OUString sSql = "SELECT * FROM x WHERE " + sField + _rPredicateValue; + const_cast< OSQLParser& >( m_aParser ).parseTree( sError, sSql, true ); + nType = DataType::DOUBLE; + } + + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + rtl::Reference<parse::OParseColumn> pColumn = new parse::OParseColumn( sField, + OUString(), + OUString(), + OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + nType, + false, + false, + xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(), + OUString(), + OUString(), + OUString()); + Reference<XPropertySet> xColumn = pColumn; + pColumn->setFunction(true); + pColumn->setRealName(sField); + + std::unique_ptr<OSQLParseNode> pParseNode = implPredicateTree( sError, _rPredicateValue, xColumn ); + if(pParseNode) + { + implParseNode(std::move(pParseNode), true) >>= sReturn; + } + return sReturn; + } + + Any OPredicateInputController::getPredicateValue( + const OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField ) const + { + OSL_ENSURE( _rxField.is(), "OPredicateInputController::getPredicateValue: invalid params!" ); + + if ( _rxField.is() ) + { + // The following is mostly stolen from the former implementation in the parameter dialog + // (dbaccess/source/ui/dlg/paramdialog.cxx). I do not fully understand this... + + OUString sError; + std::unique_ptr<OSQLParseNode> pParseNode = implPredicateTree( sError, _rPredicateValue, _rxField ); + + return implParseNode(std::move(pParseNode), false); + } + + return Any(); + } + + Any OPredicateInputController::implParseNode(std::unique_ptr<OSQLParseNode> pParseNode, bool _bForStatementUse) const + { + if ( ! pParseNode ) + return Any(); + else + { + OUString sReturn; + OSQLParseNode* pOdbcSpec = pParseNode->getByRule( OSQLParseNode::odbc_fct_spec ); + if ( pOdbcSpec ) + { + if ( _bForStatementUse ) + { + OSQLParseNode* pFuncSpecParent = pOdbcSpec->getParent(); + OSL_ENSURE( pFuncSpecParent, "OPredicateInputController::getPredicateValue: an ODBC func spec node without parent?" ); + if ( pFuncSpecParent ) + pFuncSpecParent->parseNodeToStr(sReturn, m_xConnection, &m_aParser.getContext()); + } + else + { + OSQLParseNode* pValueNode = pOdbcSpec->getChild(1); + if ( SQLNodeType::String == pValueNode->getNodeType() ) + sReturn = pValueNode->getTokenValue(); + else + pValueNode->parseNodeToStr(sReturn, m_xConnection, &m_aParser.getContext()); + } + } + else + { + if (pParseNode->getKnownRuleID() == OSQLParseNode::test_for_null ) + { + assert(pParseNode->count() == 2); + return Any(); + } + // LEM this seems overly permissive as test... + else if (pParseNode->count() >= 3) + { + OSQLParseNode* pValueNode = pParseNode->getChild(2); + assert(pValueNode && "OPredicateInputController::getPredicateValue: invalid node child!"); + if ( !_bForStatementUse ) + { + if ( SQLNodeType::String == pValueNode->getNodeType() ) + sReturn = pValueNode->getTokenValue(); + else + pValueNode->parseNodeToStr( + sReturn, m_xConnection, &m_aParser.getContext() + ); + } + else + pValueNode->parseNodeToStr( + sReturn, m_xConnection, &m_aParser.getContext() + ); + } + else + { + OSL_FAIL( "OPredicateInputController::getPredicateValue: unknown/invalid structure (noodbc)!" ); + return Any(); + } + } + return Any(sReturn); + } + } + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/propertyids.cxx b/connectivity/source/commontools/propertyids.cxx new file mode 100644 index 0000000000..cedd0f5f06 --- /dev/null +++ b/connectivity/source/commontools/propertyids.cxx @@ -0,0 +1,104 @@ +/* -*- 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 <propertyids.hxx> + +namespace dbtools +{ + OPropertyMap::OPropertyMap() + { + // MSVC complains about ambiguous operator= + m_aPropertyMap.insert({ + {PROPERTY_ID_QUERYTIMEOUT, "QueryTimeOut"}, + {PROPERTY_ID_MAXFIELDSIZE, "MaxFieldSize"}, + {PROPERTY_ID_MAXROWS, "MaxRows"}, + {PROPERTY_ID_CURSORNAME, "CursorName"}, + {PROPERTY_ID_RESULTSETCONCURRENCY, "ResultSetConcurrency"}, + + {PROPERTY_ID_RESULTSETTYPE, "ResultSetType"}, + {PROPERTY_ID_FETCHDIRECTION, "FetchDirection"}, + {PROPERTY_ID_FETCHSIZE, "FetchSize"}, + {PROPERTY_ID_ESCAPEPROCESSING, "EscapeProcessing"}, + {PROPERTY_ID_USEBOOKMARKS, "UseBookmarks"}, + // Column + {PROPERTY_ID_NAME, "Name"}, + {PROPERTY_ID_TYPE, "Type"}, + {PROPERTY_ID_TYPENAME, "TypeName"}, + {PROPERTY_ID_PRECISION, "Precision"}, + {PROPERTY_ID_SCALE, "Scale"}, + {PROPERTY_ID_ISNULLABLE, "IsNullable"}, + {PROPERTY_ID_ISAUTOINCREMENT, "IsAutoIncrement"}, + {PROPERTY_ID_ISROWVERSION, "IsRowVersion"}, + {PROPERTY_ID_DESCRIPTION, "Description"}, + {PROPERTY_ID_DEFAULTVALUE, "DefaultValue"}, + + {PROPERTY_ID_REFERENCEDTABLE, "ReferencedTable"}, + {PROPERTY_ID_UPDATERULE, "UpdateRule"}, + {PROPERTY_ID_DELETERULE, "DeleteRule"}, + {PROPERTY_ID_CATALOG, "Catalog"}, + {PROPERTY_ID_ISUNIQUE, "IsUnique"}, + {PROPERTY_ID_ISPRIMARYKEYINDEX, "IsPrimaryKeyIndex"}, + {PROPERTY_ID_ISCLUSTERED, "IsClustered"}, + {PROPERTY_ID_ISASCENDING, "IsAscending"}, + {PROPERTY_ID_SCHEMANAME, "SchemaName"}, + {PROPERTY_ID_CATALOGNAME, "CatalogName"}, + + {PROPERTY_ID_COMMAND, "Command"}, + {PROPERTY_ID_CHECKOPTION, "CheckOption"}, + {PROPERTY_ID_PASSWORD, "Password"}, + {PROPERTY_ID_RELATEDCOLUMN, "RelatedColumn"}, + + {PROPERTY_ID_FUNCTION, "Function"}, + {PROPERTY_ID_AGGREGATEFUNCTION, "AggregateFunction"}, + {PROPERTY_ID_TABLENAME, "TableName"}, + {PROPERTY_ID_REALNAME, "RealName"}, + {PROPERTY_ID_DBASEPRECISIONCHANGED,"DbasePrecisionChanged"}, + {PROPERTY_ID_ISCURRENCY, "IsCurrency"}, + {PROPERTY_ID_ISBOOKMARKABLE, "IsBookmarkable"}, + {PROPERTY_ID_HY010, "HY010"}, // error messages + {PROPERTY_ID_DELIMITER, "/"}, + {PROPERTY_ID_FORMATKEY, "FormatKey"}, + {PROPERTY_ID_LOCALE, "Locale"}, + {PROPERTY_ID_AUTOINCREMENTCREATION, "AutoIncrementCreation"}, + {PROPERTY_ID_PRIVILEGES, "Privileges"}, + {PROPERTY_ID_HAVINGCLAUSE, "HavingClause"}, + {PROPERTY_ID_ISSIGNED, "IsSigned"}, + {PROPERTY_ID_ISSEARCHABLE, "IsSearchable"}, + {PROPERTY_ID_LABEL, "Label"}, + {PROPERTY_ID_APPLYFILTER, "ApplyFilter"}, + {PROPERTY_ID_FILTER, "Filter"}, + {PROPERTY_ID_MASTERFIELDS, "MasterFields"}, + {PROPERTY_ID_DETAILFIELDS, "DetailFields"}, + {PROPERTY_ID_FIELDTYPE, "FieldType"}, + {PROPERTY_ID_VALUE, "Value"}, + {PROPERTY_ID_ACTIVE_CONNECTION, "ActiveConnection"}, + } ); + } + + const OUString& OPropertyMap::getNameByIndex(sal_Int32 _nIndex) const + { + std::map<sal_Int32, OUString>::const_iterator aIter = m_aPropertyMap.find(_nIndex); + assert(aIter != m_aPropertyMap.end()); + return aIter->second; + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/sqlerror.cxx b/connectivity/source/commontools/sqlerror.cxx new file mode 100644 index 0000000000..50c5968cd7 --- /dev/null +++ b/connectivity/source/commontools/sqlerror.cxx @@ -0,0 +1,295 @@ +/* -*- 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 <connectivity/sqlerror.hxx> + +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <unotools/resmgr.hxx> +#include <osl/diagnose.h> + +#include <strings.hrc> +#include <strings.hxx> +#include <string.h> + +namespace connectivity +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::sdbc::SQLException; + using ::com::sun::star::uno::Type; + + class SQLError_Impl + { + public: + explicit SQLError_Impl(); + + // versions of the public SQLError methods which are just delegated to this impl-class + static const OUString& getMessagePrefix(); + OUString getErrorMessage( const ErrorCondition _eCondition, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) const; + static ErrorCode getErrorCode( const ErrorCondition _eCondition ); + void raiseException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ); + void raiseException( const ErrorCondition _eCondition, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ); + void raiseTypedException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, const Type& _rExceptionType, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ); + SQLException getSQLException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ); + + private: + /// returns the basic error message associated with the given error condition, without any parameter replacements + OUString impl_getErrorMessage( ErrorCondition _eCondition ) const; + + /// returns the SQLState associated with the given error condition + static OUString + impl_getSQLState( ErrorCondition _eCondition ); + + /// returns an SQLException describing the given error condition + SQLException + impl_buildSQLException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, + const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ); + private: + std::locale m_aResources; + }; + + SQLError_Impl::SQLError_Impl() + : m_aResources(Translate::Create("cnr")) + { + } + + const OUString& SQLError_Impl::getMessagePrefix() + { + static constexpr OUString s_sMessagePrefix( u"[OOoBase]"_ustr ); + return s_sMessagePrefix; + } + + namespace + { + + /** substitutes a given placeholder in the given message with the given value + */ + void lcl_substitutePlaceholder(OUString& _rMessage, const char* _pPlaceholder, const std::optional<OUString>& rParamValue) + { + size_t nPlaceholderLen( strlen( _pPlaceholder ) ); + sal_Int32 nIndex = _rMessage.indexOfAsciiL( _pPlaceholder, nPlaceholderLen ); + + bool bHasPlaceholder = ( nIndex != -1 ); + bool bWantsPlaceholder = rParamValue.has_value(); + OSL_ENSURE( bHasPlaceholder == bWantsPlaceholder, "lcl_substitutePlaceholder: placeholder where none is expected, or no placeholder where one is needed!" ); + + if ( bHasPlaceholder && bWantsPlaceholder ) + _rMessage = _rMessage.replaceAt( nIndex, nPlaceholderLen, *rParamValue ); + } + + TranslateId lcl_getResourceErrorID(const ErrorCondition _eCondition) + { + switch (_eCondition) + { + case css::sdb::ErrorCondition::ROW_SET_OPERATION_VETOED: + return STR_ROW_SET_OPERATION_VETOED; + case css::sdb::ErrorCondition::PARSER_CYCLIC_SUB_QUERIES: + return STR_PARSER_CYCLIC_SUB_QUERIES; + case css::sdb::ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES: + return STR_DB_OBJECT_NAME_WITH_SLASHES; + case css::sdb::ErrorCondition::DB_INVALID_SQL_NAME: + return STR_DB_INVALID_SQL_NAME; + case css::sdb::ErrorCondition::DB_QUERY_NAME_WITH_QUOTES: + return STR_DB_QUERY_NAME_WITH_QUOTES; + case css::sdb::ErrorCondition::DB_OBJECT_NAME_IS_USED: + return STR_DB_OBJECT_NAME_IS_USED; + case css::sdb::ErrorCondition::DB_NOT_CONNECTED: + return STR_DB_NOT_CONNECTED; + case css::sdb::ErrorCondition::AB_ADDRESSBOOK_NOT_FOUND: + return STR_AB_ADDRESSBOOK_NOT_FOUND; + case css::sdb::ErrorCondition::DATA_CANNOT_SELECT_UNFILTERED: + return STR_DATA_CANNOT_SELECT_UNFILTERED; + } + return {}; + } + + OUString lcl_getResourceState(const ErrorCondition _eCondition) + { + switch (_eCondition) + { + case css::sdb::ErrorCondition::DB_NOT_CONNECTED: + return STR_DB_NOT_CONNECTED_STATE; + case css::sdb::ErrorCondition::DATA_CANNOT_SELECT_UNFILTERED: + return STR_DATA_CANNOT_SELECT_UNFILTERED_STATE; + } + return OUString(); + } + } + + OUString SQLError_Impl::getErrorMessage( const ErrorCondition _eCondition, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) const + { + OUString sErrorMessage( impl_getErrorMessage( _eCondition ) ); + + lcl_substitutePlaceholder( sErrorMessage, "$1$", _rParamValue1 ); + lcl_substitutePlaceholder( sErrorMessage, "$2$", _rParamValue2 ); + lcl_substitutePlaceholder( sErrorMessage, "$3$", _rParamValue3 ); + + return sErrorMessage; + } + + + ErrorCode SQLError_Impl::getErrorCode( const ErrorCondition _eCondition ) + { + return 0 - ::sal::static_int_cast< ErrorCode, ErrorCondition >( _eCondition ); + } + + + void SQLError_Impl::raiseException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) + { + raiseTypedException( + _eCondition, + _rxContext, + ::cppu::UnoType< SQLException >::get(), + _rParamValue1, + _rParamValue2, + _rParamValue3 + ); + } + + + void SQLError_Impl::raiseException( const ErrorCondition _eCondition, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) + { + raiseTypedException( + _eCondition, + nullptr, + ::cppu::UnoType< SQLException >::get(), + _rParamValue1, + _rParamValue2, + _rParamValue3 + ); + } + + void SQLError_Impl::raiseTypedException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, + const Type& _rExceptionType, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) + { + if ( !::cppu::UnoType< SQLException >::get().isAssignableFrom( _rExceptionType ) ) + throw std::bad_cast(); + + // default-construct an exception of the desired type + Any aException( nullptr, _rExceptionType ); + + // fill it + SQLException* pException = static_cast< SQLException* >( aException.pData ); + *pException = impl_buildSQLException( _eCondition, _rxContext, _rParamValue1, _rParamValue2, _rParamValue3 ); + + // throw it + ::cppu::throwException( aException ); + } + + SQLException SQLError_Impl::getSQLException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, + const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) + { + return impl_buildSQLException( _eCondition, _rxContext, _rParamValue1, _rParamValue2, _rParamValue3 ); + } + + SQLException SQLError_Impl::impl_buildSQLException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, + const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) + { + return SQLException( + getErrorMessage( _eCondition, _rParamValue1, _rParamValue2, _rParamValue3 ), + _rxContext, + impl_getSQLState( _eCondition ), + getErrorCode( _eCondition ), + Any() + ); + } + + OUString SQLError_Impl::impl_getErrorMessage( ErrorCondition _eCondition ) const + { + OUString sResMessage(Translate::get(lcl_getResourceErrorID(_eCondition), m_aResources)); + OSL_ENSURE( !sResMessage.isEmpty(), "SQLError_Impl::impl_getErrorMessage: illegal error condition, or invalid resource!" ); + return getMessagePrefix() + " " + sResMessage; + } + + OUString SQLError_Impl::impl_getSQLState( ErrorCondition _eCondition ) + { + static constexpr OUStringLiteral DEFAULT_STATE = u"S1000"; + OUString sState = lcl_getResourceState(_eCondition); + if (sState.isEmpty()) + sState = DEFAULT_STATE; + return sState; + } + + SQLError::SQLError() + :m_pImpl( std::make_shared<SQLError_Impl>() ) + { + } + + + SQLError::~SQLError() + { + } + + + const OUString& SQLError::getMessagePrefix() + { + return SQLError_Impl::getMessagePrefix(); + } + + + OUString SQLError::getErrorMessage( const ErrorCondition _eCondition ) const + { + return m_pImpl->getErrorMessage( _eCondition, std::optional<OUString>(), std::optional<OUString>(), std::optional<OUString>() ); + } + + + ErrorCode SQLError::getErrorCode( const ErrorCondition _eCondition ) + { + return SQLError_Impl::getErrorCode( _eCondition ); + } + + + void SQLError::raiseException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) const + { + m_pImpl->raiseException( _eCondition, _rxContext, _rParamValue1, _rParamValue2, _rParamValue3 ); + } + + + void SQLError::raiseException( const ErrorCondition _eCondition ) const + { + m_pImpl->raiseException( _eCondition, std::optional<OUString>(), std::optional<OUString>(), std::optional<OUString>() ); + } + + + void SQLError::raiseTypedException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, + const Type& _rExceptionType ) const + { + m_pImpl->raiseTypedException( _eCondition, _rxContext, _rExceptionType, std::optional<OUString>(), std::optional<OUString>(), std::optional<OUString>() ); + } + + + SQLException SQLError::getSQLException( const ErrorCondition _eCondition, const Reference< XInterface >& _rxContext, + const std::optional<OUString>& _rParamValue1, const std::optional<OUString>& _rParamValue2, const std::optional<OUString>& _rParamValue3 ) const + { + return m_pImpl->getSQLException( _eCondition, _rxContext, _rParamValue1, _rParamValue2, _rParamValue3 ); + } + + +} // namespace connectivity + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/statementcomposer.cxx b/connectivity/source/commontools/statementcomposer.cxx new file mode 100644 index 0000000000..8ebbd9ca86 --- /dev/null +++ b/connectivity/source/commontools/statementcomposer.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 <connectivity/statementcomposer.hxx> + +#include <connectivity/dbtools.hxx> + +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> + +#include <unotools/sharedunocomponent.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/property.hxx> + + +namespace dbtools +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::sdb::XSingleSelectQueryComposer; + using ::com::sun::star::lang::NullPointerException; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::lang::XComponent; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::sdb::XQueriesSupplier; + using ::com::sun::star::container::XNameAccess; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::lang::XMultiServiceFactory; + using ::com::sun::star::sdbc::SQLException; + + namespace CommandType = ::com::sun::star::sdb::CommandType; + + struct StatementComposer_Data + { + const Reference< XConnection > xConnection; + Reference< XSingleSelectQueryComposer > xComposer; + OUString sCommand; + OUString sFilter; + OUString sHavingClause; + OUString sOrder; + sal_Int32 nCommandType; + bool bEscapeProcessing; + bool bComposerDirty; + bool bDisposeComposer; + + explicit StatementComposer_Data( const Reference< XConnection >& _rxConnection ) + :xConnection( _rxConnection ) + ,nCommandType( CommandType::COMMAND ) + ,bEscapeProcessing( true ) + ,bComposerDirty( true ) + ,bDisposeComposer( true ) + { + if ( !_rxConnection.is() ) + throw NullPointerException(); + } + }; + + + namespace + { + + void lcl_resetComposer( StatementComposer_Data& _rData ) + { + if ( _rData.bDisposeComposer && _rData.xComposer.is() ) + { + try + { + Reference< XComponent > xComposerComponent( _rData.xComposer, UNO_QUERY_THROW ); + xComposerComponent->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + } + _rData.xComposer.clear(); + } + + + bool lcl_ensureUpToDateComposer_nothrow( StatementComposer_Data& _rData ) + { + if ( !_rData.bComposerDirty ) + return _rData.xComposer.is(); + lcl_resetComposer( _rData ); + + try + { + OUString sStatement; + switch ( _rData.nCommandType ) + { + case CommandType::COMMAND: + if ( _rData.bEscapeProcessing ) + sStatement = _rData.sCommand; + // (in case of no escape processing we assume a not parseable statement) + break; + + case CommandType::TABLE: + { + if ( _rData.sCommand.isEmpty() ) + break; + + sStatement = "SELECT * FROM "; + + OUString sCatalog, sSchema, sTable; + qualifiedNameComponents( _rData.xConnection->getMetaData(), _rData.sCommand, sCatalog, sSchema, sTable, EComposeRule::InDataManipulation ); + + sStatement += composeTableNameForSelect( _rData.xConnection, sCatalog, sSchema, sTable ); + } + break; + + case CommandType::QUERY: + { + // ask the connection for the query + Reference< XQueriesSupplier > xSupplyQueries( _rData.xConnection, UNO_QUERY_THROW ); + Reference< XNameAccess > xQueries( xSupplyQueries->getQueries(), css::uno::UNO_SET_THROW ); + + if ( !xQueries->hasByName( _rData.sCommand ) ) + break; + + Reference< XPropertySet > xQuery( xQueries->getByName( _rData.sCommand ), UNO_QUERY_THROW ); + + // a native query ? + bool bQueryEscapeProcessing = false; + xQuery->getPropertyValue("EscapeProcessing") >>= bQueryEscapeProcessing; + if ( !bQueryEscapeProcessing ) + break; + + // the command used by the query + xQuery->getPropertyValue("Command") >>= sStatement; + if ( sStatement.isEmpty() ) + break; + + // use a composer to build a statement from the query filter/order props + Reference< XMultiServiceFactory > xFactory( _rData.xConnection, UNO_QUERY_THROW ); + ::utl::SharedUNOComponent< XSingleSelectQueryComposer > xComposer; + xComposer.set( + xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), + UNO_QUERY_THROW + ); + + // the "basic" statement + xComposer->setElementaryQuery( sStatement ); + + // the sort order + static constexpr OUString sPropOrder( u"Order"_ustr ); + if ( ::comphelper::hasProperty( sPropOrder, xQuery ) ) + { + OUString sOrder; + OSL_VERIFY( xQuery->getPropertyValue( sPropOrder ) >>= sOrder ); + xComposer->setOrder( sOrder ); + } + + // the filter + bool bApplyFilter = true; + static constexpr OUString sPropApply( u"ApplyFilter"_ustr ); + if ( ::comphelper::hasProperty( sPropApply, xQuery ) ) + { + OSL_VERIFY( xQuery->getPropertyValue( sPropApply ) >>= bApplyFilter ); + } + + if ( bApplyFilter ) + { + OUString sFilter; + OSL_VERIFY( xQuery->getPropertyValue("Filter") >>= sFilter ); + xComposer->setFilter( sFilter ); + OSL_VERIFY( xQuery->getPropertyValue("HavingClause") >>= sFilter ); + xComposer->setHavingClause( sFilter ); + } + + // the composed statement + sStatement = xComposer->getQuery(); + } + break; + + default: + OSL_FAIL("lcl_ensureUpToDateComposer_nothrow: no table, no query, no statement - what else ?!"); + break; + } + + if ( !sStatement.isEmpty() ) + { + // create a composer + Reference< XMultiServiceFactory > xFactory( _rData.xConnection, UNO_QUERY_THROW ); + Reference< XSingleSelectQueryComposer > xComposer( xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), + UNO_QUERY_THROW ); + xComposer->setElementaryQuery( sStatement ); + + // append sort/filter + xComposer->setOrder( _rData.sOrder ); + xComposer->setFilter( _rData.sFilter ); + xComposer->setHavingClause( _rData.sHavingClause ); + + sStatement = xComposer->getQuery(); + + _rData.xComposer = xComposer; + _rData.bComposerDirty = false; + } + } + catch( const SQLException& ) + { + // allowed to leave here + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.commontools"); + } + + return _rData.xComposer.is(); + } + } + + StatementComposer::StatementComposer( const Reference< XConnection >& _rxConnection, + const OUString& _rCommand, const sal_Int32 _nCommandType, const bool _bEscapeProcessing ) + :m_pData( new StatementComposer_Data( _rxConnection ) ) + { + OSL_PRECOND( _rxConnection.is(), "StatementComposer::StatementComposer: illegal connection!" ); + m_pData->sCommand = _rCommand; + m_pData->nCommandType = _nCommandType; + m_pData->bEscapeProcessing = _bEscapeProcessing; + } + + + StatementComposer::~StatementComposer() + { + lcl_resetComposer( *m_pData ); + } + + + void StatementComposer::setDisposeComposer( bool _bDoDispose ) + { + m_pData->bDisposeComposer = _bDoDispose; + } + + + void StatementComposer::setFilter( const OUString& _rFilter ) + { + m_pData->sFilter = _rFilter; + m_pData->bComposerDirty = true; + } + + + void StatementComposer::setHavingClause( const OUString& _rHavingClause ) + { + m_pData->sHavingClause = _rHavingClause; + m_pData->bComposerDirty = true; + } + + + void StatementComposer::setOrder( const OUString& _rOrder ) + { + m_pData->sOrder = _rOrder; + m_pData->bComposerDirty = true; + } + + + Reference< XSingleSelectQueryComposer > const & StatementComposer::getComposer() + { + lcl_ensureUpToDateComposer_nothrow( *m_pData ); + return m_pData->xComposer; + } + + + OUString StatementComposer::getQuery() + { + if ( lcl_ensureUpToDateComposer_nothrow( *m_pData ) ) + { + return m_pData->xComposer->getQuery(); + } + + return OUString(); + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/commontools/warningscontainer.cxx b/connectivity/source/commontools/warningscontainer.cxx new file mode 100644 index 0000000000..ba78256125 --- /dev/null +++ b/connectivity/source/commontools/warningscontainer.cxx @@ -0,0 +1,110 @@ +/* -*- 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 <connectivity/warningscontainer.hxx> +#include <connectivity/dbexception.hxx> + +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> + +#include <o3tl/any.hxx> +#include <osl/diagnose.h> + + +namespace dbtools +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdb; + + static void lcl_concatWarnings( Any& _rChainLeft, const Any& _rChainRight ) + { + if ( !_rChainLeft.hasValue() ) + _rChainLeft = _rChainRight; + else + { + // to travel the chain by reference (and not by value), we need the getValue... + // looks like a hack, but the meaning of getValue is documented, and it's the only chance for reference-traveling... + + OSL_ENSURE( SQLExceptionInfo( _rChainLeft ).isValid(), + "lcl_concatWarnings: invalid warnings chain (this will crash)!" ); + + const SQLException* pChainTravel = o3tl::doAccess<SQLException>( _rChainLeft ); + SQLExceptionIteratorHelper aReferenceIterHelper( *pChainTravel ); + while ( aReferenceIterHelper.hasMoreElements() ) + pChainTravel = aReferenceIterHelper.next(); + + // reached the end of the chain, and pChainTravel points to the last element + const_cast< SQLException* >( pChainTravel )->NextException = _rChainRight; + } + } + + + void WarningsContainer::appendWarning(const SQLException& _rWarning) + { + lcl_concatWarnings( m_aOwnWarnings, Any( _rWarning ) ); + } + + + void WarningsContainer::appendWarning( const SQLContext& _rContext ) + { + lcl_concatWarnings( m_aOwnWarnings, Any( _rContext )); + } + + + void WarningsContainer::appendWarning(const SQLWarning& _rWarning) + { + lcl_concatWarnings( m_aOwnWarnings, Any( _rWarning ) ); + } + + + Any WarningsContainer::getWarnings( ) const + { + Any aAllWarnings; + if ( m_xExternalWarnings.is() ) + aAllWarnings = m_xExternalWarnings->getWarnings(); + + if ( m_aOwnWarnings.hasValue() ) + lcl_concatWarnings( aAllWarnings, m_aOwnWarnings ); + + return aAllWarnings; + } + + + void WarningsContainer::clearWarnings( ) + { + if ( m_xExternalWarnings.is() ) + m_xExternalWarnings->clearWarnings(); + m_aOwnWarnings.clear(); + } + + + void WarningsContainer::appendWarning( const OUString& _rWarning, const char* _pAsciiSQLState, const Reference< XInterface >& _rxContext ) + { + appendWarning( SQLWarning( _rWarning, _rxContext, OUString::createFromAscii( _pAsciiSQLState ), 0, Any() ) ); + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |