diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /connectivity/source/drivers/file | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'connectivity/source/drivers/file')
19 files changed, 7628 insertions, 0 deletions
diff --git a/connectivity/source/drivers/file/FCatalog.cxx b/connectivity/source/drivers/file/FCatalog.cxx new file mode 100644 index 000000000..eedda26ef --- /dev/null +++ b/connectivity/source/drivers/file/FCatalog.cxx @@ -0,0 +1,103 @@ +/* -*- 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 <file/FCatalog.hxx> +#include <file/FConnection.hxx> +#include <file/FTables.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> + +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 connectivity::file; + +OFileCatalog::OFileCatalog(OConnection* _pCon) : connectivity::sdbcx::OCatalog(_pCon) + ,m_pConnection(_pCon) +{ +} + +void SAL_CALL OFileCatalog::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + typedef connectivity::sdbcx::OCatalog OFileCatalog_BASE; + m_xMetaData.clear(); + OFileCatalog_BASE::disposing(); +} + +OUString OFileCatalog::buildName(const Reference< XRow >& _xRow) +{ + return _xRow->getString(3); +} + +void OFileCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + Sequence< OUString > aTypes; + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(), + "%", "%", aTypes); + fillNames(xResult,aVector); + + if(m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset( new OTables(m_xMetaData,*this,m_aMutex,aVector) ); +} + + +Any SAL_CALL OFileCatalog::queryInterface( const Type & rType ) +{ + if( rType == cppu::UnoType<XGroupsSupplier>::get()|| + rType == cppu::UnoType<XUsersSupplier>::get()|| + rType == cppu::UnoType<XViewsSupplier>::get()) + return Any(); + + + typedef sdbcx::OCatalog OFileCatalog_BASE; + return OFileCatalog_BASE::queryInterface(rType); +} + +Sequence< Type > SAL_CALL OFileCatalog::getTypes( ) +{ + typedef sdbcx::OCatalog OFileCatalog_BASE; + + Sequence< Type > aTypes = OFileCatalog_BASE::getTypes(); + std::vector<Type> aOwnTypes; + aOwnTypes.reserve(aTypes.getLength()); + const Type* pBegin = aTypes.getConstArray(); + const Type* pEnd = pBegin + aTypes.getLength(); + for(;pBegin != pEnd;++pBegin) + { + if(!(*pBegin == cppu::UnoType<XGroupsSupplier>::get()|| + *pBegin == cppu::UnoType<XUsersSupplier>::get()|| + *pBegin == cppu::UnoType<XViewsSupplier>::get())) + { + aOwnTypes.push_back(*pBegin); + } + } + return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FColumns.cxx b/connectivity/source/drivers/file/FColumns.cxx new file mode 100644 index 000000000..1b90385bf --- /dev/null +++ b/connectivity/source/drivers/file/FColumns.cxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <file/FColumns.hxx> +#include <connectivity/sdbcx/VColumn.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <file/FTable.hxx> + +using namespace connectivity::file; +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; + +sdbcx::ObjectType OColumns::createObject(const OUString& _rName) +{ + const OUString sCatalogName; + const OUString sSchemaName(m_pTable->getSchema()); + const OUString sTableName(m_pTable->getName()); + Reference< XResultSet > xResult = m_pTable->getConnection()->getMetaData()->getColumns(Any(), + sSchemaName, sTableName, _rName); + + sdbcx::ObjectType xRet; + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while(xResult->next()) + { + if(xRow->getString(4) == _rName) + { + xRet = new sdbcx::OColumn(_rName, + xRow->getString(6), + xRow->getString(13), + xRow->getString(12), + xRow->getInt(11), + xRow->getInt(7), + xRow->getInt(9), + xRow->getInt(5), + false, + false, + false, + m_pTable->getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(), + sCatalogName, + sSchemaName, + sTableName); + break; + } + } + } + + return xRet; +} + +void OColumns::impl_refresh() +{ + m_pTable->refreshColumns(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FConnection.cxx b/connectivity/source/drivers/file/FConnection.cxx new file mode 100644 index 000000000..90030abb5 --- /dev/null +++ b/connectivity/source/drivers/file/FConnection.cxx @@ -0,0 +1,432 @@ +/* -*- 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 <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <file/FConnection.hxx> +#include <file/FDatabaseMetaData.hxx> +#include <file/FDriver.hxx> +#include <file/FStatement.hxx> +#include <file/FPreparedStatement.hxx> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/ucb/XContent.hpp> +#include <com/sun/star/ucb/XContentIdentifier.hpp> +#include <tools/urlobj.hxx> +#include <file/FCatalog.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/pathoptions.hxx> +#include <ucbhelper/content.hxx> +#include <connectivity/dbcharset.hxx> +#include <connectivity/dbexception.hxx> +#include <o3tl/any.hxx> +#include <osl/thread.h> +#include <strings.hrc> + +using namespace connectivity::file; +using namespace dbtools; + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::sdbcx; +using namespace com::sun::star::container; +using namespace com::sun::star::ucb; +using namespace ::ucbhelper; +typedef connectivity::OMetaConnection OConnection_BASE; + +OConnection::OConnection(OFileDriver* _pDriver) + : m_pDriver(_pDriver) + , m_bAutoCommit(false) + , m_bReadOnly(false) + , m_bShowDeleted(false) + , m_bCaseSensitiveExtension( true ) + , m_bCheckSQL92(false) + , m_bDefaultTextEncoding(false) +{ + m_nTextEncoding = RTL_TEXTENCODING_DONTKNOW; +} + +OConnection::~OConnection() +{ + if(!isClosed( )) + close(); +} + +bool OConnection::matchesExtension( const OUString& _rExt ) const +{ + if ( isCaseSensitiveExtension() ) + return ( getExtension() == _rExt ); + + OUString sMyExtension( getExtension().toAsciiLowerCase() ); + OUString sExt( _rExt.toAsciiLowerCase() ); + + return sMyExtension == sExt; +} + + +void OConnection::construct(const OUString& url,const Sequence< PropertyValue >& info) +{ + osl_atomic_increment( &m_refCount ); + + OUString aExt; + const PropertyValue *pIter = info.getConstArray(); + const PropertyValue *pEnd = pIter + info.getLength(); + for(;pIter != pEnd;++pIter) + { + if( pIter->Name == "Extension" ) + OSL_VERIFY( pIter->Value >>= aExt ); + else if( pIter->Name == "CharSet" ) + { + if (auto const numeric = o3tl::tryAccess<sal_uInt16>(pIter->Value)) + { + m_nTextEncoding = *numeric; + } + else + { + OUString sIanaName; + OSL_VERIFY( pIter->Value >>= sIanaName ); + + ::dbtools::OCharsetMap aLookupIanaName; + ::dbtools::OCharsetMap::const_iterator aLookup = aLookupIanaName.findIanaName(sIanaName); + if (aLookup != aLookupIanaName.end()) + m_nTextEncoding = (*aLookup).getEncoding(); + else + m_nTextEncoding = RTL_TEXTENCODING_DONTKNOW; + } + } + else if( pIter->Name == "ShowDeleted" ) + { + OSL_VERIFY( pIter->Value >>= m_bShowDeleted ); + } + else if( pIter->Name == "EnableSQL92Check" ) + { + pIter->Value >>= m_bCheckSQL92; + } + } // for(;pIter != pEnd;++pIter) + + { + sal_Int32 nLen = url.indexOf(':'); + nLen = url.indexOf(':',nLen+1); + OUString aDSN(url.copy(nLen+1)); + + OUString aFileName = aDSN; + INetURLObject aURL; + aURL.SetSmartProtocol(INetProtocol::File); + if (!utl::ConfigManager::IsFuzzing()) + { + SvtPathOptions aPathOptions; + aFileName = aPathOptions.SubstituteVariable(aFileName); + } + + aURL.SetSmartURL(aFileName); + + setURL(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + } + + if ( m_nTextEncoding == RTL_TEXTENCODING_DONTKNOW ) + { + //m_nTextEncoding = osl_getTextEncodingFromLocale(NULL); + m_nTextEncoding = osl_getThreadTextEncoding(); + m_bDefaultTextEncoding = true; + } + + if ( !aExt.isEmpty() ) + m_aFilenameExtension = aExt; + + try + { + ::ucbhelper::Content aFile; + try + { + aFile = ::ucbhelper::Content(getURL(), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext()); + } + catch(ContentCreationException& e) + { + throwUrlNotValid(getURL(),e.Message); + } + + // set fields to fetch + Sequence< OUString > aProps { "Title" }; + + try + { + if (aFile.isFolder()) + { + m_xDir = aFile.createDynamicCursor(aProps, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ); + m_xContent = aFile.get(); + } + else if (aFile.isDocument()) + { + Reference<XContent> xParent(Reference<XChild>(aFile.get(),UNO_QUERY_THROW)->getParent(),UNO_QUERY_THROW); + Reference<XContentIdentifier> xIdent = xParent->getIdentifier(); + m_xContent = xParent; + + ::ucbhelper::Content aParent(xIdent->getContentIdentifier(), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext()); + m_xDir = aParent.createDynamicCursor(aProps, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ); + } + else + { + OSL_FAIL("OConnection::construct: ::ucbhelper::Content is neither a folder nor a document! How's that?!"); + throw SQLException(); + } + } + catch(Exception& e) // an exception is thrown when no file exists + { + throwUrlNotValid(getURL(),e.Message); + } + if(!m_xDir.is() || !m_xContent.is()) + throwUrlNotValid(getURL(),OUString()); + + if (m_aFilenameExtension.indexOf('*') >= 0 || m_aFilenameExtension.indexOf('?') >= 0) + throw SQLException(); + } + catch(const Exception&) + { + osl_atomic_decrement( &m_refCount ); + throw; + } + + osl_atomic_decrement( &m_refCount ); +} +// XServiceInfo + +IMPLEMENT_SERVICE_INFO(OConnection, "com.sun.star.sdbc.drivers.file.Connection", "com.sun.star.sdbc.Connection") + + +Reference< XStatement > SAL_CALL OConnection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + Reference< XStatement > xReturn = new OStatement(this); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL OConnection::prepareStatement( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + rtl::Reference<OPreparedStatement> pStmt = new OPreparedStatement(this); + pStmt->construct(sql); + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return pStmt; +} + +Reference< XPreparedStatement > SAL_CALL OConnection::prepareCall( const OUString& /*sql*/ ) +{ + throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this ); + return nullptr; +} + +OUString SAL_CALL OConnection::nativeSQL( const OUString& sql ) +{ + return sql; +} + +void SAL_CALL OConnection::setAutoCommit( sal_Bool autoCommit ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + m_bAutoCommit = autoCommit; +} + +sal_Bool SAL_CALL OConnection::getAutoCommit( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + return m_bAutoCommit; +} + +void SAL_CALL OConnection::commit( ) +{ +} + +void SAL_CALL OConnection::rollback( ) +{ +} + +sal_Bool SAL_CALL OConnection::isClosed( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return OConnection_BASE::rBHelper.bDisposed; +} + +Reference< XDatabaseMetaData > SAL_CALL OConnection::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + xMetaData = new ODatabaseMetaData(this); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +void SAL_CALL OConnection::setReadOnly( sal_Bool readOnly ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + m_bReadOnly = readOnly; +} + +sal_Bool SAL_CALL OConnection::isReadOnly( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + return m_bReadOnly; +} + +void SAL_CALL OConnection::setCatalog( const OUString& /*catalog*/ ) +{ + throwFeatureNotImplementedSQLException( "XConnection::setCatalog", *this ); +} + +OUString SAL_CALL OConnection::getCatalog( ) +{ + return OUString(); +} + +void SAL_CALL OConnection::setTransactionIsolation( sal_Int32 /*level*/ ) +{ + throwFeatureNotImplementedSQLException( "XConnection::setTransactionIsolation", *this ); +} + +sal_Int32 SAL_CALL OConnection::getTransactionIsolation( ) +{ + return 0; +} + +Reference< XNameAccess > SAL_CALL OConnection::getTypeMap( ) +{ + return nullptr; +} + +void SAL_CALL OConnection::setTypeMap( const Reference< XNameAccess >& /*typeMap*/ ) +{ +} + +// XCloseable +void SAL_CALL OConnection::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + } + dispose(); +} + +// XWarningsSupplier +Any SAL_CALL OConnection::getWarnings( ) +{ + return Any(); +} + +void SAL_CALL OConnection::clearWarnings( ) +{ +} + +void OConnection::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + OConnection_BASE::disposing(); + + m_xDir.clear(); + m_xContent.clear(); + m_xCatalog = WeakReference< XTablesSupplier>(); +} + +Reference< XTablesSupplier > OConnection::createCatalog() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XTablesSupplier > xTab = m_xCatalog; + if(!xTab.is()) + { + xTab = new OFileCatalog(this); + m_xCatalog = xTab; + } + return xTab; +} + +Reference< XDynamicResultSet > OConnection::getDir() const +{ + Reference<XDynamicResultSet> xContent; + Sequence< OUString > aProps { "Title" }; + try + { + Reference<XContentIdentifier> xIdent = getContent()->getIdentifier(); + ::ucbhelper::Content aParent(xIdent->getContentIdentifier(), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext()); + xContent = aParent.createDynamicCursor(aProps, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ); + } + catch(Exception&) + { + } + return xContent; +} + +sal_Int64 SAL_CALL OConnection::getSomething( const Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +const Sequence< sal_Int8 > & OConnection::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +void OConnection::throwUrlNotValid(const OUString & _rsUrl,const OUString & _rsMessage) +{ + SQLException aError; + aError.Message = getResources().getResourceStringWithSubstitution( + STR_NO_VALID_FILE_URL, + "$URL$", _rsUrl + ); + + aError.SQLState = "S1000"; + aError.ErrorCode = 0; + aError.Context = static_cast< XConnection* >(this); + if (!_rsMessage.isEmpty()) + aError.NextException <<= SQLException(_rsMessage, aError.Context, OUString(), 0, Any()); + + throw aError; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FDatabaseMetaData.cxx b/connectivity/source/drivers/file/FDatabaseMetaData.cxx new file mode 100644 index 000000000..6d0a4f2fe --- /dev/null +++ b/connectivity/source/drivers/file/FDatabaseMetaData.cxx @@ -0,0 +1,1055 @@ +/* -*- 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 <file/FDatabaseMetaData.hxx> +#include <FDatabaseMetaDataResultSet.hxx> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/ucb/UniversalContentBroker.hpp> +#include <com/sun/star/ucb/SortedDynamicResultSetFactory.hpp> +#include <tools/urlobj.hxx> +#include <sal/log.hxx> +#include <file/FDriver.hxx> +#include <file/FTable.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <tools/diagnose_ex.h> +#include <ucbhelper/content.hxx> + +using namespace com::sun::star::ucb; +using namespace connectivity::file; +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::sdbcx; +using namespace com::sun::star::container; + +ODatabaseMetaData::ODatabaseMetaData(OConnection* _pCon) : ::connectivity::ODatabaseMetaDataBase(_pCon,_pCon->getConnectionInfo()) + ,m_pConnection(_pCon) +{ +} + +ODatabaseMetaData::~ODatabaseMetaData() +{ +} + +Reference< XResultSet > ODatabaseMetaData::impl_getTypeInfo_throw( ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTypeInfo ); +} + +OUString ODatabaseMetaData::impl_getCatalogSeparator_throw( ) +{ + return OUString(); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumns( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& /*tableNamePattern*/, + const OUString& /*columnNamePattern*/ ) +{ + SAL_WARN("connectivity.drivers", "ODatabaseMetaData::getColumns() should be overridden!"); + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eColumns ); +} + + +namespace +{ +#if !ENABLE_FUZZERS + sal_Int16 isCaseSensitiveParentFolder( const OUString& _rFolderOrDoc, std::u16string_view _rDocName ) + { + sal_Int16 nIsCS = 1; + try + { + // first get the real content for the URL + INetURLObject aContentURL( _rFolderOrDoc ); + ::ucbhelper::Content aContent1; + { + ::ucbhelper::Content aFolderOrDoc( _rFolderOrDoc, Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + if ( aFolderOrDoc.isDocument() ) + aContent1 = aFolderOrDoc; + else + { + aContentURL = INetURLObject( _rFolderOrDoc, INetURLObject::EncodeMechanism::WasEncoded ); + aContentURL.Append( _rDocName ); + aContent1 = ::ucbhelper::Content( aContentURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + } + } + + // get two extensions which differ by case only + OUString sExtension1(aContentURL.getExtension()); + OUString sExtension2(sExtension1.toAsciiLowerCase()); + if (sExtension2 == sExtension1) + { + // the extension was already in lower case + sExtension2 = sExtension2.toAsciiUpperCase(); + } + + // the complete URL for the second extension + INetURLObject aURL2( aContentURL ); + if (!sExtension2.isEmpty()) + aURL2.SetExtension( sExtension2 ); + if ( aURL2.GetMainURL(INetURLObject::DecodeMechanism::NONE) == aContentURL.GetMainURL(INetURLObject::DecodeMechanism::NONE) ) + return -1; + + // the second context + bool bCanAccess = false; + ::ucbhelper::Content aContent2; + try + { + aContent2 = ::ucbhelper::Content( aURL2.GetMainURL( INetURLObject::DecodeMechanism::NONE ), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + bCanAccess = aContent2.isDocument(); + } + catch( const Exception& ) + { + } + + if ( bCanAccess ) + { + // here we have two contents whose URLs differ by case only. + // Now let's check if both really refer to the same object... + Reference< XContent > xContent1 = aContent1.get(); + Reference< XContent > xContent2 = aContent2.get(); + OSL_ENSURE( xContent1.is() && xContent2.is(), "isCaseSensitiveParentFolder: invalid content interfaces!" ); + if ( xContent1.is() && xContent2.is() ) + { + Reference< XContentIdentifier > xID1 = xContent1->getIdentifier(); + Reference< XContentIdentifier > xID2 = xContent2->getIdentifier(); + OSL_ENSURE( xID1.is() && xID2.is(), "isCaseSensitiveParentFolder: invalid ID interfaces!" ); + if ( xID1.is() && xID2.is() + && ( UniversalContentBroker::create( + comphelper::getProcessComponentContext() )-> + compareContentIds( xID1, xID2 ) == 0 ) ) + { + // finally, we know that the folder is not case-sensitive... + nIsCS = 0; + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.drivers", "isCaseSensitiveParentFolder" ); + } + + return nIsCS; + } +#endif +} + + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTables( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& tableNamePattern, const Sequence< OUString >& types ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + rtl::Reference<ODatabaseMetaDataResultSet> pResult = new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTables ); + + // check if any type is given + // when no types are given then we have to return all tables e.g. TABLE + + static constexpr OUStringLiteral aTable = u"TABLE"; + + bool bTableFound = true; + sal_Int32 nLength = types.getLength(); + if(nLength) + { + bTableFound = false; + + const OUString* pBegin = types.getConstArray(); + const OUString* pEnd = pBegin + nLength; + for(;pBegin != pEnd;++pBegin) + { + if(*pBegin == aTable) + { + bTableFound = true; + break; + } + } + } + if(!bTableFound) + return pResult; + + Reference<XDynamicResultSet> xContent = m_pConnection->getDir(); + Reference < XSortedDynamicResultSetFactory > xSRSFac = + SortedDynamicResultSetFactory::create( m_pConnection->getDriver()->getComponentContext() ); + + Sequence< NumberedSortingInfo > aSortInfo( 1 ); + NumberedSortingInfo* pInfo = aSortInfo.getArray(); + pInfo[ 0 ].ColumnIndex = 1; + pInfo[ 0 ].Ascending = true; + + Reference < XAnyCompareFactory > xFactory; + Reference< XDynamicResultSet > xDynamicResultSet = xSRSFac->createSortedDynamicResultSet( xContent, aSortInfo, xFactory ); + Reference<XResultSet> xResultSet = xDynamicResultSet->getStaticResultSet(); + + Reference<XRow> xRow(xResultSet,UNO_QUERY); + + OUString aFilenameExtension = m_pConnection->getExtension(); + OUString sThisContentExtension; + ODatabaseMetaDataResultSet::ORows aRows; + // scan the directory for tables + OUString aName; + INetURLObject aURL; + xResultSet->beforeFirst(); + + bool bKnowCaseSensitivity = false; + bool bCaseSensitiveDir = true; + bool bCheckEnabled = m_pConnection->isCheckEnabled(); + + while(xResultSet->next()) + { + aName = xRow->getString(1); + aURL.SetSmartProtocol(INetProtocol::File); + OUString sUrl = m_pConnection->getURL() + "/" + aName; + aURL.SetSmartURL( sUrl ); + sThisContentExtension = aURL.getExtension(); + + ODatabaseMetaDataResultSet::ORow aRow { nullptr, nullptr, nullptr }; + aRow.reserve(6); + bool bNewRow = false; + + if ( !bKnowCaseSensitivity ) + { + bKnowCaseSensitivity = true; +#if ENABLE_FUZZERS + sal_Int16 nCase = 1; +#else + sal_Int16 nCase = isCaseSensitiveParentFolder( m_pConnection->getURL(), aURL.getName() ); +#endif + switch( nCase ) + { + case 1: + bCaseSensitiveDir = true; + break; + case -1: + bKnowCaseSensitivity = false; + [[fallthrough]]; + case 0: + bCaseSensitiveDir = false; + } + if ( bKnowCaseSensitivity ) + { + m_pConnection->setCaseSensitiveExtension( bCaseSensitiveDir, OConnection::GrantAccess() ); + if ( !bCaseSensitiveDir ) + { + aFilenameExtension = aFilenameExtension.toAsciiLowerCase(); + } + } + } + + if (!aFilenameExtension.isEmpty()) + { + if ( !bCaseSensitiveDir ) + { + sThisContentExtension = sThisContentExtension.toAsciiLowerCase(); + } + + if ( sThisContentExtension == aFilenameExtension ) + { + aName = aName.copy(0, (aName.getLength()-(aFilenameExtension.getLength()+1))); + sal_Unicode nChar = aName.toChar(); + if ( match(tableNamePattern,aName,'\0') && ( !bCheckEnabled || (nChar < '0' || nChar > '9')) ) + { + aRow.push_back(new ORowSetValueDecorator(aName)); + bNewRow = true; + } + } + } + else // no extension, filter myself + { + for (;;) + { + if (aURL.getExtension().isEmpty()) + { + sal_Unicode nChar = aURL.getBase()[0]; + if( match(tableNamePattern,aURL.getBase(),'\0') && ( !bCheckEnabled || nChar < '0' || nChar > '9' ) ) + { + aRow.push_back(new ORowSetValueDecorator(aURL.getBase())); + bNewRow = true; + } + break; + } + if ( !xResultSet->next() ) + { + break; + } + aName = xRow->getString(1); + aURL.SetSmartURL(aName); + } + } + if(bNewRow) + { + aRow.push_back(new ORowSetValueDecorator(OUString(aTable))); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + + aRows.push_back(aRow); + } + } + + pResult->setRows(std::move(aRows)); + + return pResult; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxRowSize( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCatalogNameLength( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCharLiteralLength( ) +{ + return SAL_MAX_INT32; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnNameLength( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInIndex( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCursorNameLength( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxConnections( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInTable( ) +{ + return 0; +} + +sal_Int32 ODatabaseMetaData::impl_getMaxStatements_throw( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTableNameLength( ) +{ + return 0; +} + +sal_Int32 ODatabaseMetaData::impl_getMaxTablesInSelect_throw( ) +{ + return 1; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTablePrivileges( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& tableNamePattern ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + rtl::Reference<ODatabaseMetaDataResultSet> pResult = new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTablePrivileges ); + ODatabaseMetaDataResultSet::ORows aRows; + + Reference< XTablesSupplier > xTabSup = m_pConnection->createCatalog(); + if( xTabSup.is()) + { + Reference< XNameAccess> xNames = xTabSup->getTables(); + Sequence< OUString > aNames = xNames->getElementNames(); + const OUString* pBegin = aNames.getConstArray(); + const OUString* pEnd = pBegin + aNames.getLength(); + for(;pBegin != pEnd;++pBegin) + { + if(match(tableNamePattern,*pBegin,'\0')) + { + ODatabaseMetaDataResultSet::ORow aRow(8); + + aRow[2] = new ORowSetValueDecorator(*pBegin); + aRow[6] = ODatabaseMetaDataResultSet::getSelectValue(); + aRow[7] = new ORowSetValueDecorator(OUString("NO")); + aRows.push_back(aRow); + + Reference< XPropertySet> xTable( + xNames->getByName(*pBegin), css::uno::UNO_QUERY); + if(xTable.is()) + { + auto pTable = comphelper::getFromUnoTunnel<OFileTable>(xTable); + if(pTable && !pTable->isReadOnly()) + { + aRow[6] = ODatabaseMetaDataResultSet::getInsertValue(); + aRows.push_back(aRow); + if(!m_pConnection->showDeleted()) + { + 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); + } + } + } + } + } + + pResult->setRows(std::move(aRows)); + return pResult; +} + +sal_Bool SAL_CALL ODatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseQuotedIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseIdentifiers( ) +{ + return false; +} + +bool ODatabaseMetaData::impl_storesMixedCaseQuotedIdentifiers_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseQuotedIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseIdentifiers( ) +{ + return false; +} + +bool ODatabaseMetaData::impl_supportsAlterTableWithAddColumn_throw( ) +{ + return false; +} + +bool ODatabaseMetaData::impl_supportsAlterTableWithDropColumn_throw( ) +{ + return false; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxIndexLength( ) +{ + return 0; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsNonNullableColumns( ) +{ + return false; +} + +OUString SAL_CALL ODatabaseMetaData::getCatalogTerm( ) +{ + return OUString(); +} + +OUString ODatabaseMetaData::impl_getIdentifierQuoteString_throw( ) +{ + return "\""; +} + +OUString SAL_CALL ODatabaseMetaData::getExtraNameCharacters( ) +{ + return OUString(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDifferentTableCorrelationNames( ) +{ + return true; +} + +bool ODatabaseMetaData::impl_isCatalogAtStart_throw( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionIgnoredInTransactions( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionCausesTransactionCommit( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDataManipulationTransactionsOnly( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDataDefinitionAndDataManipulationTransactions( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedDelete( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedUpdate( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossRollback( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossCommit( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossCommit( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossRollback( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactionIsolationLevel( sal_Int32 /*level*/ ) +{ + return false; +} + +bool ODatabaseMetaData::impl_supportsSchemasInDataManipulation_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92FullSQL( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92EntryLevelSQL( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsIntegrityEnhancementFacility( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInIndexDefinitions( ) +{ + return false; +} + +bool ODatabaseMetaData::impl_supportsSchemasInTableDefinitions_throw( ) +{ + return false; +} + +bool ODatabaseMetaData::impl_supportsCatalogsInTableDefinitions_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInIndexDefinitions( ) +{ + return false; +} + +bool ODatabaseMetaData::impl_supportsCatalogsInDataManipulation_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOuterJoins( ) +{ + return false; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTableTypes( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + rtl::Reference<ODatabaseMetaDataResultSet> pResult = new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTableTypes ); + static ODatabaseMetaDataResultSet::ORows aRows; + if(aRows.empty()) + { + aRows.push_back( { ODatabaseMetaDataResultSet::getEmptyValue(), new ORowSetValueDecorator(OUString("TABLE")) } ); + } + pResult->setRows(std::move(aRows)); + return pResult; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatementLength( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxProcedureNameLength( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxSchemaNameLength( ) +{ + return 0; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactions( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::allProceduresAreCallable( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsStoredProcedures( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSelectForUpdate( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::allTablesAreSelectable( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::isReadOnly( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFiles( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFilePerTable( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTypeConversion( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullPlusNonNullIsNull( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsColumnAliasing( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTableCorrelationNames( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsConvert( sal_Int32 /*fromType*/, sal_Int32 /*toType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExpressionsInOrderBy( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupBy( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByBeyondSelect( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByUnrelated( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleTransactions( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleResultSets( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLikeEscapeClause( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOrderByUnrelated( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnion( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnionAll( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseIdentifiers( ) +{ + return true; +} + +bool ODatabaseMetaData::impl_supportsMixedCaseQuotedIdentifiers_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtEnd( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtStart( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedHigh( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedLow( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInProcedureCalls( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInPrivilegeDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInProcedureCalls( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInPrivilegeDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCorrelatedSubqueries( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInComparisons( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInExists( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInIns( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInQuantifieds( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92IntermediateSQL( ) +{ + return false; +} + +OUString SAL_CALL ODatabaseMetaData::getURL( ) +{ + return "sdbc:file:"; +} + +OUString SAL_CALL ODatabaseMetaData::getUserName( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getDriverName( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getDriverVersion( ) +{ + return OUString::number(1); +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductVersion( ) +{ + return OUString::number(0); +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductName( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getProcedureTerm( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getSchemaTerm( ) +{ + return OUString(); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMajorVersion( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDefaultTransactionIsolation( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMinorVersion( ) +{ + return 0; +} + +OUString SAL_CALL ODatabaseMetaData::getSQLKeywords( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getSearchStringEscape( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getStringFunctions( ) +{ + return "UCASE,LCASE,ASCII,LENGTH,OCTET_LENGTH,CHAR_LENGTH,CHARACTER_LENGTH,CHAR,CONCAT,LOCATE,SUBSTRING,LTRIM,RTRIM,SPACE,REPLACE,REPEAT,INSERT,LEFT,RIGHT"; +} + +OUString SAL_CALL ODatabaseMetaData::getTimeDateFunctions( ) +{ + return "DAYOFWEEK,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,CURDATE,CURTIME,NOW"; +} + +OUString SAL_CALL ODatabaseMetaData::getSystemFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getNumericFunctions( ) +{ + return "ABS,SIGN,MOD,FLOOR,CEILING,ROUND,EXP,LN,LOG,LOG10,POWER,SQRT,PI,COS,SIN,TAN,ACOS,ASIN,ATAN,ATAN2,DEGREES,RADIANS"; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExtendedSQLGrammar( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCoreSQLGrammar( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMinimumSQLGrammar( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsFullOuterJoins( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLimitedOuterJoins( ) +{ + return false; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInGroupBy( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInSelect( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxUserNameLength( ) +{ + return 0; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetType( sal_Int32 setType ) +{ + switch(setType) + { + case ResultSetType::FORWARD_ONLY: + return true; + case ResultSetType::SCROLL_INSENSITIVE: + case ResultSetType::SCROLL_SENSITIVE: + break; + } + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 /*concurrency*/ ) +{ + switch(setType) + { + case ResultSetType::FORWARD_ONLY: + return true; + case ResultSetType::SCROLL_INSENSITIVE: + case ResultSetType::SCROLL_SENSITIVE: + break; + } + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownUpdatesAreVisible( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownDeletesAreVisible( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownInsertsAreVisible( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersUpdatesAreVisible( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersDeletesAreVisible( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersInsertsAreVisible( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::updatesAreDetected( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::deletesAreDetected( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::insertsAreDetected( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsBatchUpdates( ) +{ + return false; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getUDTs( const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& /*typeNamePattern*/, const Sequence< sal_Int32 >& /*types*/ ) +{ + return nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FDateFunctions.cxx b/connectivity/source/drivers/file/FDateFunctions.cxx new file mode 100644 index 000000000..1be992d4b --- /dev/null +++ b/connectivity/source/drivers/file/FDateFunctions.cxx @@ -0,0 +1,278 @@ +/* -*- 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 <file/FDateFunctions.hxx> +#include <tools/date.hxx> +#include <tools/time.hxx> +#include <tools/datetime.hxx> +#include <osl/diagnose.h> + +using namespace connectivity; +using namespace connectivity::file; + +ORowSetValue OOp_DayOfWeek::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + sal_Int32 nRet = 0; + css::util::Date aD = lhs.getDate(); + Date aDate(aD.Day, aD.Month, aD.Year); + DayOfWeek eDayOfWeek = aDate.GetDayOfWeek(); + switch (eDayOfWeek) + { + case MONDAY: + nRet = 2; + break; + case TUESDAY: + nRet = 3; + break; + case WEDNESDAY: + nRet = 4; + break; + case THURSDAY: + nRet = 5; + break; + case FRIDAY: + nRet = 6; + break; + case SATURDAY: + nRet = 7; + break; + case SUNDAY: + nRet = 1; + break; + default: + OSL_FAIL("Error in enum values for date"); + } + return nRet; +} + +ORowSetValue OOp_DayOfMonth::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + css::util::Date aD = lhs.getDate(); + return static_cast<sal_Int16>(aD.Day); +} + +ORowSetValue OOp_DayOfYear::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + css::util::Date aD = lhs.getDate(); + Date aDate(aD.Day, aD.Month, aD.Year); + return static_cast<sal_Int16>(aDate.GetDayOfYear()); +} + +ORowSetValue OOp_Month::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + css::util::Date aD = lhs.getDate(); + return static_cast<sal_Int16>(aD.Month); +} + +ORowSetValue OOp_DayName::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + OUString sRet; + css::util::Date aD = lhs.getDate(); + Date aDate(aD.Day, aD.Month, aD.Year); + DayOfWeek eDayOfWeek = aDate.GetDayOfWeek(); + switch (eDayOfWeek) + { + case MONDAY: + sRet = "Monday"; + break; + case TUESDAY: + sRet = "Tuesday"; + break; + case WEDNESDAY: + sRet = "Wednesday"; + break; + case THURSDAY: + sRet = "Thursday"; + break; + case FRIDAY: + sRet = "Friday"; + break; + case SATURDAY: + sRet = "Saturday"; + break; + case SUNDAY: + sRet = "Sunday"; + break; + default: + OSL_FAIL("Error in enum values for date"); + } + return sRet; +} + +ORowSetValue OOp_MonthName::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + OUString sRet; + css::util::Date aD = lhs.getDate(); + switch (aD.Month) + { + case 1: + sRet = "January"; + break; + case 2: + sRet = "February"; + break; + case 3: + sRet = "March"; + break; + case 4: + sRet = "April"; + break; + case 5: + sRet = "May"; + break; + case 6: + sRet = "June"; + break; + case 7: + sRet = "July"; + break; + case 8: + sRet = "August"; + break; + case 9: + sRet = "September"; + break; + case 10: + sRet = "October"; + break; + case 11: + sRet = "November"; + break; + case 12: + sRet = "December"; + break; + } + return sRet; +} + +ORowSetValue OOp_Quarter::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + sal_Int32 nRet = 1; + css::util::Date aD = lhs.getDate(); + if (aD.Month >= 4 && aD.Month < 7) + nRet = 2; + else if (aD.Month >= 7 && aD.Month < 10) + nRet = 3; + else if (aD.Month >= 10 && aD.Month <= 12) + nRet = 4; + return nRet; +} + +ORowSetValue OOp_Week::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (lhs.empty() || lhs.size() > 2) + return ORowSetValue(); + + size_t nSize = lhs.size(); + + css::util::Date aD = lhs[nSize - 1].getDate(); + Date aDate(aD.Day, aD.Month, aD.Year); + + sal_Int16 nStartDay = SUNDAY; + if (nSize == 2 && !lhs[0].isNull()) + nStartDay = lhs[0].getInt16(); + + return static_cast<sal_Int16>(aDate.GetWeekOfYear(static_cast<DayOfWeek>(nStartDay))); +} + +ORowSetValue OOp_Year::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + css::util::Date aD = lhs.getDate(); + return aD.Year; +} + +ORowSetValue OOp_Hour::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + css::util::Time aT = lhs.getTime(); + return static_cast<sal_Int16>(aT.Hours); +} + +ORowSetValue OOp_Minute::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + css::util::Time aT = lhs.getTime(); + return static_cast<sal_Int16>(aT.Minutes); +} + +ORowSetValue OOp_Second::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + css::util::Time aT = lhs.getTime(); + return static_cast<sal_Int16>(aT.Seconds); +} + +ORowSetValue OOp_CurDate::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (!lhs.empty()) + return ORowSetValue(); + + Date aCurDate(Date::SYSTEM); + return aCurDate.GetUNODate(); +} + +ORowSetValue OOp_CurTime::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (!lhs.empty()) + return ORowSetValue(); + + tools::Time aCurTime(tools::Time::SYSTEM); + return aCurTime.GetUNOTime(); +} + +ORowSetValue OOp_Now::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (!lhs.empty()) + return ORowSetValue(); + + DateTime aCurTime(DateTime::SYSTEM); + return aCurTime.GetUNODateTime(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FDriver.cxx b/connectivity/source/drivers/file/FDriver.cxx new file mode 100644 index 000000000..4b6d45fe7 --- /dev/null +++ b/connectivity/source/drivers/file/FDriver.cxx @@ -0,0 +1,205 @@ +/* -*- 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 <file/FDriver.hxx> +#include <file/FConnection.hxx> +#include <file/fcode.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + + +using namespace connectivity::file; +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::sdbcx; +using namespace com::sun::star::container; + +OFileDriver::OFileDriver(const css::uno::Reference< css::uno::XComponentContext >& _rxContext) + : ODriver_BASE(m_aMutex) + ,m_xContext(_rxContext) +{ +} + +void OFileDriver::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + + for (auto const& connection : m_xConnections) + { + Reference< XComponent > xComp(connection.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + } + m_xConnections.clear(); + + ODriver_BASE::disposing(); +} + +// XServiceInfo + +OUString SAL_CALL OFileDriver::getImplementationName( ) +{ + return "com.sun.star.sdbc.driver.file.Driver"; +} + +sal_Bool SAL_CALL OFileDriver::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + + +Sequence< OUString > SAL_CALL OFileDriver::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdbc.Driver", "com.sun.star.sdbcx.Driver" }; +} + + +Reference< XConnection > SAL_CALL OFileDriver::connect( const OUString& url, const Sequence< PropertyValue >& info ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODriver_BASE::rBHelper.bDisposed); + + rtl::Reference<OConnection> pCon = new OConnection(this); + pCon->construct(url,info); + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return pCon; +} + +sal_Bool SAL_CALL OFileDriver::acceptsURL( const OUString& url ) +{ + return url.startsWith("sdbc:file:"); +} + +Sequence< DriverPropertyInfo > SAL_CALL OFileDriver::getPropertyInfo( const OUString& url, const Sequence< PropertyValue >& /*info*/ ) +{ + if ( acceptsURL(url) ) + { + + Sequence< OUString > aBoolean { "0", "1" }; + + return + { + { + "CharSet" + ,"CharSet of the database." + ,false + ,{} + ,{} + }, + { + "Extension" + ,"Extension of the file format." + ,false + ,".*" + ,{} + }, + { + "ShowDeleted" + ,"Display inactive records." + ,false + ,"0" + ,aBoolean + }, + { + "EnableSQL92Check" + ,"Use SQL92 naming constraints." + ,false + ,"0" + ,aBoolean + }, + { + "UseRelativePath" + ,"Handle the connection url as relative path." + ,false + ,"0" + ,aBoolean + }, + { + "URL" + ,"The URL of the database document which is used to create an absolute path." + ,false + ,{} + ,{} + } + }; + } // if ( acceptsURL(url) ) + { + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } // if ( ! acceptsURL(url) ) + return Sequence< DriverPropertyInfo >(); +} + +sal_Int32 SAL_CALL OFileDriver::getMajorVersion( ) +{ + return 1; +} + +sal_Int32 SAL_CALL OFileDriver::getMinorVersion( ) +{ + return 0; +} + + +// XDataDefinitionSupplier +Reference< XTablesSupplier > SAL_CALL OFileDriver::getDataDefinitionByConnection( const Reference< css::sdbc::XConnection >& connection ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODriver_BASE::rBHelper.bDisposed); + + if (OConnection* pSearchConnection = comphelper::getFromUnoTunnel<OConnection>(connection)) + { + for (auto const& elem : m_xConnections) + { + if (static_cast<OConnection*>( Reference< XConnection >::query(elem.get()).get() ) == pSearchConnection) + return pSearchConnection->createCatalog(); + } + } + return {}; +} + + +Reference< XTablesSupplier > SAL_CALL OFileDriver::getDataDefinitionByURL( const OUString& url, const Sequence< PropertyValue >& info ) +{ + if ( ! acceptsURL(url) ) + { + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + return getDataDefinitionByConnection(connect(url,info)); +} + + +OOperandAttr::OOperandAttr(sal_uInt16 _nPos,const Reference< XPropertySet>& _xColumn) + : OOperandRow(_nPos,::comphelper::getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)))) +{ +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FNoException.cxx b/connectivity/source/drivers/file/FNoException.cxx new file mode 100644 index 000000000..920bb3885 --- /dev/null +++ b/connectivity/source/drivers/file/FNoException.cxx @@ -0,0 +1,102 @@ +/* -*- 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 <file/FCatalog.hxx> +#include <file/fcomp.hxx> +#include <file/fanalyzer.hxx> +#include <file/FResultSet.hxx> +#include <file/FPreparedStatement.hxx> +#include <connectivity/FValue.hxx> +#include <tools/debug.hxx> +#include <TKeyValue.hxx> + +using namespace connectivity; +using namespace connectivity::file; + +void OFileCatalog::refreshViews() +{} +void OFileCatalog::refreshGroups() +{} +void OFileCatalog::refreshUsers() +{ +} + +OPredicateInterpreter::~OPredicateInterpreter() +{ + while(!m_aStack.empty()) + { + delete m_aStack.top(); + m_aStack.pop(); + } + // m_aStack.clear(); +} + +void OPredicateCompiler::Clean() +{ + m_aCodeList.clear(); +} + +void OSQLAnalyzer::bindParameterRow(OValueRefRow const & _pRow) +{ + OCodeList& rCodeList = m_aCompiler->m_aCodeList; + for (auto const& code : rCodeList) + { + OOperandParam* pParam = dynamic_cast<OOperandParam*>(code.get()); + if ( pParam ) + pParam->bindValue(_pRow); + } +} + +void OPreparedStatement::scanParameter(OSQLParseNode* pParseNode,std::vector< OSQLParseNode*>& _rParaNodes) +{ + DBG_ASSERT(pParseNode != nullptr,"OResultSet: internal error: invalid ParseNode"); + + // found parameter Name-Rule? + if (SQL_ISRULE(pParseNode,parameter)) + { + DBG_ASSERT(pParseNode->count() >= 1,"OResultSet: faulty Parse Tree"); + DBG_ASSERT(pParseNode->getChild(0)->getNodeType() == SQLNodeType::Punctuation,"OResultSet: faulty Parse Tree"); + + _rParaNodes.push_back(pParseNode); + // Further descend not necessary + return; + } + + // Further descend in Parse Tree + for (size_t i = 0; i < pParseNode->count(); i++) + scanParameter(pParseNode->getChild(i),_rParaNodes); +} + +std::unique_ptr<OKeyValue> OResultSet::GetOrderbyKeyValue(OValueRefRow const & _rRow) +{ + sal_uInt32 nBookmarkValue = std::abs((*_rRow)[0]->getValue().getInt32()); + + std::unique_ptr<OKeyValue> pKeyValue = OKeyValue::createKeyValue(nBookmarkValue); + + for (auto const& elem : m_aOrderbyColumnNumber) + { + OSL_ENSURE(elem < static_cast<sal_Int32>(_rRow->size()),"Invalid index for orderkey values!"); + pKeyValue->pushKey(new ORowSetValueDecorator((*_rRow)[elem]->getValue())); + } + + return pKeyValue; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FNumericFunctions.cxx b/connectivity/source/drivers/file/FNumericFunctions.cxx new file mode 100644 index 000000000..7c7fdc754 --- /dev/null +++ b/connectivity/source/drivers/file/FNumericFunctions.cxx @@ -0,0 +1,242 @@ +/* -*- 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 <cmath> +#include <basegfx/numeric/ftools.hxx> +#include <file/FNumericFunctions.hxx> +#include <rtl/math.hxx> + +using namespace connectivity; +using namespace connectivity::file; + +ORowSetValue OOp_Abs::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nVal = lhs.getDouble(); + if ( nVal < 0 ) + nVal *= -1.0; + return fabs(nVal); +} + +ORowSetValue OOp_Sign::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + sal_Int32 nRet = 0; + double nVal = lhs.getDouble(); + if ( nVal < 0 ) + nRet = -1; + else if ( nVal > 0 ) + nRet = 1; + + return nRet; +} + +ORowSetValue OOp_Mod::operate(const ORowSetValue& lhs,const ORowSetValue& rhs) const +{ + if ( lhs.isNull() || rhs.isNull() ) + return ORowSetValue(); + + return fmod(lhs.getDouble(), rhs.getDouble()); +} + +ORowSetValue OOp_Floor::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return floor(lhs.getDouble()); +} + +ORowSetValue OOp_Ceiling::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nVal = lhs.getDouble(); + return ceil(nVal); +} + +ORowSetValue OOp_Round::operate(const std::vector<ORowSetValue>& lhs) const +{ + if ( lhs.empty() || lhs.size() > 2 ) + return ORowSetValue(); + + size_t nSize = lhs.size(); + double nVal = lhs[nSize-1].getDouble(); + + sal_Int32 nDec = 0; + if ( nSize == 2 && !lhs[0].isNull() ) + nDec = lhs[0].getDouble(); + return ::rtl::math::round(nVal,nDec); +} + +ORowSetValue OOp_Exp::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nVal = lhs.getDouble(); + return exp(nVal); +} + +ORowSetValue OOp_Ln::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() || lhs.getDouble() < 0.0 ) + return lhs; + + double nVal = lhs.getDouble(); + nVal = log(nVal); + if ( std::isnan(nVal) ) + return ORowSetValue(); + return nVal; +} + +ORowSetValue OOp_Log::operate(const std::vector<ORowSetValue>& lhs) const +{ + if ( lhs.empty() || lhs.size() > 2 ) + return ORowSetValue(); + size_t nSize = lhs.size(); + double nVal = log( lhs[nSize-1].getDouble() ); + + + if ( nSize == 2 && !lhs[0].isNull() ) + nVal /= log(lhs[0].getDouble()); + + if ( std::isnan(nVal) ) + return ORowSetValue(); + return nVal; +} + +ORowSetValue OOp_Log10::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() || lhs.getDouble() < 0.0 ) + return lhs; + + double nVal = log(lhs.getDouble()); + if ( std::isnan(nVal) ) + return ORowSetValue(); + nVal /= log(10.0); + return nVal; +} + +ORowSetValue OOp_Pow::operate(const ORowSetValue& lhs,const ORowSetValue& rhs) const +{ + if ( lhs.isNull() || rhs.isNull() ) + return lhs; + + return pow(lhs.getDouble(), rhs.getDouble()); +} + +ORowSetValue OOp_Sqrt::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nVal = sqrt(lhs.getDouble()); + if ( std::isnan(nVal) ) + return ORowSetValue(); + return nVal; +} + +ORowSetValue OOp_Pi::operate(const std::vector<ORowSetValue>& /*lhs*/) const +{ + return M_PI; +} + +ORowSetValue OOp_Cos::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return cos(lhs.getDouble()); +} + +ORowSetValue OOp_Sin::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return sin(lhs.getDouble()); +} + +ORowSetValue OOp_Tan::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return tan(lhs.getDouble()); +} + +ORowSetValue OOp_ACos::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return acos(lhs.getDouble()); +} + +ORowSetValue OOp_ASin::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return asin(lhs.getDouble()); +} + +ORowSetValue OOp_ATan::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return atan(lhs.getDouble()); +} + +ORowSetValue OOp_ATan2::operate(const ORowSetValue& lhs,const ORowSetValue& rhs) const +{ + if ( lhs.isNull() || rhs.isNull() ) + return lhs; + + return atan2(lhs.getDouble(), rhs.getDouble()); +} + +ORowSetValue OOp_Degrees::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nLhs = lhs.getDouble(); + return basegfx::rad2deg(nLhs); +} + +ORowSetValue OOp_Radians::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nLhs = lhs.getDouble(); + return basegfx::deg2rad(nLhs); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FPreparedStatement.cxx b/connectivity/source/drivers/file/FPreparedStatement.cxx new file mode 100644 index 000000000..f2a8571b6 --- /dev/null +++ b/connectivity/source/drivers/file/FPreparedStatement.cxx @@ -0,0 +1,555 @@ +/* -*- 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 <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <file/FPreparedStatement.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <file/FResultSetMetaData.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/sequence.hxx> +#include <connectivity/dbconversion.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/PColumn.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <strings.hrc> + +using namespace connectivity; +using namespace comphelper; +using namespace ::dbtools; +using namespace connectivity::file; +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::sdbcx; +using namespace com::sun::star::container; +using namespace com::sun::star; + +IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbc.driver.file.PreparedStatement","com.sun.star.sdbc.PreparedStatement"); + +OPreparedStatement::OPreparedStatement( OConnection* _pConnection) + : OStatement_BASE2( _pConnection ) +{ +} + + +OPreparedStatement::~OPreparedStatement() +{ +} + + +void OPreparedStatement::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + OStatement_BASE2::disposing(); + + m_xParamColumns = nullptr; + m_xMetaData.clear(); + if(m_aParameterRow.is()) + { + m_aParameterRow->clear(); + m_aParameterRow = nullptr; + } +} + +void OPreparedStatement::construct(const OUString& sql) +{ + OStatement_Base::construct(sql); + + m_aParameterRow = new OValueRefVector(); + m_aParameterRow->push_back(new ORowSetValueDecorator(sal_Int32(0)) ); + + Reference<XIndexAccess> xNames(m_xColNames,UNO_QUERY); + + if ( m_aSQLIterator.getStatementType() == OSQLStatementType::Select ) + m_xParamColumns = m_aSQLIterator.getParameters(); + else + { + m_xParamColumns = new OSQLColumns(); + // describe all parameters need for the resultset + describeParameter(); + } + + OValueRefRow aTemp; + OResultSet::setBoundedColumns(m_aEvaluateRow,aTemp,m_xParamColumns,xNames,false,m_xDBMetaData,m_aColMapping); +} + +rtl::Reference<OResultSet> OPreparedStatement::makeResultSet() +{ + closeResultSet(); + + rtl::Reference<OResultSet> xResultSet(createResultSet()); + m_xResultSet = uno::Reference<uno::XWeak>(xResultSet); + initializeResultSet(xResultSet.get()); + initResultSet(xResultSet.get()); + return xResultSet; +} + +Any SAL_CALL OPreparedStatement::queryInterface( const Type & rType ) +{ + Any aRet = OStatement_BASE2::queryInterface(rType); + return aRet.hasValue() ? aRet : ::cppu::queryInterface( rType, + static_cast< XPreparedStatement*>(this), + static_cast< XParameters*>(this), + static_cast< XResultSetMetaDataSupplier*>(this)); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL OPreparedStatement::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<XPreparedStatement>::get(), + cppu::UnoType<XParameters>::get(), + cppu::UnoType<XResultSetMetaDataSupplier>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),OStatement_BASE2::getTypes()); +} + + +Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + if(!m_xMetaData.is()) + m_xMetaData = new OResultSetMetaData(m_aSQLIterator.getSelectColumns(),m_aSQLIterator.getTables().begin()->first,m_pTable.get()); + return m_xMetaData; +} + + +void SAL_CALL OPreparedStatement::close( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + closeResultSet(); +} + + +sal_Bool SAL_CALL OPreparedStatement::execute( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + rtl::Reference<OResultSet> xRS(makeResultSet()); + // since we don't support the XMultipleResults interface, nobody will ever get that ResultSet... + if(xRS.is()) + xRS->dispose(); + + return m_aSQLIterator.getStatementType() == OSQLStatementType::Select; +} + + +sal_Int32 SAL_CALL OPreparedStatement::executeUpdate( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + rtl::Reference<OResultSet> xRS(makeResultSet()); + if(xRS.is()) + { + const sal_Int32 res(xRS->getRowCountResult()); + // nobody will ever get that ResultSet... + xRS->dispose(); + return res; + } + else + return 0; +} + + +void SAL_CALL OPreparedStatement::setString( sal_Int32 parameterIndex, const OUString& x ) +{ + setParameter(parameterIndex,x); +} + + +Reference< XConnection > SAL_CALL OPreparedStatement::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + return m_pConnection; +} + + +Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + return makeResultSet(); +} + + +void SAL_CALL OPreparedStatement::setBoolean( sal_Int32 parameterIndex, sal_Bool x ) +{ + setParameter(parameterIndex,static_cast<bool>(x)); +} + +void SAL_CALL OPreparedStatement::setByte( sal_Int32 parameterIndex, sal_Int8 x ) +{ + setParameter(parameterIndex,x); +} + + +void SAL_CALL OPreparedStatement::setDate( sal_Int32 parameterIndex, const util::Date& aData ) +{ + setParameter(parameterIndex,DBTypeConversion::toDouble(aData)); +} + +void SAL_CALL OPreparedStatement::setTime( sal_Int32 parameterIndex, const util::Time& aVal ) +{ + setParameter(parameterIndex,DBTypeConversion::toDouble(aVal)); +} + + +void SAL_CALL OPreparedStatement::setTimestamp( sal_Int32 parameterIndex, const util::DateTime& aVal ) +{ + setParameter(parameterIndex,DBTypeConversion::toDouble(aVal)); +} + + +void SAL_CALL OPreparedStatement::setDouble( sal_Int32 parameterIndex, double x ) +{ + setParameter(parameterIndex,x); +} + + +void SAL_CALL OPreparedStatement::setFloat( sal_Int32 parameterIndex, float x ) +{ + setParameter(parameterIndex,x); +} + + +void SAL_CALL OPreparedStatement::setInt( sal_Int32 parameterIndex, sal_Int32 x ) +{ + setParameter(parameterIndex,x); +} + + +void SAL_CALL OPreparedStatement::setLong( sal_Int32 /*parameterIndex*/, sal_Int64 /*aVal*/ ) +{ + throwFeatureNotImplementedSQLException( "XParameters::setLong", *this ); +} + + +void SAL_CALL OPreparedStatement::setNull( sal_Int32 parameterIndex, sal_Int32 /*sqlType*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkAndResizeParameters(parameterIndex); + + if ( m_aAssignValues.is() ) + (*m_aAssignValues)[m_aParameterIndexes[parameterIndex]]->setNull(); + else + (*m_aParameterRow)[parameterIndex]->setNull(); +} + + +void SAL_CALL OPreparedStatement::setClob( sal_Int32 /*parameterIndex*/, const Reference< XClob >& /*x*/ ) +{ + throwFeatureNotImplementedSQLException( "XParameters::setClob", *this ); +} + + +void SAL_CALL OPreparedStatement::setBlob( sal_Int32 /*parameterIndex*/, const Reference< XBlob >& /*x*/ ) +{ + throwFeatureNotImplementedSQLException( "XParameters::setBlob", *this ); +} + + +void SAL_CALL OPreparedStatement::setArray( sal_Int32 /*parameterIndex*/, const Reference< XArray >& /*x*/ ) +{ + throwFeatureNotImplementedSQLException( "XParameters::setArray", *this ); +} + + +void SAL_CALL OPreparedStatement::setRef( sal_Int32 /*parameterIndex*/, const Reference< XRef >& /*x*/ ) +{ + throwFeatureNotImplementedSQLException( "XParameters::setRef", *this ); +} + + +void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale ) +{ + switch(sqlType) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + setString(parameterIndex,::comphelper::getString(x)); + break; + default: + ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale); + break; + } +} + + +void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& /*typeName*/ ) +{ + setNull(parameterIndex,sqlType); +} + + +void SAL_CALL OPreparedStatement::setObject( sal_Int32 parameterIndex, const Any& x ) +{ + if(!::dbtools::implSetObject(this,parameterIndex,x)) + { + const OUString sError( m_pConnection->getResources().getResourceStringWithSubstitution( + STR_UNKNOWN_PARA_TYPE, + "$position$", OUString::number(parameterIndex) + ) ); + ::dbtools::throwGenericSQLException(sError,*this); + } + // setObject (parameterIndex, x, sqlType, 0); +} + + +void SAL_CALL OPreparedStatement::setShort( sal_Int32 parameterIndex, sal_Int16 x ) +{ + setParameter(parameterIndex,x); +} + + +void SAL_CALL OPreparedStatement::setBytes( sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x ) +{ + setParameter(parameterIndex,x); +} + + +void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + setBinaryStream(parameterIndex,x,length ); +} + + +void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + if(!x.is()) + ::dbtools::throwFunctionSequenceException(*this); + + Sequence<sal_Int8> aSeq; + x->readBytes(aSeq,length); + setParameter(parameterIndex,aSeq); +} + + +void SAL_CALL OPreparedStatement::clearParameters( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + m_aParameterRow->clear(); + m_aParameterRow->push_back(new ORowSetValueDecorator(sal_Int32(0)) ); +} + +rtl::Reference<OResultSet> OPreparedStatement::createResultSet() +{ + return new OResultSet(this,m_aSQLIterator); +} + +void OPreparedStatement::initResultSet(OResultSet *pResultSet) +{ + // check if we got enough parameters + if ( (m_aParameterRow.is() && ( m_aParameterRow->size() -1 ) < m_xParamColumns->size()) || + (m_xParamColumns.is() && !m_aParameterRow.is() && !m_aParameterRow->empty()) ) + m_pConnection->throwGenericSQLException(STR_INVALID_PARA_COUNT,*this); + + pResultSet->OpenImpl(); + pResultSet->setMetaData(getMetaData()); +} + +void SAL_CALL OPreparedStatement::acquire() noexcept +{ + OStatement_BASE2::acquire(); +} + +void SAL_CALL OPreparedStatement::release() noexcept +{ + OStatement_BASE2::release(); +} + +void OPreparedStatement::checkAndResizeParameters(sal_Int32 parameterIndex) +{ + ::connectivity::checkDisposed(OStatement_BASE::rBHelper.bDisposed); + if ( m_aAssignValues.is() && (parameterIndex < 1 || o3tl::make_unsigned(parameterIndex) >= m_aParameterIndexes.size()) ) + throwInvalidIndexException(*this); + else if ( static_cast<sal_Int32>(m_aParameterRow->size()) <= parameterIndex ) + { + sal_Int32 i = m_aParameterRow->size(); + m_aParameterRow->resize(parameterIndex+1); + for ( ; i <= parameterIndex; ++i) + { + if ( !(*m_aParameterRow)[i].is() ) + (*m_aParameterRow)[i] = new ORowSetValueDecorator; + } + } +} + +void OPreparedStatement::setParameter(sal_Int32 parameterIndex, const ORowSetValue& x) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkAndResizeParameters(parameterIndex); + + if(m_aAssignValues.is()) + *(*m_aAssignValues)[m_aParameterIndexes[parameterIndex]] = x; + else + *((*m_aParameterRow)[parameterIndex]) = x; +} + +sal_uInt32 OPreparedStatement::AddParameter(OSQLParseNode const * pParameter, const Reference<XPropertySet>& _xCol) +{ + OSL_ENSURE(SQL_ISRULE(pParameter,parameter),"OResultSet::AddParameter: Argument is not a parameter"); + OSL_ENSURE(pParameter->count() > 0,"OResultSet: Error in Parse Tree"); + + OUString sParameterName; + // set up Parameter-Column: + sal_Int32 eType = DataType::VARCHAR; + sal_uInt32 nPrecision = 255; + sal_Int32 nScale = 0; + sal_Int32 nNullable = ColumnValue::NULLABLE; + + if (_xCol.is()) + { + // Use type, precision, scale ... from the given column, + // because this Column will get a value assigned or + // with this Column the value will be compared. + _xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= eType; + _xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)) >>= nPrecision; + _xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE)) >>= nScale; + _xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE)) >>= nNullable; + _xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= sParameterName; + } + + Reference<XPropertySet> xParaColumn = new connectivity::parse::OParseColumn(sParameterName + ,OUString() + ,OUString() + ,OUString() + ,nNullable + ,nPrecision + ,nScale + ,eType + ,false + ,false + ,m_aSQLIterator.isCaseSensitive() + ,OUString() + ,OUString() + ,OUString()); + m_xParamColumns->push_back(xParaColumn); + return m_xParamColumns->size(); +} + +void OPreparedStatement::describeColumn(OSQLParseNode const * _pParameter, OSQLParseNode const * _pNode,const OSQLTable& _xTable) +{ + Reference<XPropertySet> xProp; + if(SQL_ISRULE(_pNode,column_ref)) + { + OUString sColumnName,sTableRange; + m_aSQLIterator.getColumnRange(_pNode,sColumnName,sTableRange); + if ( !sColumnName.isEmpty() ) + { + Reference<XNameAccess> xNameAccess = _xTable->getColumns(); + if(xNameAccess->hasByName(sColumnName)) + xNameAccess->getByName(sColumnName) >>= xProp; + AddParameter(_pParameter,xProp); + } + } + // else + // AddParameter(_pParameter,xProp); +} + +void OPreparedStatement::describeParameter() +{ + std::vector< OSQLParseNode*> aParseNodes; + scanParameter(m_pParseTree,aParseNodes); + if ( aParseNodes.empty() ) + return; + + // m_xParamColumns = new OSQLColumns(); + const OSQLTables& rTabs = m_aSQLIterator.getTables(); + if( !rTabs.empty() ) + { + OSQLTable xTable = rTabs.begin()->second; + for (auto const& parseNode : aParseNodes) + { + describeColumn(parseNode,parseNode->getParent()->getChild(0),xTable); + } + } +} +void OPreparedStatement::initializeResultSet(OResultSet* pRS) +{ + OStatement_Base::initializeResultSet(pRS); + + // Substitute parameter (AssignValues and criteria): + if (m_xParamColumns->empty()) + return; + + // begin with AssignValues + sal_uInt16 nParaCount=0; // gives the current number of previously set Parameters + + // search for parameters to be substituted: + size_t nCount = m_aAssignValues.is() ? m_aAssignValues->size() : 1; // 1 is important for the Criteria + for (size_t j = 1; j < nCount; j++) + { + sal_uInt32 nParameter = (*m_aAssignValues).getParameterIndex(j); + if (nParameter == SQL_NO_PARAMETER) + continue; // this AssignValue is no Parameter + + ++nParaCount; // now the Parameter is valid + } + + if (m_aParameterRow.is() && (m_xParamColumns->size()+1) != m_aParameterRow->size() ) + { + sal_Int32 i = m_aParameterRow->size(); + sal_Int32 nParamColumns = m_xParamColumns->size()+1; + m_aParameterRow->resize(nParamColumns); + for ( ;i < nParamColumns; ++i ) + { + if ( !(*m_aParameterRow)[i].is() ) + (*m_aParameterRow)[i] = new ORowSetValueDecorator; + } + } + if (m_aParameterRow.is() && nParaCount < m_aParameterRow->size() ) + m_pSQLAnalyzer->bindParameterRow(m_aParameterRow); +} + +void OPreparedStatement::parseParamterElem(const OUString& _sColumnName, OSQLParseNode* pRow_Value_Constructor_Elem) +{ + Reference<XPropertySet> xCol; + m_xColNames->getByName(_sColumnName) >>= xCol; + sal_Int32 nParameter = -1; + if(m_xParamColumns.is()) + { + OSQLColumns::const_iterator aIter = find(m_xParamColumns->begin(),m_xParamColumns->end(),_sColumnName,::comphelper::UStringMixEqual(m_pTable->isCaseSensitive())); + if(aIter != m_xParamColumns->end()) + nParameter = m_xParamColumns->size() - (m_xParamColumns->end() - aIter) + 1;// +1 because the rows start at 1 + } + if(nParameter == -1) + nParameter = AddParameter(pRow_Value_Constructor_Elem,xCol); + // Save number of parameter in the variable: + SetAssignValue(_sColumnName, OUString(), true, nParameter); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FResultSet.cxx b/connectivity/source/drivers/file/FResultSet.cxx new file mode 100644 index 000000000..4d0e256b9 --- /dev/null +++ b/connectivity/source/drivers/file/FResultSet.cxx @@ -0,0 +1,1593 @@ +/* -*- 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 <file/FResultSet.hxx> +#include <sqlbison.hxx> +#include <file/FResultSetMetaData.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <connectivity/dbtools.hxx> +#include <cppuhelper/propshlp.hxx> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <iterator> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbcx/XIndexesSupplier.hpp> + +#include <algorithm> +#include <connectivity/dbexception.hxx> +#include <comphelper/types.hxx> +#include <resource/sharedresources.hxx> +#include <strings.hrc> +#include <tools/diagnose_ex.h> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::file; +using namespace ::cppu; +using namespace dbtools; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::sdbcx; +using namespace com::sun::star::container; + +namespace +{ + void lcl_throwError(TranslateId pErrorId, const css::uno::Reference< css::uno::XInterface>& _xContext) + { + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(pErrorId); + ::dbtools::throwGenericSQLException(sMessage ,_xContext); + } +} + +IMPLEMENT_SERVICE_INFO(OResultSet,"com.sun.star.sdbcx.drivers.file.ResultSet","com.sun.star.sdbc.ResultSet"); + +OResultSet::OResultSet(OStatement_Base* pStmt,OSQLParseTreeIterator& _aSQLIterator) : OResultSet_BASE(m_aMutex) + ,::comphelper::OPropertyContainer(OResultSet_BASE::rBHelper) + ,m_aSkipDeletedSet(this) + ,m_pParseTree(pStmt->getParseTree()) + ,m_pSQLAnalyzer(nullptr) + ,m_aSQLIterator(_aSQLIterator) + ,m_nFetchSize(0) + ,m_nResultSetType(ResultSetType::SCROLL_INSENSITIVE) + ,m_nFetchDirection(FetchDirection::FORWARD) + ,m_nResultSetConcurrency(ResultSetConcurrency::UPDATABLE) + ,m_xStatement(*pStmt) + ,m_nRowPos(-1) + ,m_nFilePos(0) + ,m_nLastVisitedPos(-1) + ,m_nRowCountResult(-1) + ,m_nColumnCount(0) + ,m_bWasNull(false) + ,m_bInserted(false) + ,m_bRowUpdated(false) + ,m_bRowInserted(false) + ,m_bRowDeleted(false) + ,m_bShowDeleted(pStmt->getOwnConnection()->showDeleted()) + ,m_bIsCount(false) +{ + osl_atomic_increment( &m_refCount ); + m_bIsCount = (m_pParseTree && + m_pParseTree->count() > 2 && + SQL_ISRULE(m_pParseTree->getChild(2),scalar_exp_commalist) && + SQL_ISRULE(m_pParseTree->getChild(2)->getChild(0),derived_column) && + SQL_ISRULE(m_pParseTree->getChild(2)->getChild(0)->getChild(0),general_set_fct) && + m_pParseTree->getChild(2)->getChild(0)->getChild(0)->count() == 4 + ); + + m_nResultSetConcurrency = isCount() ? ResultSetConcurrency::READ_ONLY : ResultSetConcurrency::UPDATABLE; + construct(); + m_aSkipDeletedSet.SetDeletedVisible(m_bShowDeleted); + osl_atomic_decrement( &m_refCount ); +} + + +OResultSet::~OResultSet() +{ + osl_atomic_increment( &m_refCount ); + disposing(); +} + +void OResultSet::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 OResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + m_xStatement.clear(); + m_xMetaData.clear(); + m_pParseTree = nullptr; + m_xColNames.clear(); + m_xColumns = nullptr; + m_xColsIdx.clear(); + + if ( m_pTable.is() ) + m_pTable->removeEventListener(this); + m_pTable.clear(); + + m_pFileSet = nullptr; + m_pSortIndex.reset(); + + if(m_aInsertRow.is()) + m_aInsertRow->clear(); + + m_aSkipDeletedSet.clear(); +} + +Any SAL_CALL OResultSet::queryInterface( const Type & rType ) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + return aRet.hasValue() ? aRet : OResultSet_BASE::queryInterface(rType); +} + +Sequence< Type > SAL_CALL OResultSet::getTypes( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + OTypeCollection aTypes( cppu::UnoType<css::beans::XMultiPropertySet>::get(), + cppu::UnoType<css::beans::XPropertySet>::get(), + cppu::UnoType<css::beans::XPropertySet>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),OResultSet_BASE::getTypes()); +} + + +sal_Int32 SAL_CALL OResultSet::findColumn( const OUString& columnName ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_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 ); + assert(false); + return 0; // Never reached +} + +const ORowSetValue& OResultSet::getValue(sal_Int32 columnIndex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + checkIndex(columnIndex ); + + + m_bWasNull = (*m_aSelectRow)[columnIndex]->getValue().isNull(); + return *(*m_aSelectRow)[columnIndex]; +} + +void OResultSet::checkIndex(sal_Int32 columnIndex ) +{ + if ( columnIndex <= 0 + || columnIndex >= m_nColumnCount ) + ::dbtools::throwInvalidIndexException(*this); +} + +Reference< css::io::XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + +Reference< css::io::XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +sal_Bool SAL_CALL OResultSet::getBoolean( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getBool(); +} + + +sal_Int8 SAL_CALL OResultSet::getByte( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getInt8(); +} + + +Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getSequence(); +} + + +css::util::Date SAL_CALL OResultSet::getDate( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDate(); +} + + +double SAL_CALL OResultSet::getDouble( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDouble(); +} + + +float SAL_CALL OResultSet::getFloat( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getFloat(); +} + + +sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getInt32(); +} + + +sal_Int32 SAL_CALL OResultSet::getRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + OSL_ENSURE((m_bShowDeleted || !m_aRow->isDeleted()),"getRow called for deleted row"); + + return m_aSkipDeletedSet.getMappedPosition((*m_aRow)[0]->getValue().getInt32()); +} + + +sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getLong(); +} + + +Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(!m_xMetaData.is()) + m_xMetaData = new OResultSetMetaData(m_xColumns,m_aSQLIterator.getTables().begin()->first,m_pTable.get()); + return m_xMetaData; +} + +Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + +Reference< XBlob > SAL_CALL OResultSet::getBlob( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +Any SAL_CALL OResultSet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + return getValue(columnIndex).makeAny(); +} + + +sal_Int16 SAL_CALL OResultSet::getShort( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getInt16(); +} + +OUString SAL_CALL OResultSet::getString( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getString(); +} + +css::util::Time SAL_CALL OResultSet::getTime( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getTime(); +} + +css::util::DateTime SAL_CALL OResultSet::getTimestamp( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDateTime(); +} + + +sal_Bool SAL_CALL OResultSet::isAfterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_nRowPos == sal_Int32(m_pFileSet->size()); +} + +sal_Bool SAL_CALL OResultSet::isFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_nRowPos == 0; +} + +sal_Bool SAL_CALL OResultSet::isLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_nRowPos == sal_Int32(m_pFileSet->size() - 1); +} + +void SAL_CALL OResultSet::beforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(first()) + previous(); +} + +void SAL_CALL OResultSet::afterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(last()) + next(); +} + + +void SAL_CALL OResultSet::close( ) +{ + dispose(); +} + + +sal_Bool SAL_CALL OResultSet::first( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + return m_pTable.is() && m_aSkipDeletedSet.skipDeleted(IResultSetHelper::FIRST,1,true); +} + + +sal_Bool SAL_CALL OResultSet::last( ) +{ + // here I know definitely that I stand on the last record + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + return m_pTable.is() && m_aSkipDeletedSet.skipDeleted(IResultSetHelper::LAST,1,true); +} + +sal_Bool SAL_CALL OResultSet::absolute( sal_Int32 row ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + return m_pTable.is() && m_aSkipDeletedSet.skipDeleted(IResultSetHelper::ABSOLUTE1,row,true); +} + +sal_Bool SAL_CALL OResultSet::relative( sal_Int32 row ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + return m_pTable.is() && m_aSkipDeletedSet.skipDeleted(IResultSetHelper::RELATIVE1,row,true); +} + +sal_Bool SAL_CALL OResultSet::previous( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + return m_pTable.is() && m_aSkipDeletedSet.skipDeleted(IResultSetHelper::PRIOR,0,true); +} + +Reference< XInterface > SAL_CALL OResultSet::getStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_xStatement; +} + + +sal_Bool SAL_CALL OResultSet::rowDeleted( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_bRowDeleted; +} + +sal_Bool SAL_CALL OResultSet::rowInserted( ) +{ ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_bRowInserted; +} + +sal_Bool SAL_CALL OResultSet::rowUpdated( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_bRowUpdated; +} + + +sal_Bool SAL_CALL OResultSet::isBeforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_nRowPos == -1; +} + +sal_Bool SAL_CALL OResultSet::next( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_pTable.is() && m_aSkipDeletedSet.skipDeleted(IResultSetHelper::NEXT,1,true); +} + + +sal_Bool SAL_CALL OResultSet::wasNull( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_bWasNull; +} + + +void SAL_CALL OResultSet::cancel( ) +{ +} + +void SAL_CALL OResultSet::clearWarnings( ) +{ +} + +Any SAL_CALL OResultSet::getWarnings( ) +{ + return Any(); +} + +void SAL_CALL OResultSet::insertRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(!m_bInserted || !m_pTable.is()) + throwFunctionSequenceException(*this); + + // we know that we append new rows at the end + // so we have to know where the end is + (void)m_aSkipDeletedSet.skipDeleted(IResultSetHelper::LAST,1,false); + m_bRowInserted = m_pTable->InsertRow(*m_aInsertRow, m_xColsIdx); + if(m_bRowInserted && m_pFileSet.is()) + { + sal_Int32 nPos = (*m_aInsertRow)[0]->getValue().getInt32(); + m_pFileSet->push_back(nPos); + *(*m_aInsertRow)[0] = sal_Int32(m_pFileSet->size()); + clearInsertRow(); + + m_aSkipDeletedSet.insertNewPosition((*m_aRow)[0]->getValue().getInt32()); + } +} + +void SAL_CALL OResultSet::updateRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if(!m_pTable.is() || m_pTable->isReadOnly()) + lcl_throwError(STR_TABLE_READONLY,*this); + + m_bRowUpdated = m_pTable->UpdateRow(*m_aInsertRow, m_aRow,m_xColsIdx); + *(*m_aInsertRow)[0] = (*m_aRow)[0]->getValue().getInt32(); + + clearInsertRow(); +} + +void SAL_CALL OResultSet::deleteRow() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if(!m_pTable.is() || m_pTable->isReadOnly()) + lcl_throwError(STR_TABLE_READONLY,*this); + if (m_bShowDeleted) + lcl_throwError(STR_DELETE_ROW,*this); + if(m_aRow->isDeleted()) + lcl_throwError(STR_ROW_ALREADY_DELETED,*this); + + sal_Int32 nPos = (*m_aRow)[0]->getValue().getInt32(); + m_bRowDeleted = m_pTable->DeleteRow(*m_xColumns); + if(m_bRowDeleted && m_pFileSet.is()) + { + m_aRow->setDeleted(true); + // don't touch the m_pFileSet member here + m_aSkipDeletedSet.deletePosition(nPos); + } +} + +void SAL_CALL OResultSet::cancelRowUpdates( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_bInserted = false; + m_bRowUpdated = false; + m_bRowInserted = false; + m_bRowDeleted = false; + + if(m_aInsertRow.is()) + { + OValueRefVector::iterator aIter = m_aInsertRow->begin()+1; + for(;aIter != m_aInsertRow->end();++aIter) + { + (*aIter)->setBound(false); + (*aIter)->setNull(); + } + } +} + + +void SAL_CALL OResultSet::moveToInsertRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if(!m_pTable.is() || m_pTable->isReadOnly()) + lcl_throwError(STR_TABLE_READONLY,*this); + + m_bInserted = true; + + OValueRefVector::iterator aIter = m_aInsertRow->begin()+1; + for(;aIter != m_aInsertRow->end();++aIter) + { + (*aIter)->setBound(false); + (*aIter)->setNull(); + } +} + + +void SAL_CALL OResultSet::moveToCurrentRow( ) +{ +} + +void OResultSet::updateValue(sal_Int32 columnIndex ,const ORowSetValue& x) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + checkIndex(columnIndex ); + columnIndex = mapColumn(columnIndex); + + (*m_aInsertRow)[columnIndex]->setBound(true); + *(*m_aInsertRow)[columnIndex] = x; +} + + +void SAL_CALL OResultSet::updateNull( sal_Int32 columnIndex ) +{ + ORowSetValue aEmpty; + updateValue(columnIndex,aEmpty); +} + + +void SAL_CALL OResultSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x ) +{ + updateValue(columnIndex, static_cast<bool>(x)); +} + +void SAL_CALL OResultSet::updateByte( sal_Int32 columnIndex, sal_Int8 x ) +{ + updateValue(columnIndex,x); +} + + +void SAL_CALL OResultSet::updateShort( sal_Int32 columnIndex, sal_Int16 x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL OResultSet::updateInt( sal_Int32 columnIndex, sal_Int32 x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL OResultSet::updateLong( sal_Int32 /*columnIndex*/, sal_Int64 /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRowUpdate::updateLong", *this ); +} + +void SAL_CALL OResultSet::updateFloat( sal_Int32 columnIndex, float x ) +{ + updateValue(columnIndex,x); +} + + +void SAL_CALL OResultSet::updateDouble( sal_Int32 columnIndex, double x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL OResultSet::updateString( sal_Int32 columnIndex, const OUString& x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL OResultSet::updateBytes( sal_Int32 columnIndex, const Sequence< sal_Int8 >& x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL OResultSet::updateDate( sal_Int32 columnIndex, const css::util::Date& x ) +{ + updateValue(columnIndex,x); +} + + +void SAL_CALL OResultSet::updateTime( sal_Int32 columnIndex, const css::util::Time& x ) +{ + updateValue(columnIndex,x); +} + + +void SAL_CALL OResultSet::updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) +{ + updateValue(columnIndex,x); +} + + +void SAL_CALL OResultSet::updateBinaryStream( sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if(!x.is()) + ::dbtools::throwFunctionSequenceException(*this); + + Sequence<sal_Int8> aSeq; + x->readBytes(aSeq,length); + updateValue(columnIndex,aSeq); +} + +void SAL_CALL OResultSet::updateCharacterStream( sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + updateBinaryStream(columnIndex,x,length); +} + +void SAL_CALL OResultSet::refreshRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL OResultSet::updateObject( sal_Int32 columnIndex, const Any& x ) +{ + if (!::dbtools::implUpdateObject(this, columnIndex, x)) + throw SQLException(); +} + + +void SAL_CALL OResultSet::updateNumericObject( sal_Int32 columnIndex, const Any& x, sal_Int32 /*scale*/ ) +{ + if (!::dbtools::implUpdateObject(this, columnIndex, x)) + throw SQLException(); +} + +IPropertyArrayHelper* OResultSet::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +IPropertyArrayHelper & OResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + + +bool OResultSet::ExecuteRow(IResultSetHelper::Movement eFirstCursorPosition, + sal_Int32 nFirstOffset, + bool bEvaluate, + bool bRetrieveData) +{ + OSL_ENSURE(m_pSQLAnalyzer,"OResultSet::ExecuteRow: Analyzer isn't set!"); + + // For further Fetch-Operations this information may possibly be changed ... + IResultSetHelper::Movement eCursorPosition = eFirstCursorPosition; + sal_Int32 nOffset = nFirstOffset; + + if (!m_pTable.is()) + return false; + + const OSQLColumns & rTableCols = *(m_pTable->getTableColumns()); + bool bHasRestriction = m_pSQLAnalyzer->hasRestriction(); +again: + + // protect from reading over the end when somebody is inserting while we are reading + // this method works only for dBase at the moment!!! + if (eCursorPosition == IResultSetHelper::NEXT && m_nFilePos == m_nLastVisitedPos) + { + return false; + } + + if (!m_pTable.is() || !m_pTable->seekRow(eCursorPosition, nOffset, m_nFilePos)) + { + return false; + } + + if (!bEvaluate) // If no evaluation runs, then just fill the results-row + { + m_pTable->fetchRow(m_aRow,rTableCols, bRetrieveData); + } + else + { + m_pTable->fetchRow(m_aEvaluateRow, rTableCols, bRetrieveData || bHasRestriction); + + if ( ( !m_bShowDeleted + && m_aEvaluateRow->isDeleted() + ) + || ( bHasRestriction + && !m_pSQLAnalyzer->evaluateRestriction() + ) + ) + { // Evaluate the next record + // delete current row in Keyset + if (m_pFileSet.is()) + { + OSL_ENSURE(eCursorPosition == IResultSetHelper::NEXT, "Wrong CursorPosition!"); + eCursorPosition = IResultSetHelper::NEXT; + nOffset = 1; + } + else if (eCursorPosition == IResultSetHelper::FIRST || + eCursorPosition == IResultSetHelper::NEXT || + eCursorPosition == IResultSetHelper::ABSOLUTE1) + { + eCursorPosition = IResultSetHelper::NEXT; + nOffset = 1; + } + else if (eCursorPosition == IResultSetHelper::LAST || + eCursorPosition == IResultSetHelper::PRIOR) + { + eCursorPosition = IResultSetHelper::PRIOR; + nOffset = 1; + } + else if (eCursorPosition == IResultSetHelper::RELATIVE1) + { + eCursorPosition = (nOffset >= 0) ? IResultSetHelper::NEXT : IResultSetHelper::PRIOR; + } + else + { + return false; + } + // Try again ... + goto again; + } + } + + // Evaluate may only be set, + // if the Keyset will be constructed further + if ( ( m_aSQLIterator.getStatementType() == OSQLStatementType::Select ) + && !isCount() + && bEvaluate + ) + { + if (m_pSortIndex) + { + std::unique_ptr<OKeyValue> pKeyValue = GetOrderbyKeyValue( m_aSelectRow ); + m_pSortIndex->AddKeyValue(std::move(pKeyValue)); + } + else if (m_pFileSet.is()) + { + sal_uInt32 nBookmarkValue = std::abs((*m_aEvaluateRow)[0]->getValue().getInt32()); + m_pFileSet->push_back(nBookmarkValue); + } + } + else if (m_aSQLIterator.getStatementType() == OSQLStatementType::Update) + { + bool bOK = true; + if (bEvaluate) + { + // read the actual result-row + bOK = m_pTable->fetchRow(m_aEvaluateRow, *(m_pTable->getTableColumns()), true); + } + + if (bOK) + { + // just give the values to be changed: + if(!m_pTable->UpdateRow(*m_aAssignValues,m_aEvaluateRow, m_xColsIdx)) + return false; + } + } + else if (m_aSQLIterator.getStatementType() == OSQLStatementType::Delete) + { + bool bOK = true; + if (bEvaluate) + { + bOK = m_pTable->fetchRow(m_aEvaluateRow, *(m_pTable->getTableColumns()), true); + } + if (bOK) + { + if(!m_pTable->DeleteRow(*m_xColumns)) + return false; + } + } + return true; +} + + +bool OResultSet::Move(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, bool bRetrieveData) +{ + sal_Int32 nTempPos = m_nRowPos; + + if (m_aSQLIterator.getStatementType() == OSQLStatementType::Select && + !isCount()) + { + if (!m_pFileSet.is()) //no Index available + { + // Normal FETCH + ExecuteRow(eCursorPosition,nOffset,false,bRetrieveData); + + // now set the bookmark for outside this is the logical pos and not the file pos + *(*m_aRow->begin()) = sal_Int32(m_nRowPos + 1); + } + else + { + switch(eCursorPosition) + { + case IResultSetHelper::NEXT: + ++m_nRowPos; + break; + case IResultSetHelper::PRIOR: + if (m_nRowPos >= 0) + --m_nRowPos; + break; + case IResultSetHelper::FIRST: + m_nRowPos = 0; + break; + case IResultSetHelper::LAST: + m_nRowPos = m_pFileSet->size() - 1; + break; + case IResultSetHelper::RELATIVE1: + m_nRowPos += nOffset; + break; + case IResultSetHelper::ABSOLUTE1: + case IResultSetHelper::BOOKMARK: + if ( m_nRowPos == (nOffset -1) ) + return true; + m_nRowPos = nOffset -1; + break; + } + + // OffRange? + // The FileCursor is outside of the valid range, if: + // a.) m_nRowPos < 1 + // b.) a KeySet exists and m_nRowPos > m_pFileSet->size() + if (m_nRowPos < 0 || (m_pFileSet->isFrozen() && eCursorPosition != IResultSetHelper::BOOKMARK && o3tl::make_unsigned(m_nRowPos) >= m_pFileSet->size() )) // && m_pFileSet->IsFrozen() + { + goto Error; + } + else + { + if (m_nRowPos < static_cast<sal_Int32>(m_pFileSet->size())) + { + // Fetch via Index + bool bOK = ExecuteRow(IResultSetHelper::BOOKMARK,(*m_pFileSet)[m_nRowPos],false,bRetrieveData); + if (!bOK) + goto Error; + + // now set the bookmark for outside + *(*m_aRow->begin()) = sal_Int32(m_nRowPos + 1); + if ( (bRetrieveData || m_pSQLAnalyzer->hasRestriction()) && m_pSQLAnalyzer->hasFunctions() ) + { + m_pSQLAnalyzer->setSelectionEvaluationResult(m_aSelectRow,m_aColMapping); + } + } + else // Index must be further constructed + { + // set first on the last known row + if (m_pFileSet->empty()) + { + m_pTable->seekRow(IResultSetHelper::ABSOLUTE1, 0, m_nFilePos); + } + else + { + m_aFileSetIter = m_pFileSet->end()-1; + m_pTable->seekRow(IResultSetHelper::BOOKMARK, *m_aFileSetIter, m_nFilePos); + } + bool bOK = true; + // Determine the number of further Fetches + while (bOK && m_nRowPos >= static_cast<sal_Int32>(m_pFileSet->size())) + { + bOK = ExecuteRow(IResultSetHelper::NEXT,1,true, false);//bRetrieveData); + } + + if (bOK) + { + // read the results again + m_pTable->fetchRow(m_aRow, *(m_pTable->getTableColumns()), bRetrieveData); + + // now set the bookmark for outside + *(*m_aRow->begin()) = sal_Int32(m_nRowPos + 1); + + if ( (bRetrieveData || m_pSQLAnalyzer->hasRestriction()) && m_pSQLAnalyzer->hasFunctions() ) + { + m_pSQLAnalyzer->setSelectionEvaluationResult(m_aSelectRow,m_aColMapping); + } + } + else if (!m_pFileSet->isFrozen()) // no valid record found + { + m_pFileSet->setFrozen(); + goto Error; + } + } + } + } + } + else if (m_aSQLIterator.getStatementType() == OSQLStatementType::Select && isCount()) + { + // Fetch the COUNT(*) + switch (eCursorPosition) + { + case IResultSetHelper::NEXT: + ++m_nRowPos; + break; + case IResultSetHelper::PRIOR: + --m_nRowPos; + break; + case IResultSetHelper::FIRST: + m_nRowPos = 0; + break; + case IResultSetHelper::LAST: + m_nRowPos = 0; + break; + case IResultSetHelper::RELATIVE1: + m_nRowPos += nOffset; + break; + case IResultSetHelper::ABSOLUTE1: + case IResultSetHelper::BOOKMARK: + m_nRowPos = nOffset - 1; + break; + } + + if ( m_nRowPos < 0 ) + goto Error; + else if (m_nRowPos == 0) + { + // put COUNT(*) in result-row + // (must be the first and only variable in the row) + if (m_aRow->size() >= 2) + { + *(*m_aRow)[1] = m_nRowCountResult; + *(*m_aRow)[0] = sal_Int32(1); + (*m_aRow)[1]->setBound(true); + (*m_aSelectRow)[1] = (*m_aRow)[1]; + } + } + else + { + m_nRowPos = 1; + return false; + } + } + else + // Fetch only possible at SELECT! + return false; + + return true; + +Error: + // is the Cursor positioned before the first row + // then the position will be maintained + if (nTempPos == -1) + m_nRowPos = nTempPos; + else + { + switch(eCursorPosition) + { + case IResultSetHelper::PRIOR: + case IResultSetHelper::FIRST: + m_nRowPos = -1; + break; + case IResultSetHelper::LAST: + case IResultSetHelper::NEXT: + case IResultSetHelper::ABSOLUTE1: + case IResultSetHelper::RELATIVE1: + if (nOffset > 0) + m_nRowPos = m_pFileSet.is() ? static_cast<sal_Int32>(m_pFileSet->size()) : -1; + else if (nOffset < 0) + m_nRowPos = -1; + break; + case IResultSetHelper::BOOKMARK: + m_nRowPos = nTempPos; // last Position + } + } + return false; +} + +void OResultSet::sortRows() +{ + if (!m_pSQLAnalyzer->hasRestriction() && m_aOrderbyColumnNumber.size() == 1) + { + // is just one field given for sorting + // and this field is indexed, then the Index will be used + Reference<XIndexesSupplier> xIndexSup; + m_pTable->queryInterface(cppu::UnoType<XIndexesSupplier>::get()) >>= xIndexSup; + + Reference<XIndexAccess> xIndexes; + if(xIndexSup.is()) + { + xIndexes.set(xIndexSup->getIndexes(),UNO_QUERY); + Reference<XPropertySet> xColProp; + if(m_aOrderbyColumnNumber[0] < xIndexes->getCount()) + { + xColProp.set(xIndexes->getByIndex(m_aOrderbyColumnNumber[0]),UNO_QUERY); + // iterate through the indexes to find the matching column + const sal_Int32 nCount = xIndexes->getCount(); + for(sal_Int32 i=0; i < nCount;++i) + { + Reference<XColumnsSupplier> xIndex(xIndexes->getByIndex(i),UNO_QUERY); + Reference<XNameAccess> xIndexCols = xIndex->getColumns(); + if(xIndexCols->hasByName(comphelper::getString(xColProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))))) + { + m_pFileSet = new OKeySet(); + + if(fillIndexValues(xIndex)) + return; + } + } + } + } + } + + OSortIndex::TKeyTypeVector eKeyType(m_aOrderbyColumnNumber.size()); + size_t i = 0; + for (auto const& elem : m_aOrderbyColumnNumber) + { + OSL_ENSURE(static_cast<sal_Int32>(m_aSelectRow->size()) > elem,"Invalid Index"); + switch ((*(m_aSelectRow->begin()+elem))->getValue().getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + eKeyType[i] = OKeyType::String; + break; + + case DataType::OTHER: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::REAL: + case DataType::DOUBLE: + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + case DataType::BIT: + eKeyType[i] = OKeyType::Double; + break; + + // Other types aren't implemented (so they are always FALSE) + default: + eKeyType[i] = OKeyType::NONE; + SAL_WARN( "connectivity.drivers","OFILECursor::Execute: Data type not implemented"); + break; + } + (*m_aSelectRow)[elem]->setBound(true); + ++i; + } + + m_pSortIndex.reset(new OSortIndex(std::move(eKeyType), std::vector(m_aOrderbyAscending))); + + while ( ExecuteRow( IResultSetHelper::NEXT, 1, false ) ) + { + (*m_aSelectRow)[0]->setValue( (*m_aRow)[0]->getValue() ); + if ( m_pSQLAnalyzer->hasFunctions() ) + m_pSQLAnalyzer->setSelectionEvaluationResult( m_aSelectRow, m_aColMapping ); + const sal_Int32 nBookmark = (*m_aRow->begin())->getValue().getInt32(); + ExecuteRow( IResultSetHelper::BOOKMARK, nBookmark, true, false ); + } + + // create sorted Keyset + m_pFileSet = nullptr; + m_pFileSet = m_pSortIndex->CreateKeySet(); + m_pSortIndex.reset(); + // now access to a sorted set is possible via Index +} + + +void OResultSet::OpenImpl() +{ + OSL_ENSURE(m_pSQLAnalyzer,"No analyzer set with setSqlAnalyzer!"); + if(!m_pTable.is()) + { + const OSQLTables& rTabs = m_aSQLIterator.getTables(); + if (rTabs.empty() || !rTabs.begin()->second.is()) + lcl_throwError(STR_QUERY_TOO_COMPLEX,*this); + + if ( rTabs.size() > 1 || m_aSQLIterator.hasErrors() ) + lcl_throwError(STR_QUERY_MORE_TABLES,*this); + + OSQLTable xTable = rTabs.begin()->second; + m_xColumns = m_aSQLIterator.getSelectColumns(); + + m_xColNames = xTable->getColumns(); + m_xColsIdx.set(m_xColNames,UNO_QUERY); + doTableSpecials(xTable); + Reference<XComponent> xComp(xTable,UNO_QUERY); + if(xComp.is()) + xComp->addEventListener(this); + } + + m_pTable->refreshHeader(); + + sal_Int32 nColumnCount = m_xColsIdx->getCount(); + + initializeRow(m_aRow,nColumnCount); + initializeRow(m_aEvaluateRow,nColumnCount); + initializeRow(m_aInsertRow,nColumnCount); + + + m_nResultSetConcurrency = (m_pTable->isReadOnly() || isCount()) ? ResultSetConcurrency::READ_ONLY : ResultSetConcurrency::UPDATABLE; + + // create new Index: + m_pFileSet = nullptr; + + // position at the beginning + m_nRowPos = -1; + m_nFilePos = 0; + m_nRowCountResult = -1; + m_pTable->seekRow(IResultSetHelper::ABSOLUTE1, 0, m_nFilePos); + + m_nLastVisitedPos = m_pTable->getCurrentLastPos(); + + switch(m_aSQLIterator.getStatementType()) + { + case OSQLStatementType::Select: + { + if(isCount()) + { + if(m_xColumns->size() > 1) + lcl_throwError(STR_QUERY_COMPLEX_COUNT,*this); + + m_nRowCountResult = 0; + // for now simply iterate over all rows and + // do all actions (or just count) + { + bool bOK = true; + while (bOK) + { + bOK = ExecuteRow(IResultSetHelper::NEXT); + + if (bOK) + { + m_nRowCountResult++; + } + } + + // save result of COUNT(*) in m_nRowCountResult. + // nRowCount (number of Rows in the result) = 1 for this request! + } + } + else + { + bool bDistinct = false; + assert(m_pParseTree != nullptr); + OSQLParseNode *pDistinct = m_pParseTree->getChild(1); + + assert(m_aOrderbyColumnNumber.size() == + m_aOrderbyAscending.size()); + if (pDistinct && pDistinct->getTokenID() == SQL_TOKEN_DISTINCT ) + { + // To eliminate duplicates we need to sort on all columns. + // This is not a problem because the SQL spec says that the + // order of columns that are not specified in ORDER BY + // clause is undefined, so it doesn't hurt to sort on + // these; pad the vectors to include them. + for (size_t i = 1; // 0: bookmark (see setBoundedColumns) + i < m_aColMapping.size(); ++i) + { + if (std::find(m_aOrderbyColumnNumber.begin(), + m_aOrderbyColumnNumber.end(), + sal::static_int_cast<sal_Int32>(i)) + == m_aOrderbyColumnNumber.end()) + { + m_aOrderbyColumnNumber.push_back(i); + // ASC or DESC doesn't matter + m_aOrderbyAscending.push_back(TAscendingOrder::ASC); + } + } + bDistinct = true; + } + + if (IsSorted()) + sortRows(); + + if (!m_pFileSet.is()) + { + m_pFileSet = new OKeySet(); + + if (!m_pSQLAnalyzer->hasRestriction()) + // now the Keyset can be filled! + // But be careful: It is assumed, that the FilePositions will be stored as sequence 1..n + { + if ( m_nLastVisitedPos > 0) + m_pFileSet->reserve( m_nLastVisitedPos ); + for (sal_Int32 i = 0; i < m_nLastVisitedPos; i++) + m_pFileSet->push_back(i + 1); + } + } + OSL_ENSURE(m_pFileSet.is(),"No KeySet existing! :-("); + + if(bDistinct && m_pFileSet.is()) + { + OValueRow aSearchRow = new OValueVector(m_aRow->size()); + OValueRefVector::iterator aRowIter = m_aRow->begin(); + OValueVector::iterator aSearchIter = aSearchRow->begin(); + for ( ++aRowIter,++aSearchIter; // the first column is the bookmark column + aRowIter != m_aRow->end(); + ++aRowIter,++aSearchIter) + aSearchIter->setBound((*aRowIter)->isBound()); + + size_t nMaxRow = m_pFileSet->size(); + + if (nMaxRow) + { + #if OSL_DEBUG_LEVEL > 1 + sal_Int32 nFound=0; + #endif + sal_Int32 nPos; + sal_Int32 nKey; + + for( size_t j = nMaxRow-1; j > 0; --j) + { + nPos = (*m_pFileSet)[j]; + ExecuteRow(IResultSetHelper::BOOKMARK,nPos,false); + m_pSQLAnalyzer->setSelectionEvaluationResult(m_aSelectRow,m_aColMapping); + { // cop*y row values + OValueRefVector::iterator copyFrom = m_aSelectRow->begin(); + OValueVector::iterator copyTo = aSearchRow->begin(); + for ( ++copyFrom,++copyTo; // the first column is the bookmark column + copyFrom != m_aSelectRow->end(); + ++copyFrom,++copyTo) + *copyTo = *(*copyFrom); + } + + // compare with next row + nKey = (*m_pFileSet)[j-1]; + ExecuteRow(IResultSetHelper::BOOKMARK,nKey,false); + m_pSQLAnalyzer->setSelectionEvaluationResult(m_aSelectRow,m_aColMapping); + auto rowsMismatchIters = std::mismatch(std::next(m_aSelectRow->begin()), m_aSelectRow->end(), + std::next(aSearchRow->begin()), // the first column is the bookmark column + [](const OValueRefVector::value_type& a, const OValueVector::value_type& b) { + return !a->isBound() || (*a == b); }); + + if(rowsMismatchIters.first == m_aSelectRow->end()) + (*m_pFileSet)[j] = 0; // Rows match -- Mark for deletion by setting key to 0 + #if OSL_DEBUG_LEVEL > 1 + else + nFound++; + #endif + } + + m_pFileSet->erase(std::remove(m_pFileSet->begin(),m_pFileSet->end(),0) + ,m_pFileSet->end()); + } + } + } + } break; + + case OSQLStatementType::Update: + case OSQLStatementType::Delete: + // during processing count the number of processed Rows + m_nRowCountResult = 0; + // for now simply iterate over all rows and + // run the actions (or simply count): + { + + bool bOK = true; + while (bOK) + { + bOK = ExecuteRow(IResultSetHelper::NEXT); + + if (bOK) + { + m_nRowCountResult++; + } + } + + // save result of COUNT(*) in nRowCountResult. + // nRowCount (number of rows in the result-set) = 1 for this request! + } + break; + case OSQLStatementType::Insert: + m_nRowCountResult = 0; + + OSL_ENSURE(m_aAssignValues.is(),"No assign values set!"); + if(!m_pTable->InsertRow(*m_aAssignValues, m_xColsIdx)) + { + m_nFilePos = 0; + return; + } + + m_nRowCountResult = 1; + break; + default: + SAL_WARN( "connectivity.drivers", "OResultSet::OpenImpl: unsupported statement type!" ); + break; + } + + // reset FilePos + m_nFilePos = 0; +} + +const Sequence< sal_Int8 > & OResultSet::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OResultSet::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +void OResultSet::setBoundedColumns(const OValueRefRow& _rRow, + const OValueRefRow& _rSelectRow, + const ::rtl::Reference<connectivity::OSQLColumns>& _rxColumns, + const Reference<XIndexAccess>& _xNames, + bool _bSetColumnMapping, + const Reference<XDatabaseMetaData>& _xMetaData, + std::vector<sal_Int32>& _rColMapping) +{ + ::comphelper::UStringMixEqual aCase(_xMetaData->supportsMixedCaseQuotedIdentifiers()); + + Reference<XPropertySet> xTableColumn; + OUString sTableColumnName, sSelectColumnRealName; + + const OUString sName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME); + const OUString sRealName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME); + const OUString sType = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE); + + std::map<OSQLColumns::iterator,bool> aSelectIters; + OValueRefVector::const_iterator aRowIter = _rRow->begin()+1; + for (sal_Int32 i=0; // the first column is the bookmark column + aRowIter != _rRow->end(); + ++i, ++aRowIter + ) + { + (*aRowIter)->setBound(false); + try + { + // get the table column and its name + _xNames->getByIndex(i) >>= xTableColumn; + OSL_ENSURE(xTableColumn.is(), "OResultSet::setBoundedColumns: invalid table column!"); + if (xTableColumn.is()) + xTableColumn->getPropertyValue(sName) >>= sTableColumnName; + else + sTableColumnName.clear(); + + // look if we have such a select column + // TODO: would like to have a O(log n) search here ... + for ( OSQLColumns::iterator aIter = _rxColumns->begin(); + aIter != _rxColumns->end(); + ++aIter + ) + { + if((*aIter)->getPropertySetInfo()->hasPropertyByName(sRealName)) + (*aIter)->getPropertyValue(sRealName) >>= sSelectColumnRealName; + else + (*aIter)->getPropertyValue(sName) >>= sSelectColumnRealName; + + if ( aCase(sTableColumnName, sSelectColumnRealName) && !(*aRowIter)->isBound() && aSelectIters.end() == aSelectIters.find(aIter) ) + { + aSelectIters.emplace(aIter,true); + if(_bSetColumnMapping) + { + sal_Int32 nSelectColumnPos = aIter - _rxColumns->begin() + 1; + // the getXXX methods are 1-based ... + sal_Int32 nTableColumnPos = i + 1; + // get first table column is the bookmark column ... + _rColMapping[nSelectColumnPos] = nTableColumnPos; + (*_rSelectRow)[nSelectColumnPos] = *aRowIter; + } + + (*aRowIter)->setBound(true); + sal_Int32 nType = DataType::OTHER; + if (xTableColumn.is()) + xTableColumn->getPropertyValue(sType) >>= nType; + (*aRowIter)->setTypeKind(nType); + + break; + } + } + } + catch (Exception&) + { + TOOLS_WARN_EXCEPTION( "connectivity.drivers",""); + } + } + // in this case we got more select columns as columns exist in the table + if ( !(_bSetColumnMapping && aSelectIters.size() != _rColMapping.size()) ) + return; + + Reference<XNameAccess> xNameAccess(_xNames,UNO_QUERY); + Sequence< OUString > aSelectColumns = xNameAccess->getElementNames(); + + for ( OSQLColumns::iterator aIter = _rxColumns->begin(); + aIter != _rxColumns->end(); + ++aIter + ) + { + if ( aSelectIters.end() == aSelectIters.find(aIter) ) + { + if ( (*aIter)->getPropertySetInfo()->hasPropertyByName(sRealName) ) + (*aIter)->getPropertyValue(sRealName) >>= sSelectColumnRealName; + else + (*aIter)->getPropertyValue(sName) >>= sSelectColumnRealName; + + if ( xNameAccess->hasByName( sSelectColumnRealName ) ) + { + aSelectIters.emplace(aIter,true); + sal_Int32 nSelectColumnPos = aIter - _rxColumns->begin() + 1; + const OUString* pBegin = aSelectColumns.getConstArray(); + const OUString* pEnd = pBegin + aSelectColumns.getLength(); + for(sal_Int32 i=0;pBegin != pEnd;++pBegin,++i) + { + if ( aCase(*pBegin, sSelectColumnRealName) ) + { + // the getXXX methods are 1-based ... + sal_Int32 nTableColumnPos = i + 1; + // get first table column is the bookmark column ... + _rColMapping[nSelectColumnPos] = nTableColumnPos; + (*_rSelectRow)[nSelectColumnPos] = (*_rRow)[nTableColumnPos]; + break; + } + } + } + } + } +} + +void SAL_CALL OResultSet::acquire() noexcept +{ + OResultSet_BASE::acquire(); +} + +void SAL_CALL OResultSet::release() noexcept +{ + OResultSet_BASE::release(); +} + +Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +void OResultSet::doTableSpecials(const OSQLTable& _xTable) +{ + Reference<css::lang::XUnoTunnel> xTunnel(_xTable, UNO_QUERY_THROW); + m_pTable = comphelper::getFromUnoTunnel<OFileTable>(xTunnel); + assert(m_pTable.is()); +} + +void OResultSet::clearInsertRow() +{ + m_aRow->setDeleted(false); // set to false here because this is the new row + sal_Int32 nPos = 0; + for(ORowSetValueDecoratorRef& rValue : *m_aInsertRow) + { + if ( rValue->isBound() ) + { + (*m_aRow)[nPos]->setValue( rValue->getValue() ); + } + rValue->setBound(nPos == 0); + rValue->setModified(false); + rValue->setNull(); + ++nPos; + } +} + +void OResultSet::initializeRow(OValueRefRow& _rRow,sal_Int32 _nColumnCount) +{ + if(!_rRow.is()) + { + _rRow = new OValueRefVector(_nColumnCount); + (*_rRow)[0]->setBound(true); + std::for_each(_rRow->begin()+1,_rRow->end(),TSetRefBound(false)); + } +} + +bool OResultSet::fillIndexValues(const Reference< XColumnsSupplier> &/*_xIndex*/) +{ + return false; +} + +bool OResultSet::move(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset, bool _bRetrieveData) +{ + return Move(_eCursorPosition,_nOffset,_bRetrieveData); +} + +sal_Int32 OResultSet::getDriverPos() const +{ + return (*m_aRow)[0]->getValue().getInt32(); +} + +bool OResultSet::isRowDeleted() const +{ + return m_aRow->isDeleted(); +} + +void SAL_CALL OResultSet::disposing( const EventObject& Source ) +{ + Reference<XPropertySet> xProp = m_pTable; + if(m_pTable.is() && Source.Source == xProp) + { + m_pTable.clear(); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FResultSetMetaData.cxx b/connectivity/source/drivers/file/FResultSetMetaData.cxx new file mode 100644 index 000000000..fdc944043 --- /dev/null +++ b/connectivity/source/drivers/file/FResultSetMetaData.cxx @@ -0,0 +1,188 @@ +/* -*- 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 <file/FResultSetMetaData.hxx> +#include <file/FTable.hxx> +#include <comphelper/extract.hxx> +#include <connectivity/dbexception.hxx> +#include <comphelper/types.hxx> +#include <o3tl/safeint.hxx> + + +using namespace ::comphelper; +using namespace connectivity; +using namespace dbtools; +using namespace connectivity::file; +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; + + +OResultSetMetaData::OResultSetMetaData(const ::rtl::Reference<connectivity::OSQLColumns>& _rxColumns,const OUString& _aTableName,OFileTable* _pTable) + :m_aTableName(_aTableName) + ,m_xColumns(_rxColumns) + ,m_pTable(_pTable) +{ +} + + +OResultSetMetaData::~OResultSetMetaData() +{ + m_xColumns = nullptr; +} + +void OResultSetMetaData::checkColumnIndex(sal_Int32 column) +{ + if(column <= 0 || o3tl::make_unsigned(column) > m_xColumns->size()) + throwInvalidIndexException(*this); +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnDisplaySize( sal_Int32 column ) +{ + return getPrecision(column); +} + + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnType( sal_Int32 column ) +{ + checkColumnIndex(column); + return getINT32((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))); +} + + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount( ) +{ + return m_xColumns->size(); +} + + +sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive( sal_Int32 /*column*/ ) +{ + return false; +} + + +OUString SAL_CALL OResultSetMetaData::getSchemaName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + + +OUString SAL_CALL OResultSetMetaData::getColumnName( sal_Int32 column ) +{ + checkColumnIndex(column); + + Any aName((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))); + return aName.hasValue() ? getString(aName) : getString((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))); +} + +OUString SAL_CALL OResultSetMetaData::getTableName( sal_Int32 /*column*/ ) +{ + return m_aTableName; +} + +OUString SAL_CALL OResultSetMetaData::getCatalogName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +OUString SAL_CALL OResultSetMetaData::getColumnTypeName( sal_Int32 column ) +{ + checkColumnIndex(column); + return getString((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME))); +} + +OUString SAL_CALL OResultSetMetaData::getColumnLabel( sal_Int32 column ) +{ + return getColumnName(column); +} + +OUString SAL_CALL OResultSetMetaData::getColumnServiceName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + + +sal_Bool SAL_CALL OResultSetMetaData::isCurrency( sal_Int32 column ) +{ + checkColumnIndex(column); + return getBOOL((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY))); +} + + +sal_Bool SAL_CALL OResultSetMetaData::isAutoIncrement( sal_Int32 /*setCatalogcolumn*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OResultSetMetaData::isSigned( sal_Int32 /*column*/ ) +{ + return true; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getPrecision( sal_Int32 column ) +{ + checkColumnIndex(column); + return getINT32((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))); +} + +sal_Int32 SAL_CALL OResultSetMetaData::getScale( sal_Int32 column ) +{ + checkColumnIndex(column); + return getINT32((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))); +} + + +sal_Int32 SAL_CALL OResultSetMetaData::isNullable( sal_Int32 column ) +{ + checkColumnIndex(column); + return getINT32((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE))); +} + + +sal_Bool SAL_CALL OResultSetMetaData::isSearchable( sal_Int32 /*column*/ ) +{ + return true; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isReadOnly( sal_Int32 column ) +{ + checkColumnIndex(column); + return m_pTable->isReadOnly() || ( + (*m_xColumns)[column-1]->getPropertySetInfo()->hasPropertyByName(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FUNCTION)) && + ::cppu::any2bool((*m_xColumns)[column-1]->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FUNCTION)))); +} + + +sal_Bool SAL_CALL OResultSetMetaData::isDefinitelyWritable( sal_Int32 column ) +{ + return !isReadOnly(column); +} + +sal_Bool SAL_CALL OResultSetMetaData::isWritable( sal_Int32 column ) +{ + return !isReadOnly(column); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FStatement.cxx b/connectivity/source/drivers/file/FStatement.cxx new file mode 100644 index 000000000..d32ac0ed2 --- /dev/null +++ b/connectivity/source/drivers/file/FStatement.cxx @@ -0,0 +1,711 @@ +/* -*- 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 <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <file/FStatement.hxx> +#include <file/FConnection.hxx> +#include <sqlbison.hxx> +#include <file/FDriver.hxx> +#include <file/FResultSet.hxx> +#include <sal/log.hxx> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> +#include <algorithm> + +namespace connectivity::file +{ + + +using namespace dbtools; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::sdbcx; +using namespace com::sun::star::container; + +OStatement_Base::OStatement_Base(OConnection* _pConnection ) + :OStatement_BASE(m_aMutex) + ,::comphelper::OPropertyContainer(OStatement_BASE::rBHelper) + ,m_xDBMetaData(_pConnection->getMetaData()) + ,m_aParser( _pConnection->getDriver()->getComponentContext() ) + ,m_aSQLIterator( _pConnection, _pConnection->createCatalog()->getTables(), m_aParser ) + ,m_pConnection(_pConnection) + ,m_pParseTree(nullptr) + ,m_nMaxFieldSize(0) + ,m_nMaxRows(0) + ,m_nQueryTimeOut(0) + ,m_nFetchSize(0) + ,m_nResultSetType(ResultSetType::FORWARD_ONLY) + ,m_nFetchDirection(FetchDirection::FORWARD) + ,m_nResultSetConcurrency(ResultSetConcurrency::UPDATABLE) + ,m_bEscapeProcessing(true) +{ + sal_Int32 nAttrib = 0; + + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), PROPERTY_ID_CURSORNAME, nAttrib,&m_aCursorName, ::cppu::UnoType<OUString>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE), PROPERTY_ID_MAXFIELDSIZE, nAttrib,&m_nMaxFieldSize, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS), PROPERTY_ID_MAXROWS, nAttrib,&m_nMaxRows, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT), PROPERTY_ID_QUERYTIMEOUT, nAttrib,&m_nQueryTimeOut, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), PROPERTY_ID_FETCHSIZE, nAttrib,&m_nFetchSize, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), PROPERTY_ID_RESULTSETTYPE, nAttrib,&m_nResultSetType, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), PROPERTY_ID_FETCHDIRECTION, nAttrib,&m_nFetchDirection, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING),PROPERTY_ID_ESCAPEPROCESSING, nAttrib,&m_bEscapeProcessing,cppu::UnoType<bool>::get()); + + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), PROPERTY_ID_RESULTSETCONCURRENCY, nAttrib,&m_nResultSetConcurrency, ::cppu::UnoType<sal_Int32>::get()); +} + +OStatement_Base::~OStatement_Base() +{ + osl_atomic_increment( &m_refCount ); + disposing(); +} + +void OStatement_Base::disposeResultSet() +{ + SAL_INFO( "connectivity.drivers", "file Ocke.Janssen@sun.com OStatement_Base::disposeResultSet" ); + // free the cursor if alive + Reference< XComponent > xComp(m_xResultSet.get(), UNO_QUERY); + assert(xComp.is() || !m_xResultSet.get().is()); + if (xComp.is()) + xComp->dispose(); + m_xResultSet.clear(); +} + +void OStatement_BASE2::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + disposeResultSet(); + + if(m_pSQLAnalyzer) + m_pSQLAnalyzer->dispose(); + + if(m_aRow.is()) + { + m_aRow->clear(); + m_aRow = nullptr; + } + + m_aSQLIterator.dispose(); + + m_pTable.clear(); + + m_pConnection.clear(); + + if ( m_pParseTree ) + { + delete m_pParseTree; + m_pParseTree = nullptr; + } + + OStatement_Base::disposing(); +} + +void SAL_CALL OStatement_Base::acquire() noexcept +{ + OStatement_BASE::acquire(); +} + +void SAL_CALL OStatement_BASE2::release() noexcept +{ + OStatement_BASE::release(); +} + +Any SAL_CALL OStatement_Base::queryInterface( const Type & rType ) +{ + const Any aRet = OStatement_BASE::queryInterface(rType); + return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType); +} + +Sequence< Type > SAL_CALL OStatement_Base::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(),OStatement_BASE::getTypes()); +} + + +void SAL_CALL OStatement_Base::cancel( ) +{ +} + +void SAL_CALL OStatement_Base::close() +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + } + dispose(); +} + +void OStatement_Base::closeResultSet() +{ + SAL_INFO( "connectivity.drivers", "file Ocke.Janssen@sun.com OStatement_Base::clearMyResultSet " ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + Reference< XCloseable > xCloseable(m_xResultSet.get(), UNO_QUERY); + assert(xCloseable.is() || !m_xResultSet.get().is()); + if (xCloseable.is()) + { + try + { + xCloseable->close(); + } + catch( const DisposedException& ) { } + } + + m_xResultSet.clear(); +} + +Any SAL_CALL OStatement_Base::getWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + return Any(m_aLastWarning); +} + +void SAL_CALL OStatement_Base::clearWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + m_aLastWarning = SQLWarning(); +} + +::cppu::IPropertyArrayHelper* OStatement_Base::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +::cppu::IPropertyArrayHelper & OStatement_Base::getInfoHelper() +{ + return *getArrayHelper(); +} + +rtl::Reference<OResultSet> OStatement::createResultSet() +{ + return new OResultSet(this,m_aSQLIterator); +} + +IMPLEMENT_SERVICE_INFO(OStatement,"com.sun.star.sdbc.driver.file.Statement","com.sun.star.sdbc.Statement"); + +void SAL_CALL OStatement::acquire() noexcept +{ + OStatement_BASE2::acquire(); +} + +void SAL_CALL OStatement::release() noexcept +{ + OStatement_BASE2::release(); +} + + +sal_Bool SAL_CALL OStatement::execute( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + executeQuery(sql); + + return m_aSQLIterator.getStatementType() == OSQLStatementType::Select; +} + + +Reference< XResultSet > SAL_CALL OStatement::executeQuery( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + construct(sql); + Reference< XResultSet > xRS; + rtl::Reference<OResultSet> pResult = createResultSet(); + xRS = pResult; + initializeResultSet(pResult.get()); + m_xResultSet = xRS; + + pResult->OpenImpl(); + + return xRS; +} + +Reference< XConnection > SAL_CALL OStatement::getConnection( ) +{ + return m_pConnection; +} + +sal_Int32 SAL_CALL OStatement::executeUpdate( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + construct(sql); + rtl::Reference<OResultSet> pResult = createResultSet(); + initializeResultSet(pResult.get()); + pResult->OpenImpl(); + + return pResult->getRowCountResult(); +} + + +void SAL_CALL OStatement_Base::disposing() +{ + if(m_aEvaluateRow.is()) + { + m_aEvaluateRow->clear(); + m_aEvaluateRow = nullptr; + } + OStatement_BASE::disposing(); +} + +Reference< css::beans::XPropertySetInfo > SAL_CALL OStatement_Base::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +Any SAL_CALL OStatement::queryInterface( const Type & rType ) +{ + Any aRet = OStatement_XStatement::queryInterface( rType); + return aRet.hasValue() ? aRet : OStatement_BASE2::queryInterface( rType); +} + +void OStatement_Base::analyzeSQL() +{ + OSL_ENSURE(m_pSQLAnalyzer,"OResultSet::analyzeSQL: Analyzer isn't set!"); + // start analysing the statement + m_pSQLAnalyzer->setOrigColumns(m_xColNames); + m_pSQLAnalyzer->start(m_pParseTree); + + const OSQLParseNode* pOrderbyClause = m_aSQLIterator.getOrderTree(); + if(!pOrderbyClause) + return; + + OSQLParseNode * pOrderingSpecCommalist = pOrderbyClause->getChild(2); + OSL_ENSURE(SQL_ISRULE(pOrderingSpecCommalist,ordering_spec_commalist),"OResultSet: Error in Parse Tree"); + + for (size_t m = 0; m < pOrderingSpecCommalist->count(); m++) + { + OSQLParseNode * pOrderingSpec = pOrderingSpecCommalist->getChild(m); + OSL_ENSURE(SQL_ISRULE(pOrderingSpec,ordering_spec),"OResultSet: Error in Parse Tree"); + OSL_ENSURE(pOrderingSpec->count() == 2,"OResultSet: Error in Parse Tree"); + + OSQLParseNode * pColumnRef = pOrderingSpec->getChild(0); + if(!SQL_ISRULE(pColumnRef,column_ref)) + { + throw SQLException(); + } + OSQLParseNode * pAscendingDescending = pOrderingSpec->getChild(1); + setOrderbyColumn(pColumnRef,pAscendingDescending); + } +} + +void OStatement_Base::setOrderbyColumn( OSQLParseNode const * pColumnRef, + OSQLParseNode const * pAscendingDescending) +{ + OUString aColumnName; + if (pColumnRef->count() == 1) + aColumnName = pColumnRef->getChild(0)->getTokenValue(); + else if (pColumnRef->count() == 3) + { + pColumnRef->getChild(2)->parseNodeToStr( aColumnName, getOwnConnection(), nullptr, false, false ); + } + else + { + throw SQLException(); + } + + Reference<XColumnLocate> xColLocate(m_xColNames,UNO_QUERY); + if(!xColLocate.is()) + return; + // Everything tested and we have the name of the Column. + // What number is the Column? + ::rtl::Reference<OSQLColumns> aSelectColumns = m_aSQLIterator.getSelectColumns(); + ::comphelper::UStringMixEqual aCase; + OSQLColumns::const_iterator aFind = ::connectivity::find(aSelectColumns->begin(),aSelectColumns->end(),aColumnName,aCase); + if ( aFind == aSelectColumns->end() ) + throw SQLException(); + m_aOrderbyColumnNumber.push_back((aFind - aSelectColumns->begin()) + 1); + + // Ascending or Descending? + m_aOrderbyAscending.push_back(SQL_ISTOKEN(pAscendingDescending,DESC) ? TAscendingOrder::DESC : TAscendingOrder::ASC); +} + +void OStatement_Base::construct(const OUString& sql) +{ + OUString aErr; + m_pParseTree = m_aParser.parseTree(aErr,sql).release(); + if(!m_pParseTree) + throw SQLException(aErr,*this,OUString(),0,Any()); + + m_aSQLIterator.setParseTree(m_pParseTree); + m_aSQLIterator.traverseAll(); + const OSQLTables& rTabs = m_aSQLIterator.getTables(); + + // sanity checks + if ( rTabs.empty() ) + // no tables -> nothing to operate on -> error + m_pConnection->throwGenericSQLException(STR_QUERY_NO_TABLE,*this); + + if ( rTabs.size() > 1 || m_aSQLIterator.hasErrors() ) + // more than one table -> can't operate on them -> error + m_pConnection->throwGenericSQLException(STR_QUERY_MORE_TABLES,*this); + + if ( (m_aSQLIterator.getStatementType() == OSQLStatementType::Select) && m_aSQLIterator.getSelectColumns()->empty() ) + // SELECT statement without columns -> error + m_pConnection->throwGenericSQLException(STR_QUERY_NO_COLUMN,*this); + + switch(m_aSQLIterator.getStatementType()) + { + case OSQLStatementType::CreateTable: + case OSQLStatementType::OdbcCall: + case OSQLStatementType::Unknown: + m_pConnection->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,*this); + break; + case OSQLStatementType::Select: + if(SQL_ISRULE(m_aSQLIterator.getParseTree(), union_statement)) + { + m_pConnection->throwGenericSQLException(STR_QUERY_TOO_COMPLEX, *this); + } + assert(SQL_ISRULE(m_aSQLIterator.getParseTree(), select_statement)); + break; + default: + break; + } + + // at this moment we support only one table per select statement + m_pTable = comphelper::getFromUnoTunnel<OFileTable>(rTabs.begin()->second); + OSL_ENSURE(m_pTable.is(),"No table!"); + if ( m_pTable.is() ) + m_xColNames = m_pTable->getColumns(); + Reference<XIndexAccess> xNames(m_xColNames,UNO_QUERY); + // set the binding of the resultrow + m_aRow = new OValueRefVector(xNames->getCount()); + (*m_aRow)[0]->setBound(true); + std::for_each(m_aRow->begin()+1,m_aRow->end(),TSetRefBound(false)); + + // set the binding of the resultrow + m_aEvaluateRow = new OValueRefVector(xNames->getCount()); + + (*m_aEvaluateRow)[0]->setBound(true); + std::for_each(m_aEvaluateRow->begin()+1,m_aEvaluateRow->end(),TSetRefBound(false)); + + // set the select row + m_aSelectRow = new OValueRefVector(m_aSQLIterator.getSelectColumns()->size()); + std::for_each(m_aSelectRow->begin(),m_aSelectRow->end(),TSetRefBound(true)); + + // create the column mapping + createColumnMapping(); + + m_pSQLAnalyzer.reset( new OSQLAnalyzer(m_pConnection.get()) ); + + analyzeSQL(); +} + +void OStatement_Base::createColumnMapping() +{ + // initialize the column index map (mapping select columns to table columns) + ::rtl::Reference<connectivity::OSQLColumns> xColumns = m_aSQLIterator.getSelectColumns(); + m_aColMapping.resize(xColumns->size() + 1); + for (sal_Int32 i=0; i<static_cast<sal_Int32>(m_aColMapping.size()); ++i) + m_aColMapping[i] = i; + + Reference<XIndexAccess> xNames(m_xColNames,UNO_QUERY); + // now check which columns are bound + OResultSet::setBoundedColumns(m_aRow,m_aSelectRow,xColumns,xNames,true,m_xDBMetaData,m_aColMapping); +} + +void OStatement_Base::initializeResultSet(OResultSet* _pResult) +{ + GetAssignValues(); + + _pResult->setSqlAnalyzer(m_pSQLAnalyzer.get()); + _pResult->setOrderByColumns(std::vector(m_aOrderbyColumnNumber)); + _pResult->setOrderByAscending(std::vector(m_aOrderbyAscending)); + _pResult->setBindingRow(m_aRow); + _pResult->setColumnMapping(std::vector(m_aColMapping)); + _pResult->setEvaluationRow(m_aEvaluateRow); + _pResult->setAssignValues(m_aAssignValues); + _pResult->setSelectRow(m_aSelectRow); + + m_pSQLAnalyzer->bindSelectRow(m_aRow); + m_pSQLAnalyzer->bindEvaluationRow(m_aEvaluateRow); // Set values in the code of the Compiler +} + +void OStatement_Base::GetAssignValues() +{ + if (m_pParseTree == nullptr) + { + ::dbtools::throwFunctionSequenceException(*this); + return; + } + + if (SQL_ISRULE(m_pParseTree,select_statement)) + // no values have to be set for SELECT + return; + else if (SQL_ISRULE(m_pParseTree,insert_statement)) + { + // Create Row for the values to be set (Reference through new) + if(m_aAssignValues.is()) + m_aAssignValues->clear(); + sal_Int32 nCount = Reference<XIndexAccess>(m_xColNames,UNO_QUERY_THROW)->getCount(); + m_aAssignValues = new OAssignValues(nCount); + // unbound all + std::for_each(m_aAssignValues->begin()+1,m_aAssignValues->end(),TSetRefBound(false)); + + m_aParameterIndexes.resize(nCount+1,SQL_NO_PARAMETER); + + // List of Column-Names, that exist in the column_commalist (separated by ;): + std::vector<OUString> aColumnNameList; + + OSL_ENSURE(m_pParseTree->count() >= 4,"OResultSet: Error in Parse Tree"); + + OSQLParseNode * pOptColumnCommalist = m_pParseTree->getChild(3); + OSL_ENSURE(pOptColumnCommalist != nullptr,"OResultSet: Error in Parse Tree"); + OSL_ENSURE(SQL_ISRULE(pOptColumnCommalist,opt_column_commalist),"OResultSet: Error in Parse Tree"); + if (pOptColumnCommalist->count() == 0) + { + const Sequence< OUString>& aNames = m_xColNames->getElementNames(); + aColumnNameList.insert(aColumnNameList.end(), aNames.begin(), aNames.end()); + } + else + { + OSL_ENSURE(pOptColumnCommalist->count() == 3,"OResultSet: Error in Parse Tree"); + + OSQLParseNode * pColumnCommalist = pOptColumnCommalist->getChild(1); + OSL_ENSURE(pColumnCommalist != nullptr,"OResultSet: Error in Parse Tree"); + OSL_ENSURE(SQL_ISRULE(pColumnCommalist,column_commalist),"OResultSet: Error in Parse Tree"); + OSL_ENSURE(pColumnCommalist->count() > 0,"OResultSet: Error in Parse Tree"); + + // All Columns in the column_commalist ... + for (size_t i = 0; i < pColumnCommalist->count(); i++) + { + OSQLParseNode * pCol = pColumnCommalist->getChild(i); + OSL_ENSURE(pCol != nullptr,"OResultSet: Error in Parse Tree"); + aColumnNameList.push_back(pCol->getTokenValue()); + } + } + if ( aColumnNameList.empty() ) + throwFunctionSequenceException(*this); + + // Values ... + OSQLParseNode * pValuesOrQuerySpec = m_pParseTree->getChild(4); + OSL_ENSURE(pValuesOrQuerySpec != nullptr,"OResultSet: pValuesOrQuerySpec must not be NULL!"); + OSL_ENSURE(SQL_ISRULE(pValuesOrQuerySpec,values_or_query_spec),"OResultSet: ! SQL_ISRULE(pValuesOrQuerySpec,values_or_query_spec)"); + OSL_ENSURE(pValuesOrQuerySpec->count() > 0,"OResultSet: pValuesOrQuerySpec->count() <= 0"); + + // just "VALUES" is allowed ... + if (! SQL_ISTOKEN(pValuesOrQuerySpec->getChild(0),VALUES)) + throwFunctionSequenceException(*this); + + OSL_ENSURE(pValuesOrQuerySpec->count() == 4,"OResultSet: pValuesOrQuerySpec->count() != 4"); + + // List of values + OSQLParseNode * pInsertAtomCommalist = pValuesOrQuerySpec->getChild(2); + OSL_ENSURE(pInsertAtomCommalist != nullptr,"OResultSet: pInsertAtomCommalist must not be NULL!"); + OSL_ENSURE(pInsertAtomCommalist->count() > 0,"OResultSet: pInsertAtomCommalist <= 0"); + + sal_Int32 nIndex=0; + for (size_t i = 0; i < pInsertAtomCommalist->count(); i++) + { + OSQLParseNode * pRow_Value_Const = pInsertAtomCommalist->getChild(i); // row_value_constructor + OSL_ENSURE(pRow_Value_Const != nullptr,"OResultSet: pRow_Value_Const must not be NULL!"); + if(SQL_ISRULE(pRow_Value_Const,parameter)) + { + ParseAssignValues(aColumnNameList,pRow_Value_Const,nIndex++); // only one Columnname allowed per loop + } + else if(pRow_Value_Const->isToken()) + ParseAssignValues(aColumnNameList,pRow_Value_Const,i); + else + { + if(pRow_Value_Const->count() == aColumnNameList.size()) + { + for (size_t j = 0; j < pRow_Value_Const->count(); ++j) + ParseAssignValues(aColumnNameList,pRow_Value_Const->getChild(j),nIndex++); + } + else + throwFunctionSequenceException(*this); + } + } + } + else if (SQL_ISRULE(m_pParseTree,update_statement_searched)) + { + if(m_aAssignValues.is()) + m_aAssignValues->clear(); + sal_Int32 nCount = Reference<XIndexAccess>(m_xColNames,UNO_QUERY_THROW)->getCount(); + m_aAssignValues = new OAssignValues(nCount); + // unbound all + std::for_each(m_aAssignValues->begin()+1,m_aAssignValues->end(),TSetRefBound(false)); + + m_aParameterIndexes.resize(nCount+1,SQL_NO_PARAMETER); + + OSL_ENSURE(m_pParseTree->count() >= 4,"OResultSet: Error in Parse Tree"); + + OSQLParseNode * pAssignmentCommalist = m_pParseTree->getChild(3); + OSL_ENSURE(pAssignmentCommalist != nullptr,"OResultSet: pAssignmentCommalist == NULL"); + OSL_ENSURE(SQL_ISRULE(pAssignmentCommalist,assignment_commalist),"OResultSet: Error in Parse Tree"); + OSL_ENSURE(pAssignmentCommalist->count() > 0,"OResultSet: pAssignmentCommalist->count() <= 0"); + + // work on all assignments (commalist) ... + std::vector< OUString> aList(1); + for (size_t i = 0; i < pAssignmentCommalist->count(); i++) + { + OSQLParseNode * pAssignment = pAssignmentCommalist->getChild(i); + OSL_ENSURE(pAssignment != nullptr,"OResultSet: pAssignment == NULL"); + OSL_ENSURE(SQL_ISRULE(pAssignment,assignment),"OResultSet: Error in Parse Tree"); + OSL_ENSURE(pAssignment->count() == 3,"OResultSet: pAssignment->count() != 3"); + + OSQLParseNode * pCol = pAssignment->getChild(0); + OSL_ENSURE(pCol != nullptr,"OResultSet: pCol == NULL"); + + OSQLParseNode * pComp = pAssignment->getChild(1); + OSL_ENSURE(pComp != nullptr,"OResultSet: pComp == NULL"); + OSL_ENSURE(pComp->getNodeType() == SQLNodeType::Equal,"OResultSet: pComp->getNodeType() != SQLNodeType::Comparison"); + if (pComp->getTokenValue().toChar() != '=') + { + throwFunctionSequenceException(*this); + } + + OSQLParseNode * pVal = pAssignment->getChild(2); + OSL_ENSURE(pVal != nullptr,"OResultSet: pVal == NULL"); + aList[0] = pCol->getTokenValue(); + ParseAssignValues(aList,pVal,0); + } + + } +} + +void OStatement_Base::ParseAssignValues(const std::vector< OUString>& aColumnNameList,OSQLParseNode* pRow_Value_Constructor_Elem, sal_Int32 nIndex) +{ + OSL_ENSURE(o3tl::make_unsigned(nIndex) <= aColumnNameList.size(),"SdbFileCursor::ParseAssignValues: nIndex > aColumnNameList.GetTokenCount()"); + OUString aColumnName(aColumnNameList[nIndex]); + OSL_ENSURE(aColumnName.getLength() > 0,"OResultSet: Column-Name not found"); + OSL_ENSURE(pRow_Value_Constructor_Elem != nullptr,"OResultSet: pRow_Value_Constructor_Elem must not be NULL!"); + + if (pRow_Value_Constructor_Elem->getNodeType() == SQLNodeType::String || + pRow_Value_Constructor_Elem->getNodeType() == SQLNodeType::IntNum || + pRow_Value_Constructor_Elem->getNodeType() == SQLNodeType::ApproxNum) + { + // set value: + SetAssignValue(aColumnName, pRow_Value_Constructor_Elem->getTokenValue()); + } + else if (SQL_ISTOKEN(pRow_Value_Constructor_Elem,NULL)) + { + // set NULL + SetAssignValue(aColumnName, OUString(), true); + } + else if (SQL_ISRULE(pRow_Value_Constructor_Elem,parameter)) + parseParamterElem(aColumnName,pRow_Value_Constructor_Elem); + else + { + throwFunctionSequenceException(*this); + } +} + +void OStatement_Base::SetAssignValue(const OUString& aColumnName, + const OUString& aValue, + bool bSetNull, + sal_uInt32 nParameter) +{ + Reference<XPropertySet> xCol; + m_xColNames->getByName(aColumnName) >>= xCol; + sal_Int32 nId = Reference<XColumnLocate>(m_xColNames,UNO_QUERY_THROW)->findColumn(aColumnName); + // does this column actually exist in the file? + + if (!xCol.is()) + { + // This Column doesn't exist! + throwFunctionSequenceException(*this); + } + + + // Everything tested and we have the names of the Column. + // Now allocate one Value, set the value and tie the value to the Row. + if (bSetNull) + (*m_aAssignValues)[nId]->setNull(); + else + { + switch (::comphelper::getINT32(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)))) + { + // put criteria depending on the Type as String or double in the variable + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + *(*m_aAssignValues)[nId] = ORowSetValue(aValue); + //Characterset is already converted, since the entire statement was converted + break; + + case DataType::BIT: + if (aValue.equalsIgnoreAsciiCase("TRUE") || aValue[0] == '1') + *(*m_aAssignValues)[nId] = true; + else if (aValue.equalsIgnoreAsciiCase("FALSE") || aValue[0] == '0') + *(*m_aAssignValues)[nId] = false; + else + throwFunctionSequenceException(*this); + break; + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::REAL: + case DataType::DOUBLE: + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + *(*m_aAssignValues)[nId] = ORowSetValue(aValue); + break; + default: + throwFunctionSequenceException(*this); + } + } + + // save Parameter-No. (as User Data) + // SQL_NO_PARAMETER = no Parameter. + m_aAssignValues->setParameterIndex(nId,nParameter); + if(nParameter != SQL_NO_PARAMETER) + m_aParameterIndexes[nParameter] = nId; +} + +void OStatement_Base::parseParamterElem(const OUString& /*_sColumnName*/,OSQLParseNode* /*pRow_Value_Constructor_Elem*/) +{ + // do nothing here +} + +}// namespace + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FStringFunctions.cxx b/connectivity/source/drivers/file/FStringFunctions.cxx new file mode 100644 index 000000000..94152bc5d --- /dev/null +++ b/connectivity/source/drivers/file/FStringFunctions.cxx @@ -0,0 +1,233 @@ +/* -*- 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 <file/FStringFunctions.hxx> + +#include <comphelper/string.hxx> +#include <rtl/ustrbuf.hxx> + +using namespace connectivity; +using namespace connectivity::file; + +ORowSetValue OOp_Upper::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + return lhs.getString().toAsciiUpperCase(); +} + +ORowSetValue OOp_Lower::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + return lhs.getString().toAsciiLowerCase(); +} + +ORowSetValue OOp_Ascii::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + OString sStr(OUStringToOString(lhs.getString(), RTL_TEXTENCODING_ASCII_US)); + sal_Int32 nAscii = sStr.toChar(); + return nAscii; +} + +ORowSetValue OOp_CharLength::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + return lhs.getString().getLength(); +} + +ORowSetValue OOp_Char::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (lhs.empty()) + return ORowSetValue(); + + OUStringBuffer sRet(static_cast<sal_Int32>(lhs.size())); + std::vector<ORowSetValue>::const_reverse_iterator aIter = lhs.rbegin(); + std::vector<ORowSetValue>::const_reverse_iterator aEnd = lhs.rend(); + for (; aIter != aEnd; ++aIter) + { + if (!aIter->isNull()) + { + char c = static_cast<char>(aIter->getInt32()); + + sRet.appendAscii(&c, 1); + } + } + + return sRet.makeStringAndClear(); +} + +ORowSetValue OOp_Concat::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (lhs.empty()) + return ORowSetValue(); + + OUStringBuffer sRet; + std::vector<ORowSetValue>::const_reverse_iterator aIter = lhs.rbegin(); + std::vector<ORowSetValue>::const_reverse_iterator aEnd = lhs.rend(); + for (; aIter != aEnd; ++aIter) + { + if (aIter->isNull()) + return ORowSetValue(); + + sRet.append(aIter->getString()); + } + + return sRet.makeStringAndClear(); +} + +ORowSetValue OOp_Locate::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (std::any_of(lhs.begin(), lhs.end(), + [](const ORowSetValue& rValue) { return rValue.isNull(); })) + return ORowSetValue(); + + if (lhs.size() == 2) + return OUString(OUString::number(lhs[0].getString().indexOf(lhs[1].getString()) + 1)); + + else if (lhs.size() != 3) + return ORowSetValue(); + + return lhs[1].getString().indexOf(lhs[2].getString(), lhs[0].getInt32()) + 1; +} + +ORowSetValue OOp_SubString::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (std::any_of(lhs.begin(), lhs.end(), + [](const ORowSetValue& rValue) { return rValue.isNull(); })) + return ORowSetValue(); + + if (lhs.size() == 2 && lhs[0].getInt32() >= sal_Int32(0)) + return lhs[1].getString().copy(lhs[0].getInt32() - 1); + + else if (lhs.size() != 3 || lhs[1].getInt32() < sal_Int32(0)) + return ORowSetValue(); + + return lhs[2].getString().copy(lhs[1].getInt32() - 1, lhs[0].getInt32()); +} + +ORowSetValue OOp_LTrim::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + OUString sRet = lhs.getString(); + OUString sNew = sRet.trim(); + return sRet.copy(sRet.indexOf(sNew)); +} + +ORowSetValue OOp_RTrim::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + OUString sRet = lhs.getString(); + OUString sNew = sRet.trim(); + return sRet.copy(0, sRet.lastIndexOf(sNew[sNew.getLength() - 1]) + 1); +} + +ORowSetValue OOp_Space::operate(const ORowSetValue& lhs) const +{ + if (lhs.isNull()) + return lhs; + + sal_Int32 nCount = std::max(lhs.getInt32(), sal_Int32(0)); + OUStringBuffer sRet(nCount); + comphelper::string::padToLength(sRet, nCount, ' '); + return sRet.makeStringAndClear(); +} + +ORowSetValue OOp_Replace::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (lhs.size() != 3) + return ORowSetValue(); + + OUString sStr = lhs[2].getString(); + OUString sFrom = lhs[1].getString(); + OUString sTo = lhs[0].getString(); + sal_Int32 nIndexOf = sStr.indexOf(sFrom); + while (nIndexOf != -1) + { + sStr = sStr.replaceAt(nIndexOf, sFrom.getLength(), sTo); + nIndexOf = sStr.indexOf(sFrom, nIndexOf + sTo.getLength()); + } + + return sStr; +} + +ORowSetValue OOp_Repeat::operate(const ORowSetValue& lhs, const ORowSetValue& rhs) const +{ + if (lhs.isNull() || rhs.isNull()) + return lhs; + + const OUString s = lhs.getString(); + const sal_Int32 nCount = std::max(rhs.getInt32(), sal_Int32(0)); + OUStringBuffer sRet(s.getLength() * nCount); + for (sal_Int32 i = 0; i < nCount; ++i) + { + sRet.append(s); + } + return sRet.makeStringAndClear(); +} + +ORowSetValue OOp_Insert::operate(const std::vector<ORowSetValue>& lhs) const +{ + if (lhs.size() != 4) + return ORowSetValue(); + + OUString sStr = lhs[3].getString(); + + sal_Int32 nStart = lhs[2].getInt32(); + if (nStart < 1) + nStart = 1; + return sStr.replaceAt(nStart - 1, lhs[1].getInt32(), lhs[0].getString()); +} + +ORowSetValue OOp_Left::operate(const ORowSetValue& lhs, const ORowSetValue& rhs) const +{ + if (lhs.isNull() || rhs.isNull()) + return lhs; + + OUString sRet = lhs.getString(); + sal_Int32 nCount = rhs.getInt32(); + if (nCount < 0) + return ORowSetValue(); + return sRet.copy(0, nCount); +} + +ORowSetValue OOp_Right::operate(const ORowSetValue& lhs, const ORowSetValue& rhs) const +{ + if (lhs.isNull() || rhs.isNull()) + return lhs; + + sal_Int32 nCount = rhs.getInt32(); + OUString sRet = lhs.getString(); + if (nCount < 0 || nCount >= sRet.getLength()) + return ORowSetValue(); + + return sRet.copy(sRet.getLength() - nCount, nCount); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FTable.cxx b/connectivity/source/drivers/file/FTable.cxx new file mode 100644 index 000000000..6ec776452 --- /dev/null +++ b/connectivity/source/drivers/file/FTable.cxx @@ -0,0 +1,185 @@ +/* -*- 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 <file/FTable.hxx> +#include <file/FColumns.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <comphelper/servicehelper.hxx> +#include <unotools/ucbstreamhelper.hxx> + +using namespace connectivity; +using namespace connectivity::file; +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; + +OFileTable::OFileTable(sdbcx::OCollection* _pTables,OConnection* _pConnection) +: OTable_TYPEDEF(_pTables,_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()) + ,m_pConnection(_pConnection) + ,m_nFilePos(0) + ,m_nBufferSize(0) + ,m_bWriteable(false) +{ + construct(); + m_aColumns = new OSQLColumns(); +} + +OFileTable::OFileTable( sdbcx::OCollection* _pTables,OConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName ) + : OTable_TYPEDEF(_pTables,_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers(), + Name, + Type, + Description, + SchemaName, + CatalogName) + , m_pConnection(_pConnection) + , m_nFilePos(0) + , m_nBufferSize(0) + , m_bWriteable(false) +{ + m_aColumns = new OSQLColumns(); + construct(); + // refreshColumns(); +} + +OFileTable::~OFileTable( ) +{ +} + +void OFileTable::refreshColumns() +{ + ::std::vector< OUString> aVector; + Reference< XResultSet > xResult = m_pConnection->getMetaData()->getColumns(Any(), + m_SchemaName,m_Name, "%"); + + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while(xResult->next()) + aVector.push_back(xRow->getString(4)); + } + + if(m_xColumns) + m_xColumns->reFill(aVector); + else + m_xColumns.reset(new OColumns(this,m_aMutex,aVector)); +} + +void OFileTable::refreshKeys() +{ +} + +void OFileTable::refreshIndexes() +{ +} + +Any SAL_CALL OFileTable::queryInterface( const Type & rType ) +{ + if( rType == cppu::UnoType<XKeysSupplier>::get()|| + rType == cppu::UnoType<XRename>::get()|| + rType == cppu::UnoType<XAlterTable>::get()|| + rType == cppu::UnoType<XIndexesSupplier>::get()|| + rType == cppu::UnoType<XDataDescriptorFactory>::get()) + return Any(); + + return OTable_TYPEDEF::queryInterface(rType); +} + +void SAL_CALL OFileTable::disposing() +{ + OTable::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + + FileClose(); +} + +const Sequence< sal_Int8 > & OFileTable::getUnoTunnelId() +{ + static const comphelper::UnoIdInit s_Id; + return s_Id.getSeq(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OFileTable::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<OTable_TYPEDEF>{}); +} + +void OFileTable::FileClose() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + m_pFileStream.reset(); + m_pBuffer.reset(); +} + +bool OFileTable::InsertRow(OValueRefVector& /*rRow*/, const css::uno::Reference< css::container::XIndexAccess>& /*_xCols*/) +{ + return false; +} + +bool OFileTable::DeleteRow(const OSQLColumns& /*_rCols*/) +{ + return false; +} + +bool OFileTable::UpdateRow(OValueRefVector& /*rRow*/, OValueRefRow& /*pOrgRow*/,const css::uno::Reference< css::container::XIndexAccess>& /*_xCols*/) +{ + return false; +} + +void OFileTable::addColumn(const css::uno::Reference< css::beans::XPropertySet>& /*descriptor*/) +{ + OSL_FAIL( "OFileTable::addColumn: not implemented!" ); +} + +void OFileTable::dropColumn(sal_Int32 /*_nPos*/) +{ + OSL_FAIL( "OFileTable::addColumn: not implemented!" ); +} + + +std::unique_ptr<SvStream> OFileTable::createStream_simpleError( const OUString& _rFileName, StreamMode _eOpenMode) +{ + std::unique_ptr<SvStream> pReturn(::utl::UcbStreamHelper::CreateStream( _rFileName, _eOpenMode, bool(_eOpenMode & StreamMode::NOCREATE))); + if (pReturn && (ERRCODE_NONE != pReturn->GetErrorCode())) + { + pReturn.reset(); + } + return pReturn; +} + + +void OFileTable::refreshHeader() +{ +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/FTables.cxx b/connectivity/source/drivers/file/FTables.cxx new file mode 100644 index 000000000..c063f4a89 --- /dev/null +++ b/connectivity/source/drivers/file/FTables.cxx @@ -0,0 +1,54 @@ +/* -*- 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 <file/FTables.hxx> +#include <file/FCatalog.hxx> + +using namespace connectivity; +using namespace connectivity::file; +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; + +sdbcx::ObjectType OTables::createObject(const OUString& /*_rName*/) +{ + return sdbcx::ObjectType(); +} + +void OTables::impl_refresh( ) +{ + static_cast<OFileCatalog&>(m_rParent).refreshTables(); +} + +Any SAL_CALL OTables::queryInterface( const Type & rType ) +{ + if( rType == cppu::UnoType<XColumnLocate>::get()|| + rType == cppu::UnoType<XDataDescriptorFactory>::get()|| + rType == cppu::UnoType<XAppend>::get()|| + rType == cppu::UnoType<XDrop>::get()) + return Any(); + + typedef sdbcx::OCollection OTables_BASE; + return OTables_BASE::queryInterface(rType); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/fanalyzer.cxx b/connectivity/source/drivers/file/fanalyzer.cxx new file mode 100644 index 000000000..a0d1305f6 --- /dev/null +++ b/connectivity/source/drivers/file/fanalyzer.cxx @@ -0,0 +1,207 @@ +/* -*- 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 <file/fanalyzer.hxx> +#include <connectivity/sqlparse.hxx> +#include <tools/debug.hxx> +#include <connectivity/sqlnode.hxx> +#include <file/FConnection.hxx> +#include <strings.hrc> + +using namespace ::connectivity; +using namespace ::connectivity::file; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; + +OSQLAnalyzer::OSQLAnalyzer(OConnection* _pConnection) + :m_pConnection(_pConnection) + ,m_bHasSelectionCode(false) + ,m_bSelectionFirstTime(true) +{ + m_aCompiler = new OPredicateCompiler(this); + m_aInterpreter = new OPredicateInterpreter(m_aCompiler); +} + + +OSQLAnalyzer::~OSQLAnalyzer() +{ +} + + +void OSQLAnalyzer::start(OSQLParseNode const * pSQLParseNode) +{ + if (SQL_ISRULE(pSQLParseNode,select_statement)) + { + DBG_ASSERT(pSQLParseNode->count() >= 4,"OFILECursor: Error in Parse Tree"); + + // check that we don't use anything other than count(*) as function + OSQLParseNode* pSelection = pSQLParseNode->getChild(2); + if ( SQL_ISRULE(pSelection,scalar_exp_commalist) ) + { + for (size_t i = 0; i < pSelection->count(); i++) + { + OSQLParseNode *pColumnRef = pSelection->getChild(i)->getChild(0); + if ( ( SQL_ISRULE(pColumnRef,set_fct_spec) && pColumnRef->count() == 4 ) + || SQL_ISRULE(pColumnRef,char_value_fct) + || SQL_ISRULE(pColumnRef,char_substring_fct) + || SQL_ISRULE(pColumnRef,position_exp) + || SQL_ISRULE(pColumnRef,fold) + || SQL_ISRULE(pColumnRef,length_exp) + || SQL_ISRULE(pColumnRef,num_value_exp) + || SQL_ISRULE(pColumnRef,term) + || SQL_ISRULE(pColumnRef,factor) + || SQL_ISRULE(pColumnRef,set_fct_spec) ) + { + ::rtl::Reference<OPredicateCompiler> pCompiler = new OPredicateCompiler(this); + pCompiler->setOrigColumns(m_aCompiler->getOrigColumns()); + ::rtl::Reference<OPredicateInterpreter> pInterpreter = new OPredicateInterpreter(pCompiler); + pCompiler->execute( pColumnRef ); + m_aSelectionEvaluations.push_back( TPredicates(pCompiler,pInterpreter) ); + } + else if ( SQL_ISRULE(pColumnRef,general_set_fct) && pColumnRef->count() != 4 ) + { + m_pConnection->throwGenericSQLException(STR_QUERY_COMPLEX_COUNT,nullptr); + } + else + { + if ( SQL_ISPUNCTUATION( pColumnRef, "*" ) + || ( SQL_ISRULE( pColumnRef, column_ref ) + && ( pColumnRef->count() == 3 ) + && ( pColumnRef->getChild(0)->getNodeType() == SQLNodeType::Name ) + && SQL_ISPUNCTUATION( pColumnRef->getChild(1), "." ) + && SQL_ISRULE( pColumnRef->getChild(2), column_val ) + && SQL_ISPUNCTUATION( pColumnRef->getChild(2)->getChild(0), "*" ) + ) + ) + { + // push one element for each column of our table + const Reference< XNameAccess > xColumnNames( m_aCompiler->getOrigColumns() ); + const Sequence< OUString > aColumnNames( xColumnNames->getElementNames() ); + for ( sal_Int32 j=0; j<aColumnNames.getLength(); ++j ) + m_aSelectionEvaluations.push_back( TPredicates() ); + } + else + m_aSelectionEvaluations.push_back( TPredicates() ); + } + } + } + } + + m_aCompiler->start(pSQLParseNode); +} + + +void OSQLAnalyzer::bindRow(OCodeList& rCodeList,const OValueRefRow& _pRow) +{ + for (auto const& code : rCodeList) + { + OOperandAttr* pAttr = dynamic_cast<OOperandAttr*>(code.get()); + if (pAttr) + { + pAttr->bindValue(_pRow); + } + } +} + +void OSQLAnalyzer::bindSelectRow(const OValueRefRow& _pRow) +{ + // first the select part + for (auto const& selectionEval : m_aSelectionEvaluations) + { + if ( selectionEval.first.is() ) + bindRow(selectionEval.first->m_aCodeList,_pRow); + } +} + +void OSQLAnalyzer::bindEvaluationRow(OValueRefRow const & _pRow) +{ + bindRow(m_aCompiler->m_aCodeList,_pRow); +} + +OOperandAttr* OSQLAnalyzer::createOperandAttr(sal_Int32 _nPos, + const Reference< XPropertySet>& _xCol) +{ + return new OOperandAttr(static_cast<sal_uInt16>(_nPos),_xCol); +} + +bool OSQLAnalyzer::hasRestriction() const +{ + return m_aCompiler->hasCode(); +} + +bool OSQLAnalyzer::hasFunctions() const +{ + if ( m_bSelectionFirstTime ) + { + m_bSelectionFirstTime = false; + for (auto const& selectionEval : m_aSelectionEvaluations) + { + if ( selectionEval.first.is() ) + { + m_bHasSelectionCode = selectionEval.first->hasCode(); + if (m_bHasSelectionCode) + break; + } + } + } + return m_bHasSelectionCode; +} + +void OSQLAnalyzer::setSelectionEvaluationResult(OValueRefRow const & _pRow,const std::vector<sal_Int32>& _rColumnMapping) +{ + sal_Int32 nPos = 1; + for (auto const& selectionEval : m_aSelectionEvaluations) + { + if ( selectionEval.second.is() ) + { + // the first column (index 0) is for convenience only. The first real select column is no 1. + sal_Int32 map = nPos; + if ( nPos < static_cast< sal_Int32 >( _rColumnMapping.size() ) ) + map = _rColumnMapping[nPos]; + if ( map > 0 ) + selectionEval.second->startSelection( (*_pRow)[map] ); + } + ++nPos; + } +} + +void OSQLAnalyzer::dispose() +{ + m_aCompiler->dispose(); + for (auto const& selectionEval : m_aSelectionEvaluations) + { + if ( selectionEval.first.is() ) + selectionEval.first->dispose(); + } +} + +void OSQLAnalyzer::setOrigColumns(const css::uno::Reference< css::container::XNameAccess>& rCols) +{ + m_aCompiler->setOrigColumns(rCols); + for (auto const& selectionEval : m_aSelectionEvaluations) + { + if ( selectionEval.first.is() ) + selectionEval.first->setOrigColumns(rCols); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/fcode.cxx b/connectivity/source/drivers/file/fcode.cxx new file mode 100644 index 000000000..d41d3e607 --- /dev/null +++ b/connectivity/source/drivers/file/fcode.cxx @@ -0,0 +1,373 @@ +/* -*- 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 <file/fcode.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <connectivity/sqlnode.hxx> +#include <sqlbison.hxx> +#include <com/sun/star/sdb/SQLFilterOperator.hpp> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::file; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; + +OCode::~OCode() = default; + +OOperandRow::OOperandRow(sal_uInt16 _nPos, sal_Int32 _rType) + : OOperand(_rType) + , m_nRowPos(_nPos) +{} + +void OOperandRow::bindValue(const OValueRefRow& _pRow) +{ + OSL_ENSURE(_pRow.is(),"NO EMPTY row allowed!"); + m_pRow = _pRow; + OSL_ENSURE(m_pRow.is() && m_nRowPos < m_pRow->size(),"Invalid RowPos is >= vector.size()"); + (*m_pRow)[m_nRowPos]->setBound(true); +} + +void OOperandRow::setValue(const ORowSetValue& _rVal) +{ + OSL_ENSURE(m_pRow.is() && m_nRowPos < m_pRow->size(),"Invalid RowPos is >= vector.size()"); + (*(*m_pRow)[m_nRowPos]) = _rVal; +} + +const ORowSetValue& OOperandRow::getValue() const +{ + OSL_ENSURE(m_pRow.is() && m_nRowPos < m_pRow->size(),"Invalid RowPos is >= vector.size()"); + return (*m_pRow)[m_nRowPos]->getValue(); +} + + +void OOperandValue::setValue(const ORowSetValue& _rVal) +{ + m_aValue = _rVal; +} + +OOperandParam::OOperandParam(sal_Int32 _nPos) + : OOperandRow(static_cast<sal_uInt16>(_nPos), DataType::VARCHAR) // Standard-Type +{ + //TODO: Actually do something here (the current state of OOperandParam appears to be "the + // remains of the very beginnings of a never finished implementation of support for parameters + // in this code", as Lionel put it in the comments at <https://gerrit.libreoffice.org/c/core/+/ + // 116839/1#message-7b2bbf3543f559a0b67dc35cd940e2ab8829c274> "-Werror,-Wunused-but-set-variable + // (Clang 13 trunk)"). +} + + +const ORowSetValue& OOperandValue::getValue() const +{ + return m_aValue; +} + + +OOperandConst::OOperandConst(const OSQLParseNode& rColumnRef, const OUString& aStrValue) +{ + switch (rColumnRef.getNodeType()) + { + case SQLNodeType::String: + m_aValue = aStrValue; + m_eDBType = DataType::VARCHAR; + m_aValue.setBound(true); + return; + case SQLNodeType::IntNum: + case SQLNodeType::ApproxNum: + m_aValue = aStrValue.toDouble(); + m_eDBType = DataType::DOUBLE; + m_aValue.setBound(true); + return; + default: + break; + } + + if (SQL_ISTOKEN(&rColumnRef, TRUE)) + { + m_aValue = 1.0; + m_eDBType = DataType::BIT; + } + else if (SQL_ISTOKEN(&rColumnRef, FALSE)) + { + m_aValue = 0.0; + m_eDBType = DataType::BIT; + } + else + { + SAL_WARN( "connectivity.drivers", "Parse Error"); + } + m_aValue.setBound(true); +} + + +// Implementation of the operators + + +bool OBoolOperator::operate(const OOperand*, const OOperand*) const +{ + return false; +} + + +void OBoolOperator::Exec(OCodeStack& rCodeStack) +{ + OOperand *pRight = rCodeStack.top(); + rCodeStack.pop(); + OOperand *pLeft = rCodeStack.top(); + rCodeStack.pop(); + + rCodeStack.push(new OOperandResultBOOL(operate(pLeft, pRight))); + if( typeid(OOperandResult) == typeid(*pLeft)) + delete pLeft; + if( typeid(OOperandResult) == typeid(*pRight)) + delete pRight; +} + +bool OOp_NOT::operate(const OOperand* pLeft, const OOperand* ) const +{ + return !pLeft->isValid(); +} + +void OOp_NOT::Exec(OCodeStack& rCodeStack) +{ + OOperand* pOperand = rCodeStack.top(); + rCodeStack.pop(); + + rCodeStack.push(new OOperandResultBOOL(operate(pOperand, nullptr))); + + if( typeid(OOperandResult) == typeid(*pOperand)) + delete pOperand; +} + +bool OOp_AND::operate(const OOperand* pLeft, const OOperand* pRight) const +{ + return pLeft->isValid() && pRight->isValid(); +} + + +bool OOp_OR::operate(const OOperand* pLeft, const OOperand* pRight) const +{ + return pLeft->isValid() || pRight->isValid(); +} + + +void OOp_ISNULL::Exec(OCodeStack& rCodeStack) +{ + OOperand* pOperand = rCodeStack.top(); + rCodeStack.pop(); + + rCodeStack.push(new OOperandResultBOOL(operate(pOperand, nullptr))); + if( typeid(OOperandResult) == typeid(*pOperand)) + delete pOperand; +} + + +bool OOp_ISNULL::operate(const OOperand* pOperand, const OOperand*) const +{ + return pOperand->getValue().isNull(); +} + + +bool OOp_ISNOTNULL::operate(const OOperand* pOperand, const OOperand*) const +{ + return !OOp_ISNULL::operate(pOperand, nullptr); +} + + +bool OOp_LIKE::operate(const OOperand* pLeft, const OOperand* pRight) const +{ + bool bMatch; + const ORowSetValue& aLH(pLeft->getValue()); + const ORowSetValue& aRH(pRight->getValue()); + + if (aLH.isNull() || aRH.isNull()) + bMatch = false; + else + { + bMatch = match(aRH.getString(), aLH.getString(), cEscape); + } + return bMatch; +} + + +bool OOp_NOTLIKE::operate(const OOperand* pLeft, const OOperand* pRight) const +{ + return !OOp_LIKE::operate(pLeft, pRight); +} + + +bool OOp_COMPARE::operate(const OOperand* pLeft, const OOperand* pRight) const +{ + const ORowSetValue& aLH(pLeft->getValue()); + const ORowSetValue& aRH(pRight->getValue()); + + if (aLH.isNull() || aRH.isNull()) // if (!aLH.getValue() || !aRH.getValue()) + return false; + + bool bResult = false; + sal_Int32 eDBType = pLeft->getDBType(); + + // Comparison (depending on Data-type): + switch (eDBType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + { + OUString sLH = aLH.getString(), sRH = aRH.getString(); + sal_Int32 nRes = sLH.compareToIgnoreAsciiCase(sRH); + switch(aPredicateType) + { + case SQLFilterOperator::EQUAL: bResult = (nRes == 0); break; + case SQLFilterOperator::NOT_EQUAL: bResult = (nRes != 0); break; + case SQLFilterOperator::LESS: bResult = (nRes < 0); break; + case SQLFilterOperator::LESS_EQUAL: bResult = (nRes <= 0); break; + case SQLFilterOperator::GREATER: bResult = (nRes > 0); break; + case SQLFilterOperator::GREATER_EQUAL: bResult = (nRes >= 0); break; + default: bResult = false; + } + } break; + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::REAL: + case DataType::DOUBLE: + case DataType::BIT: + case DataType::TIMESTAMP: + case DataType::DATE: + case DataType::TIME: + { + double n = aLH.getDouble(), m = aRH.getDouble(); + + switch (aPredicateType) + { + case SQLFilterOperator::EQUAL: bResult = (n == m); break; + case SQLFilterOperator::LIKE: bResult = (n == m); break; + case SQLFilterOperator::NOT_EQUAL: bResult = (n != m); break; + case SQLFilterOperator::NOT_LIKE: bResult = (n != m); break; + case SQLFilterOperator::LESS: bResult = (n < m); break; + case SQLFilterOperator::LESS_EQUAL: bResult = (n <= m); break; + case SQLFilterOperator::GREATER: bResult = (n > m); break; + case SQLFilterOperator::GREATER_EQUAL: bResult = (n >= m); break; + default: bResult = false; + } + } break; + default: + bResult = aLH == aRH; + } + return bResult; +} + + +void ONumOperator::Exec(OCodeStack& rCodeStack) +{ + OOperand *pRight = rCodeStack.top(); + rCodeStack.pop(); + OOperand *pLeft = rCodeStack.top(); + rCodeStack.pop(); + + rCodeStack.push(new OOperandResultNUM(operate(pLeft->getValue().getDouble(), pRight->getValue().getDouble()))); + if( typeid(OOperandResult) == typeid(*pLeft)) + delete pLeft; + if( typeid(OOperandResult) == typeid(*pRight)) + delete pRight; +} + +double OOp_ADD::operate(const double& fLeft,const double& fRight) const +{ + return fLeft + fRight; +} + + +double OOp_SUB::operate(const double& fLeft,const double& fRight) const +{ + return fLeft - fRight; +} + + +double OOp_MUL::operate(const double& fLeft,const double& fRight) const +{ + return fLeft * fRight; +} + + +double OOp_DIV::operate(const double& fLeft,const double& fRight) const +{ + return fLeft / fRight; +} + +void ONthOperator::Exec(OCodeStack& rCodeStack) +{ + std::vector<ORowSetValue> aValues; + std::vector<OOperand*> aOperands; + OOperand* pOperand; + do + { + OSL_ENSURE(!rCodeStack.empty(),"Stack must be none empty!"); + pOperand = rCodeStack.top(); + rCodeStack.pop(); + assert(pOperand); + if (pOperand && typeid(OStopOperand) != typeid(*pOperand)) + aValues.push_back( pOperand->getValue() ); + aOperands.push_back( pOperand ); + } + while (pOperand && typeid(OStopOperand) != typeid(*pOperand)); + + rCodeStack.push(new OOperandResult(operate(aValues))); + + for (const auto& rpOperand : aOperands) + { + if (typeid(OOperandResult) == typeid(*rpOperand)) + delete rpOperand; + } +} + +void OBinaryOperator::Exec(OCodeStack& rCodeStack) +{ + OOperand *pRight = rCodeStack.top(); + rCodeStack.pop(); + OOperand *pLeft = rCodeStack.top(); + rCodeStack.pop(); + + if ( !rCodeStack.empty() && typeid(OStopOperand) == typeid(*rCodeStack.top()) ) + rCodeStack.pop(); + + rCodeStack.push(new OOperandResult(operate(pLeft->getValue(),pRight->getValue()))); + if(typeid(OOperandResult) == typeid(*pRight)) + delete pRight; + if(typeid(OOperandResult) == typeid(*pLeft)) + delete pLeft; +} + +void OUnaryOperator::Exec(OCodeStack& rCodeStack) +{ + OSL_ENSURE(!rCodeStack.empty(),"Stack is empty!"); + OOperand* pOperand = rCodeStack.top(); + rCodeStack.pop(); + + rCodeStack.push(new OOperandResult(operate(pOperand->getValue()))); + if (typeid(OOperandResult) == typeid(*pOperand)) + delete pOperand; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/fcomp.cxx b/connectivity/source/drivers/file/fcomp.cxx new file mode 100644 index 000000000..5a1646a20 --- /dev/null +++ b/connectivity/source/drivers/file/fcomp.cxx @@ -0,0 +1,886 @@ +/* -*- 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 <file/fcomp.hxx> +#include <tools/debug.hxx> +#include <connectivity/sqlparse.hxx> +#include <file/fanalyzer.hxx> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbconversion.hxx> +#include <com/sun/star/sdb/SQLFilterOperator.hpp> +#include <file/FStringFunctions.hxx> +#include <file/FDateFunctions.hxx> +#include <file/FNumericFunctions.hxx> +#include <file/FConnection.hxx> +#include <tools/diagnose_ex.h> +#include <sqlbison.hxx> +#include <strings.hrc> + +using namespace connectivity; +using namespace connectivity::file; +using namespace com::sun::star::uno; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::sdb; +using namespace ::com::sun::star::container; +using namespace com::sun::star; + +OPredicateCompiler::OPredicateCompiler(OSQLAnalyzer* pAnalyzer)//,OCursor& rCurs) + : m_pAnalyzer(pAnalyzer) + , m_nParamCounter(0) +{ +} + + +OPredicateCompiler::~OPredicateCompiler() +{ + Clean(); +} + +void OPredicateCompiler::dispose() +{ + Clean(); + m_orgColumns = nullptr; +} + +void OPredicateCompiler::start(OSQLParseNode const * pSQLParseNode) +{ + if (!pSQLParseNode) + return; + + m_nParamCounter = 0; + // analyse Parse Tree (depending on Statement-type) + // and set pointer on WHERE-clause: + OSQLParseNode * pWhereClause = nullptr; + + if (SQL_ISRULE(pSQLParseNode,select_statement)) + { + OSQLParseNode * pOrderbyClause = nullptr; + DBG_ASSERT(pSQLParseNode->count() >= 4,"OFILECursor: Error in Parse Tree"); + + OSQLParseNode * pTableExp = pSQLParseNode->getChild(3); + DBG_ASSERT(pTableExp != nullptr,"Error in Parse Tree"); + DBG_ASSERT(SQL_ISRULE(pTableExp,table_exp)," Error in Parse Tree"); + DBG_ASSERT(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"Error in Parse Tree"); + + // check that we don't use anything other than count(*) as function + OSQLParseNode* pSelection = pSQLParseNode->getChild(2); + if ( SQL_ISRULE(pSelection,scalar_exp_commalist) ) + { + for (size_t i = 0; i < pSelection->count(); i++) + { + OSQLParseNode *pColumnRef = pSelection->getChild(i)->getChild(0); + if ( SQL_ISRULE(pColumnRef,general_set_fct) && pColumnRef->count() != 4 ) + { + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_COMPLEX_COUNT,nullptr); + } + } + } + + + pWhereClause = pTableExp->getChild(1); + pOrderbyClause = pTableExp->getChild(ORDER_BY_CHILD_POS); + (void)pOrderbyClause; + } + else if (SQL_ISRULE(pSQLParseNode,update_statement_searched)) + { + DBG_ASSERT(pSQLParseNode->count() == 5,"OFILECursor: Error in Parse Tree"); + pWhereClause = pSQLParseNode->getChild(4); + } + else if (SQL_ISRULE(pSQLParseNode,delete_statement_searched)) + { + DBG_ASSERT(pSQLParseNode->count() == 4,"Error in Parse Tree"); + pWhereClause = pSQLParseNode->getChild(3); + } + else + // Other Statement. no selection-criteria + return; + + if (SQL_ISRULE(pWhereClause,where_clause)) + { + // a where-clause is not allowed to be empty: + DBG_ASSERT(pWhereClause->count() == 2,"OFILECursor: Error in Parse Tree"); + + OSQLParseNode * pComparisonPredicate = pWhereClause->getChild(1); + DBG_ASSERT(pComparisonPredicate != nullptr,"OFILECursor: Error in Parse Tree"); + + execute( pComparisonPredicate ); + } + else + { + // The where-clause is optionally in the majority of cases, i.e. it might be an "optional-where-clause". + DBG_ASSERT(SQL_ISRULE(pWhereClause,opt_where_clause),"OPredicateCompiler: Error in Parse Tree"); + } +} + + +OOperand* OPredicateCompiler::execute(OSQLParseNode const * pPredicateNode) +{ + OOperand* pOperand = nullptr; + if (pPredicateNode->count() == 3 && // Expression is bracketed + SQL_ISPUNCTUATION(pPredicateNode->getChild(0),"(") && + SQL_ISPUNCTUATION(pPredicateNode->getChild(2),")")) + { + execute(pPredicateNode->getChild(1)); + } + else if ((SQL_ISRULE(pPredicateNode,search_condition) || SQL_ISRULE(pPredicateNode,boolean_term)) + && // AND/OR-linkage: + pPredicateNode->count() == 3) + { + execute(pPredicateNode->getChild(0)); // process the left branch + execute(pPredicateNode->getChild(2)); // process the right branch + + if (SQL_ISTOKEN(pPredicateNode->getChild(1),OR)) // OR-Operator + { + m_aCodeList.emplace_back(new OOp_OR); + } + else if (SQL_ISTOKEN(pPredicateNode->getChild(1),AND)) // AND-Operator + m_aCodeList.emplace_back(new OOp_AND); + else + { + OSL_FAIL("OPredicateCompiler: Error in Parse Tree"); + } + } + else if (SQL_ISRULE(pPredicateNode,boolean_factor)) + { + execute(pPredicateNode->getChild(1)); + m_aCodeList.emplace_back(new OOp_NOT); + } + else if (SQL_ISRULE(pPredicateNode,comparison_predicate)) + { + execute_COMPARE(pPredicateNode); + } + else if (SQL_ISRULE(pPredicateNode,like_predicate)) + { + execute_LIKE(pPredicateNode); + } + else if (SQL_ISRULE(pPredicateNode,between_predicate)) + { + execute_BETWEEN(pPredicateNode); + } + else if (SQL_ISRULE(pPredicateNode,test_for_null)) + { + execute_ISNULL(pPredicateNode); + } + else if(SQL_ISRULE(pPredicateNode,num_value_exp)) + { + execute(pPredicateNode->getChild(0)); // process the left branch + execute(pPredicateNode->getChild(2)); // process the right branch + if (SQL_ISPUNCTUATION(pPredicateNode->getChild(1),"+")) + { + m_aCodeList.emplace_back(new OOp_ADD); + } + else if (SQL_ISPUNCTUATION(pPredicateNode->getChild(1),"-")) + m_aCodeList.emplace_back(new OOp_SUB); + else + { + OSL_FAIL("OPredicateCompiler: Error in Parse Tree num_value_exp"); + } + } + else if(SQL_ISRULE(pPredicateNode,term)) + { + execute(pPredicateNode->getChild(0)); // process the left branch + execute(pPredicateNode->getChild(2)); // process the right branch + if (SQL_ISPUNCTUATION(pPredicateNode->getChild(1),"*")) + { + m_aCodeList.emplace_back(new OOp_MUL); + } + else if (SQL_ISPUNCTUATION(pPredicateNode->getChild(1),"/")) + m_aCodeList.emplace_back(new OOp_DIV); + else + { + OSL_FAIL("OPredicateCompiler: Error in Parse Tree num_value_exp"); + } + } + else + pOperand = execute_Operand(pPredicateNode); // now only simple operands will be processed + + return pOperand; +} + + +void OPredicateCompiler::execute_COMPARE(OSQLParseNode const * pPredicateNode) +{ + DBG_ASSERT(pPredicateNode->count() == 3,"OFILECursor: Error in Parse Tree"); + + if ( !(SQL_ISRULE(pPredicateNode->getChild(0),column_ref) || + pPredicateNode->getChild(2)->getNodeType() == SQLNodeType::String || + pPredicateNode->getChild(2)->getNodeType() == SQLNodeType::IntNum || + pPredicateNode->getChild(2)->getNodeType() == SQLNodeType::ApproxNum || + SQL_ISTOKEN(pPredicateNode->getChild(2),TRUE) || + SQL_ISTOKEN(pPredicateNode->getChild(2),FALSE) || + SQL_ISRULE(pPredicateNode->getChild(2),parameter) || + // odbc date + SQL_ISRULE(pPredicateNode->getChild(2),set_fct_spec) || + SQL_ISRULE(pPredicateNode->getChild(2),position_exp) || + SQL_ISRULE(pPredicateNode->getChild(2),char_substring_fct) || + // upper, lower etc. + SQL_ISRULE(pPredicateNode->getChild(2),fold)) ) + { + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,nullptr); + return; + } + + sal_Int32 ePredicateType( SQLFilterOperator::EQUAL ); + OSQLParseNode *pPrec = pPredicateNode->getChild(1); + + if (pPrec->getNodeType() == SQLNodeType::Equal) + ePredicateType = SQLFilterOperator::EQUAL; + else if (pPrec->getNodeType() == SQLNodeType::NotEqual) + ePredicateType = SQLFilterOperator::NOT_EQUAL; + else if (pPrec->getNodeType() == SQLNodeType::Less) + ePredicateType = SQLFilterOperator::LESS; + else if (pPrec->getNodeType() == SQLNodeType::LessEq) + ePredicateType = SQLFilterOperator::LESS_EQUAL; + else if (pPrec->getNodeType() == SQLNodeType::GreatEq) + ePredicateType = SQLFilterOperator::GREATER_EQUAL; + else if (pPrec->getNodeType() == SQLNodeType::Great) + ePredicateType = SQLFilterOperator::GREATER; + else + OSL_FAIL( "OPredicateCompiler::execute_COMPARE: unexpected node type!" ); + + execute(pPredicateNode->getChild(0)); + execute(pPredicateNode->getChild(2)); + m_aCodeList.emplace_back( new OOp_COMPARE(ePredicateType) ); +} + + +void OPredicateCompiler::execute_LIKE(OSQLParseNode const * pPredicateNode) +{ + DBG_ASSERT(pPredicateNode->count() == 2,"OFILECursor: Error in Parse Tree"); + const OSQLParseNode* pPart2 = pPredicateNode->getChild(1); + + sal_Unicode cEscape = L'\0'; + const bool bNotLike = pPart2->getChild(0)->isToken(); + + OSQLParseNode* pAtom = pPart2->getChild(pPart2->count()-2); + OSQLParseNode* pOptEscape = pPart2->getChild(pPart2->count()-1); + + if (!(pAtom->getNodeType() == SQLNodeType::String || + SQL_ISRULE(pAtom,parameter) || + // odbc date + SQL_ISRULE(pAtom,set_fct_spec) || + SQL_ISRULE(pAtom,position_exp) || + SQL_ISRULE(pAtom,char_substring_fct) || + // upper, lower etc. + SQL_ISRULE(pAtom,fold)) ) + { + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,nullptr); + return; + } + + if (pOptEscape->count() != 0) + { + if (pOptEscape->count() != 2) + { + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_INVALID_LIKE_STRING,nullptr); + } + OSQLParseNode *pEscNode = pOptEscape->getChild(1); + if (pEscNode->getNodeType() != SQLNodeType::String) + { + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_INVALID_LIKE_STRING,nullptr); + } + else + cEscape = pEscNode->getTokenValue().toChar(); + } + + execute(pPredicateNode->getChild(0)); + execute(pAtom); + + OBoolOperator* pOperator = bNotLike + ? new OOp_NOTLIKE(cEscape) + : new OOp_LIKE(cEscape); + m_aCodeList.emplace_back(pOperator); +} + +void OPredicateCompiler::execute_BETWEEN(OSQLParseNode const * pPredicateNode) +{ + DBG_ASSERT(pPredicateNode->count() == 2,"OFILECursor: Error in Parse Tree"); + + OSQLParseNode* pColumn = pPredicateNode->getChild(0); + const OSQLParseNode* pPart2 = pPredicateNode->getChild(1); + OSQLParseNode* p1stValue = pPart2->getChild(2); + OSQLParseNode* p2ndtValue = pPart2->getChild(4); + + if ( + !(p1stValue->getNodeType() == SQLNodeType::String || SQL_ISRULE(p1stValue,parameter)) + && !(p2ndtValue->getNodeType() == SQLNodeType::String || SQL_ISRULE(p2ndtValue,parameter)) + ) + { + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_INVALID_BETWEEN,nullptr); + } + + bool bNot = SQL_ISTOKEN(pPart2->getChild(0),NOT); + + OOperand* pColumnOp = execute(pColumn); + OOperand* pOb1 = execute(p1stValue); + OBoolOperator* pOperator = new OOp_COMPARE(bNot ? SQLFilterOperator::LESS_EQUAL : SQLFilterOperator::GREATER); + m_aCodeList.emplace_back(pOperator); + + execute(pColumn); + OOperand* pOb2 = execute(p2ndtValue); + pOperator = new OOp_COMPARE(bNot ? SQLFilterOperator::GREATER_EQUAL : SQLFilterOperator::LESS); + m_aCodeList.emplace_back(pOperator); + + if ( pColumnOp && pOb1 && pOb2 ) + { + switch(pColumnOp->getDBType()) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + pOb1->setValue(pOb1->getValue().getString()); + pOb2->setValue(pOb2->getValue().getString()); + break; + case DataType::DECIMAL: + case DataType::NUMERIC: + pOb1->setValue(pOb1->getValue().getDouble()); + pOb2->setValue(pOb2->getValue().getDouble()); + break; + case DataType::FLOAT: + pOb1->setValue(pOb1->getValue().getFloat()); + pOb2->setValue(pOb2->getValue().getFloat()); + break; + case DataType::DOUBLE: + case DataType::REAL: + pOb1->setValue(pOb1->getValue().getDouble()); + pOb2->setValue(pOb2->getValue().getDouble()); + break; + case DataType::DATE: + pOb1->setValue(pOb1->getValue().getDate()); + pOb2->setValue(pOb2->getValue().getDate()); + break; + case DataType::TIME: + pOb1->setValue(pOb1->getValue().getTime()); + pOb2->setValue(pOb2->getValue().getTime()); + break; + case DataType::TIMESTAMP: + pOb1->setValue(pOb1->getValue().getDateTime()); + pOb2->setValue(pOb2->getValue().getDateTime()); + break; + } + } + + + OBoolOperator* pBoolOp = nullptr; + if ( bNot ) + pBoolOp = new OOp_OR; + else + pBoolOp = new OOp_AND; + m_aCodeList.emplace_back(pBoolOp); +} + +void OPredicateCompiler::execute_ISNULL(OSQLParseNode const * pPredicateNode) +{ + DBG_ASSERT(pPredicateNode->count() == 2,"OFILECursor: Error in Parse Tree"); + const OSQLParseNode* pPart2 = pPredicateNode->getChild(1); + DBG_ASSERT(SQL_ISTOKEN(pPart2->getChild(0),IS),"OFILECursor: Error in Parse Tree"); + + sal_Int32 ePredicateType; + if (SQL_ISTOKEN(pPart2->getChild(1),NOT)) + ePredicateType = SQLFilterOperator::NOT_SQLNULL; + else + ePredicateType = SQLFilterOperator::SQLNULL; + + execute(pPredicateNode->getChild(0)); + OBoolOperator* pOperator = (ePredicateType == SQLFilterOperator::SQLNULL) ? + new OOp_ISNULL : new OOp_ISNOTNULL; + m_aCodeList.emplace_back(pOperator); +} + +OOperand* OPredicateCompiler::execute_Operand(OSQLParseNode const * pPredicateNode) +{ + OOperand* pOperand = nullptr; + + if (SQL_ISRULE(pPredicateNode,column_ref)) + { + OUString aColumnName; + if (pPredicateNode->count() == 1) + { + aColumnName = pPredicateNode->getChild(0)->getTokenValue(); + } + else if (pPredicateNode->count() == 3) + { + if(SQL_ISRULE(pPredicateNode->getChild(2),column_val)) + aColumnName = pPredicateNode->getChild(2)->getChild(0)->getTokenValue(); + else + aColumnName = pPredicateNode->getChild(2)->getTokenValue(); + } + + if(!m_orgColumns->hasByName(aColumnName)) + { + const OUString sError( m_pAnalyzer->getConnection()->getResources().getResourceStringWithSubstitution( + STR_INVALID_COLUMNNAME, + "$columnname$", aColumnName + ) ); + ::dbtools::throwGenericSQLException( sError, nullptr ); + } + css::uno::Reference< css::beans::XPropertySet> xCol; + try + { + if (m_orgColumns->getByName(aColumnName) >>= xCol) + { + pOperand = OSQLAnalyzer::createOperandAttr(Reference< XColumnLocate>(m_orgColumns,UNO_QUERY_THROW)->findColumn(aColumnName),xCol); + } + else + {// Column doesn't exist in the Result-set + const OUString sError( m_pAnalyzer->getConnection()->getResources().getResourceStringWithSubstitution( + STR_INVALID_COLUMNNAME, + "$columnname$", aColumnName + ) ); + ::dbtools::throwGenericSQLException( sError, nullptr ); + } + } + catch(Exception &) + { + TOOLS_WARN_EXCEPTION( "connectivity.drivers", "OPredicateCompiler::execute_Operand Exception"); + } + } + else if (SQL_ISRULE(pPredicateNode,parameter)) + { + pOperand = new OOperandParam(++m_nParamCounter); + } + else if (pPredicateNode->getNodeType() == SQLNodeType::String || + pPredicateNode->getNodeType() == SQLNodeType::IntNum || + pPredicateNode->getNodeType() == SQLNodeType::ApproxNum || + pPredicateNode->getNodeType() == SQLNodeType::Name || + SQL_ISTOKEN(pPredicateNode,TRUE) || + SQL_ISTOKEN(pPredicateNode,FALSE) || + SQL_ISRULE(pPredicateNode,parameter)) + { + pOperand = new OOperandConst(*pPredicateNode, pPredicateNode->getTokenValue()); + } + else if((pPredicateNode->count() == 2) && + (SQL_ISPUNCTUATION(pPredicateNode->getChild(0),"+") || SQL_ISPUNCTUATION(pPredicateNode->getChild(0),"-")) && + pPredicateNode->getChild(1)->getNodeType() == SQLNodeType::IntNum) + { // if -1 or +1 is there + OUString aValue = pPredicateNode->getChild(0)->getTokenValue() + pPredicateNode->getChild(1)->getTokenValue(); + pOperand = new OOperandConst(*pPredicateNode->getChild(1), aValue); + } + else if( SQL_ISRULE(pPredicateNode,set_fct_spec) && SQL_ISPUNCTUATION(pPredicateNode->getChild(0),"{") ) + { + const OSQLParseNode* pODBCNode = pPredicateNode->getChild(1); + const OSQLParseNode* pODBCNodeChild = pODBCNode->getChild(0); + + // Odbc Date or time + if (pODBCNodeChild->getNodeType() == SQLNodeType::Keyword && ( + SQL_ISTOKEN(pODBCNodeChild,D) || + SQL_ISTOKEN(pODBCNodeChild,T) || + SQL_ISTOKEN(pODBCNodeChild,TS) )) + { + OUString sDateTime = pODBCNode->getChild(1)->getTokenValue(); + pOperand = new OOperandConst(*pODBCNode->getChild(1), sDateTime); + if(SQL_ISTOKEN(pODBCNodeChild,D)) + { + pOperand->setValue(::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(sDateTime))); + } + else if(SQL_ISTOKEN(pODBCNodeChild,T)) + { + pOperand->setValue(::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(sDateTime))); + } + else if(SQL_ISTOKEN(pODBCNodeChild,TS)) + { + pOperand->setValue(::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(sDateTime))); + } + } + else + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,nullptr); + + } + else if( SQL_ISRULE(pPredicateNode,fold) ) + { + execute_Fold(pPredicateNode); + } + else if( SQL_ISRULE(pPredicateNode,set_fct_spec) + || SQL_ISRULE(pPredicateNode,position_exp) + || SQL_ISRULE(pPredicateNode,char_substring_fct) + ) + { + executeFunction(pPredicateNode); + } + else if( SQL_ISRULE(pPredicateNode,length_exp) ) + { + executeFunction(pPredicateNode->getChild(0)); + } + else + { + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,nullptr); + } + if (pOperand) + m_aCodeList.emplace_back(pOperand); + return pOperand; +} + + +bool OPredicateInterpreter::evaluate(OCodeList& rCodeList) +{ + if (!(rCodeList[0])) + return true; // no Predicate + + for (auto const& code : rCodeList) + { + OOperand* pOperand = dynamic_cast<OOperand* >(code.get()); + if (pOperand) + m_aStack.push(pOperand); + else + static_cast<OOperator *>(code.get())->Exec(m_aStack); + } + + OOperand* pOperand = m_aStack.top(); + m_aStack.pop(); + + DBG_ASSERT(m_aStack.empty(), "Stack error"); + DBG_ASSERT(pOperand, "Stack error"); + + const bool bResult = pOperand->isValid(); + if (typeid(OOperandResult) == typeid(*pOperand)) + delete pOperand; + return bResult; +} + +void OPredicateInterpreter::evaluateSelection(OCodeList& rCodeList, ORowSetValueDecoratorRef const & _rVal) +{ + if (!(rCodeList[0])) + return ; // no Predicate + + for (auto const& code : rCodeList) + { + OOperand* pOperand = dynamic_cast<OOperand* >(code.get()); + if (pOperand) + m_aStack.push(pOperand); + else + static_cast<OOperator *>(code.get())->Exec(m_aStack); + } + + OOperand* pOperand = m_aStack.top(); + m_aStack.pop(); + + DBG_ASSERT(m_aStack.empty(), "Stack error"); + DBG_ASSERT(pOperand, "Stack error"); + + (*_rVal) = pOperand->getValue(); + if (typeid(OOperandResult) == typeid(*pOperand)) + delete pOperand; +} + +void OPredicateCompiler::execute_Fold(OSQLParseNode const * pPredicateNode) +{ + DBG_ASSERT(pPredicateNode->count() >= 4,"OFILECursor: Error in Parse Tree"); + + bool bUpper = SQL_ISTOKEN(pPredicateNode->getChild(0),UPPER); + + execute(pPredicateNode->getChild(2)); + OOperator* pOperator = nullptr; + if ( bUpper ) + pOperator = new OOp_Upper; + else + pOperator = new OOp_Lower; + + m_aCodeList.emplace_back(pOperator); +} + +void OPredicateCompiler::executeFunction(OSQLParseNode const * pPredicateNode) +{ + OOperator* pOperator = nullptr; + + OSL_ENSURE(pPredicateNode->getChild(0)->isToken(),"The first one must be the name of the function!"); + sal_Int32 nTokenId = pPredicateNode->getChild(0)->getTokenID(); + switch ( nTokenId ) + { + case SQL_TOKEN_CHAR_LENGTH: + case SQL_TOKEN_LENGTH: + case SQL_TOKEN_OCTET_LENGTH: + case SQL_TOKEN_ASCII: + case SQL_TOKEN_LCASE: + case SQL_TOKEN_LTRIM: + case SQL_TOKEN_RTRIM: + case SQL_TOKEN_SPACE: + case SQL_TOKEN_UCASE: + case SQL_TOKEN_ABS: + case SQL_TOKEN_ACOS: + case SQL_TOKEN_ASIN: + case SQL_TOKEN_ATAN: + case SQL_TOKEN_CEILING: + case SQL_TOKEN_COS: + case SQL_TOKEN_DEGREES: + case SQL_TOKEN_EXP: + case SQL_TOKEN_FLOOR: + case SQL_TOKEN_LOG10: + case SQL_TOKEN_LN: + case SQL_TOKEN_RADIANS: + case SQL_TOKEN_SIGN: + case SQL_TOKEN_SIN: + case SQL_TOKEN_SQRT: + case SQL_TOKEN_TAN: + case SQL_TOKEN_DAYNAME: + case SQL_TOKEN_DAYOFMONTH: + case SQL_TOKEN_DAYOFWEEK: + case SQL_TOKEN_DAYOFYEAR: + case SQL_TOKEN_HOUR: + case SQL_TOKEN_MINUTE: + case SQL_TOKEN_MONTH: + case SQL_TOKEN_MONTHNAME: + case SQL_TOKEN_QUARTER: + case SQL_TOKEN_SECOND: + case SQL_TOKEN_YEAR: + + execute(pPredicateNode->getChild(2)); + + switch( nTokenId ) + { + case SQL_TOKEN_CHAR_LENGTH: + case SQL_TOKEN_LENGTH: + case SQL_TOKEN_OCTET_LENGTH: + pOperator = new OOp_CharLength; + break; + case SQL_TOKEN_ASCII: + pOperator = new OOp_Ascii; + break; + case SQL_TOKEN_LCASE: + pOperator = new OOp_Lower; + break; + + case SQL_TOKEN_LTRIM: + pOperator = new OOp_LTrim; + break; + case SQL_TOKEN_RTRIM: + pOperator = new OOp_RTrim; + break; + case SQL_TOKEN_SPACE: + pOperator = new OOp_Space; + break; + case SQL_TOKEN_UCASE: + pOperator = new OOp_Upper; + break; + case SQL_TOKEN_ABS: + pOperator = new OOp_Abs; + break; + case SQL_TOKEN_ACOS: + pOperator = new OOp_ACos; + break; + case SQL_TOKEN_ASIN: + pOperator = new OOp_ASin; + break; + case SQL_TOKEN_ATAN: + pOperator = new OOp_ATan; + break; + case SQL_TOKEN_CEILING: + pOperator = new OOp_Ceiling; + break; + case SQL_TOKEN_COS: + pOperator = new OOp_Cos; + break; + case SQL_TOKEN_DEGREES: + pOperator = new OOp_Degrees; + break; + case SQL_TOKEN_EXP: + pOperator = new OOp_Exp; + break; + case SQL_TOKEN_FLOOR: + pOperator = new OOp_Floor; + break; + case SQL_TOKEN_LOG10: + pOperator = new OOp_Log10; + break; + case SQL_TOKEN_LN: + pOperator = new OOp_Ln; + break; + case SQL_TOKEN_RADIANS: + pOperator = new OOp_Radians; + break; + case SQL_TOKEN_SIGN: + pOperator = new OOp_Sign; + break; + case SQL_TOKEN_SIN: + pOperator = new OOp_Sin; + break; + case SQL_TOKEN_SQRT: + pOperator = new OOp_Sqrt; + break; + case SQL_TOKEN_TAN: + pOperator = new OOp_Tan; + break; + case SQL_TOKEN_DAYOFWEEK: + pOperator = new OOp_DayOfWeek; + break; + case SQL_TOKEN_DAYOFMONTH: + pOperator = new OOp_DayOfMonth; + break; + case SQL_TOKEN_DAYOFYEAR: + pOperator = new OOp_DayOfYear; + break; + case SQL_TOKEN_MONTH: + pOperator = new OOp_Month; + break; + case SQL_TOKEN_DAYNAME: + pOperator = new OOp_DayName; + break; + case SQL_TOKEN_MONTHNAME: + pOperator = new OOp_MonthName; + break; + case SQL_TOKEN_QUARTER: + pOperator = new OOp_Quarter; + break; + case SQL_TOKEN_YEAR: + pOperator = new OOp_Year; + break; + case SQL_TOKEN_HOUR: + pOperator = new OOp_Hour; + break; + case SQL_TOKEN_MINUTE: + pOperator = new OOp_Minute; + break; + case SQL_TOKEN_SECOND: + pOperator = new OOp_Second; + break; + default: + OSL_FAIL("Error in switch!"); + } + break; + case SQL_TOKEN_CHAR: + case SQL_TOKEN_CONCAT: + case SQL_TOKEN_INSERT: + case SQL_TOKEN_LEFT: + case SQL_TOKEN_LOCATE: + case SQL_TOKEN_LOCATE_2: + case SQL_TOKEN_REPEAT: + case SQL_TOKEN_REPLACE: + case SQL_TOKEN_RIGHT: + case SQL_TOKEN_MOD: + case SQL_TOKEN_ROUND: + case SQL_TOKEN_LOGF: + case SQL_TOKEN_LOG: + case SQL_TOKEN_POWER: + case SQL_TOKEN_ATAN2: + case SQL_TOKEN_PI: + case SQL_TOKEN_CURDATE: + case SQL_TOKEN_CURTIME: + case SQL_TOKEN_NOW: + case SQL_TOKEN_WEEK: + { + m_aCodeList.emplace_back(new OStopOperand); + OSQLParseNode* pList = pPredicateNode->getChild(2); + for (size_t i=0; i < pList->count(); ++i) + execute(pList->getChild(i)); + + switch( nTokenId ) + { + case SQL_TOKEN_CHAR: + pOperator = new OOp_Char; + break; + case SQL_TOKEN_CONCAT: + pOperator = new OOp_Concat; + break; + case SQL_TOKEN_INSERT: + pOperator = new OOp_Insert; + break; + case SQL_TOKEN_LEFT: + pOperator = new OOp_Left; + break; + case SQL_TOKEN_LOCATE: + case SQL_TOKEN_LOCATE_2: + pOperator = new OOp_Locate; + break; + case SQL_TOKEN_REPEAT: + pOperator = new OOp_Repeat; + break; + case SQL_TOKEN_REPLACE: + pOperator = new OOp_Replace; + break; + case SQL_TOKEN_RIGHT: + pOperator = new OOp_Right; + break; + case SQL_TOKEN_MOD: + pOperator = new OOp_Mod; + break; + case SQL_TOKEN_ROUND: + pOperator = new OOp_Round; + break; + case SQL_TOKEN_LOGF: + case SQL_TOKEN_LOG: + pOperator = new OOp_Log; + break; + case SQL_TOKEN_POWER: + pOperator = new OOp_Pow; + break; + case SQL_TOKEN_ATAN2: + pOperator = new OOp_ATan2; + break; + case SQL_TOKEN_PI: + pOperator = new OOp_Pi; + break; + case SQL_TOKEN_CURDATE: + pOperator = new OOp_CurDate; + break; + case SQL_TOKEN_CURTIME: + pOperator = new OOp_CurTime; + break; + case SQL_TOKEN_NOW: + pOperator = new OOp_Now; + break; + case SQL_TOKEN_WEEK: + pOperator = new OOp_Week; + break; + default: + OSL_FAIL("Error in switch!"); + } + } + break; + + case SQL_TOKEN_SUBSTRING: + m_aCodeList.emplace_back(new OStopOperand); + if ( pPredicateNode->count() == 4 ) //char_substring_fct + { + OSQLParseNode* pList = pPredicateNode->getChild(2); + for (size_t i=0; i < pList->count(); ++i) + execute(pList->getChild(i)); + } + else + { + execute(pPredicateNode->getChild(2)); + execute(pPredicateNode->getChild(4)); + execute(pPredicateNode->getChild(5)->getChild(1)); + } + pOperator = new OOp_SubString; + break; + + case SQL_TOKEN_POSITION: + m_aCodeList.emplace_back(new OStopOperand); + if ( pPredicateNode->count() == 4 ) //position_exp + { + OSQLParseNode* pList = pPredicateNode->getChild(2); + for (size_t i=0; i < pList->count(); ++i) + execute(pList->getChild(i)); + } + else + { + execute(pPredicateNode->getChild(2)); + execute(pPredicateNode->getChild(4)); + } + pOperator = new OOp_Locate; + break; + default: + m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_FUNCTION_NOT_SUPPORTED,nullptr); + } + + m_aCodeList.emplace_back(pOperator); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/file/quotedstring.cxx b/connectivity/source/drivers/file/quotedstring.cxx new file mode 100644 index 000000000..f654c709e --- /dev/null +++ b/connectivity/source/drivers/file/quotedstring.cxx @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <file/quotedstring.hxx> +#include <rtl/ustrbuf.hxx> + +namespace connectivity +{ + + sal_Int32 QuotedTokenizedString::GetTokenCount( sal_Unicode cTok, sal_Unicode cStrDel ) const + { + const sal_Int32 nLen = m_sString.getLength(); + if ( !nLen ) + return 0; + + sal_Int32 nTokCount = 1; + bool bStart = true; // Are we on the first character in the Token? + bool bInString = false; // Are we WITHIN a (cStrDel delimited) String? + + // Search for String-end after the first not matching character + for( sal_Int32 i = 0; i < nLen; ++i ) + { + const sal_Unicode cChar = m_sString[i]; + if (bStart) + { + bStart = false; + // First character a String-Delimiter? + if ( cChar == cStrDel ) + { + bInString = true; // then we are now WITHIN the string! + continue; // skip this character! + } + } + + if (bInString) + { + // when now the String-Delimiter-character occurs... + if ( cChar == cStrDel ) + { + if ((i+1 < nLen) && (m_sString[i+1] == cStrDel)) + { + // double String-Delimiter-character: + ++i; // no string-end, skip next character. + } + else + { + // String-End + bInString = false; + } + } + } // if (bInString) + else + { + // does the Token-character match, then raise TokCount + if ( cChar == cTok ) + { + ++nTokCount; + bStart = true; + } + } + } + + return nTokCount; + } + + + OUString QuotedTokenizedString::GetTokenSpecial(sal_Int32& nStartPos, sal_Unicode cTok, sal_Unicode cStrDel) const + { + const sal_Int32 nLen = m_sString.getLength(); + if ( nLen ) + { + bool bInString = (nStartPos < nLen) && (m_sString[nStartPos] == cStrDel); // are we WITHIN a (cStrDel delimited) String? + + // First character a String-Delimiter? + if (bInString ) + ++nStartPos; // skip this character! + if ( nStartPos >= nLen ) + return OUString(); + + OUStringBuffer sBuff( nLen - nStartPos + 1 ); + + // Search until end of string for the first not matching character + for( sal_Int32 i = nStartPos; i < nLen; ++i ) + { + const sal_Unicode cChar = m_sString[i]; + if (bInString) + { + // when now the String-Delimiter-character occurs ... + if ( cChar == cStrDel ) + { + if ((i+1 < nLen) && (m_sString[i+1] == cStrDel)) + { + // double String Delimiter-character + // no end of string, skip next character. + ++i; + sBuff.append(m_sString[i]); // character belongs to Result-String + } + else + { + //end of String + bInString = false; + } + } + else + { + sBuff.append(cChar); + } + } + else + { + // does the Token-sign match, then raise nTok + if ( cChar == cTok ) + { + // premature break of loop possible, because we found what we were looking for + nStartPos = i+1; + break; + } + else + { + sBuff.append(cChar); + } + } + } // for( sal_Int32 i = nStartPos; i < nLen; ++i ) + return sBuff.makeStringAndClear(); + } + return OUString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |