diff options
Diffstat (limited to '')
424 files changed, 115942 insertions, 0 deletions
diff --git a/connectivity/source/drivers/ado/ACallableStatement.cxx b/connectivity/source/drivers/ado/ACallableStatement.cxx new file mode 100644 index 000000000..d572a8f99 --- /dev/null +++ b/connectivity/source/drivers/ado/ACallableStatement.cxx @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/ACallableStatement.hxx> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/queryinterface.hxx> + +using namespace connectivity::ado; +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::container; + +IMPLEMENT_SERVICE_INFO(OCallableStatement,"com.sun.star.sdbcx.ACallableStatement","com.sun.star.sdbc.CallableStatement"); + +#define GET_PARAM() \ + ADOParameter* pParam = nullptr; \ + m_pParameters->get_Item(OLEVariant(sal_Int32(columnIndex-1)),&pParam); \ + if(pParam) \ + pParam->get_Value(&m_aValue); + +//************ Class: java.sql.CallableStatement + +OCallableStatement::OCallableStatement( OConnection* _pConnection, const OUString& sql ) + : OPreparedStatement( _pConnection, sql ) +{ + m_Command.put_CommandType(adCmdStoredProc); +} + + +Any SAL_CALL OCallableStatement::queryInterface( const Type & rType ) +{ + Any aRet = OPreparedStatement::queryInterface(rType); + return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< XRow*>(this)); +} + + +sal_Bool SAL_CALL OCallableStatement::wasNull( ) +{ + return m_aValue.isNull(); +} + + +sal_Bool SAL_CALL OCallableStatement::getBoolean( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getBool(); +} + +sal_Int8 SAL_CALL OCallableStatement::getByte( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getInt8(); +} + +Sequence< sal_Int8 > SAL_CALL OCallableStatement::getBytes( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getByteSequence(); +} + +css::util::Date SAL_CALL OCallableStatement::getDate( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getDate(); +} + +double SAL_CALL OCallableStatement::getDouble( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getDouble(); +} + + +float SAL_CALL OCallableStatement::getFloat( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getFloat(); +} + + +sal_Int32 SAL_CALL OCallableStatement::getInt( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getInt32(); +} + + +sal_Int64 SAL_CALL OCallableStatement::getLong( sal_Int32 columnIndex ) +{ + GET_PARAM() + return static_cast<sal_Int64>(m_aValue.getCurrency().int64); +} + + +Any SAL_CALL OCallableStatement::getObject( sal_Int32 /*columnIndex*/, const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getObject", *this ); + return Any(); +} + + +sal_Int16 SAL_CALL OCallableStatement::getShort( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getInt16(); +} + + +OUString SAL_CALL OCallableStatement::getString( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getString(); +} + + + css::util::Time SAL_CALL OCallableStatement::getTime( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getTime(); +} + + + css::util::DateTime SAL_CALL OCallableStatement::getTimestamp( sal_Int32 columnIndex ) +{ + GET_PARAM() + return m_aValue.getDateTime(); +} + + +void SAL_CALL OCallableStatement::registerOutParameter( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& /*typeName*/ ) +{ + ADOParameter* pParam = nullptr; + m_pParameters->get_Item(OLEVariant(sal_Int32(parameterIndex-1)),&pParam); + if(pParam) + { + pParam->put_Type(ADOS::MapJdbc2ADOType(sqlType,m_pConnection->getEngineType())); + pParam->put_Direction(adParamOutput); + } +} + +void SAL_CALL OCallableStatement::registerNumericOutParameter( sal_Int32 parameterIndex, sal_Int32 sqlType, sal_Int32 scale ) +{ + ADOParameter* pParam = nullptr; + m_pParameters->get_Item(OLEVariant(sal_Int32(parameterIndex-1)),&pParam); + if(pParam) + { + pParam->put_Type(ADOS::MapJdbc2ADOType(sqlType,m_pConnection->getEngineType())); + pParam->put_Direction(adParamOutput); + pParam->put_NumericScale(static_cast<sal_Int8>(scale)); + } +} + + +Reference< css::io::XInputStream > SAL_CALL OCallableStatement::getBinaryStream( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getBinaryStream", *this ); + return nullptr; +} + +Reference< css::io::XInputStream > SAL_CALL OCallableStatement::getCharacterStream( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getCharacterStream", *this ); + return nullptr; +} + + +Reference< XArray > SAL_CALL OCallableStatement::getArray( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getArray", *this ); + return nullptr; +} + + +Reference< XClob > SAL_CALL OCallableStatement::getClob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getClob", *this ); + return nullptr; +} + +Reference< XBlob > SAL_CALL OCallableStatement::getBlob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getBlob", *this ); + return nullptr; +} + + +Reference< XRef > SAL_CALL OCallableStatement::getRef( sal_Int32 /*columnIndex*/) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getRef", *this ); + return nullptr; +} + + +void SAL_CALL OCallableStatement::acquire() throw() +{ + OPreparedStatement::acquire(); +} + +void SAL_CALL OCallableStatement::release() throw() +{ + OPreparedStatement::release(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ACatalog.cxx b/connectivity/source/drivers/ado/ACatalog.cxx new file mode 100644 index 000000000..efab28344 --- /dev/null +++ b/connectivity/source/drivers/ado/ACatalog.cxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/ACatalog.hxx> +#include <ado/AConnection.hxx> +#include <ado/AGroups.hxx> +#include <ado/AUsers.hxx> +#include <ado/ATables.hxx> +#include <ado/AViews.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> + + +using namespace connectivity; +using namespace connectivity::ado; + +OCatalog::OCatalog(_ADOCatalog* _pCatalog,OConnection* _pCon) : connectivity::sdbcx::OCatalog(_pCon) + ,m_aCatalog(_pCatalog) + ,m_pConnection(_pCon) +{ +} + +OCatalog::~OCatalog() +{ + if(m_aCatalog.IsValid()) + m_aCatalog.putref_ActiveConnection(nullptr); + m_aCatalog.clear(); +} + +void OCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + + WpADOTables aTables(m_aCatalog.get_Tables()); + if ( aTables.IsValid() ) + { + aTables.Refresh(); + sal_Int32 nCount = aTables.GetItemCount(); + aVector.reserve(nCount); + for(sal_Int32 i=0;i< nCount;++i) + { + WpADOTable aElement = aTables.GetItem(i); + if ( aElement.IsValid() ) + { + OUString sTypeName = aElement.get_Type(); + if ( !sTypeName.equalsIgnoreAsciiCase("SYSTEM TABLE") + && !sTypeName.equalsIgnoreAsciiCase("ACCESS TABLE") ) + aVector.push_back(aElement.get_Name()); + } + } + } + + if(m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset( new OTables(this,m_aMutex,aVector,aTables,m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()) ); +} + +void OCatalog::refreshViews() +{ + ::std::vector< OUString> aVector; + + WpADOViews aViews = m_aCatalog.get_Views(); + aViews.fillElementNames(aVector); + + if(m_pViews) + m_pViews->reFill(aVector); + else + m_pViews.reset( new OViews(this,m_aMutex,aVector,aViews,m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()) ); +} + +void OCatalog::refreshGroups() +{ + ::std::vector< OUString> aVector; + + WpADOGroups aGroups = m_aCatalog.get_Groups(); + aGroups.fillElementNames(aVector); + + if(m_pGroups) + m_pGroups->reFill(aVector); + else + m_pGroups.reset( new OGroups(this,m_aMutex,aVector,aGroups,m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()) ); +} + +void OCatalog::refreshUsers() +{ + ::std::vector< OUString> aVector; + + WpADOUsers aUsers = m_aCatalog.get_Users(); + aUsers.fillElementNames(aVector); + + if(m_pUsers) + m_pUsers->reFill(aVector); + else + m_pUsers.reset( new OUsers(this,m_aMutex,aVector,aUsers,m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()) ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AColumn.cxx b/connectivity/source/drivers/ado/AColumn.cxx new file mode 100644 index 000000000..b7175d54c --- /dev/null +++ b/connectivity/source/drivers/ado/AColumn.cxx @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/AColumn.hxx> +#include <ado/AConnection.hxx> +#include <ado/Awrapado.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <comphelper/extract.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <ado/ACatalog.hxx> + +using namespace ::comphelper; + +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + +void WpADOColumn::Create() +{ + _ADOColumn* pColumn = nullptr; + HRESULT hr = CoCreateInstance(ADOS::CLSID_ADOCOLUMN_25, + nullptr, + CLSCTX_INPROC_SERVER, + ADOS::IID_ADOCOLUMN_25, + reinterpret_cast<void**>(&pColumn) ); + + + if( !FAILED( hr ) ) + { + operator=( pColumn ); + pColumn->Release( ); + } +} + +OAdoColumn::OAdoColumn(bool _bCase,OConnection* _pConnection,_ADOColumn* _pColumn) + : connectivity::sdbcx::OColumn(_bCase) + ,m_pConnection(_pConnection) +{ + construct(); + OSL_ENSURE(_pColumn,"Column can not be null!"); + m_aColumn = WpADOColumn(_pColumn); + // m_aColumn.put_ParentCatalog(_pConnection->getAdoCatalog()->getCatalog()); + fillPropertyValues(); +} + +OAdoColumn::OAdoColumn(bool _bCase,OConnection* _pConnection) + : connectivity::sdbcx::OColumn(_bCase) + ,m_pConnection(_pConnection) +{ + m_aColumn.Create(); + m_aColumn.put_ParentCatalog(_pConnection->getAdoCatalog()->getCatalog()); + construct(); + fillPropertyValues(); + m_Type = DataType::OTHER; +} + + +Sequence< sal_Int8 > OAdoColumn::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OAdoColumn::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OAdoColumn>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : OColumn_ADO::getSomething(rId); +} + +void OAdoColumn::construct() +{ + sal_Int32 nAttrib = isNew() ? 0 : PropertyAttribute::READONLY; + + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISASCENDING), PROPERTY_ID_ISASCENDING, nAttrib,&m_IsAscending, cppu::UnoType<bool>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RELATEDCOLUMN), PROPERTY_ID_RELATEDCOLUMN, nAttrib,&m_ReferencedColumn, ::cppu::UnoType<OUString>::get()); +} + +void OAdoColumn::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + if(m_aColumn.IsValid()) + { + const char* pAdoPropertyName = nullptr; + + switch(nHandle) + { + case PROPERTY_ID_ISASCENDING: + m_aColumn.put_SortOrder(::cppu::any2bool(rValue) ? adSortAscending : adSortDescending); + break; + case PROPERTY_ID_RELATEDCOLUMN: + { + OUString aVal; + rValue >>= aVal; + m_aColumn.put_RelatedColumn(aVal); + } + break; + case PROPERTY_ID_NAME: + { + OUString aVal; + rValue >>= aVal; + m_aColumn.put_Name(aVal); + } + break; + case PROPERTY_ID_TYPE: + { + sal_Int32 nVal=0; + rValue >>= nVal; + m_aColumn.put_Type(ADOS::MapJdbc2ADOType(nVal,m_pConnection->getEngineType())); + } + break; + case PROPERTY_ID_TYPENAME: + // rValue <<= m_pTable->getCatalog()->getConnection()->getTypeInfo()->find(); + break; + case PROPERTY_ID_PRECISION: + { + sal_Int32 nVal=0; + rValue >>= nVal; + m_aColumn.put_Precision(nVal); + } + break; + case PROPERTY_ID_SCALE: + { + sal_Int32 nVal=0; + rValue >>= nVal; + if ( !m_IsCurrency ) + m_aColumn.put_NumericScale(static_cast<sal_Int8>(nVal)); + } + break; + case PROPERTY_ID_ISNULLABLE: + { + sal_Int32 nVal=0; + rValue >>= nVal; + if ( nVal == ColumnValue::NULLABLE ) + m_aColumn.put_Attributes( adColNullable ); + } + break; + case PROPERTY_ID_ISROWVERSION: + break; + + case PROPERTY_ID_ISAUTOINCREMENT: + OTools::putValue( m_aColumn.get_Properties(), OUString( "Autoincrement" ), getBOOL( rValue ) ); + break; + + case PROPERTY_ID_IM001: + case PROPERTY_ID_DESCRIPTION: + pAdoPropertyName = "Description"; + break; + + case PROPERTY_ID_DEFAULTVALUE: + pAdoPropertyName = "Default"; + break; + } + + if ( pAdoPropertyName ) + OTools::putValue( m_aColumn.get_Properties(), OUString::createFromAscii( pAdoPropertyName ), getString( rValue ) ); + } + OColumn_ADO::setFastPropertyValue_NoBroadcast(nHandle,rValue); +} + +void OAdoColumn::fillPropertyValues() +{ + if(m_aColumn.IsValid()) + { + m_IsAscending = m_aColumn.get_SortOrder() == adSortAscending; + m_ReferencedColumn = m_aColumn.get_RelatedColumn(); + m_Name = m_aColumn.get_Name(); + m_Precision = m_aColumn.get_Precision(); + m_Scale = m_aColumn.get_NumericScale(); + m_IsNullable = ((m_aColumn.get_Attributes() & adColNullable) == adColNullable) ? ColumnValue::NULLABLE : ColumnValue::NO_NULLS; + + DataTypeEnum eType = m_aColumn.get_Type(); + m_IsCurrency = (eType == adCurrency); + if ( m_IsCurrency && !m_Scale) + m_Scale = 4; + m_Type = ADOS::MapADOType2Jdbc(eType); + + bool bForceTo = true; + const OTypeInfoMap* pTypeInfoMap = m_pConnection->getTypeInfo(); + const OExtendedTypeInfo* pTypeInfo = OConnection::getTypeInfoFromType(*m_pConnection->getTypeInfo(),eType,OUString(),m_Precision,m_Scale,bForceTo); + if ( pTypeInfo ) + m_TypeName = pTypeInfo->aSimpleType.aTypeName; + else if ( eType == adVarBinary && ADOS::isJetEngine(m_pConnection->getEngineType()) ) + { + ::comphelper::UStringMixEqual aCase(false); + OTypeInfoMap::const_iterator aFind = std::find_if(pTypeInfoMap->begin(), pTypeInfoMap->end(), + [&aCase] (const OTypeInfoMap::value_type& typeInfo) { + return aCase(typeInfo.second->getDBName(), OUString("VarBinary")); + }); + + if ( aFind != pTypeInfoMap->end() ) // change column type if necessary + { + eType = aFind->first; + pTypeInfo = aFind->second; + } + + if ( !pTypeInfo ) + { + pTypeInfo = OConnection::getTypeInfoFromType(*m_pConnection->getTypeInfo(),adBinary,OUString(),m_Precision,m_Scale,bForceTo); + eType = adBinary; + } + + if ( pTypeInfo ) + { + m_TypeName = pTypeInfo->aSimpleType.aTypeName; + m_Type = ADOS::MapADOType2Jdbc(eType); + } + } + + + // fill some specific props + { + WpADOProperties aProps( m_aColumn.get_Properties() ); + + if ( aProps.IsValid() ) + { + m_IsAutoIncrement = OTools::getValue( aProps, OUString("Autoincrement") ).getBool(); + + m_Description = OTools::getValue( aProps, OUString("Description") ).getString(); + + m_DefaultValue = OTools::getValue( aProps, OUString("Default") ).getString(); + } + } + } +} + +WpADOColumn OAdoColumn::getColumnImpl() const +{ + return m_aColumn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AColumns.cxx b/connectivity/source/drivers/ado/AColumns.cxx new file mode 100644 index 000000000..e9dc7720f --- /dev/null +++ b/connectivity/source/drivers/ado/AColumns.cxx @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AColumns.hxx> +#include <ado/AColumn.hxx> +#include <ado/AConnection.hxx> +#include <ado/Awrapado.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <comphelper/property.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <algorithm> +#include <strings.hrc> + +using namespace connectivity::ado; +using namespace connectivity; +using namespace comphelper; +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::container; + +sdbcx::ObjectType OColumns::createObject(const OUString& _rName) +{ + return new OAdoColumn(isCaseSensitive(),m_pConnection,m_aCollection.GetItem(_rName)); +} + + +void OColumns::impl_refresh() +{ + m_aCollection.Refresh(); +} + +Reference< XPropertySet > OColumns::createDescriptor() +{ + return new OAdoColumn(isCaseSensitive(),m_pConnection); +} + +// XAppend +sdbcx::ObjectType OColumns::appendObject( const OUString&, const Reference< XPropertySet >& descriptor ) +{ + OAdoColumn* pColumn = getUnoTunnelImplementation<OAdoColumn>( descriptor ); + Reference< XPropertySet > xColumn; + if ( pColumn == nullptr ) + { + // m_pConnection->throwGenericSQLException( STR_INVALID_COLUMN_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + pColumn = new OAdoColumn(isCaseSensitive(),m_pConnection); + xColumn = pColumn; + ::comphelper::copyProperties(descriptor,xColumn); + } + + WpADOColumn aColumn = pColumn->getColumnImpl(); + +#if OSL_DEBUG_LEVEL > 0 + sal_Int32 nPrecision; + sal_Int32 nScale; + sal_Int32 nType; + nPrecision = aColumn.get_Precision(); + nScale = aColumn.get_NumericScale(); + nType = ADOS::MapADOType2Jdbc(aColumn.get_Type()); +#endif + + OUString sTypeName; + pColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME)) >>= sTypeName; + + const OTypeInfoMap* pTypeInfoMap = m_pConnection->getTypeInfo(); + ::comphelper::UStringMixEqual aCase(false); + // search for typeinfo where the typename is equal sTypeName + OTypeInfoMap::const_iterator aFind = std::find_if(pTypeInfoMap->begin(), pTypeInfoMap->end(), + [&aCase, &sTypeName] (const OTypeInfoMap::value_type& typeInfo) { + return aCase(typeInfo.second->getDBName(), sTypeName); + }); + + if ( aFind != pTypeInfoMap->end() ) // change column type if necessary + aColumn.put_Type(aFind->first); + + if ( SUCCEEDED(static_cast<ADOColumns*>(m_aCollection)->Append(OLEVariant(aColumn.get_Name()),aColumn.get_Type(),aColumn.get_DefinedSize())) ) + { + WpADOColumn aAddedColumn = m_aCollection.GetItem(OLEVariant(aColumn.get_Name())); + if ( aAddedColumn.IsValid() ) + { + bool bAutoIncrement = false; + pColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT)) >>= bAutoIncrement; + if ( bAutoIncrement ) + OTools::putValue( aAddedColumn.get_Properties(), OUString("Autoincrement"), bAutoIncrement ); + + if ( aFind != pTypeInfoMap->end() && aColumn.get_Type() != aAddedColumn.get_Type() ) // change column type if necessary + aColumn.put_Type(aFind->first); + aAddedColumn.put_Precision(aColumn.get_Precision()); + aAddedColumn.put_NumericScale(aColumn.get_NumericScale()); + aAddedColumn.put_Attributes(aColumn.get_Attributes()); + aAddedColumn.put_SortOrder(aColumn.get_SortOrder()); + aAddedColumn.put_RelatedColumn(aColumn.get_RelatedColumn()); + } + } + ADOS::ThrowException(*m_pConnection->getConnection(),static_cast<XTypeProvider*>(this)); + + return new OAdoColumn(isCaseSensitive(),m_pConnection,pColumn->getColumnImpl()); +} + +// XDrop +void OColumns::dropObject(sal_Int32 /*_nPos*/,const OUString& _sElementName) +{ + if(!m_aCollection.Delete(_sElementName)) + ADOS::ThrowException(*m_pConnection->getConnection(),static_cast<XTypeProvider*>(this)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AConnection.cxx b/connectivity/source/drivers/ado/AConnection.cxx new file mode 100644 index 000000000..c9251a9be --- /dev/null +++ b/connectivity/source/drivers/ado/AConnection.cxx @@ -0,0 +1,587 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AConnection.hxx> +#include <ado/ADatabaseMetaData.hxx> +#include <ado/ADriver.hxx> +#include <ado/AStatement.hxx> +#include <ado/ACallableStatement.hxx> +#include <ado/APreparedStatement.hxx> +#include <ado/ACatalog.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <connectivity/dbexception.hxx> +#include <osl/file.hxx> +#include <strings.hrc> + +using namespace dbtools; +using namespace connectivity::ado; +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; + + +IMPLEMENT_SERVICE_INFO(OConnection,"com.sun.star.sdbcx.AConnection","com.sun.star.sdbc.Connection"); + +OConnection::OConnection(ODriver* _pDriver) + : m_xCatalog(nullptr), + m_pDriver(_pDriver), + m_pAdoConnection(nullptr), + m_pCatalog(nullptr), + m_nEngineType(0), + m_bClosed(false), + m_bAutocommit(true) +{ + osl_atomic_increment( &m_refCount ); + + IClassFactory2* pIUnknown = nullptr; + HRESULT hr; + hr = CoGetClassObject( ADOS::CLSID_ADOCONNECTION_21, + CLSCTX_INPROC_SERVER, + nullptr, + IID_IClassFactory2, + reinterpret_cast<void**>(&pIUnknown) ); + + if( !FAILED( hr ) ) + { + ADOConnection *pCon = nullptr; + IUnknown *pOuter = nullptr; + hr = pIUnknown->CreateInstanceLic( pOuter, + nullptr, + ADOS::IID_ADOCONNECTION_21, + ADOS::GetKeyStr().asBSTR(), + reinterpret_cast<void**>(&pCon)); + + if( !FAILED( hr ) ) + { + OSL_ENSURE( pCon, "OConnection::OConnection: invalid ADO object!" ); + + m_pAdoConnection = new WpADOConnection( pCon ); + // CreateInstanceLic returned an object which was already acquired + pCon->Release( ); + + } + + // Class Factory is no longer needed + pIUnknown->Release(); + } + + osl_atomic_decrement( &m_refCount ); +} + +OConnection::~OConnection() +{ +} + +void OConnection::construct(const OUString& url,const Sequence< PropertyValue >& info) +{ + osl_atomic_increment( &m_refCount ); + + setConnectionInfo(info); + + sal_Int32 nLen = url.indexOf(':'); + nLen = url.indexOf(':',nLen+1); + OUString aDSN(url.copy(nLen+1)),aUID,aPWD; + if ( aDSN.startsWith("access:") ) + aDSN = aDSN.copy(7); + + sal_Int32 nTimeout = 20; + const PropertyValue *pIter = info.getConstArray(); + const PropertyValue *pEnd = pIter + info.getLength(); + for(;pIter != pEnd;++pIter) + { + if(pIter->Name == "Timeout") + pIter->Value >>= nTimeout; + else if(pIter->Name == "user") + pIter->Value >>= aUID; + else if(pIter->Name == "password") + pIter->Value >>= aPWD; + } + try + { + if(m_pAdoConnection) + { + if(m_pAdoConnection->Open(aDSN,aUID,aPWD,adConnectUnspecified)) + m_pAdoConnection->PutCommandTimeout(nTimeout); + else + ADOS::ThrowException(*m_pAdoConnection,*this); + if(m_pAdoConnection->get_State() != adStateOpen) + throwGenericSQLException( STR_NO_CONNECTION,*this ); + + WpADOProperties aProps = m_pAdoConnection->get_Properties(); + if(aProps.IsValid()) + { + OTools::putValue(aProps,OUString("Jet OLEDB:ODBC Parsing"),true); + OLEVariant aVar(OTools::getValue(aProps,OUString("Jet OLEDB:Engine Type"))); + if(!aVar.isNull() && !aVar.isEmpty()) + m_nEngineType = aVar.getInt32(); + } + buildTypeInfo(); + //bErg = TRUE; + } + else + ::dbtools::throwFunctionSequenceException(*this); + + } + catch(const Exception& ) + { + osl_atomic_decrement( &m_refCount ); + throw; + } + osl_atomic_decrement( &m_refCount ); +} + +Reference< XStatement > SAL_CALL OConnection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + OStatement* pStmt = new OStatement(this); + Reference< XStatement > xStmt = pStmt; + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return pStmt; +} + +Reference< XPreparedStatement > SAL_CALL OConnection::prepareStatement( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OPreparedStatement* pStmt = new OPreparedStatement(this, sql); + Reference< XPreparedStatement > xPStmt = pStmt; + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return xPStmt; +} + +Reference< XPreparedStatement > SAL_CALL OConnection::prepareCall( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OCallableStatement* pStmt = new OCallableStatement(this, sql); + Reference< XPreparedStatement > xPStmt = pStmt; + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return xPStmt; +} + +OUString SAL_CALL OConnection::nativeSQL( const OUString& _sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OUString sql = _sql; + WpADOProperties aProps = m_pAdoConnection->get_Properties(); + if(aProps.IsValid()) + { + OTools::putValue(aProps,OUString("Jet OLEDB:ODBC Parsing"),true); + WpADOCommand aCommand; + aCommand.Create(); + aCommand.put_ActiveConnection(static_cast<IDispatch*>(*m_pAdoConnection)); + aCommand.put_CommandText(sql); + sql = aCommand.get_CommandText(); + } + + return sql; +} + +void SAL_CALL OConnection::setAutoCommit( sal_Bool autoCommit ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + m_bAutocommit = autoCommit; + if(!autoCommit) + m_pAdoConnection->BeginTrans(); + else + m_pAdoConnection->RollbackTrans(); +} + +sal_Bool SAL_CALL OConnection::getAutoCommit( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + return m_bAutocommit; +} + +void SAL_CALL OConnection::commit( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + m_pAdoConnection->CommitTrans(); +} + +void SAL_CALL OConnection::rollback( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + m_pAdoConnection->RollbackTrans(); +} + +sal_Bool SAL_CALL OConnection::isClosed( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return OConnection_BASE::rBHelper.bDisposed && !m_pAdoConnection->get_State(); +} + +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_pAdoConnection->put_Mode(readOnly ? adModeRead : adModeReadWrite); + ADOS::ThrowException(*m_pAdoConnection,*this); +} + +sal_Bool SAL_CALL OConnection::isReadOnly( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + return m_pAdoConnection->get_Mode() == adModeRead; +} + +void SAL_CALL OConnection::setCatalog( const OUString& catalog ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + m_pAdoConnection->PutDefaultDatabase(catalog); + ADOS::ThrowException(*m_pAdoConnection,*this); +} + +OUString SAL_CALL OConnection::getCatalog( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + return m_pAdoConnection->GetDefaultDatabase(); +} + +void SAL_CALL OConnection::setTransactionIsolation( sal_Int32 level ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + IsolationLevelEnum eIso; + switch(level) + { + case TransactionIsolation::NONE: + eIso = adXactUnspecified; + break; + case TransactionIsolation::READ_UNCOMMITTED: + eIso = adXactReadUncommitted; + break; + case TransactionIsolation::READ_COMMITTED: + eIso = adXactReadCommitted; + break; + case TransactionIsolation::REPEATABLE_READ: + eIso = adXactRepeatableRead; + break; + case TransactionIsolation::SERIALIZABLE: + eIso = adXactSerializable; + break; + default: + OSL_FAIL("OConnection::setTransactionIsolation invalid level"); + return; + } + m_pAdoConnection->put_IsolationLevel(eIso); + ADOS::ThrowException(*m_pAdoConnection,*this); +} + +sal_Int32 SAL_CALL OConnection::getTransactionIsolation( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + sal_Int32 nRet = 0; + switch(m_pAdoConnection->get_IsolationLevel()) + { + case adXactUnspecified: + nRet = TransactionIsolation::NONE; + break; + case adXactReadUncommitted: + nRet = TransactionIsolation::READ_UNCOMMITTED; + break; + case adXactReadCommitted: + nRet = TransactionIsolation::READ_COMMITTED; + break; + case adXactRepeatableRead: + nRet = TransactionIsolation::REPEATABLE_READ; + break; + case adXactSerializable: + nRet = TransactionIsolation::SERIALIZABLE; + break; + default: + OSL_FAIL("OConnection::setTransactionIsolation invalid level"); + } + ADOS::ThrowException(*m_pAdoConnection,*this); + return nRet; +} + +Reference< css::container::XNameAccess > SAL_CALL OConnection::getTypeMap( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + return nullptr; +} + +void SAL_CALL OConnection::setTypeMap( const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this ); +} + +// 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::buildTypeInfo() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ADORecordset *pRecordset = m_pAdoConnection->getTypeInfo(); + if ( pRecordset ) + { + pRecordset->AddRef(); + VARIANT_BOOL bIsAtBOF; + pRecordset->get_BOF(&bIsAtBOF); + + bool bOk = true; + if ( bIsAtBOF == VARIANT_TRUE ) + bOk = SUCCEEDED(pRecordset->MoveNext()); + + if ( bOk ) + { + // HACK for access + static const char s_sVarChar[] = "VarChar"; + do + { + sal_Int32 nPos = 1; + OExtendedTypeInfo* aInfo = new OExtendedTypeInfo; + aInfo->aSimpleType.aTypeName = ADOS::getField(pRecordset,nPos++).get_Value().getString(); + aInfo->eType = static_cast<DataTypeEnum>(ADOS::getField(pRecordset,nPos++).get_Value().getInt32()); + if ( aInfo->eType == adWChar && aInfo->aSimpleType.aTypeName == s_sVarChar ) + aInfo->eType = adVarWChar; + aInfo->aSimpleType.nType = static_cast<sal_Int16>(ADOS::MapADOType2Jdbc(aInfo->eType)); + aInfo->aSimpleType.nPrecision = ADOS::getField(pRecordset,nPos++).get_Value().getInt32(); + nPos++; // aLiteralPrefix + nPos++; // aLiteralSuffix + nPos++; // aCreateParams + nPos++; // bNullable + nPos++; // bCaseSensitive + nPos++; // nSearchType + nPos++; // bUnsigned + nPos++; // bCurrency + nPos++; // bAutoIncrement + aInfo->aSimpleType.aLocalTypeName = ADOS::getField(pRecordset,nPos++).get_Value().getString(); + nPos++; // nMinimumScale + aInfo->aSimpleType.nMaximumScale = ADOS::getField(pRecordset,nPos++).get_Value().getInt16(); + if ( adCurrency == aInfo->eType && !aInfo->aSimpleType.nMaximumScale) + { + aInfo->aSimpleType.nMaximumScale = 4; + } + nPos++; // nNumPrecRadix + // Now that we have the type info, save it + // in the Hashtable if we don't already have an + // entry for this SQL type. + + m_aTypeInfo.emplace(aInfo->eType,aInfo); + } + while ( SUCCEEDED(pRecordset->MoveNext()) ); + } + pRecordset->Release(); + } +} + +void OConnection::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + OConnection_BASE::disposing(); + + m_bClosed = true; + m_xMetaData = css::uno::WeakReference< css::sdbc::XDatabaseMetaData>(); + m_xCatalog = css::uno::WeakReference< css::sdbcx::XTablesSupplier>(); + m_pDriver = nullptr; + + m_pAdoConnection->Close(); + + for (auto& rEntry : m_aTypeInfo) + delete rEntry.second; + + m_aTypeInfo.clear(); + + delete m_pAdoConnection; + m_pAdoConnection = nullptr; +} + +sal_Int64 SAL_CALL OConnection::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return isUnoTunnelId<OConnection>(rId) + ? + reinterpret_cast< sal_Int64 >( this ) + : + OConnection_BASE::getSomething(rId); +} + +Sequence< sal_Int8 > OConnection::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +const OExtendedTypeInfo* OConnection::getTypeInfoFromType(const OTypeInfoMap& _rTypeInfo, + DataTypeEnum _nType, + const OUString& _sTypeName, + sal_Int32 _nPrecision, + sal_Int32 _nScale, + bool& _brForceToType) +{ + const OExtendedTypeInfo* pTypeInfo = nullptr; + _brForceToType = false; + // search for type + std::pair<OTypeInfoMap::const_iterator, OTypeInfoMap::const_iterator> aPair = _rTypeInfo.equal_range(_nType); + OTypeInfoMap::const_iterator aIter = aPair.first; + if(aIter != _rTypeInfo.end()) // compare with end is correct here + { + for(;aIter != aPair.second;++aIter) + { + // search the best matching type + OExtendedTypeInfo* pInfo = aIter->second; + if ( ( !_sTypeName.getLength() + || (pInfo->aSimpleType.aTypeName.equalsIgnoreAsciiCase(_sTypeName)) + ) + && (pInfo->aSimpleType.nPrecision >= _nPrecision) + && (pInfo->aSimpleType.nMaximumScale >= _nScale) + + ) + break; + } + + if (aIter == aPair.second) + { + for(aIter = aPair.first; aIter != aPair.second; ++aIter) + { + // search the best matching type (now comparing the local names) + if ( (aIter->second->aSimpleType.aLocalTypeName.equalsIgnoreAsciiCase(_sTypeName)) + && (aIter->second->aSimpleType.nPrecision >= _nPrecision) + && (aIter->second->aSimpleType.nMaximumScale >= _nScale) + ) + { +// we can not assert here because we could be in d&d +/* + OSL_FAIL(( OString("getTypeInfoFromType: assuming column type ") + += OString(aIter->second->aTypeName.getStr(), aIter->second->aTypeName.getLength(), osl_getThreadTextEncoding()) + += OString("\" (expected type name ") + += OString(_sTypeName.getStr(), _sTypeName.getLength(), osl_getThreadTextEncoding()) + += OString(" matches the type's local name).")).getStr()); +*/ + break; + } + } + } + + if (aIter == aPair.second) + { // no match for the names, no match for the local names + // -> drop the precision and the scale restriction, accept any type with the property + // type id (nType) + + // we can not assert here because we could be in d&d + pTypeInfo = aPair.first->second; + _brForceToType = true; + } + else + pTypeInfo = aIter->second; + } + else if ( _sTypeName.getLength() ) + { + ::comphelper::UStringMixEqual aCase(false); + // search for typeinfo where the typename is equal _sTypeName + OTypeInfoMap::const_iterator aFind = std::find_if(_rTypeInfo.begin(), _rTypeInfo.end(), + [&aCase, &_sTypeName] (const OTypeInfoMap::value_type& typeInfo) { + return aCase(typeInfo.second->getDBName(), _sTypeName); + }); + + if(aFind != _rTypeInfo.end()) + pTypeInfo = aFind->second; + } + +// we can not assert here because we could be in d&d +// OSL_ENSURE(pTypeInfo, "getTypeInfoFromType: no type info found for this type!"); + return pTypeInfo; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ADatabaseMetaData.cxx b/connectivity/source/drivers/ado/ADatabaseMetaData.cxx new file mode 100644 index 000000000..8e46761e8 --- /dev/null +++ b/connectivity/source/drivers/ado/ADatabaseMetaData.cxx @@ -0,0 +1,1073 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/ADatabaseMetaData.hxx> +#include <ado/ADatabaseMetaDataResultSet.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <ado/AConnection.hxx> +#include <ado/adoimp.hxx> +#include <FDatabaseMetaDataResultSet.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> + +using namespace ::comphelper; + +using namespace connectivity; +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + + +ODatabaseMetaData::ODatabaseMetaData(OConnection* _pCon) + : ::connectivity::ODatabaseMetaDataBase(_pCon,_pCon->getConnectionInfo()) + ,m_pADOConnection(_pCon->getConnection()) + ,m_pConnection(_pCon) +{ +} + +sal_Int32 ODatabaseMetaData::getInt32Property(const OUString& _aProperty) +{ + connectivity::ado::WpADOProperties aProps(m_pADOConnection->get_Properties()); + // ADOS::ThrowException(*m_pADOConnection,*this); + OSL_ENSURE(aProps.IsValid(),"There are no properties at the connection"); + ADO_PROP(_aProperty); + sal_Int32 nValue(0); + if(!aVar.isNull() && !aVar.isEmpty()) + nValue = aVar.getInt32(); + return nValue; +} + + +bool ODatabaseMetaData::getBoolProperty(const OUString& _aProperty) +{ + connectivity::ado::WpADOProperties aProps(m_pADOConnection->get_Properties()); + ADOS::ThrowException(*m_pADOConnection,*this); + OSL_ENSURE(aProps.IsValid(),"There are no properties at the connection"); + ADO_PROP(_aProperty); + return !aVar.isNull() && !aVar.isEmpty() && aVar.getBool(); +} + +OUString ODatabaseMetaData::getStringProperty(const OUString& _aProperty) +{ + connectivity::ado::WpADOProperties aProps(m_pADOConnection->get_Properties()); + ADOS::ThrowException(*m_pADOConnection,*this); + OSL_ENSURE(aProps.IsValid(),"There are no properties at the connection"); + + ADO_PROP(_aProperty); + OUString aValue; + if(!aVar.isNull() && !aVar.isEmpty() && aVar.getType() == VT_BSTR) + aValue = aVar.getString(); + + return aValue; +} + +Reference< XResultSet > ODatabaseMetaData::impl_getTypeInfo_throw( ) +{ + ADORecordset *pRecordset = m_pADOConnection->getTypeInfo(); + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setTypeInfoMap(ADOS::isJetEngine(m_pConnection->getEngineType())); + Reference< XResultSet > xRef = pResult; + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCatalogs( ) +{ + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + ADORecordset *pRecordset = nullptr; + m_pADOConnection->OpenSchema(adSchemaCatalogs,vtEmpty,vtEmpty,&pRecordset); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setCatalogsMap(); + xRef = pResult; + + return xRef; +} + +OUString ODatabaseMetaData::impl_getCatalogSeparator_throw( ) +{ + return getLiteral(DBLITERAL_CATALOG_SEPARATOR); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getSchemas( ) +{ + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + ADORecordset *pRecordset = nullptr; + m_pADOConnection->OpenSchema(adSchemaSchemata,vtEmpty,vtEmpty,&pRecordset); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setSchemasMap(); + xRef = pResult; + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumnPrivileges( + const Any& catalog, const OUString& schema, const OUString& table, + const OUString& columnNamePattern ) +{ + ADORecordset *pRecordset = m_pADOConnection->getColumnPrivileges(catalog,schema,table,columnNamePattern); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setColumnPrivilegesMap(); + xRef = pResult; + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumns( + const Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + ADORecordset *pRecordset = m_pADOConnection->getColumns(catalog,schemaPattern,tableNamePattern,columnNamePattern); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setColumnsMap(); + xRef = pResult; + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTables( + const Any& catalog, const OUString& schemaPattern, + const OUString& tableNamePattern, const Sequence< OUString >& types ) +{ + ADORecordset *pRecordset = m_pADOConnection->getTables(catalog,schemaPattern,tableNamePattern,types); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setTablesMap(); + xRef = pResult; + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedureColumns( + const Any& catalog, const OUString& schemaPattern, + const OUString& procedureNamePattern, const OUString& columnNamePattern ) +{ + ADORecordset *pRecordset = m_pADOConnection->getProcedureColumns(catalog,schemaPattern,procedureNamePattern,columnNamePattern); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setProcedureColumnsMap(); + xRef = pResult; + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedures( + const Any& catalog, const OUString& schemaPattern, + const OUString& procedureNamePattern ) +{ + // Create elements used in the array + ADORecordset *pRecordset = m_pADOConnection->getProcedures(catalog,schemaPattern,procedureNamePattern); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setProceduresMap(); + xRef = pResult; + + return xRef; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + return getMaxSize(DBLITERAL_BINARY_LITERAL); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxRowSize( ) +{ + return getInt32Property("Maximum Row Size"); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCatalogNameLength( ) +{ + return getMaxSize(DBLITERAL_CATALOG_NAME); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCharLiteralLength( ) +{ + return getMaxSize(DBLITERAL_CHAR_LITERAL); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnNameLength( ) +{ + return getMaxSize(DBLITERAL_COLUMN_NAME); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInIndex( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCursorNameLength( ) +{ + return getMaxSize(DBLITERAL_CURSOR_NAME); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxConnections( ) +{ + return getInt32Property("Active Sessions"); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInTable( ) +{ + return getInt32Property("Max Columns in Table"); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatementLength( ) +{ + return getMaxSize(DBLITERAL_TEXT_COMMAND); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTableNameLength( ) +{ + return getMaxSize(DBLITERAL_TABLE_NAME); +} + +sal_Int32 ODatabaseMetaData::impl_getMaxTablesInSelect_throw( ) +{ + return getInt32Property("Maximum Tables in SELECT"); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getExportedKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + ADORecordset *pRecordset = m_pADOConnection->getExportedKeys(catalog,schema,table); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setCrossReferenceMap(); + xRef = pResult; + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getImportedKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + ADORecordset *pRecordset = m_pADOConnection->getImportedKeys(catalog,schema,table); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setCrossReferenceMap(); + xRef = pResult; + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getPrimaryKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + ADORecordset *pRecordset = m_pADOConnection->getPrimaryKeys(catalog,schema,table); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setPrimaryKeysMap(); + xRef = pResult; + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getIndexInfo( + const Any& catalog, const OUString& schema, const OUString& table, + sal_Bool unique, sal_Bool approximate ) +{ + ADORecordset *pRecordset = m_pADOConnection->getIndexInfo(catalog,schema,table,unique,approximate); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setIndexInfoMap(); + xRef = pResult; + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTablePrivileges( + const Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern ) +{ + Reference< XResultSet > xRef; + if(!ADOS::isJetEngine(m_pConnection->getEngineType())) + { // the jet provider doesn't support this method + // Create elements used in the array + + ADORecordset *pRecordset = m_pADOConnection->getTablePrivileges(catalog,schemaPattern,tableNamePattern); + ADOS::ThrowException(*m_pADOConnection,*this); + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setTablePrivilegesMap(); + xRef = pResult; + } + else + { + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTablePrivileges); + xRef = pResult; + ::connectivity::ODatabaseMetaDataResultSet::ORows aRows; + ::connectivity::ODatabaseMetaDataResultSet::ORow aRow(8); + aRows.reserve(8); + + aRow[0] = ::connectivity::ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[1] = ::connectivity::ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[2] = new ::connectivity::ORowSetValueDecorator(tableNamePattern); + aRow[3] = ::connectivity::ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[4] = ::connectivity::ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[5] = new ::connectivity::ORowSetValueDecorator(getUserName()); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getSelectValue(); + aRow[7] = new ::connectivity::ORowSetValueDecorator(OUString("NO")); + + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getInsertValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getDeleteValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getUpdateValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getCreateValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getReadValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getAlterValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getDropValue(); + aRows.push_back(aRow); + pResult->setRows(aRows); + } + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCrossReference( + const Any& primaryCatalog, const OUString& primarySchema, + const OUString& primaryTable, const Any& foreignCatalog, + const OUString& foreignSchema, const OUString& foreignTable ) +{ + ADORecordset *pRecordset = m_pADOConnection->getCrossReference(primaryCatalog,primarySchema,primaryTable,foreignCatalog,foreignSchema,foreignTable); + ADOS::ThrowException(*m_pADOConnection,*this); + + Reference< XResultSet > xRef; + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(pRecordset); + pResult->setCrossReferenceMap(); + xRef = pResult; + + return xRef; +} + +sal_Bool SAL_CALL ODatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + return getBoolProperty("Maximum Row Size Includes BLOB"); +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseQuotedIdentifiers( ) +{ + return (getInt32Property("Identifier Case Sensitivity") & DBPROPVAL_IC_LOWER) == DBPROPVAL_IC_LOWER ; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseIdentifiers( ) +{ + return (getInt32Property("Identifier Case Sensitivity") & DBPROPVAL_IC_LOWER) == DBPROPVAL_IC_LOWER ; +} + +bool ODatabaseMetaData::impl_storesMixedCaseQuotedIdentifiers_throw( ) +{ + return (getInt32Property("Identifier Case Sensitivity") & DBPROPVAL_IC_MIXED) == DBPROPVAL_IC_MIXED ; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseIdentifiers( ) +{ + return (getInt32Property("Identifier Case Sensitivity") & DBPROPVAL_IC_MIXED) == DBPROPVAL_IC_MIXED ; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseQuotedIdentifiers( ) +{ + return (getInt32Property("Identifier Case Sensitivity") & DBPROPVAL_IC_UPPER) == DBPROPVAL_IC_UPPER ; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseIdentifiers( ) +{ + return (getInt32Property("Identifier Case Sensitivity") & DBPROPVAL_IC_UPPER) == DBPROPVAL_IC_UPPER ; +} + +bool ODatabaseMetaData::impl_supportsAlterTableWithAddColumn_throw( ) +{ + return true; +} + +bool ODatabaseMetaData::impl_supportsAlterTableWithDropColumn_throw( ) +{ + return true; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxIndexLength( ) +{ + return getInt32Property("Maximum Index Size"); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsNonNullableColumns( ) +{ + return getInt32Property("NULL Concatenation Behavior") == DBPROPVAL_CB_NON_NULL; +} + +OUString SAL_CALL ODatabaseMetaData::getCatalogTerm( ) +{ + return getStringProperty("Catalog Term"); +} + +OUString ODatabaseMetaData::impl_getIdentifierQuoteString_throw( ) +{ + return getLiteral(DBLITERAL_QUOTE_PREFIX); + +} + +OUString SAL_CALL ODatabaseMetaData::getExtraNameCharacters( ) +{ + return OUString(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDifferentTableCorrelationNames( ) +{ + return isCapable(DBLITERAL_CORRELATION_NAME); +} + +bool ODatabaseMetaData::impl_isCatalogAtStart_throw( ) +{ + return getInt32Property("Catalog Location") == DBPROPVAL_CL_START; +} + +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionIgnoredInTransactions( ) +{ + return getInt32Property("Transaction DDL") == DBPROPVAL_TC_DDL_IGNORE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionCausesTransactionCommit( ) +{ + return getInt32Property("Transaction DDL") == DBPROPVAL_TC_DDL_COMMIT; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDataManipulationTransactionsOnly( ) +{ + return getInt32Property("Transaction DDL") == DBPROPVAL_TC_DML; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDataDefinitionAndDataManipulationTransactions( ) +{ + return getInt32Property("Transaction DDL") == DBPROPVAL_TC_ALL; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedDelete( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedUpdate( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossRollback( ) +{ + return getInt32Property("Prepare Abort Behavior") == DBPROPVAL_CB_PRESERVE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossCommit( ) +{ + return getInt32Property("Prepare Commit Behavior") == DBPROPVAL_CB_PRESERVE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossCommit( ) +{ + return (getInt32Property("Isolation Retention") & DBPROPVAL_TR_COMMIT) == DBPROPVAL_TR_COMMIT; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossRollback( ) +{ + return (getInt32Property("Isolation Retention") & DBPROPVAL_TR_ABORT) == DBPROPVAL_TR_ABORT; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactionIsolationLevel( sal_Int32 level ) +{ + bool bValue(false); + + sal_Int32 nTxn = getInt32Property("Isolation Levels"); + if(level == TransactionIsolation::NONE) + bValue = true; + else if(level == TransactionIsolation::READ_UNCOMMITTED) + bValue = (nTxn & DBPROPVAL_TI_READUNCOMMITTED) == DBPROPVAL_TI_READUNCOMMITTED; + else if(level == TransactionIsolation::READ_COMMITTED) + bValue = (nTxn & DBPROPVAL_TI_READCOMMITTED) == DBPROPVAL_TI_READCOMMITTED; + else if(level == TransactionIsolation::REPEATABLE_READ) + bValue = (nTxn & DBPROPVAL_TI_REPEATABLEREAD) == DBPROPVAL_TI_REPEATABLEREAD; + else if(level == TransactionIsolation::SERIALIZABLE) + bValue = (nTxn & DBPROPVAL_TI_SERIALIZABLE) == DBPROPVAL_TI_SERIALIZABLE; + + return bValue; +} + +bool ODatabaseMetaData::impl_supportsSchemasInDataManipulation_throw( ) +{ + return (getInt32Property("Schema Usage") & DBPROPVAL_SU_DML_STATEMENTS) == DBPROPVAL_SU_DML_STATEMENTS; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92FullSQL( ) +{ + sal_Int32 nProp = getInt32Property("SQL Support"); + return (nProp == 512) || ((nProp & DBPROPVAL_SQL_ANSI92_FULL) == DBPROPVAL_SQL_ANSI92_FULL); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92EntryLevelSQL( ) +{ + sal_Int32 nProp = getInt32Property("SQL Support"); + return (nProp == 512) || ((nProp & DBPROPVAL_SQL_ANSI92_ENTRY) == DBPROPVAL_SQL_ANSI92_ENTRY); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsIntegrityEnhancementFacility( ) +{ + sal_Int32 nProp = getInt32Property("SQL Support"); + return (nProp == 512) || ((nProp & DBPROPVAL_SQL_ANSI89_IEF) == DBPROPVAL_SQL_ANSI89_IEF); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInIndexDefinitions( ) +{ + return (getInt32Property("Schema Usage") & DBPROPVAL_SU_INDEX_DEFINITION) == DBPROPVAL_SU_INDEX_DEFINITION; +} + +bool ODatabaseMetaData::impl_supportsSchemasInTableDefinitions_throw( ) +{ + return (getInt32Property("Schema Usage") & DBPROPVAL_SU_TABLE_DEFINITION) == DBPROPVAL_SU_TABLE_DEFINITION; +} + +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( ) +{ + if ( ADOS::isJetEngine(m_pConnection->getEngineType()) ) + return true; + return getBoolProperty("Outer Join Capabilities"); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTableTypes( ) +{ + return new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTableTypes); +} + +sal_Int32 ODatabaseMetaData::impl_getMaxStatements_throw( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxProcedureNameLength( ) +{ + return getMaxSize(DBLITERAL_PROCEDURE_NAME); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxSchemaNameLength( ) +{ + return getMaxSize(DBLITERAL_SCHEMA_NAME); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactions( ) +{ + return getInt32Property("Transaction DDL") == DBPROPVAL_TC_NONE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::allProceduresAreCallable( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsStoredProcedures( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSelectForUpdate( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::allTablesAreSelectable( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::isReadOnly( ) +{ + return getBoolProperty("Read-Only Data Source"); +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFiles( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFilePerTable( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTypeConversion( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullPlusNonNullIsNull( ) +{ + return getInt32Property("NULL Concatenation Behavior") == DBPROPVAL_CB_NULL; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsColumnAliasing( ) +{ + return isCapable(DBLITERAL_COLUMN_ALIAS); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTableCorrelationNames( ) +{ + return isCapable(DBLITERAL_CORRELATION_NAME); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsConvert( sal_Int32 /*fromType*/, sal_Int32 /*toType*/ ) +{ + return getBoolProperty("Rowset Conversions on Command"); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExpressionsInOrderBy( ) +{ + return getBoolProperty("ORDER BY Columns in Select List"); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupBy( ) +{ + return getInt32Property("GROUP BY Support") != DBPROPVAL_GB_NOT_SUPPORTED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByBeyondSelect( ) +{ + return getInt32Property("GROUP BY Support") != DBPROPVAL_GB_CONTAINS_SELECT; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByUnrelated( ) +{ + return getInt32Property("GROUP BY Support") == DBPROPVAL_GB_NO_RELATION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleTransactions( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleResultSets( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLikeEscapeClause( ) +{ + return isCapable(DBLITERAL_ESCAPE_PERCENT); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOrderByUnrelated( ) +{ + return getBoolProperty("ORDER BY Columns in Select List"); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnion( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnionAll( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseIdentifiers( ) +{ + return (getInt32Property("Identifier Case Sensitivity") & DBPROPVAL_IC_MIXED) == DBPROPVAL_IC_MIXED; +} + +bool ODatabaseMetaData::impl_supportsMixedCaseQuotedIdentifiers_throw( ) +{ + return (getInt32Property("Identifier Case Sensitivity") & DBPROPVAL_IC_MIXED) == DBPROPVAL_IC_MIXED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtEnd( ) +{ + return (getInt32Property("NULL Collation Order") & DBPROPVAL_NC_END) == DBPROPVAL_NC_END; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtStart( ) +{ + return (getInt32Property("NULL Collation Order") & DBPROPVAL_NC_START) == DBPROPVAL_NC_START; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedHigh( ) +{ + return (getInt32Property("NULL Collation Order") & DBPROPVAL_NC_HIGH) == DBPROPVAL_NC_HIGH; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedLow( ) +{ + return (getInt32Property("NULL Collation Order") & DBPROPVAL_NC_LOW) == DBPROPVAL_NC_LOW; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInProcedureCalls( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInPrivilegeDefinitions( ) +{ + return (getInt32Property("Schema Usage") & DBPROPVAL_SU_PRIVILEGE_DEFINITION) == DBPROPVAL_SU_PRIVILEGE_DEFINITION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInProcedureCalls( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInPrivilegeDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCorrelatedSubqueries( ) +{ + return (getInt32Property("Subquery Support") & DBPROPVAL_SQ_CORRELATEDSUBQUERIES) == DBPROPVAL_SQ_CORRELATEDSUBQUERIES; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInComparisons( ) +{ + return (getInt32Property("Subquery Support") & DBPROPVAL_SQ_COMPARISON) == DBPROPVAL_SQ_COMPARISON; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInExists( ) +{ + return (getInt32Property("Subquery Support") & DBPROPVAL_SQ_EXISTS) == DBPROPVAL_SQ_EXISTS; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInIns( ) +{ + return (getInt32Property("Subquery Support") & DBPROPVAL_SQ_IN) == DBPROPVAL_SQ_IN; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInQuantifieds( ) +{ + return (getInt32Property("Subquery Support") & DBPROPVAL_SQ_QUANTIFIED) == DBPROPVAL_SQ_QUANTIFIED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92IntermediateSQL( ) +{ + sal_Int32 nProp = getInt32Property("SQL Support"); + return (nProp == 512) || ((nProp & DBPROPVAL_SQL_ANSI92_INTERMEDIATE) == DBPROPVAL_SQL_ANSI92_INTERMEDIATE); +} + +OUString SAL_CALL ODatabaseMetaData::getURL( ) +{ + return "sdbc:ado:"+ m_pADOConnection->GetConnectionString(); +} + +OUString SAL_CALL ODatabaseMetaData::getUserName( ) +{ + return getStringProperty("User Name"); +} + +OUString SAL_CALL ODatabaseMetaData::getDriverName( ) +{ + return getStringProperty("Provider Friendly Name"); +} + +OUString SAL_CALL ODatabaseMetaData::getDriverVersion( ) +{ + return getStringProperty("Provider Version"); +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductVersion( ) +{ + return getStringProperty("DBMS Version"); +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductName( ) +{ + return getStringProperty("DBMS Name"); +} + +OUString SAL_CALL ODatabaseMetaData::getProcedureTerm( ) +{ + return getStringProperty("Procedure Term"); +} + +OUString SAL_CALL ODatabaseMetaData::getSchemaTerm( ) +{ + return getStringProperty("Schema Term"); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMajorVersion( ) +{ + return 1; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDefaultTransactionIsolation( ) +{ + sal_Int32 nRet = TransactionIsolation::NONE; + switch(m_pADOConnection->get_IsolationLevel()) + { + case adXactReadCommitted: + nRet = TransactionIsolation::READ_COMMITTED; + break; + case adXactRepeatableRead: + nRet = TransactionIsolation::REPEATABLE_READ; + break; + case adXactSerializable: + nRet = TransactionIsolation::SERIALIZABLE; + break; + case adXactReadUncommitted: + nRet = TransactionIsolation::READ_UNCOMMITTED; + break; + default: + ; + } + return nRet; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMinorVersion( ) +{ + return 0; +} + +OUString SAL_CALL ODatabaseMetaData::getSQLKeywords( ) +{ + ADORecordset *pRecordset = nullptr; + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + m_pADOConnection->OpenSchema(adSchemaDBInfoKeywords,vtEmpty,vtEmpty,&pRecordset); + OSL_ENSURE(pRecordset,"getSQLKeywords: no resultset!"); + ADOS::ThrowException(*m_pADOConnection,*this); + if ( pRecordset ) + { + WpADORecordset aRecordset(pRecordset); + + aRecordset.MoveFirst(); + OLEVariant aValue; + OUString aRet; + while(!aRecordset.IsAtEOF()) + { + WpOLEAppendCollection<ADOFields, ADOField, WpADOField> aFields(aRecordset.GetFields()); + WpADOField aField(aFields.GetItem(0)); + aField.get_Value(aValue); + aRet += aValue.getString() + ","; + aRecordset.MoveNext(); + } + aRecordset.Close(); + if ( !aRet.isEmpty() ) + return aRet.copy(0,aRet.lastIndexOf(',')); + } + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getSearchStringEscape( ) +{ + return getLiteral(DBLITERAL_ESCAPE_PERCENT); +} + +OUString SAL_CALL ODatabaseMetaData::getStringFunctions( ) +{ + OUString aValue; + return aValue.copy(0,aValue.lastIndexOf(',')); +} + +OUString SAL_CALL ODatabaseMetaData::getTimeDateFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getSystemFunctions( ) +{ + OUString aValue; + return aValue.copy(0,aValue.lastIndexOf(',')); +} + +OUString SAL_CALL ODatabaseMetaData::getNumericFunctions( ) +{ + return OUString(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExtendedSQLGrammar( ) +{ + sal_Int32 nProp = getInt32Property("SQL Support"); + return (nProp == 512) || ((nProp & DBPROPVAL_SQL_ODBC_EXTENDED) == DBPROPVAL_SQL_ODBC_EXTENDED); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCoreSQLGrammar( ) +{ + sal_Int32 nProp = getInt32Property("SQL Support"); + return (nProp == 512) || ((nProp & DBPROPVAL_SQL_ODBC_CORE) == DBPROPVAL_SQL_ODBC_CORE); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMinimumSQLGrammar( ) +{ + sal_Int32 nProp = getInt32Property("SQL Support"); + return (nProp == 512) || ((nProp & DBPROPVAL_SQL_ODBC_MINIMUM) == DBPROPVAL_SQL_ODBC_MINIMUM); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsFullOuterJoins( ) +{ + if ( ADOS::isJetEngine(m_pConnection->getEngineType()) ) + return true; + return (getInt32Property("Outer Join Capabilities") & 0x00000004L) == 0x00000004L; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLimitedOuterJoins( ) +{ + return supportsFullOuterJoins( ); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInGroupBy( ) +{ + return getInt32Property("Max Columns in GROUP BY"); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + return getInt32Property("Max Columns in ORDER BY"); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInSelect( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxUserNameLength( ) +{ + return getMaxSize(DBLITERAL_USER_NAME); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetType( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetConcurrency( sal_Int32 /*setType*/, sal_Int32 /*concurrency*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownUpdatesAreVisible( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownDeletesAreVisible( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownInsertsAreVisible( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersUpdatesAreVisible( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersDeletesAreVisible( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersInsertsAreVisible( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::updatesAreDetected( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::deletesAreDetected( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::insertsAreDetected( sal_Int32 setType ) +{ + return ResultSetType::FORWARD_ONLY != setType; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsBatchUpdates( ) +{ + return true; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getUDTs( const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& /*typeNamePattern*/, const Sequence< sal_Int32 >& /*types*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XDatabaseMetaData::getUDTs", *this ); + return Reference< XResultSet >(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ADatabaseMetaDataImpl.cxx b/connectivity/source/drivers/ado/ADatabaseMetaDataImpl.cxx new file mode 100644 index 000000000..34eb7501a --- /dev/null +++ b/connectivity/source/drivers/ado/ADatabaseMetaDataImpl.cxx @@ -0,0 +1,585 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/ADatabaseMetaData.hxx> +#include <ado/ADatabaseMetaDataResultSetMetaData.hxx> +#include <ado/Awrapado.hxx> +#include <ado/AGroup.hxx> +#include <ado/adoimp.hxx> +#include <ado/AIndex.hxx> +#include <ado/AKey.hxx> +#include <ado/ATable.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ProcedureResult.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#ifdef DELETE +#undef DELETE +#endif +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/sdbcx/PrivilegeObject.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> + +using namespace connectivity::ado; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::uno; + + +void ODatabaseMetaData::fillLiterals() +{ + ADORecordset *pRecordset = nullptr; + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + m_pADOConnection->OpenSchema(adSchemaDBInfoLiterals,vtEmpty,vtEmpty,&pRecordset); + + ADOS::ThrowException(*m_pADOConnection,*this); + + OSL_ENSURE(pRecordset,"fillLiterals: no resultset!"); + if ( pRecordset ) + { + WpADORecordset aRecordset(pRecordset); + + aRecordset.MoveFirst(); + OLEVariant aValue; + LiteralInfo aInfo; + while(!aRecordset.IsAtEOF()) + { + WpOLEAppendCollection<ADOFields, ADOField, WpADOField> aFields(aRecordset.GetFields()); + WpADOField aField(aFields.GetItem(1)); + aInfo.pwszLiteralValue = aField.get_Value().getString(); + aField = aFields.GetItem(5); + aInfo.fSupported = aField.get_Value().getBool(); + aField = aFields.GetItem(6); + aInfo.cchMaxLen = aField.get_Value().getUInt32(); + + aField = aFields.GetItem(4); + sal_uInt32 nId = aField.get_Value().getUInt32(); + m_aLiteralInfo[nId] = aInfo; + + aRecordset.MoveNext(); + } + aRecordset.Close(); + } +} + +sal_Int32 ODatabaseMetaData::getMaxSize(sal_uInt32 _nId) +{ + if(m_aLiteralInfo.empty()) + fillLiterals(); + + sal_Int32 nSize = 0; + std::map<sal_uInt32,LiteralInfo>::const_iterator aIter = m_aLiteralInfo.find(_nId); + if(aIter != m_aLiteralInfo.end() && (*aIter).second.fSupported) + nSize = (static_cast<sal_Int32>((*aIter).second.cchMaxLen) == -1) ? 0 : (*aIter).second.cchMaxLen; + return nSize; +} + +bool ODatabaseMetaData::isCapable(sal_uInt32 _nId) +{ + if(m_aLiteralInfo.empty()) + fillLiterals(); + bool bSupported = false; + std::map<sal_uInt32,LiteralInfo>::const_iterator aIter = m_aLiteralInfo.find(_nId); + if(aIter != m_aLiteralInfo.end()) + bSupported = (*aIter).second.fSupported; + return bSupported; +} + + +OUString ODatabaseMetaData::getLiteral(sal_uInt32 _nId) +{ + if(m_aLiteralInfo.empty()) + fillLiterals(); + OUString sStr; + std::map<sal_uInt32,LiteralInfo>::const_iterator aIter = m_aLiteralInfo.find(_nId); + if(aIter != m_aLiteralInfo.end() && (*aIter).second.fSupported) + sStr = (*aIter).second.pwszLiteralValue; + return sStr; +} + + +void ODatabaseMetaDataResultSetMetaData::setColumnPrivilegesMap() +{ + m_mColumns[8] = OColumn(OUString(),"IS_GRANTABLE", + ColumnValue::NULLABLE, + 3,3,0, + DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setColumnsMap() +{ + m_mColumns[6] = OColumn(OUString(),"TYPE_NAME", + ColumnValue::NO_NULLS, + 0,0,0, + DataType::VARCHAR); + m_mColumns[11] = OColumn(OUString(),"NULLABLE", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); + m_mColumns[12] = OColumn(OUString(),"REMARKS", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); + m_mColumns[13] = OColumn(OUString(),"COLUMN_DEF", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); + m_mColumns[14] = OColumn(OUString(),"SQL_DATA_TYPE", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); + m_mColumns[15] = OColumn(OUString(),"SQL_DATETIME_SUB", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); + m_mColumns[16] = OColumn(OUString(),"CHAR_OCTET_LENGTH", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); +} + +void ODatabaseMetaDataResultSetMetaData::setTablesMap() +{ + m_mColumns[5] = OColumn(OUString(),"REMARKS", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setProcedureColumnsMap() +{ + m_mColumns[12] = OColumn(OUString(),"NULLABLE", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); +} + +void ODatabaseMetaDataResultSetMetaData::setPrimaryKeysMap() +{ + m_mColumns[5] = OColumn(OUString(),"KEY_SEQ", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); + m_mColumns[6] = OColumn(OUString(),"PK_NAME", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setIndexInfoMap() +{ + m_mColumns[4] = OColumn(OUString(),"NON_UNIQUE", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::BIT); + m_mColumns[5] = OColumn(OUString(),"INDEX_QUALIFIER", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); + m_mColumns[10] = OColumn(OUString(),"ASC_OR_DESC", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setTablePrivilegesMap() +{ + m_mColumns[6] = OColumn(OUString(),"PRIVILEGE", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); + m_mColumns[7] = OColumn(OUString(),"IS_GRANTABLE", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); +} + +void ODatabaseMetaDataResultSetMetaData::setCrossReferenceMap() +{ + m_mColumns[9] = OColumn(OUString(),"KEY_SEQ", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); +} + +void ODatabaseMetaDataResultSetMetaData::setTypeInfoMap() +{ + m_mColumns[3] = OColumn(OUString(),"PRECISION", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); + m_mColumns[7] = OColumn(OUString(),"NULLABLE", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); + m_mColumns[12] = OColumn(OUString(),"AUTO_INCREMENT", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::BIT); + m_mColumns[16] = OColumn(OUString(),"SQL_DATA_TYPE", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); + m_mColumns[17] = OColumn(OUString(),"SQL_DATETIME_SUB", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); + m_mColumns[18] = OColumn(OUString(),"NUM_PREC_RADIX", + ColumnValue::NO_NULLS, + 1,1,0, + DataType::INTEGER); +} + +void ODatabaseMetaDataResultSetMetaData::setProceduresMap() +{ + m_mColumns[7] = OColumn(OUString(),"REMARKS", + ColumnValue::NULLABLE, + 0,0,0, + DataType::VARCHAR); +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isSearchable( sal_Int32 column ) +{ + if(!m_mColumns.empty() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isSearchable(); + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isAutoIncrement( sal_Int32 column ) +{ + if(!m_mColumns.empty() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isAutoIncrement(); + return false; +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnServiceName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getTableName( sal_Int32 column ) +{ + if(!m_mColumns.empty() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getTableName(); + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getCatalogName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnTypeName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isCaseSensitive( sal_Int32 column ) +{ + if(!m_mColumns.empty() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isCaseSensitive(); + return true; +} + + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getSchemaName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + + +ObjectTypeEnum OAdoGroup::MapObjectType(sal_Int32 ObjType) +{ + ObjectTypeEnum eNumType= adPermObjTable; + switch(ObjType) + { + case PrivilegeObject::TABLE: + break; + case PrivilegeObject::VIEW: + eNumType = adPermObjView; + break; + case PrivilegeObject::COLUMN: + eNumType = adPermObjColumn; + break; + } + return eNumType; +} + +sal_Int32 OAdoGroup::MapRight(RightsEnum _eNum) +{ + sal_Int32 nRight = 0; + if(_eNum & adRightRead) + nRight |= Privilege::SELECT; + if(_eNum & adRightInsert) + nRight |= Privilege::INSERT; + if(_eNum & adRightUpdate) + nRight |= Privilege::UPDATE; + if(_eNum & adRightDelete) + nRight |= Privilege::DELETE; + if(_eNum & adRightReadDesign) + nRight |= Privilege::READ; + if(_eNum & adRightCreate) + nRight |= Privilege::CREATE; + if(_eNum & adRightWriteDesign) + nRight |= Privilege::ALTER; + if(_eNum & adRightReference) + nRight |= Privilege::REFERENCE; + if(_eNum & adRightDrop) + nRight |= Privilege::DROP; + + return nRight; +} + +RightsEnum OAdoGroup::Map2Right(sal_Int32 _eNum) +{ + sal_Int32 nRight = adRightNone; + if(_eNum & Privilege::SELECT) + nRight |= adRightRead; + + if(_eNum & Privilege::INSERT) + nRight |= adRightInsert; + + if(_eNum & Privilege::UPDATE) + nRight |= adRightUpdate; + + if(_eNum & Privilege::DELETE) + nRight |= adRightDelete; + + if(_eNum & Privilege::READ) + nRight |= adRightReadDesign; + + if(_eNum & Privilege::CREATE) + nRight |= adRightCreate; + + if(_eNum & Privilege::ALTER) + nRight |= adRightWriteDesign; + + if(_eNum & Privilege::REFERENCE) + nRight |= adRightReference; + + if(_eNum & Privilege::DROP) + nRight |= adRightDrop; + + return static_cast<RightsEnum>(nRight); +} + +void WpADOIndex::Create() +{ + _ADOIndex* pIndex = nullptr; + HRESULT hr = CoCreateInstance(ADOS::CLSID_ADOINDEX_25, + nullptr, + CLSCTX_INPROC_SERVER, + ADOS::IID_ADOINDEX_25, + reinterpret_cast<void**>(&pIndex) ); + + + if( !FAILED( hr ) ) + { + operator=( pIndex ); + pIndex->Release(); + } +} + +void OAdoIndex::fillPropertyValues() +{ + if(m_aIndex.IsValid()) + { + m_Name = m_aIndex.get_Name(); + m_IsUnique = m_aIndex.get_Unique(); + m_IsPrimaryKeyIndex = m_aIndex.get_PrimaryKey(); + m_IsClustered = m_aIndex.get_Clustered(); + } +} + +void WpADOKey::Create() +{ + _ADOKey* pKey = nullptr; + HRESULT hr = CoCreateInstance(ADOS::CLSID_ADOKEY_25, + nullptr, + CLSCTX_INPROC_SERVER, + ADOS::IID_ADOKEY_25, + reinterpret_cast<void**>(&pKey) ); + + + if( !FAILED( hr ) ) + { + operator=( pKey ); + pKey->Release(); + } +} + +void OAdoKey::fillPropertyValues() +{ + if(m_aKey.IsValid()) + { + m_aProps->m_Type = MapKeyRule(m_aKey.get_Type()); + m_Name = m_aKey.get_Name(); + m_aProps->m_ReferencedTable = m_aKey.get_RelatedTable(); + m_aProps->m_UpdateRule = MapRule(m_aKey.get_UpdateRule()); + m_aProps->m_DeleteRule = MapRule(m_aKey.get_DeleteRule()); + } +} + +sal_Int32 OAdoKey::MapRule(const RuleEnum& _eNum) +{ + sal_Int32 eNum = KeyRule::NO_ACTION; + switch(_eNum) + { + case adRICascade: + eNum = KeyRule::CASCADE; + break; + case adRISetNull: + eNum = KeyRule::SET_NULL; + break; + case adRINone: + eNum = KeyRule::NO_ACTION; + break; + case adRISetDefault: + eNum = KeyRule::SET_DEFAULT; + break; + } + return eNum; +} + +RuleEnum OAdoKey::Map2Rule(sal_Int32 _eNum) +{ + RuleEnum eNum = adRINone; + switch(_eNum) + { + case KeyRule::CASCADE: + eNum = adRICascade; + break; + case KeyRule::SET_NULL: + eNum = adRISetNull; + break; + case KeyRule::NO_ACTION: + eNum = adRINone; + break; + case KeyRule::SET_DEFAULT: + eNum = adRISetDefault; + break; + } + return eNum; +} + +sal_Int32 OAdoKey::MapKeyRule(const KeyTypeEnum& _eNum) +{ + sal_Int32 nKeyType = KeyType::PRIMARY; + switch(_eNum) + { + case adKeyPrimary: + nKeyType = KeyType::PRIMARY; + break; + case adKeyForeign: + nKeyType = KeyType::FOREIGN; + break; + case adKeyUnique: + nKeyType = KeyType::UNIQUE; + break; + } + return nKeyType; +} + +KeyTypeEnum OAdoKey::Map2KeyRule(sal_Int32 _eNum) +{ + KeyTypeEnum eNum( adKeyPrimary ); + switch(_eNum) + { + case KeyType::PRIMARY: + eNum = adKeyPrimary; + break; + case KeyType::FOREIGN: + eNum = adKeyForeign; + break; + case KeyType::UNIQUE: + eNum = adKeyUnique; + break; + default: + OSL_FAIL( "OAdoKey::Map2KeyRule: invalid key type!" ); + } + return eNum; +} + +void WpADOTable::Create() +{ + _ADOTable* pTable = nullptr; + HRESULT hr = CoCreateInstance(ADOS::CLSID_ADOTABLE_25, + nullptr, + CLSCTX_INPROC_SERVER, + ADOS::IID_ADOTABLE_25, + reinterpret_cast<void**>(&pTable) ); + + + if( !FAILED( hr ) ) + { + operator=( pTable ); + pTable->Release(); + } +} + +OUString WpADOCatalog::GetObjectOwner(const OUString& _rName, ObjectTypeEnum _eNum) +{ + OLEVariant _rVar; + _rVar.setNoArg(); + OLEString aBSTR; + OLEString sStr1(_rName); + pInterface->GetObjectOwner(sStr1.asBSTR(),_eNum,_rVar,aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void OAdoTable::fillPropertyValues() +{ + if(m_aTable.IsValid()) + { + m_Name = m_aTable.get_Name(); + m_Type = m_aTable.get_Type(); + { + WpADOCatalog aCat(m_aTable.get_ParentCatalog()); + if(aCat.IsValid()) + m_CatalogName = aCat.GetObjectOwner(m_aTable.get_Name(),adPermObjTable); + } + { + WpADOProperties aProps = m_aTable.get_Properties(); + if(aProps.IsValid()) + m_Description = OTools::getValue(aProps,OUString("Description")).getString(); + } + } +} + +void WpADOUser::Create() +{ + _ADOUser* pUser = nullptr; + HRESULT hr = CoCreateInstance(ADOS::CLSID_ADOUSER_25, + nullptr, + CLSCTX_INPROC_SERVER, + ADOS::IID_ADOUSER_25, + reinterpret_cast<void**>(&pUser) ); + + + if( !FAILED( hr ) ) + { + operator=( pUser ); + pUser->Release(); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ADatabaseMetaDataResultSet.cxx b/connectivity/source/drivers/ado/ADatabaseMetaDataResultSet.cxx new file mode 100644 index 000000000..16acbc133 --- /dev/null +++ b/connectivity/source/drivers/ado/ADatabaseMetaDataResultSet.cxx @@ -0,0 +1,1204 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <comphelper/sequence.hxx> +#include <ado/ADatabaseMetaDataResultSet.hxx> +#include <ado/ADatabaseMetaDataResultSetMetaData.hxx> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbc/ProcedureResult.hpp> +#include <com/sun/star/sdbc/IndexType.hpp> +#include <comphelper/property.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/seqstream.hxx> +#include <connectivity/dbexception.hxx> + + +#include <oledb.h> + +using namespace dbtools; +using namespace connectivity::ado; +using namespace cppu; +using namespace ::comphelper; + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + + +ODatabaseMetaDataResultSet::ODatabaseMetaDataResultSet(ADORecordset* _pRecordSet) + :ODatabaseMetaDataResultSet_BASE(m_aMutex) + ,OPropertySetHelper(ODatabaseMetaDataResultSet_BASE::rBHelper) + ,m_pRecordSet(_pRecordSet) + ,m_aStatement(nullptr) + ,m_nRowPos(0) + ,m_bWasNull(false) + ,m_bEOF(false) + ,m_bOnFirstAfterOpen(false) +{ + osl_atomic_increment( &m_refCount ); + m_aColMapping.push_back(-1); + if(_pRecordSet) + { + m_pRecordSet->AddRef(); + VARIANT_BOOL bIsAtBOF; + m_pRecordSet->get_BOF(&bIsAtBOF); + m_bOnFirstAfterOpen = bIsAtBOF != VARIANT_TRUE; + } + else + m_bOnFirstAfterOpen = false; + osl_atomic_decrement( &m_refCount ); + // allocBuffer(); +} + + +ODatabaseMetaDataResultSet::~ODatabaseMetaDataResultSet() +{ + if(m_pRecordSet) + m_pRecordSet->Release(); +} + +void ODatabaseMetaDataResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + if(m_pRecordSet) + m_pRecordSet->Close(); + m_aStatement = nullptr; + m_xMetaData.clear(); +} + +Any SAL_CALL ODatabaseMetaDataResultSet::queryInterface( const Type & rType ) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + return aRet.hasValue() ? aRet : ODatabaseMetaDataResultSet_BASE::queryInterface(rType); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL ODatabaseMetaDataResultSet::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<css::beans::XMultiPropertySet>::get(), + cppu::UnoType<css::beans::XFastPropertySet>::get(), + cppu::UnoType<css::beans::XPropertySet>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),ODatabaseMetaDataResultSet_BASE::getTypes()); +} + +void ODatabaseMetaDataResultSet::checkRecordSet() +{ + if(!m_pRecordSet) + throwFunctionSequenceException(*this); +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::findColumn( const OUString& columnName ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + + Reference< XResultSetMetaData > xMeta = getMetaData(); + sal_Int32 nLen = xMeta->getColumnCount(); + sal_Int32 i = 1; + for(;i<=nLen;++i) + { + if(xMeta->isCaseSensitive(i) ? columnName == xMeta->getColumnName(i) : + columnName.equalsIgnoreAsciiCase(xMeta->getColumnName(i))) + return i; + } + + ::dbtools::throwInvalidColumnException( columnName, *this ); + assert(false); + return 0; // Never reached +} +#define BLOCK_SIZE 256 + +Reference< css::io::XInputStream > SAL_CALL ODatabaseMetaDataResultSet::getBinaryStream( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + columnIndex = mapColumn(columnIndex); + WpADOField aField = ADOS::getField(m_pRecordSet,columnIndex); + if((aField.GetAttributes() & adFldLong) == adFldLong) + { + //Copy the data only up to the Actual Size of Field. + sal_Int32 nSize = aField.GetActualSize(); + Sequence<sal_Int8> aData(nSize); + long index = 0; + while(index < nSize) + { + m_aValue = aField.GetChunk(BLOCK_SIZE); + if(m_aValue.isNull()) + break; + UCHAR chData; + for(long index2 = 0;index2 < BLOCK_SIZE;++index2) + { + HRESULT hr = ::SafeArrayGetElement(m_aValue.parray,&index2,&chData); + if(SUCCEEDED(hr)) + { + //Take BYTE by BYTE and advance Memory Location + aData.getArray()[index++] = chData; + } + else + break; + } + } + return index ? Reference< css::io::XInputStream >(new SequenceInputStream(aData)) : Reference< css::io::XInputStream >(); + } + // else we ask for a bytesequence + aField.get_Value(m_aValue); + if(m_aValue.isNull()) + return nullptr; + return new SequenceInputStream(m_aValue.getByteSequence()); +} + +Reference< css::io::XInputStream > SAL_CALL ODatabaseMetaDataResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getCharacterStream", *this ); + return nullptr; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::getBoolean( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( !m_aValueRange.empty() && columnIndex == 11 && (m_aValueRangeIter = m_aValueRange.find(columnIndex)) != m_aValueRange.end() ) + { + getValue(2); + if ( m_aValue.getInt16() != adCurrency ) + return false; + } + return getValue(columnIndex).getBool(); +} + + +sal_Int8 SAL_CALL ODatabaseMetaDataResultSet::getByte( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + getValue(columnIndex); + + columnIndex = mapColumn(columnIndex); + + if(m_aValue.isNull()) + return 0; + if ( !m_aValueRange.empty() && (m_aValueRangeIter = m_aValueRange.find(columnIndex)) != m_aValueRange.end()) + return static_cast<sal_Int8>((*m_aValueRangeIter).second[m_aValue.getInt32()]); + else if(m_aStrValueRange.size() && (m_aStrValueRangeIter = m_aStrValueRange.find(columnIndex)) != m_aStrValueRange.end()) + return static_cast<sal_Int8>((*m_aStrValueRangeIter).second[m_aValue.getString()]); + + return m_aValue.getInt8(); +} + + +Sequence< sal_Int8 > SAL_CALL ODatabaseMetaDataResultSet::getBytes( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getByteSequence(); +} + + +css::util::Date SAL_CALL ODatabaseMetaDataResultSet::getDate( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDate(); +} + + +double SAL_CALL ODatabaseMetaDataResultSet::getDouble( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDouble(); +} + + +float SAL_CALL ODatabaseMetaDataResultSet::getFloat( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getFloat(); +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::getInt( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + + getValue(columnIndex); + + columnIndex = mapColumn(columnIndex); + if(m_aValue.isNull()) + return 0; + + if(m_aValueRange.size() && (m_aValueRangeIter = m_aValueRange.find(columnIndex)) != m_aValueRange.end()) + return (*m_aValueRangeIter).second[m_aValue.getInt32()]; + else if(m_aStrValueRange.size() && (m_aStrValueRangeIter = m_aStrValueRange.find(columnIndex)) != m_aStrValueRange.end()) + return (*m_aStrValueRangeIter).second[m_aValue.getString()]; + + return m_aValue.getInt32(); +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::getRow( ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XResultSet::getRow", *this ); + return 0; +} + + +sal_Int64 SAL_CALL ODatabaseMetaDataResultSet::getLong( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getLong", *this ); + return sal_Int64(0); +} + + +Reference< XResultSetMetaData > SAL_CALL ODatabaseMetaDataResultSet::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + if(!m_xMetaData.is()) + m_xMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + + return m_xMetaData; +} + +Reference< XArray > SAL_CALL ODatabaseMetaDataResultSet::getArray( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getRow", *this ); + return nullptr; +} + + +Reference< XClob > SAL_CALL ODatabaseMetaDataResultSet::getClob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getRow", *this ); + return nullptr; +} + +Reference< XBlob > SAL_CALL ODatabaseMetaDataResultSet::getBlob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getRow", *this ); + return nullptr; +} + + +Reference< XRef > SAL_CALL ODatabaseMetaDataResultSet::getRef( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getRow", *this ); + return nullptr; +} + + +Any SAL_CALL ODatabaseMetaDataResultSet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + columnIndex = mapColumn(columnIndex); + return Any(); +} + + +sal_Int16 SAL_CALL ODatabaseMetaDataResultSet::getShort( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + getValue(columnIndex); + + columnIndex = mapColumn(columnIndex); + if(m_aValue.isNull()) + return 0; + + if(m_aValueRange.size() && (m_aValueRangeIter = m_aValueRange.find(columnIndex)) != m_aValueRange.end()) + return static_cast<sal_Int16>((*m_aValueRangeIter).second[m_aValue.getInt32()]); + else if(m_aStrValueRange.size() && (m_aStrValueRangeIter = m_aStrValueRange.find(columnIndex)) != m_aStrValueRange.end()) + return static_cast<sal_Int16>((*m_aStrValueRangeIter).second[m_aValue.getString()]); + + return m_aValue.getInt16(); +} + + +OUString SAL_CALL ODatabaseMetaDataResultSet::getString( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + getValue(columnIndex); + + + columnIndex = mapColumn(columnIndex); + if(m_aValue.isNull()) + return OUString(); + if(m_aIntValueRange.size() && (m_aIntValueRangeIter = m_aIntValueRange.find(columnIndex)) != m_aIntValueRange.end()) + return (*m_aIntValueRangeIter).second[m_aValue.getInt32()]; + + return m_aValue.getString(); +} + + +css::util::Time SAL_CALL ODatabaseMetaDataResultSet::getTime( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getTime(); +} + + +css::util::DateTime SAL_CALL ODatabaseMetaDataResultSet::getTimestamp( sal_Int32 columnIndex ) +{ + return getValue(columnIndex).getDateTime(); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isAfterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + VARIANT_BOOL bIsAtEOF; + m_pRecordSet->get_EOF(&bIsAtEOF); + return bIsAtEOF == VARIANT_TRUE; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + return m_nRowPos == 1; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + return true; +} + +void SAL_CALL ODatabaseMetaDataResultSet::beforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + if(first()) + previous(); +} + +void SAL_CALL ODatabaseMetaDataResultSet::afterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + if(last()) + next(); + m_bEOF = true; +} + + +void SAL_CALL ODatabaseMetaDataResultSet::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + } + dispose(); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::first( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + + if(!m_pRecordSet) + return false; + + bool bRet = SUCCEEDED(m_pRecordSet->MoveFirst()); + if ( bRet ) + m_nRowPos = 1; + return bRet; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::last( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + + + return m_pRecordSet && SUCCEEDED(m_pRecordSet->MoveLast()); +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::absolute( sal_Int32 row ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + + if(first()) + { + OLEVariant aEmpty; + aEmpty.setNoArg(); + bool bRet = SUCCEEDED(m_pRecordSet->Move(row,aEmpty)); + if(bRet) + m_nRowPos = row; + return bRet; + } + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::relative( sal_Int32 row ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + + if(!m_pRecordSet) + return false; + + OLEVariant aEmpty; + aEmpty.setNoArg(); + bool bRet = SUCCEEDED(m_pRecordSet->Move(row,aEmpty)); + if(bRet) + m_nRowPos += row; + return bRet; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::previous( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + + if(!m_pRecordSet) + return false; + + bool bRet = SUCCEEDED(m_pRecordSet->MovePrevious()); + if(bRet) + --m_nRowPos; + return bRet; +} + +Reference< XInterface > SAL_CALL ODatabaseMetaDataResultSet::getStatement( ) +{ + return m_aStatement.get(); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowDeleted( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + sal_Int32 eRec; + m_pRecordSet->get_Status(&eRec); + return (RecordStatusEnum(eRec) & adRecDeleted) == adRecDeleted; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowInserted( ) +{ ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + sal_Int32 eRec; + m_pRecordSet->get_Status(&eRec); + return (RecordStatusEnum(eRec) & adRecNew) == adRecNew; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowUpdated( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + sal_Int32 eRec; + m_pRecordSet->get_Status(&eRec); + return (RecordStatusEnum(eRec) & adRecModified) == adRecModified; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isBeforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + + if(!m_pRecordSet) + return true; + + VARIANT_BOOL bIsAtBOF; + m_pRecordSet->get_BOF(&bIsAtBOF); + return bIsAtBOF == VARIANT_TRUE; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::next( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + + if(!m_pRecordSet) + return false; + + if(m_bOnFirstAfterOpen) + { + m_bOnFirstAfterOpen = false; + return true; + } + else + return SUCCEEDED(m_pRecordSet->MoveNext()); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::wasNull( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + return m_aValue.isNull(); +} + +void SAL_CALL ODatabaseMetaDataResultSet::refreshRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + m_pRecordSet->Resync(adAffectCurrent); +} + + +void SAL_CALL ODatabaseMetaDataResultSet::cancel( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + + m_pRecordSet->Cancel(); +} + +void SAL_CALL ODatabaseMetaDataResultSet::clearWarnings( ) +{ +} + +Any SAL_CALL ODatabaseMetaDataResultSet::getWarnings( ) +{ + return Any(); +} + +sal_Int32 ODatabaseMetaDataResultSet::getResultSetConcurrency() +{ + return ResultSetConcurrency::READ_ONLY; +} + +sal_Int32 ODatabaseMetaDataResultSet::getResultSetType() +{ + return ResultSetType::FORWARD_ONLY; +} + +sal_Int32 ODatabaseMetaDataResultSet::getFetchDirection() +{ + return FetchDirection::FORWARD; +} + +sal_Int32 ODatabaseMetaDataResultSet::getFetchSize() const +{ + sal_Int32 nValue=-1; + if(m_pRecordSet) + m_pRecordSet->get_CacheSize(&nValue); + return nValue; +} + +OUString ODatabaseMetaDataResultSet::getCursorName() +{ + return OUString(); +} + + +void ODatabaseMetaDataResultSet::setFetchDirection(sal_Int32 /*_par0*/) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "ResultSet::FetchDirection", *this ); +} + +void ODatabaseMetaDataResultSet::setFetchSize(sal_Int32 _par0) +{ + if(m_pRecordSet) + m_pRecordSet->put_CacheSize(_par0); +} + +::cppu::IPropertyArrayHelper* ODatabaseMetaDataResultSet::createArrayHelper( ) const +{ + + Sequence< css::beans::Property > aProps(5); + css::beans::Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + +::cppu::IPropertyArrayHelper & ODatabaseMetaDataResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool ODatabaseMetaDataResultSet::convertFastPropertyValue( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + case PROPERTY_ID_FETCHDIRECTION: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection()); + case PROPERTY_ID_FETCHSIZE: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize()); + default: + ; + } + return false; +} + +void ODatabaseMetaDataResultSet::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const Any& /*rValue*/ + ) +{ + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + throw Exception("cannot set prop " + OUString::number(nHandle), nullptr); + default: + OSL_FAIL("setFastPropertyValue_NoBroadcast: Illegal handle value!"); + } +} + +void ODatabaseMetaDataResultSet::getFastPropertyValue( + Any& rValue, + sal_Int32 nHandle + ) const +{ + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + rValue <<= getCursorName(); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= getResultSetConcurrency(); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= getResultSetType(); + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= getFetchDirection(); + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= getFetchSize(); + break; + } +} + +void ODatabaseMetaDataResultSet::setProceduresMap() +{ + + for(sal_Int32 i=1;i<4;i++) + m_aColMapping.push_back(i); + m_aColMapping.push_back(5); + m_aColMapping.push_back(7); + m_aColMapping.push_back(8); + m_aColMapping.push_back(6); + m_aColMapping.push_back(4); + + ::std::map<sal_Int32,sal_Int32> aMap; + aMap[DB_PT_UNKNOWN] = ProcedureResult::UNKNOWN; + aMap[DB_PT_PROCEDURE] = ProcedureResult::NONE; + aMap[DB_PT_FUNCTION] = ProcedureResult::RETURN; + m_aValueRange[4] = aMap; + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setProceduresMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setCatalogsMap() +{ + m_aColMapping.push_back(1); + + m_xMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); +} + +void ODatabaseMetaDataResultSet::setSchemasMap() +{ + m_aColMapping.push_back(2); + + m_xMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); +} + +void ODatabaseMetaDataResultSet::setColumnPrivilegesMap() +{ + + m_aColMapping.push_back(3); + m_aColMapping.push_back(4); + m_aColMapping.push_back(5); + m_aColMapping.push_back(6); + m_aColMapping.push_back(2); + m_aColMapping.push_back(9); + m_aColMapping.push_back(10); + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setColumnPrivilegesMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setColumnsMap() +{ + + for(sal_Int32 i=1;i<5;++i) + m_aColMapping.push_back(i); + + m_aColMapping.push_back(12); + m_aColMapping.push_back(12); // is used as TYPE_NAME + + m_aColMapping.push_back(14); + m_aColMapping.push_back(6); + m_aColMapping.push_back(17); + m_aColMapping.push_back(18); + + m_aColMapping.push_back(11); + m_aColMapping.push_back(29); + m_aColMapping.push_back(9); + m_aColMapping.push_back(18); + m_aColMapping.push_back(18); + + m_aColMapping.push_back(15); + m_aColMapping.push_back(7); + m_aColMapping.push_back(11); + + ::std::map<sal_Int32,sal_Int32> aMap; + aMap[adEmpty] = ADOS::MapADOType2Jdbc(adEmpty); + aMap[adTinyInt] = ADOS::MapADOType2Jdbc(adTinyInt); + aMap[adSmallInt] = ADOS::MapADOType2Jdbc(adSmallInt); + aMap[adInteger] = ADOS::MapADOType2Jdbc(adInteger); + aMap[adBigInt] = ADOS::MapADOType2Jdbc(adBigInt); + aMap[adUnsignedTinyInt] = ADOS::MapADOType2Jdbc(adUnsignedTinyInt); + aMap[adUnsignedSmallInt]= ADOS::MapADOType2Jdbc(adUnsignedSmallInt); + aMap[adUnsignedInt] = ADOS::MapADOType2Jdbc(adUnsignedInt); + aMap[adUnsignedBigInt] = ADOS::MapADOType2Jdbc(adUnsignedBigInt); + aMap[adSingle] = ADOS::MapADOType2Jdbc(adSingle); + aMap[adDouble] = ADOS::MapADOType2Jdbc(adDouble); + aMap[adCurrency] = ADOS::MapADOType2Jdbc(adCurrency); + aMap[adDecimal] = ADOS::MapADOType2Jdbc(adDecimal); + aMap[adNumeric] = ADOS::MapADOType2Jdbc(adNumeric); + aMap[adBoolean] = ADOS::MapADOType2Jdbc(adBoolean); + aMap[adError] = ADOS::MapADOType2Jdbc(adError); + aMap[adUserDefined] = ADOS::MapADOType2Jdbc(adUserDefined); + aMap[adVariant] = ADOS::MapADOType2Jdbc(adVariant); + aMap[adIDispatch] = ADOS::MapADOType2Jdbc(adIDispatch); + aMap[adIUnknown] = ADOS::MapADOType2Jdbc(adIUnknown); + aMap[adGUID] = ADOS::MapADOType2Jdbc(adGUID); + aMap[adDate] = ADOS::MapADOType2Jdbc(adDate); + aMap[adDBDate] = ADOS::MapADOType2Jdbc(adDBDate); + aMap[adDBTime] = ADOS::MapADOType2Jdbc(adDBTime); + aMap[adDBTimeStamp] = ADOS::MapADOType2Jdbc(adDBTimeStamp); + aMap[adBSTR] = ADOS::MapADOType2Jdbc(adBSTR); + aMap[adChar] = ADOS::MapADOType2Jdbc(adChar); + aMap[adVarChar] = ADOS::MapADOType2Jdbc(adVarChar); + aMap[adLongVarChar] = ADOS::MapADOType2Jdbc(adLongVarChar); + aMap[adWChar] = ADOS::MapADOType2Jdbc(adWChar); + aMap[adVarWChar] = ADOS::MapADOType2Jdbc(adVarWChar); + aMap[adLongVarWChar] = ADOS::MapADOType2Jdbc(adLongVarWChar); + aMap[adBinary] = ADOS::MapADOType2Jdbc(adBinary); + aMap[adVarBinary] = ADOS::MapADOType2Jdbc(adVarBinary); + aMap[adLongVarBinary] = ADOS::MapADOType2Jdbc(adLongVarBinary); + aMap[adChapter] = ADOS::MapADOType2Jdbc(adChapter); + aMap[adFileTime] = ADOS::MapADOType2Jdbc(adFileTime); + aMap[adPropVariant] = ADOS::MapADOType2Jdbc(adPropVariant); + aMap[adVarNumeric] = ADOS::MapADOType2Jdbc(adVarNumeric); + + m_aValueRange[12] = aMap; + + std::map< sal_Int32,OUString> aMap2; + aMap2[0] = "YES"; + aMap2[1] = "NO"; + m_aIntValueRange[18] = aMap2; + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setColumnsMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setTablesMap() +{ + + for(sal_Int32 i=1;i<5;i++) + m_aColMapping.push_back(i); + m_aColMapping.push_back(6); + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setTablesMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setProcedureColumnsMap() +{ + + for(sal_Int32 i=1;i<5;i++) + m_aColMapping.push_back(i); + m_aColMapping.push_back(6); + m_aColMapping.push_back(10); + m_aColMapping.push_back(16); + m_aColMapping.push_back(13); + m_aColMapping.push_back(11); + m_aColMapping.push_back(12); + + m_aColMapping.push_back(9); + m_aColMapping.push_back(14); + + ::std::map<sal_Int32,sal_Int32> aMap; + aMap[DBTYPE_EMPTY] = DataType::SQLNULL; + aMap[DBTYPE_NULL] = DataType::SQLNULL; + aMap[DBTYPE_I2] = DataType::SMALLINT; + aMap[DBTYPE_I4] = DataType::INTEGER; + aMap[DBTYPE_R4] = DataType::FLOAT; + aMap[DBTYPE_R8] = DataType::DOUBLE; + aMap[DBTYPE_CY] = DataType::BIGINT; + aMap[DBTYPE_DATE] = DataType::DATE; + aMap[DBTYPE_BSTR] = DataType::VARCHAR; + aMap[DBTYPE_IDISPATCH] = DataType::OBJECT; + aMap[DBTYPE_ERROR] = DataType::OTHER; + aMap[DBTYPE_BOOL] = DataType::BIT; + aMap[DBTYPE_VARIANT] = DataType::STRUCT; + aMap[DBTYPE_IUNKNOWN] = DataType::OTHER; + aMap[DBTYPE_DECIMAL] = DataType::DECIMAL; + aMap[DBTYPE_UI1] = DataType::TINYINT; + aMap[DBTYPE_ARRAY] = DataType::ARRAY; + aMap[DBTYPE_BYREF] = DataType::REF; + aMap[DBTYPE_I1] = DataType::CHAR; + aMap[DBTYPE_UI2] = DataType::SMALLINT; + aMap[DBTYPE_UI4] = DataType::INTEGER; + + // aMap[The] = ; + // aMap[in] = ; + aMap[DBTYPE_I8] = DataType::BIGINT; + aMap[DBTYPE_UI8] = DataType::BIGINT; + aMap[DBTYPE_GUID] = DataType::OTHER; + aMap[DBTYPE_VECTOR] = DataType::OTHER; + aMap[DBTYPE_FILETIME] = DataType::OTHER; + aMap[DBTYPE_RESERVED] = DataType::OTHER; + + // aMap[The] = ; + aMap[DBTYPE_BYTES] = DataType::VARBINARY; + aMap[DBTYPE_STR] = DataType::LONGVARCHAR; + aMap[DBTYPE_WSTR] = DataType::LONGVARCHAR; + aMap[DBTYPE_NUMERIC] = DataType::NUMERIC; + aMap[DBTYPE_UDT] = DataType::OTHER; + aMap[DBTYPE_DBDATE] = DataType::DATE; + aMap[DBTYPE_DBTIME] = DataType::TIME; + aMap[DBTYPE_DBTIMESTAMP] = DataType::TIMESTAMP; + aMap[DBTYPE_HCHAPTER] = DataType::OTHER; + aMap[DBTYPE_PROPVARIANT] = DataType::OTHER; + aMap[DBTYPE_VARNUMERIC] = DataType::NUMERIC; + + m_aValueRange[10] = aMap; + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setProcedureColumnsMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setPrimaryKeysMap() +{ + + sal_Int32 i=1; + for(;i<5;i++) + m_aColMapping.push_back(i); + m_aColMapping.push_back(7); + m_aColMapping.push_back(8); + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setProcedureColumnsMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setIndexInfoMap() +{ + + sal_Int32 i=1; + for(;i<4;i++) + m_aColMapping.push_back(i); + m_aColMapping.push_back(8); + m_aColMapping.push_back(4); + m_aColMapping.push_back(6); + m_aColMapping.push_back(10); + m_aColMapping.push_back(17); + m_aColMapping.push_back(18); + m_aColMapping.push_back(21); + m_aColMapping.push_back(22); + m_aColMapping.push_back(23); + m_aColMapping.push_back(24); + + ::std::map<sal_Int32,sal_Int32> aMap; + aMap[DBPROPVAL_IT_HASH] = IndexType::HASHED; + aMap[DBPROPVAL_IT_CONTENT] = IndexType::OTHER; + aMap[DBPROPVAL_IT_OTHER] = IndexType::OTHER; + aMap[DBPROPVAL_IT_BTREE] = IndexType::OTHER; + + m_aValueRange[10] = aMap; + + ::std::map<sal_Int32,sal_Int32> aMap2; + aMap[0] = 1; + aMap[1] = 0; + m_aValueRange[8] = aMap2; + + std::map< sal_Int32,OUString> aMap3; + aMap3[0] = ""; + aMap3[DB_COLLATION_ASC] = "A"; + aMap3[DB_COLLATION_DESC] = "D"; + + m_aIntValueRange[21] = aMap3; + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setIndexInfoMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setTablePrivilegesMap() +{ + + sal_Int32 i=3; + for(;i<6;i++) + m_aColMapping.push_back(i); + m_aColMapping.push_back(1); + m_aColMapping.push_back(2); + m_aColMapping.push_back(6); + m_aColMapping.push_back(7); + + std::map< sal_Int32,OUString> aMap; + aMap[0] = "YES"; + aMap[1] = "NO"; + m_aIntValueRange[7] = aMap; + + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setTablePrivilegesMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setCrossReferenceMap() +{ + + sal_Int32 i=1; + for(;i<5;i++) + m_aColMapping.push_back(i); + for(i=7;i<11;i++) + m_aColMapping.push_back(i); + + m_aColMapping.push_back(13); + m_aColMapping.push_back(14); + m_aColMapping.push_back(15); + m_aColMapping.push_back(17); + m_aColMapping.push_back(16); + m_aColMapping.push_back(18); + + std::map< OUString,sal_Int32> aMap; + aMap[ OUString("CASCADE")] = KeyRule::CASCADE; + aMap[ OUString("RESTRICT")] = KeyRule::RESTRICT; + aMap[ OUString("SET NULL")] = KeyRule::SET_NULL; + aMap[ OUString("SET DEFAULT")] = KeyRule::SET_DEFAULT; + aMap[ OUString("NO ACTION")] = KeyRule::NO_ACTION; + + m_aStrValueRange[14] = aMap; + m_aStrValueRange[15] = aMap; + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setCrossReferenceMap(); + m_xMetaData = pMetaData; +} + +void ODatabaseMetaDataResultSet::setTypeInfoMap(bool _bJetEngine) +{ + sal_Int32 i=1; + for(;i<19;i++) + m_aColMapping.push_back(i); + + std::map< OUString,sal_Int32> aMap1; + aMap1[ OUString()] = 10; + + m_aStrValueRange[18] = aMap1; + + ::std::map<sal_Int32,sal_Int32> aMap; + aMap[adEmpty] = ADOS::MapADOType2Jdbc(adEmpty); + aMap[adTinyInt] = ADOS::MapADOType2Jdbc(adTinyInt); + aMap[adSmallInt] = ADOS::MapADOType2Jdbc(adSmallInt); + aMap[adInteger] = ADOS::MapADOType2Jdbc(adInteger); + aMap[adBigInt] = ADOS::MapADOType2Jdbc(adBigInt); + aMap[adUnsignedTinyInt] = ADOS::MapADOType2Jdbc(adUnsignedTinyInt); + aMap[adUnsignedSmallInt]= ADOS::MapADOType2Jdbc(adUnsignedSmallInt); + aMap[adUnsignedInt] = ADOS::MapADOType2Jdbc(adUnsignedInt); + aMap[adUnsignedBigInt] = ADOS::MapADOType2Jdbc(adUnsignedBigInt); + aMap[adSingle] = ADOS::MapADOType2Jdbc(adSingle); + aMap[adDouble] = ADOS::MapADOType2Jdbc(adDouble); + aMap[adCurrency] = ADOS::MapADOType2Jdbc(adCurrency); + aMap[adDecimal] = ADOS::MapADOType2Jdbc(adDecimal); + aMap[adNumeric] = ADOS::MapADOType2Jdbc(adNumeric); + aMap[adBoolean] = ADOS::MapADOType2Jdbc(adBoolean); + aMap[adError] = ADOS::MapADOType2Jdbc(adError); + aMap[adUserDefined] = ADOS::MapADOType2Jdbc(adUserDefined); + aMap[adVariant] = ADOS::MapADOType2Jdbc(adVariant); + aMap[adIDispatch] = ADOS::MapADOType2Jdbc(adIDispatch); + aMap[adIUnknown] = ADOS::MapADOType2Jdbc(adIUnknown); + aMap[adGUID] = ADOS::MapADOType2Jdbc(adGUID); + aMap[adDate] = _bJetEngine ? ADOS::MapADOType2Jdbc(adDBTimeStamp) : ADOS::MapADOType2Jdbc(adDate); + aMap[adDBDate] = ADOS::MapADOType2Jdbc(adDBDate); + aMap[adDBTime] = ADOS::MapADOType2Jdbc(adDBTime); + aMap[adDBTimeStamp] = ADOS::MapADOType2Jdbc(adDBTimeStamp); + aMap[adBSTR] = ADOS::MapADOType2Jdbc(adBSTR); + aMap[adChar] = ADOS::MapADOType2Jdbc(adChar); + aMap[adVarChar] = ADOS::MapADOType2Jdbc(adVarChar); + aMap[adLongVarChar] = ADOS::MapADOType2Jdbc(adLongVarChar); + aMap[adWChar] = ADOS::MapADOType2Jdbc(adWChar); + aMap[adVarWChar] = ADOS::MapADOType2Jdbc(adVarWChar); + aMap[adLongVarWChar] = ADOS::MapADOType2Jdbc(adLongVarWChar); + aMap[adBinary] = ADOS::MapADOType2Jdbc(adBinary); + aMap[adVarBinary] = ADOS::MapADOType2Jdbc(adVarBinary); + aMap[adLongVarBinary] = ADOS::MapADOType2Jdbc(adLongVarBinary); + aMap[adChapter] = ADOS::MapADOType2Jdbc(adChapter); + aMap[adFileTime] = ADOS::MapADOType2Jdbc(adFileTime); + aMap[adPropVariant] = ADOS::MapADOType2Jdbc(adPropVariant); + aMap[adVarNumeric] = ADOS::MapADOType2Jdbc(adVarNumeric); +// aMap[adArray] = ADOS::MapADOType2Jdbc(adArray); + + m_aValueRange[2] = aMap; + + ::std::map<sal_Int32,sal_Int32> aColumnValueMapping; + aColumnValueMapping[VARIANT_FALSE] = ColumnValue::NO_NULLS; + aColumnValueMapping[VARIANT_TRUE] = ColumnValue::NULLABLE; + m_aValueRange[7] = aColumnValueMapping; + + // now adjust the column mapping + // OJ 24.01.2002 96860 + ::std::map<sal_Int32,sal_Int32> aSearchMapping; + aSearchMapping[DB_UNSEARCHABLE] = ColumnSearch::NONE; + aSearchMapping[DB_LIKE_ONLY] = ColumnSearch::CHAR; + aSearchMapping[DB_ALL_EXCEPT_LIKE] = ColumnSearch::BASIC; + aSearchMapping[DB_SEARCHABLE] = ColumnSearch::FULL; + + m_aValueRange[9] = aSearchMapping; + + ::std::map<sal_Int32,sal_Int32> aCurrencyMapping; + m_aValueRange[11] = aCurrencyMapping; + + ODatabaseMetaDataResultSetMetaData* pMetaData = new ODatabaseMetaDataResultSetMetaData(m_pRecordSet,this); + pMetaData->setTypeInfoMap(); + m_xMetaData = pMetaData; +} + +void SAL_CALL ODatabaseMetaDataResultSet::acquire() throw() +{ + ODatabaseMetaDataResultSet_BASE::acquire(); +} + +void SAL_CALL ODatabaseMetaDataResultSet::release() throw() +{ + ODatabaseMetaDataResultSet_BASE::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL ODatabaseMetaDataResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +OLEVariant ODatabaseMetaDataResultSet::getValue(sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + + checkRecordSet(); + + WpADOField aField = ADOS::getField(m_pRecordSet,columnIndex); + aField.get_Value(m_aValue); + return m_aValue; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ADatabaseMetaDataResultSetMetaData.cxx b/connectivity/source/drivers/ado/ADatabaseMetaDataResultSetMetaData.cxx new file mode 100644 index 000000000..9a4365abf --- /dev/null +++ b/connectivity/source/drivers/ado/ADatabaseMetaDataResultSetMetaData.cxx @@ -0,0 +1,222 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/ADatabaseMetaDataResultSetMetaData.hxx> +#include <ado/Awrapado.hxx> +#include <connectivity/dbexception.hxx> + + +using namespace connectivity; +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + + +ODatabaseMetaDataResultSetMetaData::~ODatabaseMetaDataResultSetMetaData() +{ + if(m_pRecordSet) + m_pRecordSet->Release(); +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnDisplaySize( sal_Int32 column ) +{ + sal_Int32 nSize = 0; + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + nSize = (*m_mColumnsIter).second.getColumnDisplaySize(); + else if(m_pRecordSet) + { + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + nSize = aField.GetActualSize(); + } + return nSize; +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnType( sal_Int32 column ) +{ + sal_Int32 nType = 0; + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + nType = (*m_mColumnsIter).second.getColumnType(); + else if(m_pRecordSet) + { + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + nType = ADOS::MapADOType2Jdbc(aField.GetADOType()); + } + return nType; +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnCount( ) +{ + if(!m_pRecordSet) + return 0; + if(m_nColCount != -1) + return m_nColCount; + + if(m_vMapping.size()) + return m_mColumns.size(); + + ADOFields* pFields = nullptr; + m_pRecordSet->get_Fields(&pFields); + WpOLEAppendCollection<ADOFields, ADOField, WpADOField> aFields(pFields); + m_nColCount = aFields.GetItemCount(); + return m_nColCount; +} + + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnName( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getColumnName(); + if(!m_pRecordSet) + return OUString(); + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + return aField.GetName(); + + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnLabel( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getColumnLabel(); + return getColumnName(column); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isCurrency( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isCurrency(); + if(!m_pRecordSet) + return false; + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + { + return (aField.GetAttributes() & adFldFixed) == adFldFixed; + } + return false; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isSigned( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isSigned(); + if(!m_pRecordSet) + return false; + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + { + return (aField.GetAttributes() & adFldNegativeScale) == adFldNegativeScale; + } + return false; +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getPrecision( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getPrecision(); + if(!m_pRecordSet) + return 0; + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + return aField.GetPrecision(); + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::getScale( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.getScale(); + + if(!m_pRecordSet) + return 0; + + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + return aField.GetNumericScale(); + return 0; +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSetMetaData::isNullable( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isNullable(); + + if(!m_pRecordSet) + return sal_Int32(false); + + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + { + return sal_Int32((aField.GetAttributes() & adFldIsNullable) == adFldIsNullable); + } + return sal_Int32(false); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isReadOnly( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isReadOnly(); + + if(!m_pRecordSet) + return false; + + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + { + // return (aField.GetStatus() & adFieldReadOnly) == adFieldReadOnly; + } + return false; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isDefinitelyWritable( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isDefinitelyWritable(); + + if(!m_pRecordSet) + return false; + + WpADOField aField = ADOS::getField(m_pRecordSet,m_vMapping[column]); + if(aField.IsValid()) + { + return (aField.GetAttributes() & adFldUpdatable) == adFldUpdatable; + } + return false; +; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isWritable( sal_Int32 column ) +{ + if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end()) + return (*m_mColumnsIter).second.isWritable(); + return isDefinitelyWritable(column); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ADriver.cxx b/connectivity/source/drivers/ado/ADriver.cxx new file mode 100644 index 000000000..926a818ee --- /dev/null +++ b/connectivity/source/drivers/ado/ADriver.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 <ado/ADriver.hxx> +#include <ado/AConnection.hxx> +#include <ado/Awrapadox.hxx> +#include <ado/ACatalog.hxx> +#include <ado/Awrapado.hxx> +#include <ado/adoimp.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <strings.hrc> +#include <objbase.h> + +#include <resource/sharedresources.hxx> + +#include <memory> + +using namespace connectivity; +using namespace connectivity::ado; +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; + + +ODriver::ODriver(const css::uno::Reference< css::lang::XMultiServiceFactory >& _xORB) + : ODriver_BASE(m_aMutex) + ,m_xORB(_xORB) +{ + if ( FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)) ) + { + CoUninitialize(); + int h = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + (void)h; + ++h; + } +} + +ODriver::~ODriver() +{ + CoUninitialize(); + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); +} + +void ODriver::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + + for (auto& rxConnection : m_xConnections) + { + Reference< XComponent > xComp(rxConnection.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + } + m_xConnections.clear(); + + ODriver_BASE::disposing(); +} +// static ServiceInfo + +OUString ODriver::getImplementationName_Static( ) +{ + return "com.sun.star.comp.sdbc.ado.ODriver"; +} + +Sequence< OUString > ODriver::getSupportedServiceNames_Static( ) +{ + return { "com.sun.star.sdbc.Driver", "com.sun.star.sdbcx.Driver" }; +} + +css::uno::Reference< css::uno::XInterface > connectivity::ado::ODriver_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory) +{ + return *(new ODriver(_rxFactory)); +} + + +OUString SAL_CALL ODriver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL ODriver::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + + +Sequence< OUString > SAL_CALL ODriver::getSupportedServiceNames( ) +{ + return getSupportedServiceNames_Static(); +} + + +Reference< XConnection > SAL_CALL ODriver::connect( const OUString& url, const Sequence< PropertyValue >& info ) +{ + if ( ! acceptsURL(url) ) + return nullptr; + + // we need to wrap the connection as the construct call might throw + std::unique_ptr<OConnection> pCon(new OConnection(this)); + pCon->construct(url,info); + OConnection* pPtr = pCon.get(); + Reference< XConnection > xCon = pCon.release(); + m_xConnections.push_back(WeakReferenceHelper(*pPtr)); + + return xCon; +} + +sal_Bool SAL_CALL ODriver::acceptsURL( const OUString& url ) +{ + return url.startsWith("sdbc:ado:"); +} + +void ODriver::impl_checkURL_throw(const OUString& _sUrl) +{ + if ( !acceptsURL(_sUrl) ) + { + SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } // if ( !acceptsURL(_sUrl) ) +} + +Sequence< DriverPropertyInfo > SAL_CALL ODriver::getPropertyInfo( const OUString& url, const Sequence< PropertyValue >& /*info*/ ) +{ + impl_checkURL_throw(url); + if ( acceptsURL(url) ) + { + std::vector< DriverPropertyInfo > aDriverInfo; + + Sequence< OUString > aBooleanValues(2); + aBooleanValues[0] = "false"; + aBooleanValues[1] = "true"; + + aDriverInfo.push_back(DriverPropertyInfo( + "IgnoreDriverPrivileges" + ,"Ignore the privileges from the database driver." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "EscapeDateTime" + ,"Escape date time format." + ,false + ,"true" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "TypeInfoSettings" + ,"Defines how the type info of the database metadata should be manipulated." + ,false + ,OUString( ) + ,Sequence< OUString > ()) + ); + return Sequence< DriverPropertyInfo >(aDriverInfo.data(),aDriverInfo.size()); + } + return Sequence< DriverPropertyInfo >(); +} + +sal_Int32 SAL_CALL ODriver::getMajorVersion( ) +{ + return 1; +} + +sal_Int32 SAL_CALL ODriver::getMinorVersion( ) +{ + return 0; +} + +// XDataDefinitionSupplier +Reference< XTablesSupplier > SAL_CALL ODriver::getDataDefinitionByConnection( const Reference< css::sdbc::XConnection >& connection ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if (ODriver_BASE::rBHelper.bDisposed) + throw DisposedException(); + + OConnection* pConnection = nullptr; + Reference< css::lang::XUnoTunnel> xTunnel(connection,UNO_QUERY); + if(xTunnel.is()) + { + OConnection* pSearchConnection = reinterpret_cast< OConnection* >( xTunnel->getSomething(OConnection::getUnoTunnelId()) ); + + auto foundConnection = std::any_of(m_xConnections.begin(), m_xConnections.end(), + [&pSearchConnection](const css::uno::WeakReferenceHelper& rxConnection) { + return static_cast<OConnection*>(Reference< XConnection >::query(rxConnection.get().get()).get()) == pSearchConnection; }); + if (foundConnection) + pConnection = pSearchConnection; + } + + Reference< XTablesSupplier > xTab; + if(pConnection) + { + WpADOCatalog aCatalog; + aCatalog.Create(); + if(aCatalog.IsValid()) + { + aCatalog.putref_ActiveConnection(*pConnection->getConnection()); + OCatalog* pCatalog = new OCatalog(aCatalog,pConnection); + xTab = pCatalog; + pConnection->setCatalog(xTab); + pConnection->setCatalog(pCatalog); + } + } + return xTab; +} + +Reference< XTablesSupplier > SAL_CALL ODriver::getDataDefinitionByURL( const OUString& url, const Sequence< PropertyValue >& info ) +{ + impl_checkURL_throw(url); + return getDataDefinitionByConnection(connect(url,info)); +} + + +void ADOS::ThrowException(ADOConnection* _pAdoCon,const Reference< XInterface >& _xInterface) +{ + ADOErrors *pErrors = nullptr; + _pAdoCon->get_Errors(&pErrors); + if(!pErrors) + return; // no error found + + pErrors->AddRef( ); + + // read all noted errors and issue them + sal_Int32 nLen; + pErrors->get_Count(&nLen); + if (nLen) + { + SQLException aException; + aException.ErrorCode = 1000; + for (sal_Int32 i = nLen-1; i>=0; --i) + { + ADOError *pError = nullptr; + pErrors->get_Item(OLEVariant(i),&pError); + WpADOError aErr(pError); + OSL_ENSURE(pError,"No error in collection found! BAD!"); + if(pError) + { + if(i==nLen-1) + aException = SQLException(aErr.GetDescription(),_xInterface,aErr.GetSQLState(),aErr.GetNumber(),Any()); + else + { + SQLException aTemp(aErr.GetDescription(), + _xInterface,aErr.GetSQLState(),aErr.GetNumber(),makeAny(aException)); + aTemp.NextException <<= aException; + aException = aTemp; + } + } + } + pErrors->Clear(); + pErrors->Release(); + throw aException; + } + pErrors->Release(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AGroup.cxx b/connectivity/source/drivers/ado/AGroup.cxx new file mode 100644 index 000000000..b41fbdbba --- /dev/null +++ b/connectivity/source/drivers/ado/AGroup.cxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AGroup.hxx> +#include <ado/AUsers.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <ado/AConnection.hxx> +#include <TConnection.hxx> + +using namespace connectivity::ado; +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; + + +void WpADOGroup::Create() +{ + ADOGroup* pGroup = nullptr; + HRESULT hr = CoCreateInstance(ADOS::CLSID_ADOGROUP_25, + nullptr, + CLSCTX_INPROC_SERVER, + ADOS::IID_ADOGROUP_25, + reinterpret_cast<void**>(&pGroup) ); + + + if( !FAILED( hr ) ) + { + operator=( pGroup ); + pGroup->Release(); + } +} + +OAdoGroup::OAdoGroup(OCatalog* _pParent,bool _bCase, ADOGroup* _pGroup) : OGroup_ADO(_bCase),m_pCatalog(_pParent) +{ + construct(); + if(_pGroup) + m_aGroup = WpADOGroup(_pGroup); + else + m_aGroup.Create(); + +} + +OAdoGroup::OAdoGroup(OCatalog* _pParent,bool _bCase, const OUString& Name) : OGroup_ADO(Name,_bCase),m_pCatalog(_pParent) +{ + construct(); + m_aGroup.Create(); + m_aGroup.put_Name(Name); +} + +void OAdoGroup::refreshUsers() +{ + ::std::vector< OUString> aVector; + + WpADOUsers aUsers = m_aGroup.get_Users(); + aUsers.fillElementNames(aVector); + + if(m_pUsers) + m_pUsers->reFill(aVector); + else + m_pUsers.reset(new OUsers(m_pCatalog, m_aMutex, aVector, aUsers, isCaseSensitive())); +} + +Sequence< sal_Int8 > OAdoGroup::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OAdoGroup::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OAdoGroup>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : OGroup_ADO::getSomething(rId); +} + + +void OAdoGroup::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + if(m_aGroup.IsValid()) + { + + switch(nHandle) + { + case PROPERTY_ID_NAME: + { + OUString aVal; + rValue >>= aVal; + m_aGroup.put_Name(aVal); + } + break; + } + } +} + +void OAdoGroup::getFastPropertyValue(Any& rValue,sal_Int32 nHandle) const +{ + if(m_aGroup.IsValid()) + { + switch(nHandle) + { + case PROPERTY_ID_NAME: + rValue <<= m_aGroup.get_Name(); + break; + } + } +} + + +sal_Int32 SAL_CALL OAdoGroup::getPrivileges( const OUString& objName, sal_Int32 objType ) +{ + return MapRight(m_aGroup.GetPermissions(objName,MapObjectType(objType))); +} + +sal_Int32 SAL_CALL OAdoGroup::getGrantablePrivileges( const OUString& objName, sal_Int32 objType ) +{ + RightsEnum eNum = m_aGroup.GetPermissions(objName,MapObjectType(objType)); + if(eNum & adRightWithGrant) + return MapRight(eNum); + return 0; +} + +void SAL_CALL OAdoGroup::grantPrivileges( const OUString& objName, sal_Int32 objType, sal_Int32 objPrivileges ) +{ + m_aGroup.SetPermissions(objName,MapObjectType(objType),adAccessGrant,Map2Right(objPrivileges)); +} + +void SAL_CALL OAdoGroup::revokePrivileges( const OUString& objName, sal_Int32 objType, sal_Int32 objPrivileges ) +{ + m_aGroup.SetPermissions(objName,MapObjectType(objType),adAccessDeny,Map2Right(objPrivileges)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AGroups.cxx b/connectivity/source/drivers/ado/AGroups.cxx new file mode 100644 index 000000000..211f34f6b --- /dev/null +++ b/connectivity/source/drivers/ado/AGroups.cxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/AGroups.hxx> +#include <ado/AGroup.hxx> +#include <ado/ATable.hxx> +#include <ado/AConnection.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <connectivity/sdbcx/IRefreshable.hxx> +#include <TConnection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> + +using namespace comphelper; +using namespace connectivity; +using namespace connectivity::ado; +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::container; + + +sdbcx::ObjectType OGroups::createObject(const OUString& _rName) +{ + return new OAdoGroup(m_pCatalog,isCaseSensitive(),_rName); +} + +void OGroups::impl_refresh() +{ + m_aCollection.Refresh(); +} + +Reference< XPropertySet > OGroups::createDescriptor() +{ + return new OAdoGroup(m_pCatalog,isCaseSensitive()); +} + +// XAppend +sdbcx::ObjectType OGroups::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + OAdoGroup* pGroup = getUnoTunnelImplementation<OAdoGroup>(descriptor); + if ( pGroup == nullptr ) + m_pCatalog->getConnection()->throwGenericSQLException( STR_INVALID_GROUP_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + + m_aCollection.Append( pGroup->getImpl() ); + return createObject( _rForName ); +} + +// XDrop +void OGroups::dropObject(sal_Int32 /*_nPos*/,const OUString& _sElementName) +{ + m_aCollection.Delete(_sElementName); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AIndex.cxx b/connectivity/source/drivers/ado/AIndex.cxx new file mode 100644 index 000000000..61a047bc9 --- /dev/null +++ b/connectivity/source/drivers/ado/AIndex.cxx @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/AIndex.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <ado/AColumns.hxx> +#include <TConnection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> + +using namespace ::comphelper; + +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + + +OAdoIndex::OAdoIndex(bool _bCase,OConnection* _pConnection,ADOIndex* _pIndex) + : sdbcx::OIndex(OUString(),OUString(),false,false,false,_bCase) + ,m_pConnection(_pConnection) +{ + construct(); + m_aIndex = WpADOIndex(_pIndex); + fillPropertyValues(); +} + +OAdoIndex::OAdoIndex(bool _bCase,OConnection* _pConnection) + : sdbcx::OIndex(_bCase) + ,m_pConnection(_pConnection) +{ + construct(); + m_aIndex.Create(); +} + + +void OAdoIndex::refreshColumns() +{ + ::std::vector< OUString> aVector; + + WpADOColumns aColumns; + if ( m_aIndex.IsValid() ) + { + aColumns = m_aIndex.get_Columns(); + aColumns.fillElementNames(aVector); + } + + if ( m_pColumns ) + m_pColumns->reFill(aVector); + else + m_pColumns.reset(new OColumns(*this, m_aMutex, aVector, aColumns, isCaseSensitive(), m_pConnection)); +} + + +Sequence< sal_Int8 > OAdoIndex::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OAdoIndex::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OAdoIndex>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : sdbcx::OIndex::getSomething(rId); +} + +void SAL_CALL OAdoIndex::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + if(m_aIndex.IsValid()) + { + switch(nHandle) + { + case PROPERTY_ID_NAME: + { + OUString aVal; + rValue >>= aVal; + m_aIndex.put_Name(aVal); + } + break; + case PROPERTY_ID_CATALOG: + { + OUString aVal; + rValue >>= aVal; + m_aIndex.put_Name(aVal); + } + break; + case PROPERTY_ID_ISUNIQUE: + m_aIndex.put_Unique(getBOOL(rValue)); + break; + case PROPERTY_ID_ISPRIMARYKEYINDEX: + m_aIndex.put_PrimaryKey(getBOOL(rValue)); + break; + case PROPERTY_ID_ISCLUSTERED: + m_aIndex.put_Clustered(getBOOL(rValue)); + break; + } + } + sdbcx::OIndex::setFastPropertyValue_NoBroadcast(nHandle,rValue); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AIndexes.cxx b/connectivity/source/drivers/ado/AIndexes.cxx new file mode 100644 index 000000000..616cd863a --- /dev/null +++ b/connectivity/source/drivers/ado/AIndexes.cxx @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AIndexes.hxx> +#include <ado/AIndex.hxx> +#include <ado/AConnection.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/IndexType.hpp> +#include <TConnection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> + +using namespace ::comphelper; + + +using namespace connectivity; +using namespace connectivity::ado; +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::container; + +sdbcx::ObjectType OIndexes::createObject(const OUString& _rName) +{ + return new OAdoIndex(isCaseSensitive(),m_pConnection,m_aCollection.GetItem(_rName)); +} + +void OIndexes::impl_refresh() +{ + m_aCollection.Refresh(); +} + +Reference< XPropertySet > OIndexes::createDescriptor() +{ + return new OAdoIndex(isCaseSensitive(),m_pConnection); +} + +// XAppend +sdbcx::ObjectType OIndexes::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + OAdoIndex* pIndex = getUnoTunnelImplementation<OAdoIndex>(descriptor); + if ( pIndex == nullptr ) + m_pConnection->throwGenericSQLException( STR_INVALID_INDEX_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + + ADOIndexes* pIndexes = m_aCollection; + if ( FAILED( pIndexes->Append( OLEVariant( _rForName ), OLEVariant( pIndex->getImpl() ) ) ) ) + { + ADOS::ThrowException(*m_pConnection->getConnection(),static_cast<XTypeProvider*>(this)); + m_pConnection->throwGenericSQLException( STR_INVALID_INDEX_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + } + + return new OAdoIndex(isCaseSensitive(),m_pConnection,pIndex->getImpl()); +} + +// XDrop +void OIndexes::dropObject(sal_Int32 /*_nPos*/,const OUString& _sElementName) +{ + m_aCollection.Delete(_sElementName); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AKey.cxx b/connectivity/source/drivers/ado/AKey.cxx new file mode 100644 index 000000000..764765b35 --- /dev/null +++ b/connectivity/source/drivers/ado/AKey.cxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/AKey.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <ado/AColumns.hxx> +#include <ado/AConnection.hxx> + +using namespace connectivity::ado; +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; + + +OAdoKey::OAdoKey(bool _bCase,OConnection* _pConnection, ADOKey* _pKey) + : OKey_ADO(_bCase) + ,m_pConnection(_pConnection) +{ + construct(); + m_aKey = WpADOKey(_pKey); + fillPropertyValues(); +} + +OAdoKey::OAdoKey(bool _bCase,OConnection* _pConnection) + : OKey_ADO(_bCase) + ,m_pConnection(_pConnection) +{ + construct(); + m_aKey.Create(); +} + +void OAdoKey::refreshColumns() +{ + ::std::vector< OUString> aVector; + + WpADOColumns aColumns; + if ( m_aKey.IsValid() ) + { + aColumns = m_aKey.get_Columns(); + aColumns.fillElementNames(aVector); + } + + if(m_pColumns) + m_pColumns->reFill(aVector); + else + m_pColumns.reset(new OColumns(*this, m_aMutex, aVector, aColumns, isCaseSensitive(), m_pConnection)); +} + +Sequence< sal_Int8 > OAdoKey::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OAdoKey::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OAdoKey>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : OKey_ADO::getSomething(rId); +} + +void OAdoKey::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + if(m_aKey.IsValid()) + { + switch(nHandle) + { + case PROPERTY_ID_NAME: + { + OUString aVal; + rValue >>= aVal; + m_aKey.put_Name(aVal); + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + } + break; + case PROPERTY_ID_TYPE: + { + sal_Int32 nVal=0; + rValue >>= nVal; + m_aKey.put_Type(Map2KeyRule(nVal)); + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + } + break; + case PROPERTY_ID_REFERENCEDTABLE: + { + OUString aVal; + rValue >>= aVal; + m_aKey.put_RelatedTable(aVal); + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + } + break; + case PROPERTY_ID_UPDATERULE: + { + sal_Int32 nVal=0; + rValue >>= nVal; + m_aKey.put_UpdateRule(Map2Rule(nVal)); + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + } + break; + case PROPERTY_ID_DELETERULE: + { + sal_Int32 nVal=0; + rValue >>= nVal; + m_aKey.put_DeleteRule(Map2Rule(nVal)); + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + } + break; + } + } + OKey_ADO::setFastPropertyValue_NoBroadcast(nHandle,rValue); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AKeys.cxx b/connectivity/source/drivers/ado/AKeys.cxx new file mode 100644 index 000000000..19027e79f --- /dev/null +++ b/connectivity/source/drivers/ado/AKeys.cxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AKeys.hxx> +#include <ado/AKey.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <ado/AConnection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <ado/Awrapado.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::ado; +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; + +sdbcx::ObjectType OKeys::createObject(const OUString& _rName) +{ + return new OAdoKey(isCaseSensitive(),m_pConnection,m_aCollection.GetItem(_rName)); +} + +void OKeys::impl_refresh() +{ + m_aCollection.Refresh(); +} + +Reference< XPropertySet > OKeys::createDescriptor() +{ + return new OAdoKey(isCaseSensitive(),m_pConnection); +} + +// XAppend +sdbcx::ObjectType OKeys::appendObject( const OUString&, const Reference< XPropertySet >& descriptor ) +{ + OAdoKey* pKey = getUnoTunnelImplementation<OAdoKey>( descriptor ); + if ( pKey == nullptr) + m_pConnection->throwGenericSQLException( STR_INVALID_KEY_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + + // To pass as column parameter to Key's Append method + OLEVariant vOptional; + vOptional.setNoArg(); + + OAdoKey::Map2KeyRule(getINT32(descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)))); + + WpADOKey aKey = pKey->getImpl(); + OUString sName = aKey.get_Name(); + if(!sName.getLength()) + aKey.put_Name("PrimaryKey"); + + ADOKeys* pKeys = m_aCollection; + if ( FAILED(pKeys->Append(OLEVariant(static_cast<ADOKey*>(aKey)), + adKeyPrimary, // must be every time adKeyPrimary + vOptional)) ) + { + ADOS::ThrowException(*m_pConnection->getConnection(),static_cast<XTypeProvider*>(this)); + // just make sure that an SQLExceptionis thrown here + m_pConnection->throwGenericSQLException( STR_INVALID_KEY_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + } + + return new OAdoKey(isCaseSensitive(),m_pConnection,pKey->getImpl()); +} + +// XDrop +void OKeys::dropObject(sal_Int32 /*_nPos*/,const OUString& _sElementName) +{ + if(!m_aCollection.Delete(OLEVariant(_sElementName).getString())) + ADOS::ThrowException(*m_pConnection->getConnection(),static_cast<XTypeProvider*>(this)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/APreparedStatement.cxx b/connectivity/source/drivers/ado/APreparedStatement.cxx new file mode 100644 index 000000000..ecd4ed538 --- /dev/null +++ b/connectivity/source/drivers/ado/APreparedStatement.cxx @@ -0,0 +1,460 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <connectivity/sqlparse.hxx> +#include <ado/APreparedStatement.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <ado/AResultSetMetaData.hxx> +#include <ado/AResultSet.hxx> +#include <ado/ADriver.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <strings.hrc> + +#include <limits> + +#define CHECK_RETURN(x) \ + if(!x) \ + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + +#ifdef max +# undef max +#endif + +using namespace connectivity::ado; +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::util; + +IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.APreparedStatement","com.sun.star.sdbc.PreparedStatement"); + +OPreparedStatement::OPreparedStatement( OConnection* _pConnection, const OUString& sql) + : OStatement_Base( _pConnection ) +{ + osl_atomic_increment( &m_refCount ); + + OSQLParser aParser(comphelper::getComponentContext(_pConnection->getDriver()->getORB())); + OUString sErrorMessage; + OUString sNewSql; + std::unique_ptr<OSQLParseNode> pNode = aParser.parseTree(sErrorMessage,sql); + if(pNode) + { // special handling for parameters + // we recursive replace all occurrences of ? in the statement and + // replace them with name like "parame" */ + sal_Int32 nParameterCount = 0; + replaceParameterNodeName(pNode.get(), "parame", nParameterCount); + pNode->parseNodeToStr( sNewSql, _pConnection ); + } + else + sNewSql = sql; + CHECK_RETURN(m_Command.put_CommandText(sNewSql)) + CHECK_RETURN(m_Command.put_Prepared(VARIANT_TRUE)) + m_pParameters = m_Command.get_Parameters(); + m_pParameters->AddRef(); + m_pParameters->Refresh(); + + osl_atomic_decrement( &m_refCount ); +} + +OPreparedStatement::~OPreparedStatement() +{ + if (m_pParameters) + { + OSL_FAIL( "OPreparedStatement::~OPreparedStatement: not disposed!" ); + m_pParameters->Release(); + m_pParameters = nullptr; + } +} + +Any SAL_CALL OPreparedStatement::queryInterface( const Type & rType ) +{ + Any aRet = OStatement_Base::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_Base::getTypes()); +} + +Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData( ) +{ + if(!m_xMetaData.is() && m_RecordSet.IsValid()) + m_xMetaData = new OResultSetMetaData(m_RecordSet); + return m_xMetaData; +} + +void OPreparedStatement::disposing() +{ + m_xMetaData.clear(); + if (m_pParameters) + { + m_pParameters->Release(); + m_pParameters = nullptr; + } + OStatement_Base::disposing(); +} + +void SAL_CALL OPreparedStatement::close( ) +{ + + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + } + dispose(); + +} + +sal_Bool SAL_CALL OPreparedStatement::execute( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + SQLWarning warning; + clearWarnings (); + + // Call SQLExecute + try { + ADORecordset* pSet=nullptr; + CHECK_RETURN(m_Command.Execute(m_RecordsAffected,m_Parameters,adCmdUnknown,&pSet)) + m_RecordSet = WpADORecordset(pSet); + } + catch (SQLWarning& ex) + { + // Save pointer to warning and save with ResultSet + // object once it is created. + + warning = ex; + } + return m_RecordSet.IsValid(); +} + +sal_Int32 SAL_CALL OPreparedStatement::executeUpdate( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + ADORecordset* pSet=nullptr; + CHECK_RETURN(m_Command.Execute(m_RecordsAffected,m_Parameters,adCmdUnknown,&pSet)) + if ( VT_ERROR == m_RecordsAffected.getType() ) + { + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + // to be sure that we get the error really thrown + throw SQLException(); + } + m_RecordSet = WpADORecordset(pSet); + return m_RecordsAffected.getInt32(); +} + +void OPreparedStatement::setParameter(sal_Int32 parameterIndex, const DataTypeEnum& _eType, + sal_Int32 _nSize,const OLEVariant& Val) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + sal_Int32 nCount = 0; + m_pParameters->get_Count(&nCount); + if(nCount < (parameterIndex-1)) + { + OUString sDefaultName = "parame" + OUString::number(parameterIndex); + ADOParameter* pParam = m_Command.CreateParameter(sDefaultName,_eType,adParamInput,_nSize,Val); + if(pParam) + { + m_pParameters->Append(pParam); + } + } + else + { + ADOParameter* pParam = nullptr; + m_pParameters->get_Item(OLEVariant(sal_Int32(parameterIndex-1)),&pParam); + WpADOParameter aParam(pParam); + if(pParam) + { + DataTypeEnum eType = aParam.GetADOType(); + if ( _eType != eType && _eType != adDBTimeStamp ) + { + aParam.put_Type(_eType); + eType = _eType; + aParam.put_Size(_nSize); + } + + if ( adVarBinary == eType && aParam.GetAttributes() == adParamLong ) + { + aParam.AppendChunk(Val); + } + else + CHECK_RETURN(aParam.PutValue(Val)); + } + } + ADOS::ThrowException(*m_pConnection->getConnection(),*this); +} + +void SAL_CALL OPreparedStatement::setString( sal_Int32 parameterIndex, const OUString& x ) +{ + setParameter( parameterIndex, adLongVarWChar, std::numeric_limits< sal_Int32 >::max(), x ); +} + +Reference< XConnection > SAL_CALL OPreparedStatement::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + return static_cast<Reference< XConnection >>(m_pConnection); +} + +Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + // first clear the old things + m_xMetaData.clear(); + disposeResultSet(); + if(m_RecordSet.IsValid()) + m_RecordSet.Close(); + m_RecordSet.clear(); + + // then create the new ones + m_RecordSet.Create(); + OLEVariant aCmd; + aCmd.setIDispatch(m_Command); + OLEVariant aCon; + aCon.setNoArg(); + CHECK_RETURN(m_RecordSet.put_CacheSize(m_nFetchSize)) + CHECK_RETURN(m_RecordSet.put_MaxRecords(m_nMaxRows)) + CHECK_RETURN(m_RecordSet.Open(aCmd,aCon,m_eCursorType,m_eLockType,adOpenUnspecified)) + CHECK_RETURN(m_RecordSet.get_CacheSize(m_nFetchSize)) + CHECK_RETURN(m_RecordSet.get_MaxRecords(m_nMaxRows)) + CHECK_RETURN(m_RecordSet.get_CursorType(m_eCursorType)) + CHECK_RETURN(m_RecordSet.get_LockType(m_eLockType)) + + OResultSet* pSet = new OResultSet(m_RecordSet,this); + Reference< XResultSet > xRs = pSet; + pSet->construct(); + pSet->setMetaData(getMetaData()); + m_xResultSet = WeakReference<XResultSet>(xRs); + + return xRs; +} + +void SAL_CALL OPreparedStatement::setBoolean( sal_Int32 parameterIndex, sal_Bool x ) +{ + setParameter(parameterIndex,adBoolean,sizeof(x),bool(x)); +} + +void SAL_CALL OPreparedStatement::setByte( sal_Int32 parameterIndex, sal_Int8 x ) +{ + setParameter(parameterIndex,adTinyInt,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setDate( sal_Int32 parameterIndex, const Date& x ) +{ + setParameter(parameterIndex,adDBDate,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setTime( sal_Int32 parameterIndex, const css::util::Time& x ) +{ + setParameter(parameterIndex,adDBTime,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setTimestamp( sal_Int32 parameterIndex, const DateTime& x ) +{ + setParameter(parameterIndex,adDBTimeStamp,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setDouble( sal_Int32 parameterIndex, double x ) +{ + setParameter(parameterIndex,adDouble,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setFloat( sal_Int32 parameterIndex, float x ) +{ + setParameter(parameterIndex,adSingle,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setInt( sal_Int32 parameterIndex, sal_Int32 x ) +{ + setParameter(parameterIndex,adInteger,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setLong( sal_Int32 parameterIndex, sal_Int64 x ) +{ + setParameter(parameterIndex,adBigInt,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setNull( sal_Int32 parameterIndex, sal_Int32 /*sqlType*/ ) +{ + OLEVariant aVal; + aVal.setNull(); + setParameter(parameterIndex,adEmpty,0,aVal); +} + +void SAL_CALL OPreparedStatement::setClob( sal_Int32 /*parameterIndex*/, const Reference< XClob >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRowUpdate::setClob", *this ); +} + +void SAL_CALL OPreparedStatement::setBlob( sal_Int32 /*parameterIndex*/, const Reference< XBlob >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRowUpdate::setBlob", *this ); +} + +void SAL_CALL OPreparedStatement::setArray( sal_Int32 /*parameterIndex*/, const Reference< XArray >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRowUpdate::setArray", *this ); +} + +void SAL_CALL OPreparedStatement::setRef( sal_Int32 /*parameterIndex*/, const Reference< XRef >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRowUpdate::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); + } +} + +void SAL_CALL OPreparedStatement::setShort( sal_Int32 parameterIndex, sal_Int16 x ) +{ + setParameter(parameterIndex,adSmallInt,sizeof(x),x); +} + +void SAL_CALL OPreparedStatement::setBytes( sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x ) +{ + setParameter(parameterIndex,adVarBinary,sizeof(sal_Int8)*x.getLength(),x); +} + +void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 /*parameterIndex*/, const Reference< css::io::XInputStream >& /*x*/, sal_Int32 /*length*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setCharacterStream", *this ); +} + +void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + if(x.is()) + { + Sequence< sal_Int8 > aData; + x->readBytes(aData,length); + setBytes(parameterIndex,aData); + } +} + +void SAL_CALL OPreparedStatement::clearParameters( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + if(m_pParameters) + { + sal_Int32 nCount = 0; + m_pParameters->get_Count(&nCount); + OLEVariant aVal; + aVal.setEmpty(); + for(sal_Int32 i=0;i<nCount;++i) + { + ADOParameter* pParam = nullptr; + m_pParameters->get_Item(OLEVariant(i),&pParam); + WpADOParameter aParam(pParam); + if(pParam) + { + CHECK_RETURN(aParam.PutValue(aVal)); + } + } + } +} + +void SAL_CALL OPreparedStatement::acquire() throw() +{ + OStatement_Base::acquire(); +} + +void SAL_CALL OPreparedStatement::release() throw() +{ + OStatement_Base::release(); +} + +void OPreparedStatement::replaceParameterNodeName(OSQLParseNode const * _pNode, + const OUString& _sDefaultName, + sal_Int32& _rParameterCount) +{ + sal_Int32 nCount = _pNode->count(); + for(sal_Int32 i=0;i < nCount;++i) + { + OSQLParseNode* pChildNode = _pNode->getChild(i); + if(SQL_ISRULE(pChildNode,parameter) && pChildNode->count() == 1) + { + OSQLParseNode* pNewNode = new OSQLParseNode(OUString(":") ,SQLNodeType::Punctuation,0); + delete pChildNode->replace(pChildNode->getChild(0),pNewNode); + OUString sParameterName = _sDefaultName + OUString::number(++_rParameterCount); + pChildNode->append(new OSQLParseNode( sParameterName,SQLNodeType::Name,0)); + } + else + replaceParameterNodeName(pChildNode,_sDefaultName,_rParameterCount); + + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AResultSet.cxx b/connectivity/source/drivers/ado/AResultSet.cxx new file mode 100644 index 000000000..dae0f91ef --- /dev/null +++ b/connectivity/source/drivers/ado/AResultSet.cxx @@ -0,0 +1,1155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AResultSet.hxx> +#include <ado/AResultSetMetaData.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbc/IndexType.hpp> +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <comphelper/property.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <comphelper/seqstream.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <comphelper/types.hxx> + +using namespace ::comphelper; + + +#include <oledb.h> + +#define CHECK_RETURN(x) \ + if(!SUCCEEDED(x)) \ + ADOS::ThrowException(*m_pStmt->m_pConnection->getConnection(),*this); + +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + + +// IMPLEMENT_SERVICE_INFO(OResultSet,"com.sun.star.sdbcx.AResultSet","com.sun.star.sdbc.ResultSet"); +OUString SAL_CALL OResultSet::getImplementationName( ) +{ + return "com.sun.star.sdbcx.ado.ResultSet"; +} + +css::uno::Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames( ) +{ + return {"com.sun.star.sdbc.ResultSet","com.sun.star.sdbcx.ResultSet"}; +} + +sal_Bool SAL_CALL OResultSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +OResultSet::OResultSet(ADORecordset* _pRecordSet,OStatement_Base* pStmt) : OResultSet_BASE(m_aMutex) + ,OPropertySetHelper(OResultSet_BASE::rBHelper) + ,m_pRecordSet(_pRecordSet) + ,m_pStmt(pStmt) + ,m_xStatement(*pStmt) + ,m_nRowPos(0) + ,m_bEOF(false) + ,m_bOnFirstAfterOpen(false) +{ +} + +OResultSet::OResultSet(ADORecordset* _pRecordSet) : OResultSet_BASE(m_aMutex) + ,OPropertySetHelper(OResultSet_BASE::rBHelper) + ,m_pRecordSet(_pRecordSet) + ,m_pStmt(nullptr) + ,m_nRowPos(0) + ,m_bEOF(false) + ,m_bOnFirstAfterOpen(false) +{ +} + +void OResultSet::construct() +{ + osl_atomic_increment( &m_refCount ); + if (!m_pRecordSet) + { + OSL_FAIL( "OResultSet::construct: no RecordSet!" ); + Reference< XInterface > xInt( *this ); + osl_atomic_decrement( &m_refCount ); + ::dbtools::throwFunctionSequenceException( xInt ); + } + m_pRecordSet->AddRef(); + VARIANT_BOOL bIsAtBOF; + CHECK_RETURN(m_pRecordSet->get_BOF(&bIsAtBOF)) + m_bOnFirstAfterOpen = bIsAtBOF != VARIANT_TRUE; + osl_atomic_decrement( &m_refCount ); +} + +OResultSet::~OResultSet() +{ + if(m_pRecordSet) + m_pRecordSet->Release(); +} + +void OResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + if(m_pRecordSet) + m_pRecordSet->Close(); + m_xStatement.clear(); + m_xMetaData.clear(); +} + +Any SAL_CALL OResultSet::queryInterface( const Type & rType ) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + return aRet.hasValue() ? aRet : OResultSet_BASE::queryInterface(rType); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL OResultSet::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(),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 +} +#define BLOCK_SIZE 256 + +Reference< css::io::XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + WpADOField aField = ADOS::getField(m_pRecordSet,columnIndex); + + if((aField.GetAttributes() & adFldLong) == adFldLong) + { + //Copy the data only up to the Actual Size of Field. + sal_Int32 nSize = aField.GetActualSize(); + Sequence<sal_Int8> aData(nSize); + long index = 0; + while(index < nSize) + { + m_aValue = aField.GetChunk(BLOCK_SIZE); + if(m_aValue.isNull()) + break; + UCHAR chData; + for(long index2 = 0;index2 < BLOCK_SIZE;++index2) + { + HRESULT hr = ::SafeArrayGetElement(m_aValue.parray,&index2,&chData); + if(SUCCEEDED(hr)) + { + //Take BYTE by BYTE and advance Memory Location + aData.getArray()[index++] = chData; + } + else + break; + } + } + + return new ::comphelper::SequenceInputStream(aData); + } + // else we ask for a bytesequence + aField.get_Value(m_aValue); + + return m_aValue.isNull() ? nullptr : new ::comphelper::SequenceInputStream(m_aValue.getByteSequence()); +} + +Reference< css::io::XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getCharacterStream", *this ); + return nullptr; +} + +OLEVariant OResultSet::getValue(sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + WpADOField aField = ADOS::getField(m_pRecordSet,columnIndex); + aField.get_Value(m_aValue); + return m_aValue; +} + +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).getByteSequence(); +} + + +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); + + + PositionEnum_Param aPos; + m_pRecordSet->get_AbsolutePosition(&aPos); + return (aPos > 0) ? static_cast<sal_Int32>(aPos) : m_nRowPos; + // return the rowcount from driver if the driver doesn't support this return our count +} + + +sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getLong", *this ); + return sal_Int64(0); +} + + +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_pRecordSet); + return m_xMetaData; +} + +Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getArray", *this ); + return nullptr; +} + + +Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getClob", *this ); + return nullptr; +} + +Reference< XBlob > SAL_CALL OResultSet::getBlob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getBlob", *this ); + return nullptr; +} + + +Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getRef", *this ); + 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); + + + VARIANT_BOOL bIsAtEOF; + CHECK_RETURN(m_pRecordSet->get_EOF(&bIsAtEOF)) + return bIsAtEOF == VARIANT_TRUE; +} + +sal_Bool SAL_CALL OResultSet::isFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_nRowPos == 1; +} + +sal_Bool SAL_CALL OResultSet::isLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return true; +} + +void SAL_CALL OResultSet::beforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(first()) + m_bOnFirstAfterOpen = !previous(); +} + +void SAL_CALL OResultSet::afterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(last()) + next(); + m_bEOF = true; +} + + +void SAL_CALL OResultSet::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + } + dispose(); +} + + +sal_Bool SAL_CALL OResultSet::first( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(SUCCEEDED(m_pRecordSet->MoveFirst())) + { + m_nRowPos = 1; + m_bOnFirstAfterOpen = false; + return true; + } + return false; +} + + +sal_Bool SAL_CALL OResultSet::last( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + bool bRet = SUCCEEDED(m_pRecordSet->MoveLast()); + if(bRet) + { + m_pRecordSet->get_RecordCount(&m_nRowPos); + m_bOnFirstAfterOpen = false; + } + return bRet; +} + +sal_Bool SAL_CALL OResultSet::absolute( sal_Int32 row ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(!row) // absolute with zero not allowed + ::dbtools::throwFunctionSequenceException(*this); + + bool bCheck = true; + if(row < 0) + { + bCheck = SUCCEEDED(m_pRecordSet->MoveLast()); + if ( bCheck ) + { + while(++row < 0 && bCheck) + bCheck = SUCCEEDED(m_pRecordSet->MovePrevious()); + } + } + else + { + first(); + OLEVariant aEmpty; + aEmpty.setNoArg(); + bCheck = SUCCEEDED(m_pRecordSet->Move(row-1,aEmpty)); // move to row -1 because we stand already on the first + if(bCheck) + m_nRowPos = row; + } + if(bCheck) + m_bOnFirstAfterOpen = false; + return bCheck; +} + +sal_Bool SAL_CALL OResultSet::relative( sal_Int32 row ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + OLEVariant aEmpty; + aEmpty.setNoArg(); + sal_Int32 nNewPos = row; + if ( m_bOnFirstAfterOpen && nNewPos > 0 ) + --nNewPos; + bool bRet = SUCCEEDED(m_pRecordSet->Move(row,aEmpty)); + if(bRet) + { + m_nRowPos += row; + m_bOnFirstAfterOpen = false; + } + return bRet; +} + +sal_Bool SAL_CALL OResultSet::previous( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + bool bRet = SUCCEEDED(m_pRecordSet->MovePrevious()); + if(bRet) + { + --m_nRowPos; + m_bOnFirstAfterOpen = false; + } + return bRet; +} + +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); + + + sal_Int32 eRec; + m_pRecordSet->get_Status(&eRec); + bool bRet = (RecordStatusEnum(eRec) & adRecDeleted) == adRecDeleted; + if(bRet) + --m_nRowPos; + return bRet; +} + +sal_Bool SAL_CALL OResultSet::rowInserted( ) +{ ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + sal_Int32 eRec; + m_pRecordSet->get_Status(&eRec); + bool bRet = (RecordStatusEnum(eRec) & adRecNew) == adRecNew; + if(bRet) + ++m_nRowPos; + return bRet; +} + +sal_Bool SAL_CALL OResultSet::rowUpdated( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + sal_Int32 eRec; + m_pRecordSet->get_Status(&eRec); + return (RecordStatusEnum(eRec) & adRecModified) == adRecModified; +} + + +sal_Bool SAL_CALL OResultSet::isBeforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + OSL_ENSURE(!m_nRowPos,"OResultSet::isBeforeFirst: Error in setting m_nRowPos!"); + VARIANT_BOOL bIsAtBOF = VARIANT_TRUE; + if(!m_bOnFirstAfterOpen) + { + OSL_ENSURE(!m_nRowPos,"OResultSet::isBeforeFirst: Error in setting m_nRowPos!"); + m_pRecordSet->get_BOF(&bIsAtBOF); + } + return bIsAtBOF == VARIANT_TRUE; +} + + +sal_Bool SAL_CALL OResultSet::next( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + bool bRet = true; + if(m_bOnFirstAfterOpen) + { + m_bOnFirstAfterOpen = false; + ++m_nRowPos; + } + else + { + bRet = SUCCEEDED(m_pRecordSet->MoveNext()); + + if(bRet) + { + VARIANT_BOOL bIsAtEOF; + CHECK_RETURN(m_pRecordSet->get_EOF(&bIsAtEOF)) + bRet = bIsAtEOF != VARIANT_TRUE; + ++m_nRowPos; + } + else + ADOS::ThrowException(*m_pStmt->m_pConnection->getConnection(),*this); + } + + return bRet; +} + + +sal_Bool SAL_CALL OResultSet::wasNull( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_aValue.isNull(); +} + + +void SAL_CALL OResultSet::cancel( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_pRecordSet->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); + + + OLEVariant aEmpty; + aEmpty.setNoArg(); + m_pRecordSet->AddNew(aEmpty,aEmpty); +} + +void SAL_CALL OResultSet::updateRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + OLEVariant aEmpty; + aEmpty.setNoArg(); + m_pRecordSet->Update(aEmpty,aEmpty); +} + +void SAL_CALL OResultSet::deleteRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_pRecordSet->Delete(); + m_pRecordSet->UpdateBatch(adAffectCurrent); +} + + +void SAL_CALL OResultSet::cancelRowUpdates( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_pRecordSet->CancelUpdate(); +} + + +void SAL_CALL OResultSet::moveToInsertRow( ) +{ + // ::osl::MutexGuard aGuard( m_aMutex ); + //checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + // if ( getResultSetConcurrency() == ResultSetConcurrency::READ_ONLY ) + // throw SQLException(); +} + + +void SAL_CALL OResultSet::moveToCurrentRow( ) +{ +} + +void OResultSet::updateValue(sal_Int32 columnIndex,const OLEVariant& x) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + WpADOField aField = ADOS::getField(m_pRecordSet,columnIndex); + aField.PutValue(x); +} + +void SAL_CALL OResultSet::updateNull( sal_Int32 columnIndex ) +{ + OLEVariant x; + x.setNull(); + updateValue(columnIndex,x); +} + + +void SAL_CALL OResultSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x ) +{ + updateValue(columnIndex,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 ) +{ + updateValue(columnIndex,x); +} + +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 ) +{ + if(!x.is()) + ::dbtools::throwFunctionSequenceException(*this); + + Sequence<sal_Int8> aSeq; + x->readBytes(aSeq,length); + updateBytes(columnIndex,aSeq); +} + +void SAL_CALL OResultSet::updateCharacterStream( sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + if(!x.is()) + ::dbtools::throwFunctionSequenceException(*this); + + Sequence<sal_Int8> aSeq; + x->readBytes(aSeq,length); + updateBytes(columnIndex,aSeq); +} + +void SAL_CALL OResultSet::refreshRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_pRecordSet->Resync(adAffectCurrent); +} + +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(); +} + +// XRowLocate +Any SAL_CALL OResultSet::getBookmark( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if(m_nRowPos < static_cast<sal_Int32>(m_aBookmarks.size())) // this bookmark was already fetched + return makeAny(sal_Int32(m_nRowPos-1)); + + OLEVariant aVar; + m_pRecordSet->get_Bookmark(&aVar); + m_aBookmarks.push_back(aVar); + return makeAny(static_cast<sal_Int32>(m_aBookmarks.size()-1)); + +} + +sal_Bool SAL_CALL OResultSet::moveToBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + sal_Int32 nPos = 0; + bookmark >>= nPos; + OSL_ENSURE(nPos >= 0 && nPos < static_cast<sal_Int32>(m_aBookmarks.size()),"Invalid Index for vector"); + if(nPos < 0 || nPos >= static_cast<sal_Int32>(m_aBookmarks.size())) + ::dbtools::throwFunctionSequenceException(*this); + + return SUCCEEDED(m_pRecordSet->Move(0,m_aBookmarks[nPos])); +} + +sal_Bool SAL_CALL OResultSet::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + sal_Int32 nPos = 0; + bookmark >>= nPos; + nPos += rows; + OSL_ENSURE(nPos >= 0 && nPos < static_cast<sal_Int32>(m_aBookmarks.size()),"Invalid Index for vector"); + if(nPos < 0 || nPos >= static_cast<sal_Int32>(m_aBookmarks.size())) + ::dbtools::throwFunctionSequenceException(*this); + return SUCCEEDED(m_pRecordSet->Move(rows,m_aBookmarks[nPos])); +} + +sal_Int32 SAL_CALL OResultSet::compareBookmarks( const Any& bookmark1, const Any& bookmark2 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nPos1 = 0; + bookmark1 >>= nPos1; + sal_Int32 nPos2 = 0; + bookmark2 >>= nPos2; + if(nPos1 == nPos2) // they should be equal + return css::sdbcx::CompareBookmark::EQUAL; + + OSL_ENSURE((nPos1 >= 0 && nPos1 < static_cast<sal_Int32>(m_aBookmarks.size())) || (nPos1 >= 0 && nPos2 < static_cast<sal_Int32>(m_aBookmarks.size())),"Invalid Index for vector"); + + CompareEnum eNum; + m_pRecordSet->CompareBookmarks(m_aBookmarks[nPos1],m_aBookmarks[nPos2],&eNum); + return static_cast<sal_Int32>(eNum) - 1; +} + +sal_Bool SAL_CALL OResultSet::hasOrderedBookmarks( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + ADOProperties* pProps = nullptr; + m_pRecordSet->get_Properties(&pProps); + WpADOProperties aProps; + aProps.setWithOutAddRef(pProps); + ADOS::ThrowException(*static_cast<OConnection*>(m_pStmt->getConnection().get())->getConnection(),*this); + OSL_ENSURE(aProps.IsValid(),"There are no properties at the connection"); + + WpADOProperty aProp(aProps.GetItem(OUString("Bookmarks Ordered"))); + OLEVariant aVar; + if(aProp.IsValid()) + aVar = aProp.GetValue(); + else + ADOS::ThrowException(*static_cast<OConnection*>(m_pStmt->getConnection().get())->getConnection(),*this); + + bool bValue(false); + if(!aVar.isNull() && !aVar.isEmpty()) + bValue = aVar.getBool(); + return bValue; +} + +sal_Int32 SAL_CALL OResultSet::hashBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + sal_Int32 nPos = 0; + bookmark >>= nPos; + return nPos; +} + +// XDeleteRows +Sequence< sal_Int32 > SAL_CALL OResultSet::deleteRows( const Sequence< Any >& rows ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + OLEVariant aVar; + sal_Int32 nPos = 0; + + // Create SafeArray Bounds and initialize the array + SAFEARRAYBOUND rgsabound[1]; + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = rows.getLength(); + SAFEARRAY *psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + const Any* pBegin = rows.getConstArray(); + const Any* pEnd = pBegin + rows.getLength(); + for(sal_Int32 i=0;pBegin != pEnd ;++pBegin,++i) + { + *pBegin >>= nPos; + SafeArrayPutElement(psa,&i,&m_aBookmarks[nPos]); + } + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + m_pRecordSet->put_Filter(vsa); + m_pRecordSet->Delete(adAffectGroup); + m_pRecordSet->UpdateBatch(adAffectGroup); + + Sequence< sal_Int32 > aSeq(rows.getLength()); + if(first()) + { + sal_Int32* pSeq = aSeq.getArray(); + sal_Int32 i=0; + do + { + OSL_ENSURE(i<aSeq.getLength(),"Index greater than length of sequence"); + m_pRecordSet->get_Status(&pSeq[i]); + if(pSeq[i++] == adRecDeleted) + --m_nRowPos; + } + while(next()); + } + return aSeq; +} + +sal_Int32 OResultSet::getResultSetConcurrency() const +{ + sal_Int32 nValue=ResultSetConcurrency::READ_ONLY; + LockTypeEnum eRet; + if(!SUCCEEDED(m_pRecordSet->get_LockType(&eRet))) + { + switch(eRet) + { + case adLockReadOnly: + nValue = ResultSetConcurrency::READ_ONLY; + break; + default: + nValue = ResultSetConcurrency::UPDATABLE; + break; + } + } + return nValue; +} + +sal_Int32 OResultSet::getResultSetType() const +{ + sal_Int32 nValue=0; + CursorTypeEnum eRet; + if(!SUCCEEDED(m_pRecordSet->get_CursorType(&eRet))) + { + switch(eRet) + { + case adOpenUnspecified: + case adOpenForwardOnly: + nValue = ResultSetType::FORWARD_ONLY; + break; + case adOpenStatic: + case adOpenKeyset: + nValue = ResultSetType::SCROLL_INSENSITIVE; + break; + case adOpenDynamic: + nValue = ResultSetType::SCROLL_SENSITIVE; + break; + } + } + return nValue; +} + +sal_Int32 OResultSet::getFetchDirection() +{ + return FetchDirection::FORWARD; +} + +sal_Int32 OResultSet::getFetchSize() const +{ + sal_Int32 nValue=-1; + m_pRecordSet->get_CacheSize(&nValue); + return nValue; +} + +OUString OResultSet::getCursorName() +{ + return OUString(); +} + + +void OResultSet::setFetchDirection(sal_Int32 /*_par0*/) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "ResultSet::FetchDirection", *this ); +} + +void OResultSet::setFetchSize(sal_Int32 _par0) +{ + m_pRecordSet->put_CacheSize(_par0); +} + +::cppu::IPropertyArrayHelper* OResultSet::createArrayHelper( ) const +{ + Sequence< css::beans::Property > aProps(5); + css::beans::Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), + PROPERTY_ID_ISBOOKMARKABLE, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + +::cppu::IPropertyArrayHelper & OResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OResultSet::convertFastPropertyValue( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + switch(nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + break; + case PROPERTY_ID_FETCHDIRECTION: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection()); + case PROPERTY_ID_FETCHSIZE: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize()); + default: + ; + } + return false; +} + +void OResultSet::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + switch(nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw Exception("cannot set prop " + OUString::number(nHandle), nullptr); + break; + case PROPERTY_ID_FETCHDIRECTION: + setFetchDirection(getINT32(rValue)); + break; + case PROPERTY_ID_FETCHSIZE: + setFetchSize(getINT32(rValue)); + break; + default: + ; + } +} + +void OResultSet::getFastPropertyValue(Any& rValue,sal_Int32 nHandle) const +{ + switch(nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + { + VARIANT_BOOL bBool; + m_pRecordSet->Supports(adBookmark,&bBool); + rValue <<= (bBool == VARIANT_TRUE); + } + break; + case PROPERTY_ID_CURSORNAME: + rValue <<= getCursorName(); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= getResultSetConcurrency(); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= getResultSetType(); + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= getFetchDirection(); + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= getFetchSize(); + break; + } +} + +void SAL_CALL OResultSet::acquire() throw() +{ + OResultSet_BASE::acquire(); +} + +void SAL_CALL OResultSet::release() throw() +{ + OResultSet_BASE::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AResultSetMetaData.cxx b/connectivity/source/drivers/ado/AResultSetMetaData.cxx new file mode 100644 index 000000000..23983342e --- /dev/null +++ b/connectivity/source/drivers/ado/AResultSetMetaData.cxx @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AResultSetMetaData.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <ado/Awrapado.hxx> +#include <connectivity/dbexception.hxx> + +using namespace connectivity; +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + +OResultSetMetaData::OResultSetMetaData( ADORecordset* _pRecordSet) + : m_pRecordSet(_pRecordSet), + m_nColCount(-1) +{ + if ( m_pRecordSet ) + m_pRecordSet->AddRef(); +} + +OResultSetMetaData::~OResultSetMetaData() +{ + if ( m_pRecordSet ) + m_pRecordSet->Release(); +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnDisplaySize( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid() && aField.GetActualSize() != -1) + return aField.GetActualSize(); + return 0; +} + + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnType( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + return ADOS::MapADOType2Jdbc(aField.GetADOType()); +} + + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount( ) +{ + if(m_nColCount != -1 ) + return m_nColCount; + + if ( !m_pRecordSet ) + return 0; + + ADOFields* pFields = nullptr; + m_pRecordSet->get_Fields(&pFields); + WpOLEAppendCollection<ADOFields, ADOField, WpADOField> aFields(pFields); + m_nColCount = aFields.GetItemCount(); + return m_nColCount; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive( sal_Int32 column ) +{ + bool bRet = false; + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if ( aField.IsValid() ) + { + WpADOProperties aProps( aField.get_Properties() ); + if ( aProps.IsValid() ) + bRet = OTools::getValue( aProps, OUString("ISCASESENSITIVE") ).getBool(); + } + return bRet; +} + + +OUString SAL_CALL OResultSetMetaData::getSchemaName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + + +OUString SAL_CALL OResultSetMetaData::getColumnName( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid()) + return aField.GetName(); + + return OUString(); +} + +OUString SAL_CALL OResultSetMetaData::getTableName( sal_Int32 column ) +{ + OUString sTableName; + + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if ( aField.IsValid() ) + { + WpADOProperties aProps( aField.get_Properties() ); + if ( aProps.IsValid() ) + sTableName = OTools::getValue( aProps, OUString("BASETABLENAME") ).getString(); + } + return sTableName; +} + +OUString SAL_CALL OResultSetMetaData::getCatalogName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +OUString SAL_CALL OResultSetMetaData::getColumnTypeName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + +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 ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid()) + { + return ((aField.GetAttributes() & adFldFixed) == adFldFixed) && (aField.GetADOType() == adCurrency); + } + return false; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isAutoIncrement( sal_Int32 column ) +{ + bool bRet = false; + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if ( aField.IsValid() ) + { + WpADOProperties aProps( aField.get_Properties() ); + if ( aProps.IsValid() ) + { + bRet = OTools::getValue( aProps, OUString("ISAUTOINCREMENT") ).getBool(); + } + } + return bRet; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isSigned( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid()) + { + DataTypeEnum eType = aField.GetADOType(); + return !(eType == adUnsignedBigInt || eType == adUnsignedInt || eType == adUnsignedSmallInt || eType == adUnsignedTinyInt); + } + return false; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getPrecision( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid()) + return aField.GetPrecision(); + return 0; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getScale( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid()) + return aField.GetNumericScale(); + return 0; +} + + +sal_Int32 SAL_CALL OResultSetMetaData::isNullable( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid()) + { + return sal_Int32((aField.GetAttributes() & adFldIsNullable) == adFldIsNullable); + } + return sal_Int32(false); +} + + +sal_Bool SAL_CALL OResultSetMetaData::isSearchable( sal_Int32 /*column*/ ) +{ + return true; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isReadOnly( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid()) + { + // return (aField.GetStatus() & adFieldReadOnly) == adFieldReadOnly; + } + return false; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isDefinitelyWritable( sal_Int32 column ) +{ + WpADOField aField = ADOS::getField(m_pRecordSet,column); + if(aField.IsValid()) + { + return (aField.GetAttributes() & adFldUpdatable) == adFldUpdatable; + } + return false; +; +} + +sal_Bool SAL_CALL OResultSetMetaData::isWritable( sal_Int32 column ) +{ + return isDefinitelyWritable(column); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AStatement.cxx b/connectivity/source/drivers/ado/AStatement.cxx new file mode 100644 index 000000000..01fea934c --- /dev/null +++ b/connectivity/source/drivers/ado/AStatement.cxx @@ -0,0 +1,832 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AStatement.hxx> +#include <ado/AConnection.hxx> +#include <ado/AResultSet.hxx> +#include <comphelper/property.hxx> +#include <osl/thread.h> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <connectivity/dbexception.hxx> +#include <comphelper/types.hxx> + +#undef max + +#include <algorithm> +#include <numeric> + +using namespace ::comphelper; + +#define CHECK_RETURN(x) \ + if(!x) \ + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + + +using namespace connectivity::ado; + + +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 ::std; + +OStatement_Base::OStatement_Base(OConnection* _pConnection ) : OStatement_BASE(m_aMutex) + ,OPropertySetHelper(OStatement_BASE::rBHelper) + ,m_pConnection(_pConnection) + ,m_nMaxRows(0) + ,m_nFetchSize(1) + ,m_eLockType(adLockReadOnly) + ,m_eCursorType(adOpenForwardOnly) +{ + osl_atomic_increment( &m_refCount ); + + m_Command.Create(); + if(m_Command.IsValid()) + m_Command.putref_ActiveConnection(m_pConnection->getConnection()); + else + ADOS::ThrowException(*m_pConnection->getConnection(),*this); + + m_RecordsAffected.setNoArg(); + m_Parameters.setNoArg(); + + m_pConnection->acquire(); + + osl_atomic_decrement( &m_refCount ); +} + +void OStatement_Base::disposeResultSet() +{ + // free the cursor if alive + Reference< XComponent > xComp(m_xResultSet.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + m_xResultSet.clear(); +} + + +void OStatement_Base::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + + disposeResultSet(); + + if ( m_Command.IsValid() ) + m_Command.putref_ActiveConnection( nullptr ); + m_Command.clear(); + + if ( m_RecordSet.IsValid() ) + m_RecordSet.PutRefDataSource( nullptr ); + m_RecordSet.clear(); + + if (m_pConnection) + m_pConnection->release(); + + OStatement_BASE::disposing(); +} + +void SAL_CALL OStatement_Base::release() throw() +{ + OStatement_BASE::release(); +} + +Any SAL_CALL OStatement_Base::queryInterface( const Type & rType ) +{ + Any aRet = OStatement_BASE::queryInterface(rType); + return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType); +} + +css::uno::Sequence< css::uno::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( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + CHECK_RETURN(m_Command.Cancel()) +} + + +void SAL_CALL OStatement_Base::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + } + dispose(); +} + + +void SAL_CALL OStatement::clearBatch( ) +{ + +} + + +void OStatement_Base::reset() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + clearWarnings (); + + if (m_xResultSet.get().is()) + clearMyResultSet(); +} + +// clearMyResultSet +// If a ResultSet was created for this Statement, close it + + +void OStatement_Base::clearMyResultSet () +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + try + { + Reference<XCloseable> xCloseable( + m_xResultSet.get(), css::uno::UNO_QUERY); + if ( xCloseable.is() ) + xCloseable->close(); + } + catch( const DisposedException& ) { } + + m_xResultSet.clear(); +} + +sal_Int32 OStatement_Base::getRowCount () +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + return m_RecordsAffected.getInt32(); +} + +// getPrecision +// Given a SQL type, return the maximum precision for the column. +// Returns -1 if not known + + +sal_Int32 OStatement_Base::getPrecision ( sal_Int32 sqlType) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + sal_Int32 prec = -1; + OTypeInfo aInfo; + aInfo.nType = static_cast<sal_Int16>(sqlType); + if (!m_aTypeInfo.empty()) + { + std::vector<OTypeInfo>::const_iterator aIter = std::find(m_aTypeInfo.begin(),m_aTypeInfo.end(),aInfo); + for(;aIter != m_aTypeInfo.end();++aIter) + { + prec = std::max(prec,(*aIter).nPrecision); + } + } + + return prec; +} + +// setWarning +// Sets the warning + + +void OStatement_Base::setWarning (const SQLWarning &ex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_aLastWarning = ex; +} + +void OStatement_Base::assignRecordSet( ADORecordset* _pRS ) +{ + WpADORecordset aOldRS( m_RecordSet ); + m_RecordSet = WpADORecordset( _pRS ); + + if ( aOldRS.IsValid() ) + aOldRS.PutRefDataSource( nullptr ); + + if ( m_RecordSet.IsValid() ) + m_RecordSet.PutRefDataSource( static_cast<IDispatch*>(m_Command) ); +} + +sal_Bool SAL_CALL OStatement_Base::execute( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + // Reset the statement handle and warning + + reset(); + + try + { + ADORecordset* pSet = nullptr; + CHECK_RETURN(m_Command.put_CommandText(sql)) + CHECK_RETURN(m_Command.Execute(m_RecordsAffected,m_Parameters,adCmdText,&pSet)) + + assignRecordSet( pSet ); + } + catch (SQLWarning& ex) + { + + // Save pointer to warning and save with ResultSet + // object once it is created. + + m_aLastWarning = ex; + } + + return m_RecordSet.IsValid(); +} + +Reference< XResultSet > SAL_CALL OStatement_Base::executeQuery( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + reset(); + + m_xResultSet = WeakReference<XResultSet>(nullptr); + + WpADORecordset aSet; + aSet.Create(); + CHECK_RETURN(m_Command.put_CommandText(sql)) + OLEVariant aCmd; + aCmd.setIDispatch(m_Command); + OLEVariant aCon; + aCon.setNoArg(); + CHECK_RETURN(aSet.put_CacheSize(m_nFetchSize)) + CHECK_RETURN(aSet.put_MaxRecords(m_nMaxRows)) + CHECK_RETURN(aSet.Open(aCmd,aCon,m_eCursorType,m_eLockType,adOpenUnspecified)) + + + CHECK_RETURN(aSet.get_CacheSize(m_nFetchSize)) + CHECK_RETURN(aSet.get_MaxRecords(m_nMaxRows)) + CHECK_RETURN(aSet.get_CursorType(m_eCursorType)) + CHECK_RETURN(aSet.get_LockType(m_eLockType)) + + OResultSet* pSet = new OResultSet(aSet,this); + Reference< XResultSet > xRs = pSet; + pSet->construct(); + + m_xResultSet = WeakReference<XResultSet>(xRs); + + return xRs; +} + + +Reference< XConnection > SAL_CALL OStatement_Base::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + return static_cast<Reference< XConnection >>(m_pConnection); +} + + +Any SAL_CALL OStatement::queryInterface( const Type & rType ) +{ + Any aRet = ::cppu::queryInterface(rType,static_cast< XBatchExecution*> (this)); + return aRet.hasValue() ? aRet : OStatement_Base::queryInterface(rType); +} + + +void SAL_CALL OStatement::addBatch( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_aBatchVector.push_back(sql); +} + +Sequence< sal_Int32 > SAL_CALL OStatement::executeBatch( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + reset(); + + OUString aBatchSql = std::accumulate(m_aBatchVector.begin(), m_aBatchVector.end(), OUString(), + [](const OUString& rRes, const OUString& rStr) { return rRes + rStr + ";"; }); + sal_Int32 nLen = m_aBatchVector.size(); + + + if ( m_RecordSet.IsValid() ) + m_RecordSet.PutRefDataSource( nullptr ); + m_RecordSet.clear(); + m_RecordSet.Create(); + + CHECK_RETURN(m_Command.put_CommandText(aBatchSql)) + if ( m_RecordSet.IsValid() ) + m_RecordSet.PutRefDataSource(static_cast<IDispatch*>(m_Command)); + + CHECK_RETURN(m_RecordSet.UpdateBatch(adAffectAll)) + + ADORecordset* pSet=nullptr; + Sequence< sal_Int32 > aRet(nLen); + sal_Int32* pArray = aRet.getArray(); + for(sal_Int32 j=0;j<nLen;++j) + { + pSet = nullptr; + OLEVariant aRecordsAffected; + if(m_RecordSet.NextRecordset(aRecordsAffected,&pSet) && pSet) + { + assignRecordSet( pSet ); + + ADO_LONGPTR nValue; + if(m_RecordSet.get_RecordCount(nValue)) + pArray[j] = nValue; + } + } + return aRet; +} + + +sal_Int32 SAL_CALL OStatement_Base::executeUpdate( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + reset(); + + try { + ADORecordset* pSet = nullptr; + CHECK_RETURN(m_Command.put_CommandText(sql)) + CHECK_RETURN(m_Command.Execute(m_RecordsAffected,m_Parameters,adCmdText|adExecuteNoRecords,&pSet)) + } + catch (SQLWarning& ex) { + + // Save pointer to warning and save with ResultSet + // object once it is created. + + m_aLastWarning = ex; + } + if(!m_RecordsAffected.isEmpty() && !m_RecordsAffected.isNull() && m_RecordsAffected.getType() != VT_ERROR) + return m_RecordsAffected.getInt32(); + + return 0; +} + + +Reference< XResultSet > SAL_CALL OStatement_Base::getResultSet( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + return m_xResultSet; +} + + +sal_Int32 SAL_CALL OStatement_Base::getUpdateCount( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + ADO_LONGPTR nRet; + if(m_RecordSet.IsValid() && m_RecordSet.get_RecordCount(nRet)) + return nRet; + return -1; +} + + +sal_Bool SAL_CALL OStatement_Base::getMoreResults( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + SQLWarning warning; + + // clear previous warnings + + clearWarnings (); + + // Call SQLMoreResults + + try + { + ADORecordset* pSet=nullptr; + OLEVariant aRecordsAffected; + if(m_RecordSet.IsValid() && m_RecordSet.NextRecordset(aRecordsAffected,&pSet) && pSet) + assignRecordSet( pSet ); + } + catch (SQLWarning &ex) + { + + // Save pointer to warning and save with ResultSet + // object once it is created. + + warning = ex; + } + return m_RecordSet.IsValid(); +} + + +Any SAL_CALL OStatement_Base::getWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + return makeAny(m_aLastWarning); +} + + +void SAL_CALL OStatement_Base::clearWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_aLastWarning = SQLWarning(); +} + + +sal_Int32 OStatement_Base::getQueryTimeOut() const +{ + return m_Command.get_CommandTimeout(); +} + +sal_Int32 OStatement_Base::getMaxRows() const +{ + ADO_LONGPTR nRet=-1; + if(!(m_RecordSet.IsValid() && m_RecordSet.get_MaxRecords(nRet))) + ::dbtools::throwFunctionSequenceException(nullptr); + return nRet; +} + +sal_Int32 OStatement_Base::getResultSetConcurrency() const +{ + sal_Int32 nValue; + + switch(m_eLockType) + { + case adLockReadOnly: + nValue = ResultSetConcurrency::READ_ONLY; + break; + default: + nValue = ResultSetConcurrency::UPDATABLE; + break; + } + + return nValue; +} + +sal_Int32 OStatement_Base::getResultSetType() const +{ + sal_Int32 nValue=0; + switch(m_eCursorType) + { + case adOpenUnspecified: + case adOpenForwardOnly: + nValue = ResultSetType::FORWARD_ONLY; + break; + case adOpenStatic: + case adOpenKeyset: + nValue = ResultSetType::SCROLL_INSENSITIVE; + break; + case adOpenDynamic: + nValue = ResultSetType::SCROLL_SENSITIVE; + break; + } + return nValue; +} + +sal_Int32 OStatement_Base::getFetchDirection() +{ + return FetchDirection::FORWARD; +} + +sal_Int32 OStatement_Base::getFetchSize() const +{ + return m_nFetchSize; +} + +sal_Int32 OStatement_Base::getMaxFieldSize() +{ + return 0; +} + +OUString OStatement_Base::getCursorName() const +{ + return m_Command.GetName(); +} + +void OStatement_Base::setQueryTimeOut(sal_Int32 seconds) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_Command.put_CommandTimeout(seconds); +} + +void OStatement_Base::setMaxRows(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + m_nMaxRows = _par0; +} + +void OStatement_Base::setResultSetConcurrency(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + switch(_par0) + { + case ResultSetConcurrency::READ_ONLY: + m_eLockType = adLockReadOnly; + break; + default: + m_eLockType = adLockOptimistic; + break; + } +} + +void OStatement_Base::setResultSetType(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + switch(_par0) + { + case ResultSetType::FORWARD_ONLY: + m_eCursorType = adOpenForwardOnly; + break; + case ResultSetType::SCROLL_INSENSITIVE: + m_eCursorType = adOpenKeyset; + break; + case ResultSetType::SCROLL_SENSITIVE: + m_eCursorType = adOpenDynamic; + break; + } +} + +void OStatement_Base::setFetchDirection(sal_Int32 /*_par0*/) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + ::dbtools::throwFeatureNotImplementedSQLException( "Statement::FetchDirection", *this ); +} + +void OStatement_Base::setFetchSize(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_nFetchSize = _par0; +} + +void OStatement_Base::setMaxFieldSize(sal_Int32 /*_par0*/) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + ::dbtools::throwFeatureNotImplementedSQLException( "Statement::MaxFieldSize", *this ); +} + +void OStatement_Base::setCursorName(const OUString &_par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + m_Command.put_Name(_par0); +} + + +::cppu::IPropertyArrayHelper* OStatement_Base::createArrayHelper( ) const +{ + Sequence< css::beans::Property > aProps(10); + css::beans::Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING), + PROPERTY_ID_ESCAPEPROCESSING, cppu::UnoType<bool>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE), + PROPERTY_ID_MAXFIELDSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS), + PROPERTY_ID_MAXROWS, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT), + PROPERTY_ID_QUERYTIMEOUT, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_USEBOOKMARKS), + PROPERTY_ID_USEBOOKMARKS, cppu::UnoType<bool>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +::cppu::IPropertyArrayHelper & OStatement_Base::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OStatement_Base::convertFastPropertyValue( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + bool bModified = false; + + bool bValidAdoRS = m_RecordSet.IsValid(); + // some of the properties below, when set, are remembered in a member, and applied in the next execute + // For these properties, the record set does not need to be valid to allow setting them. + // For all others (where the values are forwarded to the ADO RS directly), the recordset must be valid. + + try + { + switch(nHandle) + { + case PROPERTY_ID_MAXROWS: + bModified = ::comphelper::tryPropertyValue( rConvertedValue, rOldValue, rValue, bValidAdoRS ? getMaxRows() : m_nMaxRows ); + break; + + case PROPERTY_ID_RESULTSETTYPE: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getResultSetType()); + break; + case PROPERTY_ID_FETCHSIZE: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize()); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getResultSetConcurrency()); + break; + case PROPERTY_ID_QUERYTIMEOUT: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getQueryTimeOut()); + break; + case PROPERTY_ID_MAXFIELDSIZE: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getMaxFieldSize()); + break; + case PROPERTY_ID_CURSORNAME: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getCursorName()); + break; + case PROPERTY_ID_FETCHDIRECTION: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection()); + break; + } + } + catch( const Exception& ) + { + bModified = true; // will ensure that the property is set + OSL_FAIL( "OStatement_Base::convertFastPropertyValue: caught something strange!" ); + } + return bModified; +} + +void OStatement_Base::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + setQueryTimeOut(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_MAXFIELDSIZE: + setMaxFieldSize(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_MAXROWS: + setMaxRows(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_CURSORNAME: + setCursorName(comphelper::getString(rValue)); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + setResultSetConcurrency(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_RESULTSETTYPE: + setResultSetType(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_FETCHDIRECTION: + setFetchDirection(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_FETCHSIZE: + setFetchSize(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_ESCAPEPROCESSING: + // return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAsLink); + case PROPERTY_ID_USEBOOKMARKS: + // return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAsLink); + default: + ; + } +} + +void OStatement_Base::getFastPropertyValue(Any& rValue,sal_Int32 nHandle) const +{ + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + rValue <<= getQueryTimeOut(); + break; + case PROPERTY_ID_MAXFIELDSIZE: + rValue <<= getMaxFieldSize(); + break; + case PROPERTY_ID_MAXROWS: + rValue <<= getMaxRows(); + break; + case PROPERTY_ID_CURSORNAME: + rValue <<= getCursorName(); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= getResultSetConcurrency(); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= getResultSetType(); + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= getFetchDirection(); + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= getFetchSize(); + break; + case PROPERTY_ID_ESCAPEPROCESSING: + rValue <<= true; + break; + case PROPERTY_ID_USEBOOKMARKS: + default: + ; + } +} + +OStatement::~OStatement() +{ +} +IMPLEMENT_SERVICE_INFO(OStatement,"com.sun.star.sdbcx.AStatement","com.sun.star.sdbc.Statement"); + +void SAL_CALL OStatement_Base::acquire() throw() +{ + OStatement_BASE::acquire(); +} + +void SAL_CALL OStatement::acquire() throw() +{ + OStatement_Base::acquire(); +} + +void SAL_CALL OStatement::release() throw() +{ + OStatement_Base::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OStatement_Base::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ATable.cxx b/connectivity/source/drivers/ado/ATable.cxx new file mode 100644 index 000000000..209255598 --- /dev/null +++ b/connectivity/source/drivers/ado/ATable.cxx @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/ATable.hxx> +#include <ado/AIndexes.hxx> +#include <ado/AColumns.hxx> +#include <ado/AColumn.hxx> +#include <ado/AKeys.hxx> +#include <ado/AConnection.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <ado/Awrapado.hxx> +#include <TConnection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> + +using namespace ::comphelper; + +using namespace connectivity; +using namespace connectivity::ado; +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::container; + + +OAdoTable::OAdoTable(sdbcx::OCollection* _pTables,bool _bCase,OCatalog* _pCatalog,_ADOTable* _pTable) + : OTable_TYPEDEF(_pTables,_bCase) + ,m_pCatalog(_pCatalog) +{ + construct(); + m_aTable = WpADOTable(_pTable); + // m_aTable.putref_ParentCatalog(_pCatalog->getCatalog()); + fillPropertyValues(); + +} + +OAdoTable::OAdoTable(sdbcx::OCollection* _pTables,bool _bCase,OCatalog* _pCatalog) + : OTable_TYPEDEF(_pTables,_bCase) + ,m_pCatalog(_pCatalog) +{ + construct(); + m_aTable.Create(); + m_aTable.putref_ParentCatalog(_pCatalog->getCatalog()); + +} + +void SAL_CALL OAdoTable::disposing() +{ + OTable_TYPEDEF::disposing(); + m_aTable.clear(); +} + +void OAdoTable::refreshColumns() +{ + ::std::vector< OUString> aVector; + + WpADOColumns aColumns; + if ( m_aTable.IsValid() ) + { + aColumns = m_aTable.get_Columns(); + aColumns.fillElementNames(aVector); + } + + if(m_xColumns) + m_xColumns->reFill(aVector); + else + m_xColumns = new OColumns(*this,m_aMutex,aVector,aColumns,isCaseSensitive(),m_pCatalog->getConnection()); +} + +void OAdoTable::refreshKeys() +{ + ::std::vector< OUString> aVector; + + WpADOKeys aKeys; + if(m_aTable.IsValid()) + { + aKeys = m_aTable.get_Keys(); + aKeys.fillElementNames(aVector); + } + + if(m_xKeys) + m_xKeys->reFill(aVector); + else + m_xKeys = new OKeys(*this,m_aMutex,aVector,aKeys,isCaseSensitive(),m_pCatalog->getConnection()); +} + +void OAdoTable::refreshIndexes() +{ + ::std::vector< OUString> aVector; + + WpADOIndexes aIndexes; + if(m_aTable.IsValid()) + { + aIndexes = m_aTable.get_Indexes(); + aIndexes.fillElementNames(aVector); + } + + if(m_xIndexes) + m_xIndexes->reFill(aVector); + else + m_xIndexes = new OIndexes(*this,m_aMutex,aVector,aIndexes,isCaseSensitive(),m_pCatalog->getConnection()); +} + +Sequence< sal_Int8 > OAdoTable::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OAdoTable::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OAdoTable>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : OTable_TYPEDEF::getSomething(rId); +} + +// XRename +void SAL_CALL OAdoTable::rename( const OUString& newName ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OTableDescriptor_BASE_TYPEDEF::rBHelper.bDisposed); + + m_aTable.put_Name(newName); + ADOS::ThrowException(*(m_pCatalog->getConnection()->getConnection()),*this); + + OTable_TYPEDEF::rename(newName); +} + +Reference< XDatabaseMetaData> OAdoTable::getMetaData() const +{ + return m_pCatalog->getConnection()->getMetaData(); +} + +// XAlterTable +void SAL_CALL OAdoTable::alterColumnByName( const OUString& colName, const Reference< XPropertySet >& descriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OTableDescriptor_BASE_TYPEDEF::rBHelper.bDisposed); + + bool bError = true; + OAdoColumn* pColumn = comphelper::getUnoTunnelImplementation<OAdoColumn>(descriptor); + if(pColumn != nullptr) + { + WpADOColumns aColumns = m_aTable.get_Columns(); + bError = !aColumns.Delete(colName); + bError = bError || !aColumns.Append(pColumn->getColumnImpl()); + } + if(bError) + ADOS::ThrowException(*(m_pCatalog->getConnection()->getConnection()),*this); + + m_xColumns->refresh(); + refreshColumns(); +} + +void SAL_CALL OAdoTable::alterColumnByIndex( sal_Int32 index, const Reference< XPropertySet >& descriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OTableDescriptor_BASE_TYPEDEF::rBHelper.bDisposed); + + Reference< XPropertySet > xOld; + m_xColumns->getByIndex(index) >>= xOld; + if(xOld.is()) + alterColumnByName(getString(xOld->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),descriptor); +} + +void OAdoTable::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + if(m_aTable.IsValid()) + { + switch(nHandle) + { + case PROPERTY_ID_NAME: + m_aTable.put_Name(getString(rValue)); + break; + + case PROPERTY_ID_TYPE: + OTools::putValue( m_aTable.get_Properties(), + OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE), + getString(rValue)); + break; + + case PROPERTY_ID_DESCRIPTION: + OTools::putValue( m_aTable.get_Properties(), + OUString("Description"), + getString(rValue)); + break; + + case PROPERTY_ID_SCHEMANAME: + break; + + default: + throw Exception("unknown prop " + OUString::number(nHandle), nullptr); + } + } + OTable_TYPEDEF::setFastPropertyValue_NoBroadcast(nHandle,rValue); +} + +OUString SAL_CALL OAdoTable::getName() +{ + return m_aTable.get_Name(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ATables.cxx b/connectivity/source/drivers/ado/ATables.cxx new file mode 100644 index 000000000..9ce87634e --- /dev/null +++ b/connectivity/source/drivers/ado/ATables.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 <ado/ATables.hxx> +#include <ado/ATable.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <ado/ACatalog.hxx> +#include <ado/AConnection.hxx> +#include <ado/Awrapado.hxx> +#include <TConnection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/interfacecontainer.h> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> + +using namespace ::cppu; +using namespace connectivity; +using namespace comphelper; +using namespace connectivity::ado; +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::container; + +sdbcx::ObjectType OTables::createObject(const OUString& _rName) +{ + OSL_ENSURE(m_aCollection.IsValid(),"Collection isn't valid"); + return new OAdoTable(this,isCaseSensitive(),m_pCatalog,m_aCollection.GetItem(_rName)); +} + +void OTables::impl_refresh( ) +{ + OSL_ENSURE(m_aCollection.IsValid(),"Collection isn't valid"); + m_aCollection.Refresh(); + m_pCatalog->refreshTables(); +} + +Reference< XPropertySet > OTables::createDescriptor() +{ + return new OAdoTable(this,isCaseSensitive(),m_pCatalog); +} + +// XAppend +sdbcx::ObjectType OTables::appendObject( const OUString&, const Reference< XPropertySet >& descriptor ) +{ + OAdoTable* pTable = getUnoTunnelImplementation<OAdoTable>( descriptor ); + if ( pTable == nullptr ) + m_pCatalog->getConnection()->throwGenericSQLException( STR_INVALID_TABLE_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + + OSL_ENSURE(m_aCollection.IsValid(),"Collection isn't valid"); + if(!m_aCollection.Append(pTable->getImpl())) + ADOS::ThrowException(*m_pCatalog->getConnection()->getConnection(),static_cast<XTypeProvider*>(this)); + m_aCollection.Refresh(); + + return new OAdoTable(this,isCaseSensitive(),m_pCatalog,pTable->getImpl()); +} + +// XDrop +void OTables::dropObject(sal_Int32 /*_nPos*/,const OUString& _sElementName) +{ + OSL_ENSURE(m_aCollection.IsValid(),"Collection isn't valid"); + if ( !m_aCollection.Delete(_sElementName) ) + ADOS::ThrowException(*m_pCatalog->getConnection()->getConnection(),static_cast<XTypeProvider*>(this)); +} + +void OTables::appendNew(const OUString& _rsNewTable) +{ + OSL_ENSURE(m_aCollection.IsValid(),"Collection isn't valid"); + m_aCollection.Refresh(); + + insertElement(_rsNewTable,nullptr); + + // notify our container listeners + ContainerEvent aEvent(static_cast<XContainer*>(this), makeAny(_rsNewTable), Any(), Any()); + OInterfaceIteratorHelper2 aListenerLoop(m_aContainerListeners); + while (aListenerLoop.hasMoreElements()) + static_cast<XContainerListener*>(aListenerLoop.next())->elementInserted(aEvent); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AUser.cxx b/connectivity/source/drivers/ado/AUser.cxx new file mode 100644 index 000000000..724334cea --- /dev/null +++ b/connectivity/source/drivers/ado/AUser.cxx @@ -0,0 +1,193 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/AUser.hxx> +#include <ado/ACatalog.hxx> +#include <ado/AGroups.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <ado/AConnection.hxx> +#include <ado/Awrapado.hxx> + +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + + +OAdoUser::OAdoUser(OCatalog* _pParent,bool _bCase, ADOUser* _pUser) + : OUser_TYPEDEF(_bCase) + ,m_pCatalog(_pParent) +{ + construct(); + + if(_pUser) + m_aUser = WpADOUser(_pUser); + else + m_aUser.Create(); +} + +OAdoUser::OAdoUser(OCatalog* _pParent,bool _bCase, const OUString& Name) + : OUser_TYPEDEF(Name,_bCase) + , m_pCatalog(_pParent) +{ + construct(); + m_aUser.Create(); + m_aUser.put_Name(Name); +} + +void OAdoUser::refreshGroups() +{ + ::std::vector< OUString> aVector; + WpADOGroups aGroups(m_aUser.get_Groups()); + aGroups.fillElementNames(aVector); + if(m_pGroups) + m_pGroups->reFill(aVector); + else + m_pGroups.reset(new OGroups(m_pCatalog, m_aMutex, aVector, aGroups, isCaseSensitive())); +} + +Sequence< sal_Int8 > OAdoUser::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OAdoUser::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OAdoUser>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : OUser_TYPEDEF::getSomething(rId); +} + + +void OAdoUser::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + if(m_aUser.IsValid()) + { + + switch(nHandle) + { + case PROPERTY_ID_NAME: + { + OUString aVal; + rValue >>= aVal; + m_aUser.put_Name(aVal); + } + break; + } + } +} + +void OAdoUser::getFastPropertyValue(Any& rValue,sal_Int32 nHandle) const +{ + if(m_aUser.IsValid()) + { + switch(nHandle) + { + case PROPERTY_ID_NAME: + rValue <<= m_aUser.get_Name(); + break; + } + } +} + +OUserExtend::OUserExtend(OCatalog* _pParent,bool _bCase, ADOUser* _pUser) + : OAdoUser(_pParent,_bCase,_pUser) +{ +} + +OUserExtend::OUserExtend(OCatalog* _pParent,bool _bCase, const OUString& Name) + : OAdoUser(_pParent,_bCase,Name) +{ +} + + +void OUserExtend::construct() +{ + OUser_TYPEDEF::construct(); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD), PROPERTY_ID_PASSWORD,0,&m_Password,::cppu::UnoType<OUString>::get()); +} + +cppu::IPropertyArrayHelper* OUserExtend::createArrayHelper() const +{ + Sequence< css::beans::Property > aProps; + describeProperties(aProps); + return new cppu::OPropertyArrayHelper(aProps); +} + +cppu::IPropertyArrayHelper & OUserExtend::getInfoHelper() +{ + return *OUserExtend_PROP::getArrayHelper(); +} + +sal_Int32 SAL_CALL OAdoUser::getPrivileges( const OUString& objName, sal_Int32 objType ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_TYPEDEF::rBHelper.bDisposed); + + return ADOS::mapAdoRights2Sdbc(m_aUser.GetPermissions(objName, ADOS::mapObjectType2Ado(objType))); +} + +sal_Int32 SAL_CALL OAdoUser::getGrantablePrivileges( const OUString& objName, sal_Int32 objType ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_TYPEDEF::rBHelper.bDisposed); + + sal_Int32 nRights = 0; + RightsEnum eRights = m_aUser.GetPermissions(objName, ADOS::mapObjectType2Ado(objType)); + if((eRights & adRightWithGrant) == adRightWithGrant) + nRights = ADOS::mapAdoRights2Sdbc(eRights); + ADOS::ThrowException(*m_pCatalog->getConnection()->getConnection(),*this); + return nRights; +} + +void SAL_CALL OAdoUser::grantPrivileges( const OUString& objName, sal_Int32 objType, sal_Int32 objPrivileges ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_TYPEDEF::rBHelper.bDisposed); + m_aUser.SetPermissions(objName,ADOS::mapObjectType2Ado(objType),adAccessGrant,RightsEnum(ADOS::mapRights2Ado(objPrivileges))); + ADOS::ThrowException(*m_pCatalog->getConnection()->getConnection(),*this); +} + +void SAL_CALL OAdoUser::revokePrivileges( const OUString& objName, sal_Int32 objType, sal_Int32 objPrivileges ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_TYPEDEF::rBHelper.bDisposed); + m_aUser.SetPermissions(objName,ADOS::mapObjectType2Ado(objType),adAccessRevoke,RightsEnum(ADOS::mapRights2Ado(objPrivileges))); + ADOS::ThrowException(*m_pCatalog->getConnection()->getConnection(),*this); +} + +// XUser +void SAL_CALL OAdoUser::changePassword( const OUString& objPassword, const OUString& newPassword ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_TYPEDEF::rBHelper.bDisposed); + m_aUser.ChangePassword(objPassword,newPassword); + ADOS::ThrowException(*m_pCatalog->getConnection()->getConnection(),*this); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AUsers.cxx b/connectivity/source/drivers/ado/AUsers.cxx new file mode 100644 index 000000000..2620e915a --- /dev/null +++ b/connectivity/source/drivers/ado/AUsers.cxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/AUsers.hxx> +#include <ado/AUser.hxx> +#include <ado/ATable.hxx> +#include <ado/AConnection.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <connectivity/sdbcx/IRefreshable.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> + +using namespace comphelper; +using namespace connectivity; +using namespace connectivity::ado; +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::container; + +sdbcx::ObjectType OUsers::createObject(const OUString& _rName) +{ + return new OAdoUser(m_pCatalog,isCaseSensitive(),_rName); +} + +void OUsers::impl_refresh() +{ + m_aCollection.Refresh(); +} + +Reference< XPropertySet > OUsers::createDescriptor() +{ + return new OUserExtend(m_pCatalog,isCaseSensitive()); +} + +// XAppend +sdbcx::ObjectType OUsers::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + OUserExtend* pUser = getUnoTunnelImplementation<OUserExtend>( descriptor ); + if ( pUser == nullptr ) + m_pCatalog->getConnection()->throwGenericSQLException( STR_INVALID_USER_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + + ADOUsers* pUsers = m_aCollection; + pUsers->Append(OLEVariant(pUser->getImpl()),OLEString(pUser->getPassword()).asBSTR()); + + return createObject( _rForName ); +} + +// XDrop +void OUsers::dropObject(sal_Int32 /*_nPos*/,const OUString& _sElementName) +{ + m_aCollection.Delete(_sElementName); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AView.cxx b/connectivity/source/drivers/ado/AView.cxx new file mode 100644 index 000000000..b53c1a026 --- /dev/null +++ b/connectivity/source/drivers/ado/AView.cxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/AView.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <ado/adoimp.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <ado/Awrapado.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <TConnection.hxx> + + +using namespace comphelper; +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + +// IMPLEMENT_SERVICE_INFO(OAdoView,"com.sun.star.sdbcx.AView","com.sun.star.sdbcx.View"); + +OAdoView::OAdoView(bool _bCase,ADOView* _pView) : OView_ADO(_bCase,nullptr) +,m_aView(_pView) +{ +} + +Sequence< sal_Int8 > OAdoView::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OAdoView::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OAdoView>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : OView_ADO::getSomething(rId); +} + + +void OAdoView::getFastPropertyValue(Any& rValue,sal_Int32 nHandle) const +{ + if(m_aView.IsValid()) + { + switch(nHandle) + { + case PROPERTY_ID_NAME: + rValue <<= m_aView.get_Name(); + break; + case PROPERTY_ID_CATALOGNAME: + break; + case PROPERTY_ID_SCHEMANAME: + // rValue <<= m_aView.get_Type(); + break; + case PROPERTY_ID_COMMAND: + { + OLEVariant aVar; + m_aView.get_Command(aVar); + if(!aVar.isNull() && !aVar.isEmpty()) + { + ADOCommand* pCom = static_cast<ADOCommand*>(aVar.getIDispatch()); + OLEString aBSTR; + pCom->get_CommandText(aBSTR.getAddress()); + rValue <<= aBSTR.asOUString(); + } + } + break; + } + } + else + OView_ADO::getFastPropertyValue(rValue,nHandle); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/AViews.cxx b/connectivity/source/drivers/ado/AViews.cxx new file mode 100644 index 000000000..77cc5abfa --- /dev/null +++ b/connectivity/source/drivers/ado/AViews.cxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/AViews.hxx> +#include <ado/AView.hxx> +#include <ado/ATables.hxx> +#include <ado/ACatalog.hxx> +#include <ado/AConnection.hxx> +#include <ado/Awrapado.hxx> +#include <TConnection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> + +using namespace ::comphelper; + +using namespace connectivity; +using namespace connectivity::ado; +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::container; + +sdbcx::ObjectType OViews::createObject(const OUString& _rName) +{ + OAdoView* pView = new OAdoView(isCaseSensitive(),m_aCollection.GetItem(_rName)); + pView->setNew(false); + return pView; +} + +void OViews::impl_refresh( ) +{ + m_aCollection.Refresh(); +} + +Reference< XPropertySet > OViews::createDescriptor() +{ + return new OAdoView(isCaseSensitive()); +} + + +// XAppend +sdbcx::ObjectType OViews::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + OAdoView* pView = getUnoTunnelImplementation<OAdoView>( descriptor ); + if ( pView == nullptr ) + m_pCatalog->getConnection()->throwGenericSQLException( STR_INVALID_VIEW_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) ); + + WpADOCommand aCommand; + aCommand.Create(); + if ( !aCommand.IsValid() ) + m_pCatalog->getConnection()->throwGenericSQLException( STR_VIEW_NO_COMMAND_ERROR,static_cast<XTypeProvider*>(this) ); + + OUString sName( _rForName ); + aCommand.put_Name(sName); + aCommand.put_CommandText(getString(descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_COMMAND)))); + ADOViews* pViews = m_aCollection; + if(FAILED(pViews->Append(OLEString(sName).asBSTR(),aCommand))) + ADOS::ThrowException(*m_pCatalog->getConnection()->getConnection(),static_cast<XTypeProvider*>(this)); + + OTables* pTables = static_cast<OTables*>(static_cast<OCatalog&>(m_rParent).getPrivateTables()); + if ( pTables ) + pTables->appendNew(sName); + + return createObject( _rForName ); +} + +// XDrop +void OViews::dropObject(sal_Int32 /*_nPos*/,const OUString& _sElementName) +{ + if(!m_aCollection.Delete(_sElementName)) + ADOS::ThrowException(*m_pCatalog->getConnection()->getConnection(),static_cast<XTypeProvider*>(this)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/Aolevariant.cxx b/connectivity/source/drivers/ado/Aolevariant.cxx new file mode 100644 index 000000000..7f740e81d --- /dev/null +++ b/connectivity/source/drivers/ado/Aolevariant.cxx @@ -0,0 +1,745 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <ado/Aolevariant.hxx> +#include <connectivity/dbconversion.hxx> +#include <osl/diagnose.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <resource/sharedresources.hxx> +#include <strings.hrc> +#include <com/sun/star/bridge/oleautomation/Date.hpp> +#include <com/sun/star/bridge/oleautomation/Currency.hpp> +#include <com/sun/star/bridge/oleautomation/SCode.hpp> +#include <com/sun/star/bridge/oleautomation/Decimal.hpp> + +using namespace com::sun::star::beans; +using namespace com::sun::star::uno; +using namespace com::sun::star::bridge::oleautomation; +using namespace connectivity::ado; + +OLEString::OLEString() + :m_sStr(nullptr) +{ +} +OLEString::OLEString(const BSTR& _sBStr) + :m_sStr(_sBStr) +{ +} +OLEString::OLEString(const OUString& _sBStr) +{ + m_sStr = ::SysAllocString(o3tl::toW(_sBStr.getStr())); +} +OLEString::~OLEString() +{ + if(m_sStr) + ::SysFreeString(m_sStr); +} +OLEString& OLEString::operator=(const OUString& _rSrc) +{ + if(m_sStr) + ::SysFreeString(m_sStr); + m_sStr = ::SysAllocString(o3tl::toW(_rSrc.getStr())); + return *this; +} +OLEString& OLEString::operator=(const OLEString& _rSrc) +{ + if(this != &_rSrc) + { + if(m_sStr) + ::SysFreeString(m_sStr); + m_sStr = ::SysAllocString(_rSrc.m_sStr); + } + return *this; +} +OLEString& OLEString::operator=(const BSTR& _rSrc) +{ + if(m_sStr) + ::SysFreeString(m_sStr); + m_sStr = _rSrc; + return *this; +} +OUString OLEString::asOUString() const +{ + return (m_sStr != nullptr) ? OUString(o3tl::toU(LPCOLESTR(m_sStr)),::SysStringLen(m_sStr)) : OUString(); +} +BSTR OLEString::asBSTR() const +{ + return m_sStr; +} +BSTR* OLEString::getAddress() +{ + return &m_sStr; +} +sal_Int32 OLEString::length() const +{ + return (m_sStr != nullptr) ? ::SysStringLen(m_sStr) : 0; +} + +OLEVariant::OLEVariant() +{ + VariantInit(this); +} +OLEVariant::OLEVariant(const VARIANT& varSrc) +{ + ::VariantInit(this); + HRESULT eRet = ::VariantCopy(this, &varSrc); + OSL_ENSURE(eRet == S_OK,"Error while copying an ado variant!"); +} +OLEVariant::OLEVariant(const OLEVariant& varSrc) +{ + ::VariantInit(this); + HRESULT eRet = ::VariantCopy(this, static_cast<const VARIANT*>(&varSrc)); + OSL_ENSURE(eRet == S_OK,"Error while copying an ado variant!"); +} + +OLEVariant::OLEVariant(bool x) { VariantInit(this); vt = VT_BOOL; boolVal = (x ? VARIANT_TRUE : VARIANT_FALSE);} +OLEVariant::OLEVariant(sal_Int8 n) { VariantInit(this); vt = VT_I1; bVal = n;} +OLEVariant::OLEVariant(sal_Int16 n) { VariantInit(this); vt = VT_I2; intVal = n;} +OLEVariant::OLEVariant(sal_Int32 n) { VariantInit(this); vt = VT_I4; lVal = n;} +OLEVariant::OLEVariant(sal_Int64 x) { VariantInit(this); vt = VT_I4; lVal = static_cast<LONG>(x);} + +OLEVariant::OLEVariant(const OUString& us) +{ + ::VariantInit(this); + vt = VT_BSTR; + bstrVal = SysAllocString(o3tl::toW(us.getStr())); +} +OLEVariant::~OLEVariant() +{ + HRESULT eRet = ::VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); +} // clears all the memory that was allocated before + +OLEVariant::OLEVariant(const css::util::Date& x ) +{ + VariantInit(this); + vt = VT_DATE; + dblVal = ::dbtools::DBTypeConversion::toDouble(x,css::util::Date(30,12,1899)); +} +OLEVariant::OLEVariant(const css::util::Time& x ) +{ + VariantInit(this); + vt = VT_DATE; + dblVal = ::dbtools::DBTypeConversion::toDouble(x); +} +OLEVariant::OLEVariant(const css::util::DateTime& x ) +{ + VariantInit(this); + vt = VT_DATE; + dblVal = ::dbtools::DBTypeConversion::toDouble(x,css::util::Date(30,12,1899)); +} +OLEVariant::OLEVariant(float x) +{ + VariantInit(this); + vt = VT_R4; + fltVal = x; +} +OLEVariant::OLEVariant(const double &x) +{ + VariantInit(this); + vt = VT_R8; + dblVal = x; +} + + +OLEVariant::OLEVariant(IDispatch* pDispInterface) +{ + VariantInit(this); + setIDispatch( pDispInterface ); +} + +OLEVariant::OLEVariant(const css::uno::Sequence< sal_Int8 >& x) +{ + VariantInit(this); + + vt = VT_ARRAY|VT_UI1; + + parray = SafeArrayCreateVector(VT_UI1, 0, x.getLength()); + const sal_Int8* pBegin = x.getConstArray(); + const sal_Int8* pEnd = pBegin + x.getLength(); + + for(sal_Int32 i=0;pBegin != pEnd;++i,++pBegin) + { + sal_Int32 nData = *pBegin; + HRESULT rs = SafeArrayPutElement(parray,&i,&nData); + OSL_ENSURE(S_OK == rs,"Error while copy byte data"); + } +} + +OLEVariant& OLEVariant::operator=(const OLEVariant& varSrc) +{ + HRESULT eRet = ::VariantCopy(this, static_cast<const VARIANT*>(&varSrc)); + OSL_ENSURE(eRet == S_OK,"Error while copying an ado variant!"); + return *this; +} +// Assign a const VARIANT& (::VariantCopy handles everything) + +OLEVariant& OLEVariant::operator=(const tagVARIANT& varSrc) +{ + HRESULT eRet = ::VariantCopy(this, &varSrc); + OSL_ENSURE(eRet == S_OK,"Error while copying an ado variant!"); + + return *this; +} + +// Assign a const VARIANT* (::VariantCopy handles everything) + +OLEVariant& OLEVariant::operator=(const VARIANT* pSrc) +{ + HRESULT eRet = ::VariantCopy(this, pSrc); + OSL_ENSURE(eRet == S_OK,"Error while copying an ado variant!"); + + return *this; +} + +void OLEVariant::setByte(sal_uInt8 n) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_UI1; + bVal = n; +} +void OLEVariant::setInt16(sal_Int16 n) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_I2; + iVal = n; +} +void OLEVariant::setInt32(sal_Int32 n) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_I4; + lVal = n; +} +void OLEVariant::setFloat(float f) +{ HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_R4; + fltVal = f; +} +void OLEVariant::setDouble(double d) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_R8; + dblVal = d; +} +void OLEVariant::setDate(DATE d) +{ HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_DATE; + date = d; +} +void OLEVariant::setChar(unsigned char a) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_UI1; + bVal = a; +} +void OLEVariant::setCurrency(double aCur) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_CY; + set(aCur*10000); +} +void OLEVariant::setBool(bool b) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_BOOL; + boolVal = b ? VARIANT_TRUE : VARIANT_FALSE; +} +void OLEVariant::setString(const OUString& us) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_BSTR; + bstrVal = ::SysAllocString(o3tl::toW(us.getStr())); +} +void OLEVariant::setNoArg() +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_ERROR; + scode = DISP_E_PARAMNOTFOUND; +} + +void OLEVariant::setNull() +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_NULL; +} +void OLEVariant::setEmpty() +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_EMPTY; +} + +void OLEVariant::setUI1SAFEARRAYPtr(SAFEARRAY* pSafeAr) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = VT_ARRAY|VT_UI1; parray = pSafeAr; +} + +void OLEVariant::setArray(SAFEARRAY* pSafeArray, VARTYPE vtType) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + vt = static_cast<VARTYPE>(VT_ARRAY|vtType); + parray = pSafeArray; +} + +void OLEVariant::setIDispatch(IDispatch* pDispInterface) +{ + HRESULT eRet = VariantClear(this); + OSL_ENSURE(eRet == S_OK,"Error while clearing an ado variant!"); + + vt = VT_DISPATCH; + pdispVal = pDispInterface; + + if ( pDispInterface ) + pDispInterface->AddRef(); +} + + +bool OLEVariant::isNull() const { return (vt == VT_NULL); } +bool OLEVariant::isEmpty() const { return (vt == VT_EMPTY); } + +VARTYPE OLEVariant::getType() const { return vt; } + +css::util::Date OLEVariant::getDate() const +{ + return isNull() ? css::util::Date(30,12,1899) : ::dbtools::DBTypeConversion::toDate(getDateAsDouble(),css::util::Date(30,12,1899)); +} +css::util::Time OLEVariant::getTime() const +{ + return isNull() ? css::util::Time() : ::dbtools::DBTypeConversion::toTime(getDateAsDouble()); +} +css::util::DateTime OLEVariant::getDateTime() const +{ + return isNull() ? css::util::DateTime() : ::dbtools::DBTypeConversion::toDateTime(getDateAsDouble(),css::util::Date(30,12,1899)); +} + +VARIANT_BOOL OLEVariant::VariantBool(bool bEinBoolean) +{ + return (bEinBoolean ? VARIANT_TRUE : VARIANT_FALSE); +} + +void OLEVariant::CHS() +{ + cyVal.Lo ^= sal_uInt32(-1); + cyVal.Hi ^= -1; + cyVal.Lo++; + if( !cyVal.Lo ) + cyVal.Hi++; +} + +void OLEVariant::set(double n) +{ + if( n >= 0 ) + { + cyVal.Hi = static_cast<sal_Int32>(n / 4294967296.0); + cyVal.Lo = static_cast<sal_uInt32>(n - (static_cast<double>(cyVal.Hi) * 4294967296.0)); + } + else { + cyVal.Hi = static_cast<sal_Int32>(-n / 4294967296.0); + cyVal.Lo = static_cast<sal_uInt32>(-n - (static_cast<double>(cyVal.Hi) * 4294967296.0)); + CHS(); + } +} + +OUString OLEVariant::getString() const +{ + if (V_VT(this) == VT_BSTR) + return o3tl::toU(LPCOLESTR(V_BSTR(this))); + + if(isNull()) + return OUString(); + + OLEVariant varDest; + + varDest.ChangeType(VT_BSTR, this); + + return o3tl::toU(LPCOLESTR(V_BSTR(&varDest))); +} + + +void OLEVariant::ChangeType(VARTYPE vartype, const OLEVariant* pSrc) +{ + + // If pDest is NULL, convert type in place + + if (pSrc == nullptr) + pSrc = this; + + if ( ( this != pSrc ) + || ( vartype != V_VT( this ) ) + ) + { + if ( FAILED( ::VariantChangeType( static_cast< VARIANT* >( this ), + static_cast< const VARIANT* >( pSrc ), + 0, + vartype ) ) ) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_TYPE_NOT_CONVERT)); + throw css::sdbc::SQLException( + sError, + nullptr, + "S1000", + 1000, + css::uno::Any() + ); + } + } +} + + +css::uno::Sequence< sal_Int8 > OLEVariant::getByteSequence() const +{ + css::uno::Sequence< sal_Int8 > aRet; + if(V_VT(this) == VT_BSTR) + { + OLEString sStr(V_BSTR(this)); + aRet = css::uno::Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(sStr.asBSTR()),sizeof(sal_Unicode)*sStr.length()); + } + else if(!isNull()) + { + SAFEARRAY* pArray = getUI1SAFEARRAYPtr(); + + if(pArray) + { + HRESULT hresult1,hresult2; + long lBound,uBound; + // Verify that the SafeArray is the proper shape. + hresult1 = ::SafeArrayGetLBound(pArray, 1, &lBound); + hresult2 = ::SafeArrayGetUBound(pArray, 1, &uBound); + if ( SUCCEEDED(hresult1) && SUCCEEDED(hresult2) ) + { + long nCount = uBound-lBound+1; + aRet.realloc(nCount); + sal_Int8* pData = aRet.getArray(); + for(long i=0; SUCCEEDED(hresult1) && lBound <= uBound ;++i,++lBound) + { + sal_Int32 nData = 0; + hresult1 = ::SafeArrayGetElement(pArray,&lBound,&nData); + if ( SUCCEEDED(hresult1) ) + { + *pData = static_cast<sal_Int8>(nData); + ++pData; + } + } + } + } + } + + return aRet; +} + +bool OLEVariant::getBool() const +{ + if (V_VT(this) == VT_BOOL) + return V_BOOL(this) == VARIANT_TRUE; + if(isNull()) + return false; + + OLEVariant varDest; + + varDest.ChangeType(VT_BOOL, this); + + return V_BOOL(&varDest) == VARIANT_TRUE; +} + +IUnknown* OLEVariant::getIUnknown() const +{ + if (V_VT(this) == VT_UNKNOWN) + { + return V_UNKNOWN(this); + } + if(isNull()) + return nullptr; + + OLEVariant varDest; + + varDest.ChangeType(VT_UNKNOWN, this); + + V_UNKNOWN(&varDest)->AddRef(); + return V_UNKNOWN(&varDest); +} + +IDispatch* OLEVariant::getIDispatch() const +{ + if (V_VT(this) == VT_DISPATCH) + { + return V_DISPATCH(this); + } + + if(isNull()) + return nullptr; + + OLEVariant varDest; + + varDest.ChangeType(VT_DISPATCH, this); + + V_DISPATCH(&varDest)->AddRef(); + return V_DISPATCH(&varDest); +} + +sal_uInt8 OLEVariant::getByte() const +{ + if (V_VT(this) == VT_UI1) + return V_UI1(this); + + if(isNull()) + return sal_Int8(0); + OLEVariant varDest; + + varDest.ChangeType(VT_UI1, this); + + return V_UI1(&varDest); +} + +sal_Int16 OLEVariant::getInt16() const +{ + if (V_VT(this) == VT_I2) + return V_I2(this); + + if(isNull()) + return sal_Int16(0); + OLEVariant varDest; + + varDest.ChangeType(VT_I2, this); + + return V_I2(&varDest); +} + +sal_Int8 OLEVariant::getInt8() const +{ + if (V_VT(this) == VT_I1) + return V_I1(this); + + if(isNull()) + return sal_Int8(0); + + OLEVariant varDest; + + varDest.ChangeType(VT_I1, this); + + return V_I1(&varDest); +} + +sal_Int32 OLEVariant::getInt32() const +{ + if (V_VT(this) == VT_I4) + return V_I4(this); + + if(isNull()) + return sal_Int32(0); + + OLEVariant varDest; + + varDest.ChangeType(VT_I4, this); + + return V_I4(&varDest); +} + +sal_uInt32 OLEVariant::getUInt32() const +{ + if (V_VT(this) == VT_UI4) + return V_UI4(this); + + if(isNull()) + return sal_uInt32(0); + + OLEVariant varDest; + + varDest.ChangeType(VT_UI4, this); + + return V_UI4(&varDest); +} + +float OLEVariant::getFloat() const +{ + if (V_VT(this) == VT_R4) + return V_R4(this); + + if(isNull()) + return float(0); + OLEVariant varDest; + + varDest.ChangeType(VT_R4, this); + + return V_R4(&varDest); +} + +double OLEVariant::getDouble() const +{ + if (V_VT(this) == VT_R8) + return V_R8(this); + + if(isNull()) + return double(0); + OLEVariant varDest; + + varDest.ChangeType(VT_R8, this); + + return V_R8(&varDest); +} + +double OLEVariant::getDateAsDouble() const +{ + if (V_VT(this) == VT_DATE) + return V_DATE(this); + + if(isNull()) + return double(0); + OLEVariant varDest; + + varDest.ChangeType(VT_DATE, this); + + return V_DATE(&varDest); +} + +CY OLEVariant::getCurrency() const +{ + if (V_VT(this) == VT_CY) + return V_CY(this); + + if(isNull()) + { + CY aVar; + aVar.int64 = sal_Int64(0); + return aVar; + } + OLEVariant varDest; + + varDest.ChangeType(VT_CY, this); + + return V_CY(&varDest); +} + +SAFEARRAY* OLEVariant::getUI1SAFEARRAYPtr() const +{ + if (V_VT(this) == (VT_ARRAY|VT_UI1)) + return V_ARRAY(this); + + if(isNull()) + return nullptr; + OLEVariant varDest; + + varDest.ChangeType((VT_ARRAY|VT_UI1), this); + + return V_ARRAY(&varDest); +} + +css::uno::Any OLEVariant::makeAny() const +{ + css::uno::Any aValue; + switch (V_VT(this)) + { + case VT_EMPTY: + case VT_NULL: + aValue.setValue(nullptr, Type()); + break; + case VT_I2: + aValue.setValue( & iVal, cppu::UnoType<sal_Int16>::get()); + break; + case VT_I4: + aValue.setValue( & lVal, cppu::UnoType<sal_Int32>::get()); + break; + case VT_R4: + aValue.setValue( & fltVal, cppu::UnoType<float>::get()); + break; + case VT_R8: + aValue.setValue(& dblVal, cppu::UnoType<double>::get()); + break; + case VT_CY: + { + Currency cy(cyVal.int64); + aValue <<= cy; + break; + } + case VT_DATE: + { + aValue <<= getDate(); + break; + } + case VT_BSTR: + { + OUString b(o3tl::toU(bstrVal)); + aValue.setValue( &b, cppu::UnoType<decltype(b)>::get()); + break; + } + case VT_BOOL: + { + aValue <<= (boolVal == VARIANT_TRUE); + break; + } + case VT_I1: + aValue.setValue( & cVal, cppu::UnoType<sal_Int8>::get()); + break; + case VT_UI1: // there is no unsigned char in UNO + aValue <<= sal_Int8(bVal); + break; + case VT_UI2: + aValue.setValue( & uiVal, cppu::UnoType<cppu::UnoUnsignedShortType>::get()); + break; + case VT_UI4: + aValue.setValue( & ulVal, cppu::UnoType<sal_uInt32>::get()); + break; + case VT_INT: + aValue.setValue( & intVal, cppu::UnoType<sal_Int32>::get()); + break; + case VT_UINT: + aValue.setValue( & uintVal, cppu::UnoType<sal_uInt32>::get()); + break; + case VT_VOID: + aValue.setValue( nullptr, Type()); + break; + case VT_DECIMAL: + { + Decimal dec; + dec.Scale = decVal.scale; + dec.Sign = decVal.sign; + dec.LowValue = decVal.Lo32; + dec.MiddleValue = decVal.Mid32; + dec.HighValue = decVal.Hi32; + aValue <<= dec; + break; + } + + default: + break; + } + return aValue; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/Aservices.cxx b/connectivity/source/drivers/ado/Aservices.cxx new file mode 100644 index 000000000..0ec3ba953 --- /dev/null +++ b/connectivity/source/drivers/ado/Aservices.cxx @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ado/ADriver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::ado; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames , + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* ado_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + ODriver::getImplementationName_Static(), + ODriver::getSupportedServiceNames_Static(), + ODriver_CreateInstance, ::cppu::createSingleFactory) + ; + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/Awrapado.cxx b/connectivity/source/drivers/ado/Awrapado.cxx new file mode 100644 index 000000000..1fc664f89 --- /dev/null +++ b/connectivity/source/drivers/ado/Awrapado.cxx @@ -0,0 +1,2153 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/macros.h> +#include <ado/Awrapado.hxx> +#include <ado/Awrapadox.hxx> +#include <comphelper/types.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +using namespace connectivity::ado; + +void WpADOCatalog::Create() +{ + _ADOCatalog* pCommand; + HRESULT hr = CoCreateInstance(ADOS::CLSID_ADOCATALOG_25, + nullptr, + CLSCTX_INPROC_SERVER, + ADOS::IID_ADOCATALOG_25, + reinterpret_cast<void**>(&pCommand) ); + + + if( !FAILED( hr ) ) + setWithOutAddRef(pCommand); +} + + +WpADOProperties WpADOConnection::get_Properties() const +{ + ADOProperties* pProps=nullptr; + pInterface->get_Properties(&pProps); + WpADOProperties aProps; + aProps.setWithOutAddRef(pProps); + return aProps; +} + +OUString WpADOConnection::GetConnectionString() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_ConnectionString(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +bool WpADOConnection::PutConnectionString(const OUString &aCon) const +{ + assert(pInterface); + OLEString bstr(aCon); + bool bErg = SUCCEEDED(pInterface->put_ConnectionString(bstr.asBSTR())); + + return bErg; +} + +sal_Int32 WpADOConnection::GetCommandTimeout() const +{ + assert(pInterface); + sal_Int32 nRet=0; + pInterface->get_CommandTimeout(&nRet); + return nRet; +} + +void WpADOConnection::PutCommandTimeout(sal_Int32 nRet) +{ + assert(pInterface); + pInterface->put_CommandTimeout(nRet); +} + +sal_Int32 WpADOConnection::GetConnectionTimeout() const +{ + assert(pInterface); + sal_Int32 nRet=0; + pInterface->get_ConnectionTimeout(&nRet); + return nRet; +} + +void WpADOConnection::PutConnectionTimeout(sal_Int32 nRet) +{ + assert(pInterface); + pInterface->put_ConnectionTimeout(nRet); +} + +bool WpADOConnection::Close() +{ + assert(pInterface); + return (SUCCEEDED(pInterface->Close())); +} + +bool WpADOConnection::Execute(const OUString& CommandText,OLEVariant& RecordsAffected,long Options, WpADORecordset** ppiRset) +{ + assert(pInterface); + OLEString sStr1(CommandText); + bool bErg = SUCCEEDED(pInterface->Execute(sStr1.asBSTR(),&RecordsAffected,Options,reinterpret_cast<ADORecordset**>(ppiRset))); + return bErg; +} + +bool WpADOConnection::BeginTrans() +{ + assert(pInterface); + sal_Int32 nIso=0; + return SUCCEEDED(pInterface->BeginTrans(&nIso)); +} + +bool WpADOConnection::CommitTrans( ) +{ + assert(pInterface); + return SUCCEEDED(pInterface->CommitTrans()); +} + +bool WpADOConnection::RollbackTrans( ) +{ + assert(pInterface); + return SUCCEEDED(pInterface->RollbackTrans()); +} + +bool WpADOConnection::Open(const OUString& ConnectionString, const OUString& UserID,const OUString& Password,long Options) +{ + assert(pInterface); + OLEString sStr1(ConnectionString); + OLEString sStr2(UserID); + OLEString sStr3(Password); + bool bErg = SUCCEEDED(pInterface->Open(sStr1.asBSTR(),sStr2.asBSTR(),sStr3.asBSTR(),Options)); + return bErg; +} + +bool WpADOConnection::GetErrors(ADOErrors** pErrors) +{ + assert(pInterface); + return SUCCEEDED(pInterface->get_Errors(pErrors)); +} + +OUString WpADOConnection::GetDefaultDatabase() const +{ + assert(pInterface); + OLEString aBSTR; pInterface->get_DefaultDatabase(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +bool WpADOConnection::PutDefaultDatabase(const OUString& _bstr) +{ + assert(pInterface); + OLEString bstr(_bstr); + bool bErg = SUCCEEDED(pInterface->put_DefaultDatabase(bstr.asBSTR())); + + return bErg; +} + +IsolationLevelEnum WpADOConnection::get_IsolationLevel() const +{ + assert(pInterface); + IsolationLevelEnum eNum=adXactUnspecified; + pInterface->get_IsolationLevel(&eNum); + return eNum; +} + +bool WpADOConnection::put_IsolationLevel(const IsolationLevelEnum& eNum) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_IsolationLevel(eNum)); +} + +sal_Int32 WpADOConnection::get_Attributes() const +{ + assert(pInterface); + sal_Int32 nRet=0; + pInterface->get_Attributes(&nRet); + return nRet; +} + +bool WpADOConnection::put_Attributes(sal_Int32 nRet) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_Attributes(nRet)); +} + +CursorLocationEnum WpADOConnection::get_CursorLocation() const +{ + assert(pInterface); + CursorLocationEnum eNum=adUseNone; + pInterface->get_CursorLocation(&eNum); + return eNum; +} + +bool WpADOConnection::put_CursorLocation(const CursorLocationEnum &eNum) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_CursorLocation(eNum)); +} + +ConnectModeEnum WpADOConnection::get_Mode() const +{ + assert(pInterface); + ConnectModeEnum eNum=adModeUnknown; + pInterface->get_Mode(&eNum); + return eNum; +} + +bool WpADOConnection::put_Mode(const ConnectModeEnum &eNum) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_Mode(eNum)); +} + +OUString WpADOConnection::get_Provider() const +{ + assert(pInterface); + OLEString aBSTR; pInterface->get_Provider(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +bool WpADOConnection::put_Provider(const OUString& _bstr) +{ + assert(pInterface); + OLEString bstr(_bstr); + return SUCCEEDED(pInterface->put_Provider(bstr.asBSTR())); +} + +sal_Int32 WpADOConnection::get_State() const +{ + assert(pInterface); + sal_Int32 nRet=0; + pInterface->get_State(&nRet); + return nRet; +} + +bool WpADOConnection::OpenSchema(SchemaEnum eNum,OLEVariant const & Restrictions,OLEVariant const & SchemaID,ADORecordset**pprset) +{ + assert(pInterface); + return SUCCEEDED(pInterface->OpenSchema(eNum,Restrictions,SchemaID,pprset)); +} + +OUString WpADOConnection::get_Version() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Version(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +bool WpADOCommand::putref_ActiveConnection( WpADOConnection *pCon) +{ + assert(pInterface); + if(pCon) + return SUCCEEDED(pInterface->putref_ActiveConnection(pCon->pInterface)); + else + return SUCCEEDED(pInterface->putref_ActiveConnection(nullptr)); +} + +void WpADOCommand::put_ActiveConnection(/* [in] */ const OLEVariant& vConn) +{ + assert(pInterface); + pInterface->put_ActiveConnection(vConn); +} + +void WpADOCommand::Create() +{ + IClassFactory2* pInterface2 = nullptr; + HRESULT hr = CoGetClassObject( ADOS::CLSID_ADOCOMMAND_21, + CLSCTX_INPROC_SERVER, + nullptr, + IID_IClassFactory2, + reinterpret_cast<void**>(&pInterface2) ); + + if( !FAILED( hr ) ) + { + ADOCommand* pCommand=nullptr; + IUnknown* pOuter=nullptr; + + hr = pInterface2->CreateInstanceLic( pOuter, + nullptr, + ADOS::IID_ADOCOMMAND_21, + ADOS::GetKeyStr().asBSTR(), + reinterpret_cast<void**>(&pCommand)); + + if( !FAILED( hr ) ) + { + operator=(pCommand); + pCommand->Release(); + } + + pInterface2->Release(); + } +} + +sal_Int32 WpADOCommand::get_State() const +{ + assert(pInterface); + sal_Int32 nRet=0; + pInterface->get_State(&nRet); + return nRet; +} + +OUString WpADOCommand::get_CommandText() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_CommandText(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +bool WpADOCommand::put_CommandText(const OUString &aCon) +{ + assert(pInterface); + OLEString bstr(aCon); + bool bErg = SUCCEEDED(pInterface->put_CommandText(bstr.asBSTR())); + + return bErg; +} + +sal_Int32 WpADOCommand::get_CommandTimeout() const +{ + assert(pInterface); + sal_Int32 nRet=0; + pInterface->get_CommandTimeout(&nRet); + return nRet; +} + +void WpADOCommand::put_CommandTimeout(sal_Int32 nRet) +{ + assert(pInterface); + pInterface->put_CommandTimeout(nRet); +} + +bool WpADOCommand::get_Prepared() const +{ + assert(pInterface); + VARIANT_BOOL bPrepared = VARIANT_FALSE; + pInterface->get_Prepared(&bPrepared); + return bPrepared == VARIANT_TRUE; +} + +bool WpADOCommand::put_Prepared(VARIANT_BOOL bPrepared) const +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_Prepared(bPrepared)); +} + +bool WpADOCommand::Execute(OLEVariant& RecordsAffected,OLEVariant& Params,long Options, ADORecordset** ppiRset) +{ + assert(pInterface); + return SUCCEEDED(pInterface->Execute(&RecordsAffected,&Params,Options,ppiRset)); +} + +ADOParameter* WpADOCommand::CreateParameter(const OUString &_bstr,DataTypeEnum Type,ParameterDirectionEnum Direction,long nSize,const OLEVariant &Value) +{ + assert(pInterface); + ADOParameter* pPara = nullptr; + OLEString bstr(_bstr); + bool bErg = SUCCEEDED(pInterface->CreateParameter(bstr.asBSTR(),Type,Direction,nSize,Value,&pPara)); + + return bErg ? pPara : nullptr; +} + +ADOParameters* WpADOCommand::get_Parameters() const +{ + assert(pInterface); + ADOParameters* pPara=nullptr; + pInterface->get_Parameters(&pPara); + return pPara; +} + +bool WpADOCommand::put_CommandType( /* [in] */ CommandTypeEnum lCmdType) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_CommandType(lCmdType)); +} + +CommandTypeEnum WpADOCommand::get_CommandType() const +{ + assert(pInterface); + CommandTypeEnum eNum=adCmdUnspecified; + pInterface->get_CommandType(&eNum); + return eNum; +} + +// returns the name of the field +OUString WpADOCommand::GetName() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +bool WpADOCommand::put_Name(const OUString& Name) +{ + assert(pInterface); + OLEString bstr(Name); + bool bErg = SUCCEEDED(pInterface->put_Name(bstr.asBSTR())); + + return bErg; +} +bool WpADOCommand::Cancel() +{ + assert(pInterface); + return SUCCEEDED(pInterface->Cancel()); +} + +OUString WpADOError::GetDescription() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Description(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +OUString WpADOError::GetSource() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Source(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +sal_Int32 WpADOError::GetNumber() const +{ + assert(pInterface); + sal_Int32 nErrNr=0; + pInterface->get_Number(&nErrNr); + return nErrNr; +} + +OUString WpADOError::GetSQLState() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_SQLState(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +sal_Int32 WpADOError::GetNativeError() const +{ + assert(pInterface); + sal_Int32 nErrNr=0; + pInterface->get_NativeError(&nErrNr); + return nErrNr; +} + +WpADOProperties WpADOField::get_Properties() +{ + assert(pInterface); + ADOProperties* pProps = nullptr; + pInterface->get_Properties(&pProps); + WpADOProperties aProps; + + aProps.setWithOutAddRef(pProps); + return aProps; +} + +sal_Int32 WpADOField::GetActualSize() const +{ + assert(pInterface); + ADO_LONGPTR nActualSize=0; + pInterface->get_ActualSize(&nActualSize); + return nActualSize; +} + +sal_Int32 WpADOField::GetAttributes() const +{ + assert(pInterface); + sal_Int32 eADOSFieldAttributes=0; + pInterface->get_Attributes(&eADOSFieldAttributes); + return eADOSFieldAttributes; +} + +sal_Int32 WpADOField::GetStatus() const +{ + assert(pInterface); + // pInterface->get_Status(&eADOSFieldAttributes); + return 0; +} + +sal_Int32 WpADOField::GetDefinedSize() const +{ + assert(pInterface); + ADO_LONGPTR nDefinedSize=0; + pInterface->get_DefinedSize(&nDefinedSize); + return nDefinedSize; +} + +// returns the name of the field +OUString WpADOField::GetName() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +DataTypeEnum WpADOField::GetADOType() const +{ + assert(pInterface); + DataTypeEnum eType=adEmpty; + pInterface->get_Type(&eType); + return eType; +} + +void WpADOField::get_Value(OLEVariant& aValVar) const +{ + assert(pInterface); + aValVar.setEmpty(); + pInterface->get_Value(&aValVar); +} + +OLEVariant WpADOField::get_Value() const +{ + assert(pInterface); + OLEVariant aValVar; + pInterface->get_Value(&aValVar); + return aValVar; +} + +bool WpADOField::PutValue(const OLEVariant& aVariant) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_Value(aVariant))); +} + +sal_Int32 WpADOField::GetPrecision() const +{ + assert(pInterface); + sal_uInt8 eType=0; + pInterface->get_Precision(&eType); + return eType; +} + +sal_Int32 WpADOField::GetNumericScale() const +{ + assert(pInterface); + sal_uInt8 eType=0; + pInterface->get_NumericScale(&eType); + return eType; +} + +bool WpADOField::AppendChunk(const OLEVariant& Variant) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->AppendChunk(Variant))); +} + +OLEVariant WpADOField::GetChunk(long Length) const +{ + assert(pInterface); + OLEVariant aValVar; + pInterface->GetChunk(Length,&aValVar); + return aValVar; +} + +void WpADOField::GetChunk(long Length,OLEVariant &aValVar) const +{ + assert(pInterface); + pInterface->GetChunk(Length,&aValVar); +} + +OLEVariant WpADOField::GetOriginalValue() const +{ + assert(pInterface); + OLEVariant aValVar; + pInterface->get_OriginalValue(&aValVar); + return aValVar; +} + +void WpADOField::GetOriginalValue(OLEVariant &aValVar) const +{ + assert(pInterface); + pInterface->get_OriginalValue(&aValVar); +} + +OLEVariant WpADOField::GetUnderlyingValue() const +{ + assert(pInterface); + OLEVariant aValVar; + pInterface->get_UnderlyingValue(&aValVar); + return aValVar; +} + +void WpADOField::GetUnderlyingValue(OLEVariant &aValVar) const +{ + assert(pInterface); + pInterface->get_UnderlyingValue(&aValVar); +} + +bool WpADOField::PutPrecision(sal_Int8 _prec) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_Precision(_prec))); +} + +bool WpADOField::PutNumericScale(sal_Int8 _prec) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_NumericScale(_prec))); +} + +void WpADOField::PutADOType(DataTypeEnum eType) +{ + assert(pInterface); + pInterface->put_Type(eType); +} + +bool WpADOField::PutDefinedSize(sal_Int32 _nDefSize) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_DefinedSize(_nDefSize))); +} + +bool WpADOField::PutAttributes(sal_Int32 _nDefSize) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_Attributes(_nDefSize))); +} + +OLEVariant WpADOProperty::GetValue() const +{ + OLEVariant aValVar; + if(pInterface) + pInterface->get_Value(&aValVar); + return aValVar; +} + +void WpADOProperty::GetValue(OLEVariant &aValVar) const +{ + assert(pInterface); + if(pInterface) + pInterface->get_Value(&aValVar); +} + +bool WpADOProperty::PutValue(const OLEVariant &aValVar) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_Value(aValVar))); +} + +OUString WpADOProperty::GetName() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +DataTypeEnum WpADOProperty::GetADOType() const +{ + assert(pInterface); + DataTypeEnum eType=adEmpty; + pInterface->get_Type(&eType); + return eType; +} + +sal_Int32 WpADOProperty::GetAttributes() const +{ + assert(pInterface); + sal_Int32 eADOSFieldAttributes=0; + pInterface->get_Attributes(&eADOSFieldAttributes); + return eADOSFieldAttributes; +} + +bool WpADOProperty::PutAttributes(sal_Int32 _nDefSize) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_Attributes(_nDefSize))); +} + void WpADORecordset::Create() +{ + IClassFactory2* pInterface2 = nullptr; + HRESULT hr = CoGetClassObject( ADOS::CLSID_ADORECORDSET_21, + CLSCTX_INPROC_SERVER, + nullptr, + IID_IClassFactory2, + reinterpret_cast<void**>(&pInterface2) ); + + if( !FAILED( hr ) ) + { + ADORecordset *pRec = nullptr; + IUnknown *pOuter = nullptr; + hr = pInterface2->CreateInstanceLic( pOuter, + nullptr, + ADOS::IID_ADORECORDSET_21, + ADOS::GetKeyStr().asBSTR(), + reinterpret_cast<void**>(&pRec)); + + if( !FAILED( hr ) ) + { + operator=(pRec); + pRec->Release(); + } + + pInterface2->Release(); + } +} + +bool WpADORecordset::Open( + /* [optional][in] */ VARIANT Source, + /* [optional][in] */ VARIANT ActiveConnection, + /* [defaultvalue][in] */ CursorTypeEnum CursorType, + /* [defaultvalue][in] */ LockTypeEnum LockType, + /* [defaultvalue][in] */ sal_Int32 Options) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->Open(Source,ActiveConnection,CursorType,LockType,Options))); +} + + +LockTypeEnum WpADORecordset::GetLockType() +{ + assert(pInterface); + LockTypeEnum eType=adLockUnspecified; + pInterface->get_LockType(&eType); + return eType; +} + +void WpADORecordset::Close() +{ + assert(pInterface); + pInterface->Close(); +} + +bool WpADORecordset::Cancel() const +{ + assert(pInterface); + return (SUCCEEDED(pInterface->Cancel())); +} + +sal_Int32 WpADORecordset::get_State() +{ + assert(pInterface); + sal_Int32 nState = 0; + pInterface->get_State(&nState); + return nState; +} + +bool WpADORecordset::Supports( /* [in] */ CursorOptionEnum CursorOptions) +{ + assert(pInterface); + VARIANT_BOOL bSupports=VARIANT_FALSE; + pInterface->Supports(CursorOptions,&bSupports); + return bSupports == VARIANT_TRUE; +} + +PositionEnum_Param WpADORecordset::get_AbsolutePosition() +{ + assert(pInterface); + PositionEnum_Param aTemp=adPosUnknown; + pInterface->get_AbsolutePosition(&aTemp); + return aTemp; +} + +void WpADORecordset::GetDataSource(IUnknown** _pInterface) const +{ + assert(pInterface); + pInterface->get_DataSource(_pInterface); +} + +void WpADORecordset::PutRefDataSource(IUnknown* _pInterface) +{ + assert(pInterface); + pInterface->putref_DataSource(_pInterface); +} + +void WpADORecordset::GetBookmark(VARIANT& var) +{ + assert(pInterface); + pInterface->get_Bookmark(&var); +} + +OLEVariant WpADORecordset::GetBookmark() +{ + assert(pInterface); + OLEVariant var; + pInterface->get_Bookmark(&var); + return var; +} + +CompareEnum WpADORecordset::CompareBookmarks(const OLEVariant& left,const OLEVariant& right) +{ + assert(pInterface); + CompareEnum eNum=adCompareNotComparable; + pInterface->CompareBookmarks(left,right,&eNum); + return eNum; +} + +bool WpADORecordset::SetBookmark(const OLEVariant &pSafeAr) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_Bookmark(pSafeAr)); +} + + +WpADOFields WpADORecordset::GetFields() const +{ + assert(pInterface); + ADOFields* pFields=nullptr; + pInterface->get_Fields(&pFields); + WpADOFields aFields; + aFields.setWithOutAddRef(pFields); + return aFields; +} + + +bool WpADORecordset::Move(sal_Int32 nRows, VARIANT aBmk) {return pInterface && SUCCEEDED(pInterface->Move(nRows, aBmk));} +bool WpADORecordset::MoveNext() {return pInterface && SUCCEEDED(pInterface->MoveNext());} +bool WpADORecordset::MovePrevious() {return pInterface && SUCCEEDED(pInterface->MovePrevious());} +bool WpADORecordset::MoveFirst() {return pInterface && SUCCEEDED(pInterface->MoveFirst());} +bool WpADORecordset::MoveLast() {return pInterface && SUCCEEDED(pInterface->MoveLast());} + +bool WpADORecordset::IsAtBOF() const +{ + assert(pInterface); + VARIANT_BOOL bIsAtBOF=VARIANT_FALSE; + pInterface->get_BOF(&bIsAtBOF); + return bIsAtBOF == VARIANT_TRUE; +} + +bool WpADORecordset::IsAtEOF() const +{ + assert(pInterface); + VARIANT_BOOL bIsAtEOF=VARIANT_FALSE; + pInterface->get_EOF(&bIsAtEOF); + return bIsAtEOF == VARIANT_TRUE; +} + +bool WpADORecordset::Delete(AffectEnum eNum) +{ + assert(pInterface); + return SUCCEEDED(pInterface->Delete(eNum)); +} + +bool WpADORecordset::AddNew(const OLEVariant &FieldList,const OLEVariant &Values) +{ + assert(pInterface); + return SUCCEEDED(pInterface->AddNew(FieldList,Values)); +} + +bool WpADORecordset::Update(const OLEVariant &FieldList,const OLEVariant &Values) +{ + assert(pInterface); + return SUCCEEDED(pInterface->Update(FieldList,Values)); +} + +bool WpADORecordset::CancelUpdate() +{ + assert(pInterface); + return SUCCEEDED(pInterface->CancelUpdate()); +} + +WpADOProperties WpADORecordset::get_Properties() const +{ + assert(pInterface); + ADOProperties* pProps=nullptr; + pInterface->get_Properties(&pProps); + WpADOProperties aProps; + aProps.setWithOutAddRef(pProps); + return aProps; +} + +bool WpADORecordset::NextRecordset(OLEVariant& RecordsAffected,ADORecordset** ppiRset) +{ + assert(pInterface); + return SUCCEEDED(pInterface->NextRecordset(&RecordsAffected,ppiRset)); +} + +bool WpADORecordset::get_RecordCount(ADO_LONGPTR &_nRet) const +{ + assert(pInterface); + return SUCCEEDED(pInterface->get_RecordCount(&_nRet)); +} + +bool WpADORecordset::get_MaxRecords(ADO_LONGPTR &_nRet) const +{ + assert(pInterface); + return SUCCEEDED(pInterface->get_MaxRecords(&_nRet)); +} + +bool WpADORecordset::put_MaxRecords(ADO_LONGPTR _nRet) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_MaxRecords(_nRet)); +} + +bool WpADORecordset::get_CursorType(CursorTypeEnum &_nRet) const +{ + assert(pInterface); + return SUCCEEDED(pInterface->get_CursorType(&_nRet)); +} + +bool WpADORecordset::put_CursorType(CursorTypeEnum _nRet) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_CursorType(_nRet)); +} + +bool WpADORecordset::get_LockType(LockTypeEnum &_nRet) const +{ + assert(pInterface); + return SUCCEEDED(pInterface->get_LockType(&_nRet)); +} + +bool WpADORecordset::put_LockType(LockTypeEnum _nRet) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_LockType(_nRet)); +} + +bool WpADORecordset::get_CacheSize(sal_Int32 &_nRet) const +{ + assert(pInterface); + return SUCCEEDED(pInterface->get_CacheSize(&_nRet)); +} + +bool WpADORecordset::put_CacheSize(sal_Int32 _nRet) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_CacheSize(_nRet)); +} + +bool WpADORecordset::UpdateBatch(AffectEnum AffectRecords) +{ + assert(pInterface); + return SUCCEEDED(pInterface->UpdateBatch(AffectRecords)); +} + +OUString WpADOParameter::GetName() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +DataTypeEnum WpADOParameter::GetADOType() const +{ + assert(pInterface); + DataTypeEnum eType=adEmpty; + pInterface->get_Type(&eType); + return eType; +} + +void WpADOParameter::put_Type(const DataTypeEnum& _eType) +{ + assert(pInterface); + pInterface->put_Type(_eType); +} + +sal_Int32 WpADOParameter::GetAttributes() const +{ + assert(pInterface); + sal_Int32 eADOSFieldAttributes=0; + pInterface->get_Attributes(&eADOSFieldAttributes); + return eADOSFieldAttributes; +} + +sal_Int32 WpADOParameter::GetPrecision() const +{ + assert(pInterface); + sal_uInt8 eType=0; + pInterface->get_Precision(&eType); + return eType; +} + +sal_Int32 WpADOParameter::GetNumericScale() const +{ + assert(pInterface); + sal_uInt8 eType=0; + pInterface->get_NumericScale(&eType); + return eType; +} + +ParameterDirectionEnum WpADOParameter::get_Direction() const +{ + assert(pInterface); + ParameterDirectionEnum alParmDirection=adParamUnknown; + pInterface->get_Direction(&alParmDirection); + return alParmDirection; +} + +void WpADOParameter::GetValue(OLEVariant& aValVar) const +{ + assert(pInterface); + pInterface->get_Value(&aValVar); +} + +OLEVariant WpADOParameter::GetValue() const +{ + assert(pInterface); + OLEVariant aValVar; + pInterface->get_Value(&aValVar); + return aValVar; +} + +bool WpADOParameter::PutValue(const OLEVariant& aVariant) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_Value(aVariant))); +} +bool WpADOParameter::AppendChunk(const OLEVariant& aVariant) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->AppendChunk(aVariant))); +} +bool WpADOParameter::put_Size(sal_Int32 _nSize) +{ + assert(pInterface); + return (SUCCEEDED(pInterface->put_Size(_nSize))); +} + +OUString WpADOColumn::get_Name() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +OUString WpADOColumn::get_RelatedColumn() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_RelatedColumn(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void WpADOColumn::put_Name(const OUString& _rName) +{ + assert(pInterface); + OLEString bstr(_rName); + pInterface->put_Name(bstr.asBSTR()); +} +void WpADOColumn::put_RelatedColumn(const OUString& _rName) +{ + assert(pInterface); + OLEString bstr(_rName); + pInterface->put_RelatedColumn(bstr.asBSTR()); +} + +DataTypeEnum WpADOColumn::get_Type() const +{ + assert(pInterface); + DataTypeEnum eNum = adVarChar; + pInterface->get_Type(&eNum); + return eNum; +} + +void WpADOColumn::put_Type(const DataTypeEnum& _eNum) +{ + assert(pInterface); + pInterface->put_Type(_eNum); +} + +sal_Int32 WpADOColumn::get_Precision() const +{ + assert(pInterface); + sal_Int32 nPrec=0; + pInterface->get_Precision(&nPrec); + return nPrec; +} + +void WpADOColumn::put_Precision(sal_Int32 _nPre) +{ + assert(pInterface); + pInterface->put_Precision(_nPre); +} + +sal_Int32 WpADOColumn::get_DefinedSize() const +{ + assert(pInterface); + sal_Int32 nPrec=0; + pInterface->get_DefinedSize(&nPrec); + return nPrec; +} +sal_uInt8 WpADOColumn::get_NumericScale() const +{ + assert(pInterface); + sal_uInt8 nPrec=0; + pInterface->get_NumericScale(&nPrec); + return nPrec; +} + +void WpADOColumn::put_NumericScale(sal_Int8 _nScale) +{ + assert(pInterface); + pInterface->put_NumericScale(_nScale); +} + +SortOrderEnum WpADOColumn::get_SortOrder() const +{ + assert(pInterface); + SortOrderEnum nPrec=adSortAscending; + pInterface->get_SortOrder(&nPrec); + return nPrec; +} + +void WpADOColumn::put_SortOrder(SortOrderEnum _nScale) +{ + assert(pInterface); + pInterface->put_SortOrder(_nScale); +} + +ColumnAttributesEnum WpADOColumn::get_Attributes() const +{ + assert(pInterface); + ColumnAttributesEnum eNum=adColNullable; + pInterface->get_Attributes(&eNum); + return eNum; +} + +bool WpADOColumn::put_Attributes(const ColumnAttributesEnum& _eNum) +{ + assert(pInterface); + return SUCCEEDED(pInterface->put_Attributes(_eNum)); +} + +WpADOProperties WpADOColumn::get_Properties() const +{ + assert(pInterface); + ADOProperties* pProps = nullptr; + pInterface->get_Properties(&pProps); + WpADOProperties aProps; + + aProps.setWithOutAddRef(pProps); + return aProps; +} + +OUString WpADOKey::get_Name() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void WpADOKey::put_Name(const OUString& _rName) +{ + assert(pInterface); + OLEString bstr(_rName); + pInterface->put_Name(bstr.asBSTR()); +} + +KeyTypeEnum WpADOKey::get_Type() const +{ + assert(pInterface); + KeyTypeEnum eNum=adKeyPrimary; + pInterface->get_Type(&eNum); + return eNum; +} + +void WpADOKey::put_Type(const KeyTypeEnum& _eNum) +{ + assert(pInterface); + pInterface->put_Type(_eNum); +} + +OUString WpADOKey::get_RelatedTable() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_RelatedTable(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void WpADOKey::put_RelatedTable(const OUString& _rName) +{ + assert(pInterface); + OLEString bstr(_rName); + pInterface->put_RelatedTable(bstr.asBSTR()); +} + +RuleEnum WpADOKey::get_DeleteRule() const +{ + assert(pInterface); + RuleEnum eNum = adRINone; + pInterface->get_DeleteRule(&eNum); + return eNum; +} + +void WpADOKey::put_DeleteRule(const RuleEnum& _eNum) +{ + assert(pInterface); + pInterface->put_DeleteRule(_eNum); +} + +RuleEnum WpADOKey::get_UpdateRule() const +{ + assert(pInterface); + RuleEnum eNum = adRINone; + pInterface->get_UpdateRule(&eNum); + return eNum; +} + +void WpADOKey::put_UpdateRule(const RuleEnum& _eNum) +{ + assert(pInterface); + pInterface->put_UpdateRule(_eNum); +} + +WpADOColumns WpADOKey::get_Columns() const +{ + assert(pInterface); + ADOColumns* pCols = nullptr; + pInterface->get_Columns(&pCols); + WpADOColumns aCols; + aCols.setWithOutAddRef(pCols); + return aCols; +} + +OUString WpADOIndex::get_Name() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void WpADOIndex::put_Name(const OUString& _rName) +{ + assert(pInterface); + OLEString bstr(_rName); + pInterface->put_Name(bstr.asBSTR()); +} + +bool WpADOIndex::get_Clustered() const +{ + assert(pInterface); + VARIANT_BOOL eNum = VARIANT_FALSE; + pInterface->get_Clustered(&eNum); + return eNum == VARIANT_TRUE; +} + +void WpADOIndex::put_Clustered(bool _b) +{ + assert(pInterface); + pInterface->put_Clustered(_b ? VARIANT_TRUE : VARIANT_FALSE); +} + +bool WpADOIndex::get_Unique() const +{ + assert(pInterface); + VARIANT_BOOL eNum = VARIANT_FALSE; + pInterface->get_Unique(&eNum); + return eNum == VARIANT_TRUE; +} + +void WpADOIndex::put_Unique(bool _b) +{ + assert(pInterface); + pInterface->put_Unique(_b ? VARIANT_TRUE : VARIANT_FALSE); +} + +bool WpADOIndex::get_PrimaryKey() const +{ + assert(pInterface); + VARIANT_BOOL eNum = VARIANT_FALSE; + pInterface->get_PrimaryKey(&eNum); + return eNum == VARIANT_TRUE; +} + +void WpADOIndex::put_PrimaryKey(bool _b) +{ + assert(pInterface); + pInterface->put_PrimaryKey(_b ? VARIANT_TRUE : VARIANT_FALSE); +} + +WpADOColumns WpADOIndex::get_Columns() const +{ + assert(pInterface); + ADOColumns* pCols = nullptr; + pInterface->get_Columns(&pCols); + WpADOColumns aCols; + aCols.setWithOutAddRef(pCols); + return aCols; +} + +void WpADOCatalog::putref_ActiveConnection(IDispatch* pCon) +{ + assert(pInterface); + pInterface->putref_ActiveConnection(pCon); +} + +WpADOTables WpADOCatalog::get_Tables() +{ + assert(pInterface); + ADOTables* pRet = nullptr; + pInterface->get_Tables(&pRet); + WpADOTables aRet; + aRet.setWithOutAddRef(pRet); + return aRet; +} + +WpADOViews WpADOCatalog::get_Views() +{ + assert(pInterface); + ADOViews* pRet = nullptr; + pInterface->get_Views(&pRet); + WpADOViews aRet; + aRet.setWithOutAddRef(pRet); + return aRet; +} + +WpADOGroups WpADOCatalog::get_Groups() +{ + assert(pInterface); + ADOGroups* pRet = nullptr; + pInterface->get_Groups(&pRet); + WpADOGroups aRet; + aRet.setWithOutAddRef(pRet); + return aRet; +} + +WpADOUsers WpADOCatalog::get_Users() +{ + assert(pInterface); + ADOUsers* pRet = nullptr; + pInterface->get_Users(&pRet); + WpADOUsers aRet; + aRet.setWithOutAddRef(pRet); + return aRet; +} + +ADOProcedures* WpADOCatalog::get_Procedures() +{ + assert(pInterface); + ADOProcedures* pRet = nullptr; + pInterface->get_Procedures(&pRet); + return pRet; +} + +OUString WpADOTable::get_Name() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void WpADOTable::put_Name(const OUString& _rName) +{ + assert(pInterface); + OLEString bstr(_rName); + pInterface->put_Name(bstr.asBSTR()); +} + +OUString WpADOTable::get_Type() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Type(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +WpADOColumns WpADOTable::get_Columns() const +{ + assert(pInterface); + ADOColumns* pCols = nullptr; + pInterface->get_Columns(&pCols); + WpADOColumns aCols; + aCols.setWithOutAddRef(pCols); + return aCols; +} + +WpADOIndexes WpADOTable::get_Indexes() const +{ + assert(pInterface); + ADOIndexes* pCols = nullptr; + pInterface->get_Indexes(&pCols); + WpADOIndexes aRet; + aRet.setWithOutAddRef(pCols); + return aRet; +} + +WpADOKeys WpADOTable::get_Keys() const +{ + assert(pInterface); + ADOKeys* pCols = nullptr; + pInterface->get_Keys(&pCols); + WpADOKeys aRet; + aRet.setWithOutAddRef(pCols); + return aRet; +} + +WpADOCatalog WpADOTable::get_ParentCatalog() const +{ + assert(pInterface); + ADOCatalog* pCat = nullptr; + pInterface->get_ParentCatalog(&pCat); + WpADOCatalog aRet; + aRet.setWithOutAddRef(pCat); + return aRet; +} + +WpADOProperties WpADOTable::get_Properties() const +{ + assert(pInterface); + ADOProperties* pProps = nullptr; + pInterface->get_Properties(&pProps); + WpADOProperties aProps; + aProps.setWithOutAddRef(pProps); + return aProps; +} + +OUString WpADOView::get_Name() const +{ + assert(pInterface); + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void WpADOView::get_Command(OLEVariant& _rVar) const +{ + assert(pInterface); + pInterface->get_Command(&_rVar); +} + +void WpADOView::put_Command(OLEVariant const & _rVar) +{ + assert(pInterface); + pInterface->put_Command(_rVar); +} + +OUString WpADOGroup::get_Name() const +{ + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void WpADOGroup::put_Name(const OUString& _rName) +{ + OLEString bstr(_rName); + pInterface->put_Name(bstr.asBSTR()); +} + +RightsEnum WpADOGroup::GetPermissions( + /* [in] */ const OLEVariant& Name, + /* [in] */ ObjectTypeEnum ObjectType) +{ + RightsEnum Rights=adRightNone; + OLEVariant ObjectTypeId; + ObjectTypeId.setNoArg(); + pInterface->GetPermissions(Name,ObjectType,ObjectTypeId,&Rights); + return Rights; +} + +bool WpADOGroup::SetPermissions( + /* [in] */ const OLEVariant& Name, + /* [in] */ ObjectTypeEnum ObjectType, + /* [in] */ ActionEnum Action, + /* [in] */ RightsEnum Rights) +{ + OLEVariant ObjectTypeId; + ObjectTypeId.setNoArg(); + return SUCCEEDED(pInterface->SetPermissions(Name,ObjectType,Action,Rights,adInheritNone,ObjectTypeId)); +} + +WpADOUsers WpADOGroup::get_Users( ) +{ + ADOUsers* pRet = nullptr; + pInterface->get_Users( &pRet); + WpADOUsers aRet; + aRet.setWithOutAddRef(pRet); + return aRet; +} + +OUString WpADOUser::get_Name() const +{ + OLEString aBSTR; + pInterface->get_Name(aBSTR.getAddress()); + return aBSTR.asOUString(); +} + +void WpADOUser::put_Name(const OUString& _rName) +{ + OLEString bstr(_rName); + pInterface->put_Name(bstr.asBSTR()); +} + +bool WpADOUser::ChangePassword(const OUString& _rPwd,const OUString& _rNewPwd) +{ + OLEString sStr1(_rPwd); + OLEString sStr2(_rNewPwd); + bool bErg = SUCCEEDED(pInterface->ChangePassword(sStr1.asBSTR(),sStr2.asBSTR())); + return bErg; +} + +WpADOGroups WpADOUser::get_Groups() +{ + ADOGroups* pRet = nullptr; + pInterface->get_Groups(&pRet); + WpADOGroups aRet; + aRet.setWithOutAddRef(pRet); + return aRet; +} + +RightsEnum WpADOUser::GetPermissions( + /* [in] */ const OLEVariant& Name, + /* [in] */ ObjectTypeEnum ObjectType) +{ + RightsEnum Rights=adRightNone; + OLEVariant ObjectTypeId; + ObjectTypeId.setNoArg(); + pInterface->GetPermissions(Name,ObjectType,ObjectTypeId,&Rights); + return Rights; +} + +bool WpADOUser::SetPermissions( + /* [in] */ const OLEVariant& Name, + /* [in] */ ObjectTypeEnum ObjectType, + /* [in] */ ActionEnum Action, + /* [in] */ RightsEnum Rights) +{ + OLEVariant ObjectTypeId; + ObjectTypeId.setNoArg(); + return SUCCEEDED(pInterface->SetPermissions(Name,ObjectType,Action,Rights,adInheritNone,ObjectTypeId)); +} + +WpBase::WpBase() : pIUnknown(nullptr) +{ +} +WpBase::WpBase(IDispatch* pInt) + :pIUnknown(pInt) +{ + if (pIUnknown) + { + pIUnknown->AddRef(); + } +} + +WpBase::WpBase(const WpBase& aWrapper) + :pIUnknown(aWrapper.pIUnknown) +{ + if (pIUnknown) + pIUnknown->AddRef(); +} + +//inline +WpBase& WpBase::operator=(const WpBase& rhs) +{ + operator=(rhs.pIUnknown); + return *this; +}; + +WpBase& WpBase::operator=(IDispatch* rhs) +{ + if (pIUnknown != rhs) + { + if (pIUnknown) + pIUnknown->Release(); + pIUnknown = rhs; + if (pIUnknown) + pIUnknown->AddRef(); + } + return *this; +} + +WpBase::~WpBase() +{ + if (pIUnknown) + { + pIUnknown->Release(); + pIUnknown = nullptr; + } +} + +void WpBase::clear() +{ + if (pIUnknown) + { + pIUnknown->Release(); + pIUnknown = nullptr; + } +} + + +bool WpBase::IsValid() const +{ + return pIUnknown != nullptr; +} +WpBase::operator IDispatch*() +{ + return pIUnknown; +} + +ADORecordset* WpADOConnection::getExportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) +{ + // Create elements used in the array + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[6]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schema.getLength() && schema.toChar() != '%') + varCriteria[nPos].setString(schema); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + varCriteria[nPos].setString(table); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaForeignKeys,vsa,vtEmpty,&pRecordset); + return pRecordset; +} + +ADORecordset* WpADOConnection::getImportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) +{ + // Create elements used in the array + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[6]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schema.getLength() && schema.toChar() != '%') + varCriteria[nPos].setString(schema); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + varCriteria[nPos].setString(table); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaForeignKeys,vsa,vtEmpty,&pRecordset); + + return pRecordset; + +} + +ADORecordset* WpADOConnection::getPrimaryKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) +{ + // Create elements used in the array + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[3]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schema.getLength() && schema.toChar() != '%') + varCriteria[nPos].setString(schema); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + varCriteria[nPos].setString(table); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaPrimaryKeys,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getIndexInfo( + const css::uno::Any& catalog, const OUString& schema, const OUString& table, + bool /*unique*/, bool /*approximate*/ ) +{ + // Create elements used in the array + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[5]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schema.getLength() && schema.toChar() != '%') + varCriteria[nPos].setString(schema); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// INDEX_NAME + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TYPE + + varCriteria[nPos].setString(table); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaIndexes,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getTablePrivileges( const css::uno::Any& catalog, + const OUString& schemaPattern, + const OUString& tableNamePattern ) +{ + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[5]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schemaPattern.getLength() && schemaPattern.toChar() != '%') + varCriteria[nPos].setString(schemaPattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + if(tableNamePattern.toChar() != '%') + varCriteria[nPos].setString(tableNamePattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// GRANTOR + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// GRANTEE + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaTablePrivileges,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getCrossReference( const css::uno::Any& primaryCatalog, + const OUString& primarySchema, + const OUString& primaryTable, + const css::uno::Any& foreignCatalog, + const OUString& foreignSchema, + const OUString& foreignTable) +{ + // Create elements used in the array + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[6]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(primaryCatalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(primaryCatalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(primarySchema.getLength() && primarySchema.toChar() != '%') + varCriteria[nPos].setString(primarySchema); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + varCriteria[nPos].setString(primaryTable); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + if(foreignCatalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(foreignCatalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(foreignSchema.getLength() && foreignSchema.toChar() != '%') + varCriteria[nPos].setString(foreignSchema); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + varCriteria[nPos].setString(foreignTable); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaForeignKeys,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getProcedures( const css::uno::Any& catalog, + const OUString& schemaPattern, + const OUString& procedureNamePattern ) +{ + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[3]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schemaPattern.getLength() && schemaPattern.toChar() != '%') + varCriteria[nPos].setString(schemaPattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + if(procedureNamePattern.toChar() != '%') + varCriteria[nPos].setString(procedureNamePattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaProcedures,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getProcedureColumns( const css::uno::Any& catalog, + const OUString& schemaPattern, + const OUString& procedureNamePattern, + const OUString& columnNamePattern ) +{ + // Create elements used in the array + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[4]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schemaPattern.getLength() && schemaPattern.toChar() != '%') + varCriteria[nPos].setString(schemaPattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + if(procedureNamePattern.toChar() != '%') + varCriteria[nPos].setString(procedureNamePattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + if(columnNamePattern.toChar() != '%') + varCriteria[nPos].setString(columnNamePattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// COLUMN_NAME + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaProcedureParameters,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getTables( const css::uno::Any& catalog, + const OUString& schemaPattern, + const OUString& tableNamePattern, + const css::uno::Sequence< OUString >& types ) +{ + // Create elements used in the array + HRESULT hr = S_OK; + OLEVariant varCriteria[4]; + + sal_Int32 nPos=0; + OUString sCatalog; + if ( catalog.hasValue() && (catalog >>= sCatalog) ) + varCriteria[nPos].setString(sCatalog); + + ++nPos; + if(schemaPattern.getLength() && schemaPattern.toChar() != '%') + varCriteria[nPos].setString(schemaPattern); + + ++nPos; + if(tableNamePattern.toChar() != '%') + varCriteria[nPos].setString(tableNamePattern); + + ++nPos; + OUStringBuffer aTypes; + const OUString* pIter = types.getConstArray(); + const OUString* pEnd = pIter + types.getLength(); + for( ; pIter != pEnd ; ++pIter) + { + if ( aTypes.getLength() ) + aTypes.append(","); + aTypes.append(*pIter); + } + + OUString sTypeNames = aTypes.makeStringAndClear(); + if ( sTypeNames.getLength() ) + varCriteria[nPos].setString(sTypeNames); + + // Create SafeArray Bounds and initialize the array + const sal_Int32 nCrit = SAL_N_ELEMENTS(varCriteria); + SAFEARRAYBOUND rgsabound[1]; + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = nCrit; + SAFEARRAY *psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + // Set the values for each element of the array + for( long i = 0 ; i < nCrit && SUCCEEDED( hr );i++) + { + hr = SafeArrayPutElement(psa, &i,&varCriteria[i]); + } + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaTables,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getColumns( const css::uno::Any& catalog, + const OUString& schemaPattern, + const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + // Create elements used in the array + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[4]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schemaPattern.getLength() && schemaPattern.toChar() != '%') + varCriteria[nPos].setString(schemaPattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + if(tableNamePattern.toChar() != '%') + varCriteria[nPos].setString(tableNamePattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + varCriteria[nPos].setString(columnNamePattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// COLUMN_NAME + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaColumns,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getColumnPrivileges( const css::uno::Any& catalog, + const OUString& schema, + const OUString& table, + const OUString& columnNamePattern ) +{ + // Create elements used in the array + SAFEARRAYBOUND rgsabound[1]; + SAFEARRAY *psa = nullptr; + OLEVariant varCriteria[4]; + + // Create SafeArray Bounds and initialize the array + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = SAL_N_ELEMENTS(varCriteria); + psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos=0; + if(catalog.hasValue()) + varCriteria[nPos].setString(::comphelper::getString(catalog)); + + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_CATALOG + if(schema.getLength() && schema.toChar() != '%') + varCriteria[nPos].setString(schema); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_SCHEMA + + varCriteria[nPos].setString(table); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// TABLE_NAME + + varCriteria[nPos].setString(columnNamePattern); + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++;// COLUMN_NAME + + OLEVariant vtEmpty; + vtEmpty.setNoArg(); + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + ADORecordset *pRecordset = nullptr; + OpenSchema(adSchemaColumnPrivileges,vsa,vtEmpty,&pRecordset); + + return pRecordset; +} + +ADORecordset* WpADOConnection::getTypeInfo(DataTypeEnum /*_eType*/) +{ + // Create elements used in the array + OLEVariant varCriteria[2]; + const int nCrit = SAL_N_ELEMENTS(varCriteria); + // Create SafeArray Bounds and initialize the array + SAFEARRAYBOUND rgsabound[1]; + rgsabound[0].lLbound = 0; + rgsabound[0].cElements = nCrit; + SAFEARRAY *psa = SafeArrayCreate( VT_VARIANT, 1, rgsabound ); + + sal_Int32 nPos = 0; + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++; + SafeArrayPutElement(psa,&nPos,&varCriteria[nPos]);nPos++; + + // Initialize and fill the SafeArray + OLEVariant vsa; + vsa.setArray(psa,VT_VARIANT); + + OLEVariant aEmpty; + aEmpty.setNoArg(); + + ADORecordset *pRec=nullptr; + OpenSchema(adSchemaProviderTypes,vsa,aEmpty,&pRec); + + return pRec; +} + +void WpADOColumn::put_ParentCatalog(/* [in] */ _ADOCatalog __RPC_FAR *ppvObject) +{ + assert(pInterface); + bool bRet = SUCCEEDED(pInterface->put_ParentCatalog(ppvObject)); + SAL_WARN_IF(!bRet, "connectivity.ado", "Could not set ParentCatalog!"); +} + +void WpADOTable::putref_ParentCatalog(/* [in] */ _ADOCatalog __RPC_FAR *ppvObject) +{ + assert(pInterface); + bool bRet = SUCCEEDED(pInterface->putref_ParentCatalog(ppvObject)); + SAL_WARN_IF(!bRet, "connectivity.ado", "Could not set ParentCatalog!"); +} + +void WpBase::setIDispatch(IDispatch* _pIUnknown) +{ + pIUnknown = _pIUnknown; +} + +void OTools::putValue(const WpADOProperties& _rProps,const OLEVariant &_aPosition,const OLEVariant &_aValVar) +{ + SAL_WARN_IF(!_rProps.IsValid(), "connectivity.ado", "Properties are not valid!"); + WpADOProperty aProp(_rProps.GetItem(_aPosition)); + if ( aProp.IsValid() ) + { + bool bRet = aProp.PutValue(_aValVar); + SAL_WARN_IF(!bRet, "connectivity.ado", "Could not put value!"); + } +} + +OLEVariant OTools::getValue(const WpADOProperties& _rProps,const OLEVariant &_aPosition) +{ + WpADOProperty aProp(_rProps.GetItem(_aPosition)); + return aProp.GetValue(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/ado/ado.component b/connectivity/source/drivers/ado/ado.component new file mode 100644 index 000000000..b90673ae8 --- /dev/null +++ b/connectivity/source/drivers/ado/ado.component @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" + environment="@CPPU_ENV@:affine" prefix="ado" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.ado.ODriver"> + <service name="com.sun.star.sdbc.Driver"/> + <service name="com.sun.star.sdbcx.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/ado/adoimp.cxx b/connectivity/source/drivers/ado/adoimp.cxx new file mode 100644 index 000000000..1e368d8dd --- /dev/null +++ b/connectivity/source/drivers/ado/adoimp.cxx @@ -0,0 +1,325 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/sdbcx/PrivilegeObject.hpp> +#include <connectivity/dbexception.hxx> +#include <ado/Awrapado.hxx> +#include <ado/adoimp.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/sdbc/DataType.hpp> + + +using namespace connectivity::ado; +using namespace com::sun::star::uno; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::sdbcx; + + +#define MYADOID(l) {l, 0,0x10,{0x80,0,0,0xAA,0,0x6D,0x2E,0xA4}}; + +const CLSID ADOS::CLSID_ADOCONNECTION_21 = MYADOID(0x00000514); +const IID ADOS::IID_ADOCONNECTION_21 = MYADOID(0x00000550); + +const CLSID ADOS::CLSID_ADOCOMMAND_21 = MYADOID(0x00000507); +const IID ADOS::IID_ADOCOMMAND_21 = MYADOID(0x0000054E); + +const CLSID ADOS::CLSID_ADORECORDSET_21 = MYADOID(0x00000535); +const IID ADOS::IID_ADORECORDSET_21 = MYADOID(0x0000054F); + +const CLSID ADOS::CLSID_ADOCATALOG_25 = MYADOID(0x00000602); +const IID ADOS::IID_ADOCATALOG_25 = MYADOID(0x00000603); + +const CLSID ADOS::CLSID_ADOINDEX_25 = MYADOID(0x0000061E); +const IID ADOS::IID_ADOINDEX_25 = MYADOID(0x0000061F); + +const CLSID ADOS::CLSID_ADOTABLE_25 = MYADOID(0x00000609); +const IID ADOS::IID_ADOTABLE_25 = MYADOID(0x00000610); + +const CLSID ADOS::CLSID_ADOKEY_25 = MYADOID(0x00000621); +const IID ADOS::IID_ADOKEY_25 = MYADOID(0x00000622); + +const CLSID ADOS::CLSID_ADOCOLUMN_25 = MYADOID(0x0000061B); +const IID ADOS::IID_ADOCOLUMN_25 = MYADOID(0x0000061C); + +const CLSID ADOS::CLSID_ADOGROUP_25 = MYADOID(0x00000615); +const IID ADOS::IID_ADOGROUP_25 = MYADOID(0x00000616); + +const CLSID ADOS::CLSID_ADOUSER_25 = MYADOID(0x00000618); +const IID ADOS::IID_ADOUSER_25 = MYADOID(0x00000619); + +const CLSID ADOS::CLSID_ADOVIEW_25 = MYADOID(0x00000612); +const IID ADOS::IID_ADOVIEW_25 = MYADOID(0x00000613); + +OLEString& ADOS::GetKeyStr() +{ + static OLEString sKeyStr(OUString("gxwaezucfyqpwjgqbcmtsncuhwsnyhiohwxz")); + return sKeyStr; +} + + +sal_Int32 ADOS::MapADOType2Jdbc(DataTypeEnum eType) +{ + sal_Int32 nType = DataType::VARCHAR; + switch (eType) + { + case adUnsignedSmallInt: + case adSmallInt: nType = DataType::SMALLINT; break; + case adUnsignedInt: + case adInteger: nType = DataType::INTEGER; break; + case adUnsignedBigInt: + case adBigInt: nType = DataType::BIGINT; break; + case adSingle: nType = DataType::FLOAT; break; + case adDouble: nType = DataType::DOUBLE; break; + case adCurrency: nType = DataType::DOUBLE; break; + case adVarNumeric: + case adNumeric: nType = DataType::NUMERIC; break; + case adDecimal: nType = DataType::DECIMAL; break; + case adDBDate: nType = DataType::DATE; break; + case adDBTime: nType = DataType::TIME; break; + case adDate: + case adDBTimeStamp: nType = DataType::TIMESTAMP; break; + case adBoolean: nType = DataType::BOOLEAN; break; +// case adArray: nType = DataType::ARRAY; break; + case adBinary: nType = DataType::BINARY; break; + case adGUID: nType = DataType::OBJECT; break; + case adBSTR: + case adVarWChar: + case adWChar: + case adVarChar: nType = DataType::VARCHAR; break; + case adLongVarWChar: + case adLongVarChar: nType = DataType::LONGVARCHAR; break; + case adVarBinary: nType = DataType::VARBINARY; break; + case adLongVarBinary: nType = DataType::LONGVARBINARY;break; + case adChar: nType = DataType::CHAR; break; + case adUnsignedTinyInt: + case adTinyInt: nType = DataType::TINYINT; break; + case adEmpty: nType = DataType::SQLNULL; break; + case adUserDefined: + case adPropVariant: + case adFileTime: + case adChapter: + case adIDispatch: + case adIUnknown: + case adError: + case adVariant: + nType = DataType::OTHER; break; + default: + OSL_FAIL("MapADOType2Jdbc: Unknown Type!"); + ; + } + return nType; +} + +DataTypeEnum ADOS::MapJdbc2ADOType(sal_Int32 _nType,sal_Int32 _nJetEngine) +{ + switch (_nType) + { + case DataType::SMALLINT: return adSmallInt; break; + case DataType::INTEGER: return adInteger; break; + case DataType::BIGINT: return adBigInt; break; + case DataType::FLOAT: return adSingle; break; + case DataType::DOUBLE: return adDouble; break; + case DataType::NUMERIC: return adNumeric; break; + case DataType::DECIMAL: return adDecimal; break; + case DataType::DATE: return isJetEngine(_nJetEngine) ? adDate : adDBDate; break; + case DataType::TIME: return adDBTime; break; + case DataType::TIMESTAMP: return isJetEngine(_nJetEngine) ? adDate : adDBTimeStamp; break; + case DataType::BOOLEAN: + case DataType::BIT: return adBoolean; break; + case DataType::BINARY: return adBinary; break; + case DataType::VARCHAR: return adVarWChar; break; + case DataType::CLOB: + case DataType::LONGVARCHAR: return adLongVarWChar; break; + case DataType::VARBINARY: return adVarBinary; break; + case DataType::BLOB: + case DataType::LONGVARBINARY: return adLongVarBinary; break; + case DataType::CHAR: return adWChar; break; + case DataType::TINYINT: return isJetEngine(_nJetEngine) ? adUnsignedTinyInt : adTinyInt;break; + case DataType::OBJECT: return adGUID; break; + default: + OSL_FAIL("MapJdbc2ADOType: Unknown Type!"); + ; + } + return adEmpty; +} + +const int JET_ENGINETYPE_UNKNOWN = 0; +const int JET_ENGINETYPE_JET10 = 1; +const int JET_ENGINETYPE_JET11 = 2; +const int JET_ENGINETYPE_JET20 = 3; +const int JET_ENGINETYPE_JET3X = 4; +const int JET_ENGINETYPE_JET4X = 5; +const int JET_ENGINETYPE_DBASE3 = 10; +const int JET_ENGINETYPE_DBASE4 = 11; +const int JET_ENGINETYPE_DBASE5 = 12; +const int JET_ENGINETYPE_EXCEL30 = 20; +const int JET_ENGINETYPE_EXCEL40 = 21; +const int JET_ENGINETYPE_EXCEL50 = 22; +const int JET_ENGINETYPE_EXCEL80 = 23; +const int JET_ENGINETYPE_EXCEL90 = 24; +const int JET_ENGINETYPE_EXCHANGE4 = 30; +const int JET_ENGINETYPE_LOTUSWK1 = 40; +const int JET_ENGINETYPE_LOTUSWK3 = 41; +const int JET_ENGINETYPE_LOTUSWK4 = 42; +const int JET_ENGINETYPE_PARADOX3X = 50; +const int JET_ENGINETYPE_PARADOX4X = 51; +const int JET_ENGINETYPE_PARADOX5X = 52; +const int JET_ENGINETYPE_PARADOX7X = 53; +const int JET_ENGINETYPE_TEXT1X = 60; +const int JET_ENGINETYPE_HTML1X = 70; + +bool ADOS::isJetEngine(sal_Int32 _nEngineType) +{ + bool bRet = false; + switch(_nEngineType) + { + case JET_ENGINETYPE_UNKNOWN: + case JET_ENGINETYPE_JET10: + case JET_ENGINETYPE_JET11: + case JET_ENGINETYPE_JET20: + case JET_ENGINETYPE_JET3X: + case JET_ENGINETYPE_JET4X: + case JET_ENGINETYPE_DBASE3: + case JET_ENGINETYPE_DBASE4: + case JET_ENGINETYPE_DBASE5: + case JET_ENGINETYPE_EXCEL30: + case JET_ENGINETYPE_EXCEL40: + case JET_ENGINETYPE_EXCEL50: + case JET_ENGINETYPE_EXCEL80: + case JET_ENGINETYPE_EXCEL90: + case JET_ENGINETYPE_EXCHANGE4: + case JET_ENGINETYPE_LOTUSWK1: + case JET_ENGINETYPE_LOTUSWK3: + case JET_ENGINETYPE_LOTUSWK4: + case JET_ENGINETYPE_PARADOX3X: + case JET_ENGINETYPE_PARADOX4X: + case JET_ENGINETYPE_PARADOX5X: + case JET_ENGINETYPE_PARADOX7X: + case JET_ENGINETYPE_TEXT1X: + case JET_ENGINETYPE_HTML1X: + bRet = true; + break; + } + return bRet; +} + +ObjectTypeEnum ADOS::mapObjectType2Ado(sal_Int32 objType) +{ + ObjectTypeEnum eType = adPermObjTable; + switch(objType) + { + case PrivilegeObject::TABLE: + eType = adPermObjTable; + break; + case PrivilegeObject::VIEW: + eType = adPermObjView; + break; + case PrivilegeObject::COLUMN: + eType = adPermObjColumn; + break; + } + return eType; +} + +sal_Int32 ADOS::mapAdoType2Object(ObjectTypeEnum objType) +{ + sal_Int32 nType = PrivilegeObject::TABLE; + switch(objType) + { + case adPermObjTable: + nType = PrivilegeObject::TABLE; + break; + case adPermObjView: + nType = PrivilegeObject::VIEW; + break; + case adPermObjColumn: + nType = PrivilegeObject::COLUMN; + break; + default: + OSL_FAIL( "ADOS::mapAdoType2Object: privilege type cannot be translated!" ); + break; + } + return nType; +} +#ifdef DELETE +#undef DELETE +#endif + +sal_Int32 ADOS::mapAdoRights2Sdbc(RightsEnum eRights) +{ + sal_Int32 nRights = 0; + if((eRights & adRightInsert) == adRightInsert) + nRights |= Privilege::INSERT; + if((eRights & adRightDelete) == adRightDelete) + nRights |= css::sdbcx::Privilege::DELETE; + if((eRights & adRightUpdate) == adRightUpdate) + nRights |= Privilege::UPDATE; + if((eRights & adRightWriteDesign) == adRightWriteDesign) + nRights |= Privilege::ALTER; + if((eRights & adRightRead) == adRightRead) + nRights |= Privilege::SELECT; + if((eRights & adRightReference) == adRightReference) + nRights |= Privilege::REFERENCE; + if((eRights & adRightDrop) == adRightDrop) + nRights |= Privilege::DROP; + + return nRights; +} + +sal_Int32 ADOS::mapRights2Ado(sal_Int32 nRights) +{ + sal_Int32 eRights = adRightNone; + + if((nRights & Privilege::INSERT) == Privilege::INSERT) + eRights |= adRightInsert; + if((nRights & Privilege::DELETE) == Privilege::DELETE) + eRights |= adRightDelete; + if((nRights & Privilege::UPDATE) == Privilege::UPDATE) + eRights |= adRightUpdate; + if((nRights & Privilege::ALTER) == Privilege::ALTER) + eRights |= adRightWriteDesign; + if((nRights & Privilege::SELECT) == Privilege::SELECT) + eRights |= adRightRead; + if((nRights & Privilege::REFERENCE) == Privilege::REFERENCE) + eRights |= adRightReference; + if((nRights & Privilege::DROP) == Privilege::DROP) + eRights |= adRightDrop; + + return eRights; +} + +WpADOField ADOS::getField(ADORecordset* _pRecordSet,sal_Int32 _nColumnIndex) +{ + if ( !_pRecordSet ) + return WpADOField(); + + ADOFields* pFields = nullptr; + _pRecordSet->get_Fields(&pFields); + WpOLEAppendCollection<ADOFields, ADOField, WpADOField> aFields(pFields); + if(_nColumnIndex <= 0 || _nColumnIndex > aFields.GetItemCount()) + ::dbtools::throwInvalidIndexException(nullptr); + WpADOField aField(aFields.GetItem(_nColumnIndex-1)); + if(!aField.IsValid()) + ::dbtools::throwInvalidIndexException(nullptr); + return aField; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CCatalog.cxx b/connectivity/source/drivers/calc/CCatalog.cxx new file mode 100644 index 000000000..6853a5f9d --- /dev/null +++ b/connectivity/source/drivers/calc/CCatalog.cxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <calc/CCatalog.hxx> +#include <calc/CConnection.hxx> +#include <calc/CTables.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::calc; + +OCalcCatalog::OCalcCatalog(OCalcConnection* _pCon) : file::OFileCatalog(_pCon) +{ +} + +void OCalcCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + Sequence< OUString > aTypes; + OCalcConnection::ODocHolder aDocHolder(static_cast<OCalcConnection*>(m_pConnection)); + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(), + "%", "%", aTypes); + + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while(xResult->next()) + aVector.push_back(xRow->getString(3)); + } + if(m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset( new OCalcTables(m_xMetaData,*this,m_aMutex,aVector) ); + + // this avoids that the document will be loaded a 2nd time when one table will be accessed. + //if ( m_pTables && m_pTables->hasElements() ) + // m_pTables->getByIndex(0); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CConnection.cxx b/connectivity/source/drivers/calc/CConnection.cxx new file mode 100644 index 000000000..9b03d473b --- /dev/null +++ b/connectivity/source/drivers/calc/CConnection.cxx @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <calc/CConnection.hxx> +#include <calc/CDatabaseMetaData.hxx> +#include <calc/CCatalog.hxx> +#include <calc/CDriver.hxx> +#include <resource/sharedresources.hxx> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <tools/urlobj.hxx> +#include <component/CPreparedStatement.hxx> +#include <component/CStatement.hxx> +#include <unotools/pathoptions.hxx> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <strings.hrc> + +using namespace connectivity::calc; +using namespace connectivity::file; + +typedef connectivity::file::OConnection OConnection_BASE; + + +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::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::sheet; + + +OCalcConnection::OCalcConnection(ODriver* _pDriver) : OConnection(_pDriver),m_nDocCount(0) +{ + // m_aFilenameExtension is not used +} + +OCalcConnection::~OCalcConnection() +{ +} + +void OCalcConnection::construct(const OUString& url,const Sequence< PropertyValue >& info) +{ + // open file + + sal_Int32 nLen = url.indexOf(':'); + nLen = url.indexOf(':',nLen+1); + OUString aDSN(url.copy(nLen+1)); + + m_aFileName = aDSN; + INetURLObject aURL; + aURL.SetSmartProtocol(INetProtocol::File); + { + SvtPathOptions aPathOptions; + m_aFileName = aPathOptions.SubstituteVariable(m_aFileName); + } + aURL.SetSmartURL(m_aFileName); + if ( aURL.GetProtocol() == INetProtocol::NotValid ) + { + // don't pass invalid URL to loadComponentFromURL + throw SQLException(); + } + m_aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + + m_sPassword.clear(); + const char pPwd[] = "password"; + + const PropertyValue *pIter = info.getConstArray(); + const PropertyValue *pEnd = pIter + info.getLength(); + for(;pIter != pEnd;++pIter) + { + if(pIter->Name == pPwd) + { + pIter->Value >>= m_sPassword; + break; + } + } // for(;pIter != pEnd;++pIter) + ODocHolder aDocHolder(this); // just to test that the doc can be loaded + acquireDoc(); +} + +Reference< XSpreadsheetDocument> const & OCalcConnection::acquireDoc() +{ + if ( m_xDoc.is() ) + { + osl_atomic_increment(&m_nDocCount); + return m_xDoc; + } + // open read-only as long as updating isn't implemented + Sequence<PropertyValue> aArgs(2); + aArgs[0].Name = "Hidden"; + aArgs[0].Value <<= true; + aArgs[1].Name = "ReadOnly"; + aArgs[1].Value <<= true; + + if ( !m_sPassword.isEmpty() ) + { + const sal_Int32 nPos = aArgs.getLength(); + aArgs.realloc(nPos+1); + aArgs[nPos].Name = "Password"; + aArgs[nPos].Value <<= m_sPassword; + } + + Reference< XDesktop2 > xDesktop = Desktop::create( getDriver()->getComponentContext() ); + Reference< XComponent > xComponent; + Any aLoaderException; + try + { + xComponent = xDesktop->loadComponentFromURL( + m_aFileName, "_blank", 0, aArgs ); + } + catch( const Exception& ) + { + aLoaderException = ::cppu::getCaughtException(); + } + + m_xDoc.set(xComponent, UNO_QUERY ); + + // if the URL is not a spreadsheet document, throw the exception here + // instead of at the first access to it + if ( !m_xDoc.is() ) + { + Any aErrorDetails; + if ( aLoaderException.hasValue() ) + { + Exception aLoaderError; + OSL_VERIFY( aLoaderException >>= aLoaderError ); + + SQLException aDetailException; + aDetailException.Message = m_aResources.getResourceStringWithSubstitution( + STR_LOAD_FILE_ERROR_MESSAGE, + "$exception_type$", aLoaderException.getValueTypeName(), + "$error_message$", aLoaderError.Message + ); + aErrorDetails <<= aDetailException; + } + + const OUString sError( m_aResources.getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, + "$filename$", m_aFileName + ) ); + ::dbtools::throwGenericSQLException( sError, *this, aErrorDetails ); + } + osl_atomic_increment(&m_nDocCount); + m_xCloseVetoButTerminateListener.set(new CloseVetoButTerminateListener); + m_xCloseVetoButTerminateListener->start(m_xDoc, xDesktop); + return m_xDoc; +} + +void OCalcConnection::releaseDoc() +{ + if ( osl_atomic_decrement(&m_nDocCount) == 0 ) + { + if (m_xCloseVetoButTerminateListener.is()) + { + m_xCloseVetoButTerminateListener->stop(); // dispose m_xDoc + m_xCloseVetoButTerminateListener.clear(); + } + m_xDoc.clear(); + } +} + +void OCalcConnection::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + m_nDocCount = 0; + if (m_xCloseVetoButTerminateListener.is()) + { + m_xCloseVetoButTerminateListener->stop(); // dispose m_xDoc + m_xCloseVetoButTerminateListener.clear(); + } + m_xDoc.clear(); + + OConnection::disposing(); +} + +// XServiceInfo + + +IMPLEMENT_SERVICE_INFO(OCalcConnection, "com.sun.star.sdbc.drivers.calc.Connection", "com.sun.star.sdbc.Connection") + + +Reference< XDatabaseMetaData > SAL_CALL OCalcConnection::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + xMetaData = new OCalcDatabaseMetaData(this); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + + +css::uno::Reference< XTablesSupplier > OCalcConnection::createCatalog() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XTablesSupplier > xTab = m_xCatalog; + if(!xTab.is()) + { + OCalcCatalog *pCat = new OCalcCatalog(this); + xTab = pCat; + m_xCatalog = xTab; + } + return xTab; +} + + +Reference< XStatement > SAL_CALL OCalcConnection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + Reference< XStatement > xReturn = new connectivity::component::OComponentStatement(this); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + + +Reference< XPreparedStatement > SAL_CALL OCalcConnection::prepareStatement( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + auto pStmt = new connectivity::component::OComponentPreparedStatement(this); + Reference< XPreparedStatement > xHoldAlive = pStmt; + pStmt->construct(sql); + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return pStmt; +} + + +Reference< XPreparedStatement > SAL_CALL OCalcConnection::prepareCall( const OUString& /*sql*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this ); + return nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CDatabaseMetaData.cxx b/connectivity/source/drivers/calc/CDatabaseMetaData.cxx new file mode 100644 index 000000000..cea1b32c7 --- /dev/null +++ b/connectivity/source/drivers/calc/CDatabaseMetaData.cxx @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <calc/CDatabaseMetaData.hxx> +#include <calc/CConnection.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/sheet/XCellRangeAddressable.hpp> +#include <com/sun/star/sheet/XDatabaseRanges.hpp> +#include <com/sun/star/sheet/XDatabaseRange.hpp> +#include <FDatabaseMetaDataResultSet.hxx> + +using namespace connectivity::calc; +using namespace connectivity::file; +using namespace connectivity::component; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::sheet; + +OCalcDatabaseMetaData::OCalcDatabaseMetaData(OConnection* _pCon) :OComponentDatabaseMetaData(_pCon) +{ +} + +OCalcDatabaseMetaData::~OCalcDatabaseMetaData() +{ +} + +OUString SAL_CALL OCalcDatabaseMetaData::getURL( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return "sdbc:calc:" + m_pConnection->getURL(); +} + +static bool lcl_IsEmptyOrHidden( const Reference<XSpreadsheets>& xSheets, const OUString& rName ) +{ + Any aAny = xSheets->getByName( rName ); + Reference<XSpreadsheet> xSheet; + if ( aAny >>= xSheet ) + { + // test if sheet is hidden + + Reference<XPropertySet> xProp( xSheet, UNO_QUERY ); + if (xProp.is()) + { + bool bVisible; + Any aVisAny = xProp->getPropertyValue("IsVisible"); + if ( (aVisAny >>= bVisible) && !bVisible) + return true; // hidden + } + + // use the same data area as in OCalcTable to test for empty table + + Reference<XSheetCellCursor> xCursor = xSheet->createCursor(); + Reference<XCellRangeAddressable> xRange( xCursor, UNO_QUERY ); + if ( xRange.is() ) + { + xCursor->collapseToSize( 1, 1 ); // single (first) cell + xCursor->collapseToCurrentRegion(); // contiguous data area + + CellRangeAddress aRangeAddr = xRange->getRangeAddress(); + if ( aRangeAddr.StartColumn == aRangeAddr.EndColumn && + aRangeAddr.StartRow == aRangeAddr.EndRow ) + { + // single cell -> check content + Reference<XCell> xCell = xCursor->getCellByPosition( 0, 0 ); + if ( xCell.is() && xCell->getType() == CellContentType_EMPTY ) + return true; + } + } + } + + return false; +} + +static bool lcl_IsUnnamed( const Reference<XDatabaseRanges>& xRanges, const OUString& rName ) +{ + bool bUnnamed = false; + + Any aAny = xRanges->getByName( rName ); + Reference<XDatabaseRange> xRange; + if ( aAny >>= xRange ) + { + Reference<XPropertySet> xRangeProp( xRange, UNO_QUERY ); + if ( xRangeProp.is() ) + { + try + { + Any aUserAny = xRangeProp->getPropertyValue("IsUserDefined"); + bool bUserDefined; + if ( aUserAny >>= bUserDefined ) + bUnnamed = !bUserDefined; + } + catch ( UnknownPropertyException& ) + { + // optional property + } + } + } + + return bUnnamed; +} + +Reference< XResultSet > SAL_CALL OCalcDatabaseMetaData::getTables( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& tableNamePattern, const Sequence< OUString >& types ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTables); + Reference< XResultSet > xRef = pResult; + + // check if ORowSetValue type is given + // when no types are given then we have to return all tables e.g. TABLE + + OUString aTable("TABLE"); + + bool bTableFound = true; + sal_Int32 nLength = types.getLength(); + if(nLength) + { + bTableFound = false; + + const OUString* pIter = types.getConstArray(); + const OUString* pEnd = pIter + nLength; + for(;pIter != pEnd;++pIter) + { + if(*pIter == aTable) + { + bTableFound = true; + break; + } + } + } + if(!bTableFound) + return xRef; + + // get the sheet names from the document + + OCalcConnection::ODocHolder aDocHolder(static_cast<OCalcConnection*>(m_pConnection)); + const Reference<XSpreadsheetDocument>& xDoc = aDocHolder.getDoc(); + if ( !xDoc.is() ) + throw SQLException(); + Reference<XSpreadsheets> xSheets = xDoc->getSheets(); + if ( !xSheets.is() ) + throw SQLException(); + Sequence< OUString > aSheetNames = xSheets->getElementNames(); + + ODatabaseMetaDataResultSet::ORows aRows; + sal_Int32 nSheetCount = aSheetNames.getLength(); + for (sal_Int32 nSheet=0; nSheet<nSheetCount; nSheet++) + { + OUString aName = aSheetNames[nSheet]; + if ( !lcl_IsEmptyOrHidden( xSheets, aName ) && match(tableNamePattern,aName,'\0') ) + { + ODatabaseMetaDataResultSet::ORow aRow { nullptr, nullptr, nullptr }; + aRow.reserve(6); + aRow.push_back(new ORowSetValueDecorator(aName)); + aRow.push_back(new ORowSetValueDecorator(aTable)); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRows.push_back(aRow); + } + } + + // also use database ranges + + Reference<XPropertySet> xDocProp( xDoc, UNO_QUERY ); + if ( xDocProp.is() ) + { + Any aRangesAny = xDocProp->getPropertyValue("DatabaseRanges"); + Reference<XDatabaseRanges> xRanges; + if ( aRangesAny >>= xRanges ) + { + Sequence< OUString > aDBNames = xRanges->getElementNames(); + sal_Int32 nDBCount = aDBNames.getLength(); + for (sal_Int32 nRange=0; nRange<nDBCount; nRange++) + { + OUString aName = aDBNames[nRange]; + if ( !lcl_IsUnnamed( xRanges, aName ) && match(tableNamePattern,aName,'\0') ) + { + ODatabaseMetaDataResultSet::ORow aRow { nullptr, nullptr, nullptr }; + aRow.reserve(6); + aRow.push_back(new ORowSetValueDecorator(aName)); + aRow.push_back(new ORowSetValueDecorator(aTable)); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRows.push_back(aRow); + } + } + } + } + + pResult->setRows(aRows); + + return xRef; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CDriver.cxx b/connectivity/source/drivers/calc/CDriver.cxx new file mode 100644 index 000000000..3b53ebccd --- /dev/null +++ b/connectivity/source/drivers/calc/CDriver.cxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <calc/CDriver.hxx> +#include <calc/CConnection.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <connectivity/dbexception.hxx> +#include <resource/sharedresources.hxx> +#include <comphelper/processfactory.hxx> +#include <strings.hrc> + +using namespace connectivity::calc; +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::lang; + + +// static ServiceInfo + +OUString ODriver::getImplementationName_Static( ) +{ + return "com.sun.star.comp.sdbc.calc.ODriver"; +} + +OUString SAL_CALL ODriver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + +// service names from file::OFileDriver + + +css::uno::Reference< css::uno::XInterface > + connectivity::calc::ODriver_CreateInstance(const css::uno::Reference< + css::lang::XMultiServiceFactory >& _rxFactory) +{ + return *(new ODriver( comphelper::getComponentContext(_rxFactory) )); +} + +Reference< XConnection > SAL_CALL ODriver::connect( const OUString& url, + const Sequence< PropertyValue >& info ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if (ODriver_BASE::rBHelper.bDisposed) + throw DisposedException(); + + if ( ! acceptsURL(url) ) + return nullptr; + + OCalcConnection* pCon = new OCalcConnection(this); + pCon->construct(url,info); + Reference< XConnection > xCon = pCon; + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return xCon; +} + +sal_Bool SAL_CALL ODriver::acceptsURL( const OUString& url ) +{ + return url.startsWith("sdbc:calc:"); +} + +Sequence< DriverPropertyInfo > SAL_CALL ODriver::getPropertyInfo( const OUString& url, const Sequence< PropertyValue >& /*info*/ ) +{ + if ( !acceptsURL(url) ) + { + SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + return Sequence< DriverPropertyInfo >(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CTable.cxx b/connectivity/source/drivers/calc/CTable.cxx new file mode 100644 index 000000000..045ea5419 --- /dev/null +++ b/connectivity/source/drivers/calc/CTable.cxx @@ -0,0 +1,657 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <calc/CTable.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/sheet/XCellRangeAddressable.hpp> +#include <com/sun/star/sheet/XCellRangesQuery.hpp> +#include <com/sun/star/sheet/XDatabaseRanges.hpp> +#include <com/sun/star/sheet/XDatabaseRange.hpp> +#include <com/sun/star/sheet/XCellRangeReferrer.hpp> +#include <com/sun/star/sheet/XUsedAreaCursor.hpp> +#include <com/sun/star/sheet/CellFlags.hpp> +#include <com/sun/star/sheet/FormulaResult.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/text/XText.hpp> +#include <calc/CConnection.hxx> +#include <connectivity/sdbcx/VColumn.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <rtl/math.hxx> +#include <tools/time.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> + +using namespace connectivity; +using namespace connectivity::calc; +using namespace connectivity::file; +using namespace ::cppu; +using namespace ::dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sheet; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::util; + + +static void lcl_UpdateArea( const Reference<XCellRange>& xUsedRange, sal_Int32& rEndCol, sal_Int32& rEndRow ) +{ + // update rEndCol, rEndRow if any non-empty cell in xUsedRange is right/below + + const Reference<XCellRangesQuery> xUsedQuery( xUsedRange, UNO_QUERY ); + if ( !xUsedQuery.is() ) + return; + + const sal_Int16 nContentFlags = + CellFlags::STRING | CellFlags::VALUE | CellFlags::DATETIME | CellFlags::FORMULA | CellFlags::ANNOTATION; + + const Reference<XSheetCellRanges> xUsedRanges = xUsedQuery->queryContentCells( nContentFlags ); + const Sequence<CellRangeAddress> aAddresses = xUsedRanges->getRangeAddresses(); + + const sal_Int32 nCount = aAddresses.getLength(); + const CellRangeAddress* pData = aAddresses.getConstArray(); + for ( sal_Int32 i=0; i<nCount; i++ ) + { + rEndCol = std::max(pData[i].EndColumn, rEndCol); + rEndRow = std::max(pData[i].EndRow, rEndRow); + } +} + +static void lcl_GetDataArea( const Reference<XSpreadsheet>& xSheet, sal_Int32& rColumnCount, sal_Int32& rRowCount ) +{ + Reference<XSheetCellCursor> xCursor = xSheet->createCursor(); + Reference<XCellRangeAddressable> xRange( xCursor, UNO_QUERY ); + if ( !xRange.is() ) + { + rColumnCount = rRowCount = 0; + return; + } + + // first find the contiguous cell area starting at A1 + + xCursor->collapseToSize( 1, 1 ); // single (first) cell + xCursor->collapseToCurrentRegion(); // contiguous data area + + CellRangeAddress aRegionAddr = xRange->getRangeAddress(); + sal_Int32 nEndCol = aRegionAddr.EndColumn; + sal_Int32 nEndRow = aRegionAddr.EndRow; + + Reference<XUsedAreaCursor> xUsed( xCursor, UNO_QUERY ); + if ( xUsed.is() ) + { + // The used area from XUsedAreaCursor includes visible attributes. + // If the used area is larger than the contiguous cell area, find non-empty + // cells in that area. + + xUsed->gotoEndOfUsedArea( false ); + CellRangeAddress aUsedAddr = xRange->getRangeAddress(); + + if ( aUsedAddr.EndColumn > aRegionAddr.EndColumn ) + { + Reference<XCellRange> xUsedRange = xSheet->getCellRangeByPosition( + aRegionAddr.EndColumn + 1, 0, aUsedAddr.EndColumn, aUsedAddr.EndRow ); + lcl_UpdateArea( xUsedRange, nEndCol, nEndRow ); + } + + if ( aUsedAddr.EndRow > aRegionAddr.EndRow ) + { + // only up to the last column of aRegionAddr, the other columns are handled above + Reference<XCellRange> xUsedRange = xSheet->getCellRangeByPosition( + 0, aRegionAddr.EndRow + 1, aRegionAddr.EndColumn, aUsedAddr.EndRow ); + lcl_UpdateArea( xUsedRange, nEndCol, nEndRow ); + } + } + + rColumnCount = nEndCol + 1; // number of columns + rRowCount = nEndRow; // first row (headers) is not counted +} + +static CellContentType lcl_GetContentOrResultType( const Reference<XCell>& xCell ) +{ + CellContentType eCellType = xCell->getType(); + if ( eCellType == CellContentType_FORMULA ) + { + Reference<XPropertySet> xProp( xCell, UNO_QUERY ); + try + { + xProp->getPropertyValue( "CellContentType" ) >>= eCellType; // type of cell content + } + catch (UnknownPropertyException&) + { + eCellType = CellContentType_VALUE; // if CellContentType property not available + } + } + return eCellType; +} + +static Reference<XCell> lcl_GetUsedCell( const Reference<XSpreadsheet>& xSheet, sal_Int32 nDocColumn, sal_Int32 nDocRow ) +{ + Reference<XCell> xCell = xSheet->getCellByPosition( nDocColumn, nDocRow ); + if ( xCell.is() && xCell->getType() == CellContentType_EMPTY ) + { + // get first non-empty cell + + Reference<XCellRangeAddressable> xAddr( xSheet, UNO_QUERY ); + if (xAddr.is()) + { + CellRangeAddress aTotalRange = xAddr->getRangeAddress(); + sal_Int32 nLastRow = aTotalRange.EndRow; + Reference<XCellRangesQuery> xQuery( xSheet->getCellRangeByPosition( nDocColumn, nDocRow, nDocColumn, nLastRow ), UNO_QUERY ); + if (xQuery.is()) + { + // queryIntersection to get a ranges object + Reference<XSheetCellRanges> xRanges = xQuery->queryIntersection( aTotalRange ); + if (xRanges.is()) + { + Reference<XEnumerationAccess> xCells = xRanges->getCells(); + if (xCells.is()) + { + Reference<XEnumeration> xEnum = xCells->createEnumeration(); + if ( xEnum.is() && xEnum->hasMoreElements() ) + { + // get first non-empty cell from enumeration + xCell.set(xEnum->nextElement(),UNO_QUERY); + } + // otherwise, keep empty cell + } + } + } + } + } + return xCell; +} + +static bool lcl_HasTextInColumn( const Reference<XSpreadsheet>& xSheet, sal_Int32 nDocColumn, sal_Int32 nDocRow ) +{ + // look for any text cell or text result in the column + + Reference<XCellRangeAddressable> xAddr( xSheet, UNO_QUERY ); + if (xAddr.is()) + { + CellRangeAddress aTotalRange = xAddr->getRangeAddress(); + sal_Int32 nLastRow = aTotalRange.EndRow; + Reference<XCellRangesQuery> xQuery( xSheet->getCellRangeByPosition( nDocColumn, nDocRow, nDocColumn, nLastRow ), UNO_QUERY ); + if (xQuery.is()) + { + // are there text cells in the column? + Reference<XSheetCellRanges> xTextContent = xQuery->queryContentCells( CellFlags::STRING ); + if ( xTextContent.is() && xTextContent->hasElements() ) + return true; + + // are there formulas with text results in the column? + Reference<XSheetCellRanges> xTextFormula = xQuery->queryFormulaCells( FormulaResult::STRING ); + if ( xTextFormula.is() && xTextFormula->hasElements() ) + return true; + } + } + + return false; +} + +static void lcl_GetColumnInfo( const Reference<XSpreadsheet>& xSheet, const Reference<XNumberFormats>& xFormats, + sal_Int32 nDocColumn, sal_Int32 nStartRow, bool bHasHeaders, + OUString& rName, sal_Int32& rDataType, bool& rCurrency ) +{ + //! avoid duplicate field names + + // get column name from first row, if range contains headers + + if ( bHasHeaders ) + { + Reference<XText> xHeaderText( xSheet->getCellByPosition( nDocColumn, nStartRow ), UNO_QUERY ); + if ( xHeaderText.is() ) + rName = xHeaderText->getString(); + } + + // get column type from first data row + + sal_Int32 nDataRow = nStartRow; + if ( bHasHeaders ) + ++nDataRow; + Reference<XCell> xDataCell = lcl_GetUsedCell( xSheet, nDocColumn, nDataRow ); + + Reference<XPropertySet> xProp( xDataCell, UNO_QUERY ); + if ( !xProp.is() ) + return; + + rCurrency = false; // set to true for currency below + + const CellContentType eCellType = lcl_GetContentOrResultType( xDataCell ); + // #i35178# use "text" type if there is any text cell in the column + if ( eCellType == CellContentType_TEXT || lcl_HasTextInColumn( xSheet, nDocColumn, nDataRow ) ) + rDataType = DataType::VARCHAR; + else if ( eCellType == CellContentType_VALUE ) + { + // get number format to distinguish between different types + + sal_Int16 nNumType = NumberFormat::NUMBER; + try + { + sal_Int32 nKey = 0; + + if ( xProp->getPropertyValue( "NumberFormat" ) >>= nKey ) + { + const Reference<XPropertySet> xFormat = xFormats->getByKey( nKey ); + if ( xFormat.is() ) + { + xFormat->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE) ) >>= nNumType; + } + } + } + catch ( Exception& ) + { + } + + if ( nNumType & NumberFormat::TEXT ) + rDataType = DataType::VARCHAR; + else if ( nNumType & NumberFormat::NUMBER ) + rDataType = DataType::DECIMAL; + else if ( nNumType & NumberFormat::CURRENCY ) + { + rCurrency = true; + rDataType = DataType::DECIMAL; + } + else if ( ( nNumType & NumberFormat::DATETIME ) == NumberFormat::DATETIME ) + { + // NumberFormat::DATETIME is DATE | TIME + rDataType = DataType::TIMESTAMP; + } + else if ( nNumType & NumberFormat::DATE ) + rDataType = DataType::DATE; + else if ( nNumType & NumberFormat::TIME ) + rDataType = DataType::TIME; + else if ( nNumType & NumberFormat::LOGICAL ) + rDataType = DataType::BIT; + else + rDataType = DataType::DECIMAL; + } + else + { + // whole column empty + rDataType = DataType::VARCHAR; + } +} + + +static void lcl_SetValue( ORowSetValue& rValue, const Reference<XSpreadsheet>& xSheet, + sal_Int32 nStartCol, sal_Int32 nStartRow, bool bHasHeaders, + const ::Date& rNullDate, + sal_Int32 nDBRow, sal_Int32 nDBColumn, sal_Int32 nType ) +{ + sal_Int32 nDocColumn = nStartCol + nDBColumn - 1; // database counts from 1 + sal_Int32 nDocRow = nStartRow + nDBRow - 1; + if (bHasHeaders) + ++nDocRow; + + const Reference<XCell> xCell = xSheet->getCellByPosition( nDocColumn, nDocRow ); + if ( !xCell.is() ) + return; + + CellContentType eCellType = lcl_GetContentOrResultType( xCell ); + switch (nType) + { + case DataType::VARCHAR: + if ( eCellType == CellContentType_EMPTY ) + rValue.setNull(); + else + { + // #i25840# still let Calc convert numbers to text + const Reference<XText> xText( xCell, UNO_QUERY ); + if ( xText.is() ) + rValue = xText->getString(); + } + break; + case DataType::DECIMAL: + if ( eCellType == CellContentType_VALUE ) + rValue = xCell->getValue(); // double + else + rValue.setNull(); + break; + case DataType::BIT: + if ( eCellType == CellContentType_VALUE ) + rValue = xCell->getValue() != 0.0; + else + rValue.setNull(); + break; + case DataType::DATE: + if ( eCellType == CellContentType_VALUE ) + { + ::Date aDate( rNullDate ); + aDate.AddDays(::rtl::math::approxFloor( xCell->getValue() )); + rValue = aDate.GetUNODate(); + } + else + rValue.setNull(); + break; + case DataType::TIME: + if ( eCellType == CellContentType_VALUE ) + { + double fCellVal = xCell->getValue(); + double fTime = fCellVal - rtl::math::approxFloor( fCellVal ); + sal_Int64 nIntTime = static_cast<sal_Int64>(rtl::math::round( fTime * static_cast<double>(::tools::Time::nanoSecPerDay) )); + if ( nIntTime == ::tools::Time::nanoSecPerDay) + nIntTime = 0; // 23:59:59.9999999995 and above is 00:00:00.00 + css::util::Time aTime; + aTime.NanoSeconds = static_cast<sal_uInt32>( nIntTime % ::tools::Time::nanoSecPerSec ); + nIntTime /= ::tools::Time::nanoSecPerSec; + aTime.Seconds = static_cast<sal_uInt16>( nIntTime % 60 ); + nIntTime /= 60; + aTime.Minutes = static_cast<sal_uInt16>( nIntTime % 60 ); + nIntTime /= 60; + OSL_ENSURE( nIntTime < 24, "error in time calculation" ); + aTime.Hours = static_cast<sal_uInt16>(nIntTime); + rValue = aTime; + } + else + rValue.setNull(); + break; + case DataType::TIMESTAMP: + if ( eCellType == CellContentType_VALUE ) + { + double fCellVal = xCell->getValue(); + double fDays = ::rtl::math::approxFloor( fCellVal ); + double fTime = fCellVal - fDays; + long nIntDays = static_cast<long>(fDays); + sal_Int64 nIntTime = ::rtl::math::round( fTime * static_cast<double>(::tools::Time::nanoSecPerDay) ); + if ( nIntTime == ::tools::Time::nanoSecPerDay ) + { + nIntTime = 0; // 23:59:59.9999999995 and above is 00:00:00.00 + ++nIntDays; // (next day) + } + + css::util::DateTime aDateTime; + + aDateTime.NanoSeconds = static_cast<sal_uInt16>( nIntTime % ::tools::Time::nanoSecPerSec ); + nIntTime /= ::tools::Time::nanoSecPerSec; + aDateTime.Seconds = static_cast<sal_uInt16>( nIntTime % 60 ); + nIntTime /= 60; + aDateTime.Minutes = static_cast<sal_uInt16>( nIntTime % 60 ); + nIntTime /= 60; + OSL_ENSURE( nIntTime < 24, "error in time calculation" ); + aDateTime.Hours = static_cast<sal_uInt16>(nIntTime); + + ::Date aDate( rNullDate ); + aDate.AddDays( nIntDays ); + aDateTime.Day = aDate.GetDay(); + aDateTime.Month = aDate.GetMonth(); + aDateTime.Year = aDate.GetYear(); + + rValue = aDateTime; + } + else + rValue.setNull(); + break; + } // switch (nType) + +// rValue.setTypeKind(nType); +} + + +static OUString lcl_GetColumnStr( sal_Int32 nColumn ) +{ + if ( nColumn < 26 ) + return OUString( static_cast<sal_Unicode>( 'A' + nColumn ) ); + else + { + OUStringBuffer aBuffer(2); + aBuffer.setLength( 2 ); + aBuffer[0] = static_cast<sal_Unicode>( 'A' + ( nColumn / 26 ) - 1 ); + aBuffer[1] = static_cast<sal_Unicode>( 'A' + ( nColumn % 26 ) ); + return aBuffer.makeStringAndClear(); + } +} + +void OCalcTable::fillColumns() +{ + if ( !m_xSheet.is() ) + throw SQLException(); + + OUString aTypeName; + ::comphelper::UStringMixEqual aCase(m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()); + const bool bStoresMixedCaseQuotedIdentifiers = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(); + + for (sal_Int32 i = 0; i < m_nDataCols; i++) + { + OUString aColumnName; + sal_Int32 eType = DataType::OTHER; + bool bCurrency = false; + + lcl_GetColumnInfo( m_xSheet, m_xFormats, m_nStartCol + i, m_nStartRow, m_bHasHeaders, + aColumnName, eType, bCurrency ); + + if ( aColumnName.isEmpty() ) + aColumnName = lcl_GetColumnStr( i ); + + sal_Int32 nPrecision = 0; //! ... + sal_Int32 nDecimals = 0; //! ... + + switch ( eType ) + { + case DataType::VARCHAR: + aTypeName = "VARCHAR"; + break; + case DataType::DECIMAL: + aTypeName = "DECIMAL"; + break; + case DataType::BIT: + aTypeName = "BOOL"; + break; + case DataType::DATE: + aTypeName = "DATE"; + break; + case DataType::TIME: + aTypeName = "TIME"; + break; + case DataType::TIMESTAMP: + aTypeName = "TIMESTAMP"; + break; + default: + SAL_WARN( "connectivity.drivers","missing type name"); + aTypeName.clear(); + } + + // check if the column name already exists + OUString aAlias = aColumnName; + OSQLColumns::const_iterator aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase); + sal_Int32 nExprCnt = 0; + while(aFind != m_aColumns->end()) + { + aAlias = aColumnName + OUString::number(++nExprCnt); + aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase); + } + + sdbcx::OColumn* pColumn = new sdbcx::OColumn( aAlias, aTypeName, OUString(),OUString(), + ColumnValue::NULLABLE, nPrecision, nDecimals, + eType, false, false, bCurrency, + bStoresMixedCaseQuotedIdentifiers, + m_CatalogName, getSchema(), getName()); + Reference< XPropertySet> xCol = pColumn; + m_aColumns->push_back(xCol); + m_aTypes.push_back(eType); + } +} + + +OCalcTable::OCalcTable(sdbcx::OCollection* _pTables,OCalcConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName + ) : OCalcTable_BASE(_pTables,_pConnection,Name, + Type, + Description, + SchemaName, + CatalogName) + ,m_pCalcConnection(_pConnection) + ,m_nStartCol(0) + ,m_nStartRow(0) + ,m_nDataCols(0) + ,m_bHasHeaders(false) + ,m_aNullDate(::Date::EMPTY) +{ +} + +void OCalcTable::construct() +{ + // get sheet object + Reference< XSpreadsheetDocument> xDoc = m_pCalcConnection->acquireDoc(); + if (xDoc.is()) + { + Reference<XSpreadsheets> xSheets = xDoc->getSheets(); + if ( xSheets.is() && xSheets->hasByName( m_Name ) ) + { + m_xSheet.set(xSheets->getByName( m_Name ),UNO_QUERY); + if ( m_xSheet.is() ) + { + lcl_GetDataArea( m_xSheet, m_nDataCols, m_nDataRows ); + m_bHasHeaders = true; + // whole sheet is always assumed to include a header row + } + } + else // no sheet -> try database range + { + Reference<XPropertySet> xDocProp( xDoc, UNO_QUERY ); + if ( xDocProp.is() ) + { + Reference<XDatabaseRanges> xRanges(xDocProp->getPropertyValue("DatabaseRanges"),UNO_QUERY); + + if ( xRanges.is() && xRanges->hasByName( m_Name ) ) + { + Reference<XDatabaseRange> xDBRange(xRanges->getByName( m_Name ),UNO_QUERY); + Reference<XCellRangeReferrer> xRefer( xDBRange, UNO_QUERY ); + if ( xRefer.is() ) + { + // Header flag is always stored with database range + // Get flag from FilterDescriptor + + bool bRangeHeader = true; + Reference<XPropertySet> xFiltProp( xDBRange->getFilterDescriptor(), UNO_QUERY ); + if ( xFiltProp.is() ) + xFiltProp->getPropertyValue("ContainsHeader") >>= bRangeHeader; + + Reference<XSheetCellRange> xSheetRange( xRefer->getReferredCells(), UNO_QUERY ); + Reference<XCellRangeAddressable> xAddr( xSheetRange, UNO_QUERY ); + if ( xSheetRange.is() && xAddr.is() ) + { + m_xSheet = xSheetRange->getSpreadsheet(); + CellRangeAddress aRangeAddr = xAddr->getRangeAddress(); + m_nStartCol = aRangeAddr.StartColumn; + m_nStartRow = aRangeAddr.StartRow; + m_nDataCols = aRangeAddr.EndColumn - m_nStartCol + 1; + // m_nDataRows is excluding header row + m_nDataRows = aRangeAddr.EndRow - m_nStartRow; + if ( !bRangeHeader ) + { + // m_nDataRows counts the whole range + m_nDataRows += 1; + } + + m_bHasHeaders = bRangeHeader; + } + } + } + } + } + + Reference<XNumberFormatsSupplier> xSupp( xDoc, UNO_QUERY ); + if (xSupp.is()) + m_xFormats = xSupp->getNumberFormats(); + + Reference<XPropertySet> xProp( xDoc, UNO_QUERY ); + if (xProp.is()) + { + css::util::Date aDateStruct; + if ( xProp->getPropertyValue("NullDate") >>= aDateStruct ) + m_aNullDate = ::Date( aDateStruct.Day, aDateStruct.Month, aDateStruct.Year ); + } + } + + //! default if no null date available? + + fillColumns(); + + refreshColumns(); +} + +void SAL_CALL OCalcTable::disposing() +{ + OFileTable::disposing(); + ::osl::MutexGuard aGuard(m_aMutex); + m_aColumns = nullptr; + if ( m_pCalcConnection ) + m_pCalcConnection->releaseDoc(); + m_pCalcConnection = nullptr; + +} + +Sequence< sal_Int8 > OCalcTable::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OCalcTable::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return (isUnoTunnelId<OCalcTable>(rId)) + ? reinterpret_cast< sal_Int64 >( this ) + : OCalcTable_BASE::getSomething(rId); +} + +bool OCalcTable::fetchRow( OValueRefRow& _rRow, const OSQLColumns & _rCols, + bool bRetrieveData ) +{ + // read the bookmark + + _rRow->setDeleted(false); + *(*_rRow)[0] = m_nFilePos; + + if (!bRetrieveData) + return true; + + // fields + + const OValueRefVector::size_type nCount = std::min(_rRow->size(), _rCols.size() + 1); + for (OValueRefVector::size_type i = 1; i < nCount; i++) + { + if ( (*_rRow)[i]->isBound() ) + { + sal_Int32 nType = m_aTypes[i-1]; + + lcl_SetValue( (*_rRow)[i]->get(), m_xSheet, m_nStartCol, m_nStartRow, m_bHasHeaders, + m_aNullDate, m_nFilePos, i, nType ); + } + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/CTables.cxx b/connectivity/source/drivers/calc/CTables.cxx new file mode 100644 index 000000000..703ab2712 --- /dev/null +++ b/connectivity/source/drivers/calc/CTables.cxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <calc/CConnection.hxx> +#include <calc/CTables.hxx> +#include <calc/CTable.hxx> +#include <file/FCatalog.hxx> +#include <file/FConnection.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::calc; +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::lang; +using namespace ::com::sun::star::container; + +sdbcx::ObjectType OCalcTables::createObject(const OUString& _rName) +{ + OCalcTable* pTable = new OCalcTable(this, static_cast<OCalcConnection*>(static_cast<OFileCatalog&>(m_rParent).getConnection()), + _rName,"TABLE"); + sdbcx::ObjectType xRet = pTable; + pTable->construct(); + return xRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/Cservices.cxx b/connectivity/source/drivers/calc/Cservices.cxx new file mode 100644 index 000000000..06a516ddc --- /dev/null +++ b/connectivity/source/drivers/calc/Cservices.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <calc/CDriver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::calc; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* connectivity_calc_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + ODriver::getImplementationName_Static(), + ODriver::getSupportedServiceNames_Static(), + ODriver_CreateInstance, ::cppu::createSingleFactory) + ; + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/calc/calc.component b/connectivity/source/drivers/calc/calc.component new file mode 100644 index 000000000..ffbddfa34 --- /dev/null +++ b/connectivity/source/drivers/calc/calc.component @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="connectivity_calc" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.calc.ODriver"> + <service name="com.sun.star.sdbc.Driver"/> + <service name="com.sun.star.sdbcx.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/component/CColumns.cxx b/connectivity/source/drivers/component/CColumns.cxx new file mode 100644 index 000000000..9f802b6e4 --- /dev/null +++ b/connectivity/source/drivers/component/CColumns.cxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <component/CColumns.hxx> +#include <file/FTable.hxx> + +using namespace connectivity::component; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; + + +sdbcx::ObjectType OComponentColumns::createObject(const OUString& _rName) +{ + ::rtl::Reference<OSQLColumns> aCols = m_pTable->getTableColumns(); + + OSQLColumns::const_iterator aIter = find(aCols->begin(),aCols->end(),_rName,::comphelper::UStringMixEqual(isCaseSensitive())); + sdbcx::ObjectType xRet; + if(aIter != aCols->end()) + xRet = sdbcx::ObjectType(*aIter,UNO_QUERY); + return xRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/component/CDatabaseMetaData.cxx b/connectivity/source/drivers/component/CDatabaseMetaData.cxx new file mode 100644 index 000000000..f225747a6 --- /dev/null +++ b/connectivity/source/drivers/component/CDatabaseMetaData.cxx @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <component/CDatabaseMetaData.hxx> +#include <file/FConnection.hxx> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <FDatabaseMetaDataResultSet.hxx> +#include <comphelper/types.hxx> + +using namespace connectivity::component; +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; + +OComponentDatabaseMetaData::OComponentDatabaseMetaData(OConnection* _pCon) :ODatabaseMetaData(_pCon) +{ +} + +OComponentDatabaseMetaData::~OComponentDatabaseMetaData() +{ +} + +Reference< XResultSet > OComponentDatabaseMetaData::impl_getTypeInfo_throw( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTypeInfo); + Reference< XResultSet > xRef = pResult; + + static ODatabaseMetaDataResultSet::ORows aRows; + if(aRows.empty()) + { + ODatabaseMetaDataResultSet::ORow aRow; + + aRows.reserve(6); + aRow.reserve(18); + + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(OUString("VARCHAR"))); + aRow.push_back(new ORowSetValueDecorator(DataType::VARCHAR)); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(65535))); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); // ORowSetValue((sal_Int32)ColumnValue::NULLABLE) + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(ColumnSearch::CHAR))); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(10))); + + + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("DECIMAL")); + aRow[2] = new ORowSetValueDecorator(DataType::DECIMAL); + aRow[3] = ODatabaseMetaDataResultSet::get0Value(); + aRow[9] = ODatabaseMetaDataResultSet::getBasicValue(); + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("BOOL")); + aRow[2] = new ORowSetValueDecorator(DataType::BIT); + aRow[3] = new ORowSetValueDecorator(sal_Int32(20)); + aRow[9] = ODatabaseMetaDataResultSet::getBasicValue(); + aRow[15] = new ORowSetValueDecorator(sal_Int32(15)); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("DATE")); + aRow[2] = new ORowSetValueDecorator(DataType::DATE); + aRow[3] = ODatabaseMetaDataResultSet::get0Value(); + aRow[9] = ODatabaseMetaDataResultSet::getBasicValue(); + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("TIME")); + aRow[2] = new ORowSetValueDecorator(DataType::TIME); + aRow[3] = ODatabaseMetaDataResultSet::get0Value(); + aRow[9] = ODatabaseMetaDataResultSet::getBasicValue(); + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("TIMESTAMP")); + aRow[2] = new ORowSetValueDecorator(DataType::TIMESTAMP); + aRow[3] = ODatabaseMetaDataResultSet::get0Value(); + aRow[9] = ODatabaseMetaDataResultSet::getBasicValue(); + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); + aRows.push_back(aRow); + } + + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > SAL_CALL OComponentDatabaseMetaData::getColumns( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + + Reference< XTablesSupplier > xTables = m_pConnection->createCatalog(); + if(!xTables.is()) + throw SQLException(); + + Reference< XNameAccess> xNames = xTables->getTables(); + if(!xNames.is()) + throw SQLException(); + + ODatabaseMetaDataResultSet::ORows aRows; + ODatabaseMetaDataResultSet::ORow aRow(19); + + aRow[10] = new ORowSetValueDecorator(sal_Int32(10)); + + Sequence< OUString> aTabNames(xNames->getElementNames()); + const OUString* pTabIter = aTabNames.getConstArray(); + const OUString* pTabEnd = pTabIter + aTabNames.getLength(); + for(;pTabIter != pTabEnd;++pTabIter) + { + if(match(tableNamePattern,*pTabIter,'\0')) + { + const Reference< XColumnsSupplier> xTable(xNames->getByName(*pTabIter),UNO_QUERY_THROW); + aRow[3] = new ORowSetValueDecorator(*pTabIter); + + const Reference< XNameAccess> xColumns = xTable->getColumns(); + if(!xColumns.is()) + throw SQLException(); + + const Sequence< OUString> aColNames(xColumns->getElementNames()); + + const OUString* pColumnIter = aColNames.getConstArray(); + const OUString* pEnd = pColumnIter + aColNames.getLength(); + Reference< XPropertySet> xColumn; + for(sal_Int32 i=1;pColumnIter != pEnd;++pColumnIter,++i) + { + if(match(columnNamePattern,*pColumnIter,'\0')) + { + aRow[4] = new ORowSetValueDecorator( *pColumnIter); + + xColumns->getByName(*pColumnIter) >>= xColumn; + OSL_ENSURE(xColumn.is(),"Columns contains a column who isn't a fastpropertyset!"); + aRow[5] = new ORowSetValueDecorator(::comphelper::getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)))); + aRow[6] = new ORowSetValueDecorator(::comphelper::getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME)))); + aRow[7] = new ORowSetValueDecorator(::comphelper::getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)))); + // aRow[8] = xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME)); + aRow[9] = new ORowSetValueDecorator(::comphelper::getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE)))); + aRow[11] = new ORowSetValueDecorator(::comphelper::getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE)))); + // aRow[12] = xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME)); + aRow[13] = new ORowSetValueDecorator(::comphelper::getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE)))); + // aRow[14] = xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME)); + // aRow[15] = xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME)); + switch(sal_Int32(aRow[5]->getValue())) + { + case DataType::CHAR: + case DataType::VARCHAR: + aRow[16] = new ORowSetValueDecorator(sal_Int32(254)); + break; + case DataType::LONGVARCHAR: + aRow[16] = new ORowSetValueDecorator(sal_Int32(65535)); + break; + default: + aRow[16] = new ORowSetValueDecorator(sal_Int32(0)); + } + aRow[17] = new ORowSetValueDecorator(i); + switch(sal_Int32(aRow[11]->getValue())) + { + case ColumnValue::NO_NULLS: + aRow[18] = new ORowSetValueDecorator(OUString("NO")); + break; + case ColumnValue::NULLABLE: + aRow[18] = new ORowSetValueDecorator(OUString("YES")); + break; + default: + aRow[18] = new ORowSetValueDecorator(OUString()); + } + aRows.push_back(aRow); + } + } + } + } + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eColumns); + Reference< XResultSet > xRef = pResult; + pResult->setRows(aRows); + + return xRef; +} + +sal_Int32 SAL_CALL OComponentDatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + return SAL_MAX_INT32; +} + +sal_Int32 SAL_CALL OComponentDatabaseMetaData::getMaxCharLiteralLength( ) +{ + return SAL_MAX_INT32; +} + +sal_Int32 SAL_CALL OComponentDatabaseMetaData::getMaxColumnNameLength( ) +{ + return SAL_MAX_INT32; +} + +sal_Int32 SAL_CALL OComponentDatabaseMetaData::getMaxColumnsInIndex( ) +{ + return 1; +} + +sal_Int32 SAL_CALL OComponentDatabaseMetaData::getMaxColumnsInTable( ) +{ + return 256; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/component/CPreparedStatement.cxx b/connectivity/source/drivers/component/CPreparedStatement.cxx new file mode 100644 index 000000000..bc3af3e8c --- /dev/null +++ b/connectivity/source/drivers/component/CPreparedStatement.cxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <component/CPreparedStatement.hxx> +#include <component/CResultSet.hxx> + +using namespace connectivity::component; +using namespace connectivity::file; +using namespace com::sun::star::uno; + +OResultSet* OComponentPreparedStatement::createResultSet() +{ + return new connectivity::component::OComponentResultSet(this,m_aSQLIterator); +} + +IMPLEMENT_SERVICE_INFO(OComponentPreparedStatement,"com.sun.star.sdbc.driver.component.PreparedStatement","com.sun.star.sdbc.PreparedStatement"); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/component/CResultSet.cxx b/connectivity/source/drivers/component/CResultSet.cxx new file mode 100644 index 000000000..050b4034d --- /dev/null +++ b/connectivity/source/drivers/component/CResultSet.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <component/CResultSet.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <connectivity/dbexception.hxx> + +using namespace ::comphelper; +using namespace connectivity::component; +using namespace connectivity::file; +using namespace ::cppu; +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; + + +OComponentResultSet::OComponentResultSet( OStatement_Base* pStmt,connectivity::OSQLParseTreeIterator& _aSQLIterator) + : file::OResultSet(pStmt,_aSQLIterator) + ,m_bBookmarkable(true) +{ + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), PROPERTY_ID_ISBOOKMARKABLE, PropertyAttribute::READONLY,&m_bBookmarkable, cppu::UnoType<bool>::get()); +} + +OUString SAL_CALL OComponentResultSet::getImplementationName( ) +{ + return "com.sun.star.sdbcx.component.ResultSet"; +} + +Sequence< OUString > SAL_CALL OComponentResultSet::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdbc.ResultSet", "com.sun.star.sdbcx.ResultSet" }; +} + +sal_Bool SAL_CALL OComponentResultSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Any SAL_CALL OComponentResultSet::queryInterface( const Type & rType ) +{ + Any aRet = OResultSet::queryInterface(rType); + return aRet.hasValue() ? aRet : OComponentResultSet_BASE::queryInterface(rType); +} + + Sequence< Type > SAL_CALL OComponentResultSet::getTypes( ) +{ + return ::comphelper::concatSequences(OResultSet::getTypes(),OComponentResultSet_BASE::getTypes()); +} + + +// XRowLocate +Any SAL_CALL OComponentResultSet::getBookmark( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return makeAny(static_cast<sal_Int32>((*m_aRow)[0]->getValue())); +} + +sal_Bool SAL_CALL OComponentResultSet::moveToBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_bRowDeleted = m_bRowInserted = m_bRowUpdated = false; + + return Move(IResultSetHelper::BOOKMARK,comphelper::getINT32(bookmark),true); +} + +sal_Bool SAL_CALL OComponentResultSet::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_bRowDeleted = m_bRowInserted = m_bRowUpdated = false; + + Move(IResultSetHelper::BOOKMARK,comphelper::getINT32(bookmark),false); + + return relative(rows); +} + + +sal_Int32 SAL_CALL OComponentResultSet::compareBookmarks( const Any& lhs, const Any& rhs ) +{ + return (lhs == rhs) ? CompareBookmark::EQUAL : CompareBookmark::NOT_EQUAL; +} + +sal_Bool SAL_CALL OComponentResultSet::hasOrderedBookmarks( ) +{ + return true; +} + +sal_Int32 SAL_CALL OComponentResultSet::hashBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return comphelper::getINT32(bookmark); +} + +// XDeleteRows +Sequence< sal_Int32 > SAL_CALL OComponentResultSet::deleteRows( const Sequence< Any >& /*rows*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFeatureNotImplementedSQLException( "XDeleteRows::deleteRows", *this ); + return Sequence< sal_Int32 >(); +} + +bool OComponentResultSet::fillIndexValues(const Reference< XColumnsSupplier> &/*_xIndex*/) +{ + // Writer or Calc table has no index + return false; +} + +::cppu::IPropertyArrayHelper & OComponentResultSet::getInfoHelper() +{ + return *OComponentResultSet_BASE3::getArrayHelper(); +} + +::cppu::IPropertyArrayHelper* OComponentResultSet::createArrayHelper() const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +void SAL_CALL OComponentResultSet::acquire() throw() +{ + OComponentResultSet_BASE2::acquire(); +} + +void SAL_CALL OComponentResultSet::release() throw() +{ + OComponentResultSet_BASE2::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OComponentResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/component/CStatement.cxx b/connectivity/source/drivers/component/CStatement.cxx new file mode 100644 index 000000000..8adfefae8 --- /dev/null +++ b/connectivity/source/drivers/component/CStatement.cxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <component/CStatement.hxx> +#include <component/CResultSet.hxx> + +using namespace connectivity::component; +using namespace connectivity::file; +using namespace com::sun::star::uno; + +OResultSet* OComponentStatement::createResultSet() +{ + return new connectivity::component::OComponentResultSet(this,m_aSQLIterator); +} + +IMPLEMENT_SERVICE_INFO(OComponentStatement,"com.sun.star.sdbc.driver.component.Statement","com.sun.star.sdbc.Statement"); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/component/CTable.cxx b/connectivity/source/drivers/component/CTable.cxx new file mode 100644 index 000000000..9b5354d26 --- /dev/null +++ b/connectivity/source/drivers/component/CTable.cxx @@ -0,0 +1,190 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <component/CTable.hxx> +#include <component/CColumns.hxx> +#include <cppuhelper/queryinterface.hxx> + +using namespace connectivity; +using namespace connectivity::component; +using namespace connectivity::file; +using namespace ::cppu; +using namespace ::dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sheet; +using namespace ::com::sun::star::util; + + +OComponentTable::OComponentTable(sdbcx::OCollection* _pTables,file::OConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName + ) : OComponentTable_BASE(_pTables,_pConnection,Name, + Type, + Description, + SchemaName, + CatalogName) + ,m_nDataRows(0) +{ +} + +void OComponentTable::refreshColumns() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ::std::vector< OUString> aVector; + + for(const auto& rxColumn : *m_aColumns) + aVector.push_back(Reference< XNamed>(rxColumn,UNO_QUERY_THROW)->getName()); + + if(m_xColumns) + m_xColumns->reFill(aVector); + else + m_xColumns = new component::OComponentColumns(this,m_aMutex,aVector); +} + +void OComponentTable::refreshIndexes() +{ + // Writer or Calc table has no index +} + + +Sequence< Type > SAL_CALL OComponentTable::getTypes( ) +{ + Sequence< Type > aTypes = OTable_TYPEDEF::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<XKeysSupplier>::get()|| + *pBegin == cppu::UnoType<XIndexesSupplier>::get()|| + *pBegin == cppu::UnoType<XRename>::get()|| + *pBegin == cppu::UnoType<XAlterTable>::get()|| + *pBegin == cppu::UnoType<XDataDescriptorFactory>::get())) + aOwnTypes.push_back(*pBegin); + } + aOwnTypes.push_back(cppu::UnoType<css::lang::XUnoTunnel>::get()); + + return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size()); +} + + +Any SAL_CALL OComponentTable::queryInterface( const Type & rType ) +{ + if( rType == cppu::UnoType<XKeysSupplier>::get()|| + rType == cppu::UnoType<XIndexesSupplier>::get()|| + rType == cppu::UnoType<XRename>::get()|| + rType == cppu::UnoType<XAlterTable>::get()|| + rType == cppu::UnoType<XDataDescriptorFactory>::get()) + return Any(); + + const Any aRet = ::cppu::queryInterface(rType,static_cast< css::lang::XUnoTunnel*> (this)); + return aRet.hasValue() ? aRet : OTable_TYPEDEF::queryInterface(rType); +} + + +sal_Int32 OComponentTable::getCurrentLastPos() const +{ + return m_nDataRows; +} + +bool OComponentTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos) +{ + // prepare positioning: + + sal_uInt32 nNumberOfRecords = m_nDataRows; + sal_uInt32 nTempPos = m_nFilePos; + m_nFilePos = nCurPos; + + switch(eCursorPosition) + { + case IResultSetHelper::NEXT: + m_nFilePos++; + break; + case IResultSetHelper::PRIOR: + if (m_nFilePos > 0) + m_nFilePos--; + break; + case IResultSetHelper::FIRST: + m_nFilePos = 1; + break; + case IResultSetHelper::LAST: + m_nFilePos = nNumberOfRecords; + break; + case IResultSetHelper::RELATIVE1: + m_nFilePos = (m_nFilePos + nOffset < 0) ? 0 + : static_cast<sal_uInt32>(m_nFilePos + nOffset); + break; + case IResultSetHelper::ABSOLUTE1: + case IResultSetHelper::BOOKMARK: + m_nFilePos = static_cast<sal_uInt32>(nOffset); + break; + } + + if (m_nFilePos > static_cast<sal_Int32>(nNumberOfRecords)) + m_nFilePos = static_cast<sal_Int32>(nNumberOfRecords) + 1; + + if (m_nFilePos == 0 || m_nFilePos == static_cast<sal_Int32>(nNumberOfRecords) + 1) + { + switch(eCursorPosition) + { + case IResultSetHelper::PRIOR: + case IResultSetHelper::FIRST: + m_nFilePos = 0; + break; + case IResultSetHelper::LAST: + case IResultSetHelper::NEXT: + case IResultSetHelper::ABSOLUTE1: + case IResultSetHelper::RELATIVE1: + if (nOffset > 0) + m_nFilePos = nNumberOfRecords + 1; + else if (nOffset < 0) + m_nFilePos = 0; + break; + case IResultSetHelper::BOOKMARK: + m_nFilePos = nTempPos; // previous position + break; + } + return false; + } + + //! read buffer / setup row object etc? + nCurPos = m_nFilePos; + return true; +} + +void OComponentTable::FileClose() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + OComponentTable_BASE::FileClose(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DCatalog.cxx b/connectivity/source/drivers/dbase/DCatalog.cxx new file mode 100644 index 000000000..c35af1f28 --- /dev/null +++ b/connectivity/source/drivers/dbase/DCatalog.cxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbase/DCatalog.hxx> +#include <dbase/DConnection.hxx> +#include <dbase/DTables.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::dbase; + +ODbaseCatalog::ODbaseCatalog(ODbaseConnection* _pCon) : file::OFileCatalog(_pCon) +{ +} + +void ODbaseCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + Sequence< OUString > aTypes; + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(), + "%", "%", aTypes); + + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while(xResult->next()) + aVector.push_back(xRow->getString(3)); + } + if(m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset( new ODbaseTables(m_xMetaData,*this,m_aMutex,aVector) ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DColumns.cxx b/connectivity/source/drivers/dbase/DColumns.cxx new file mode 100644 index 000000000..b997ec8d9 --- /dev/null +++ b/connectivity/source/drivers/dbase/DColumns.cxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbase/DColumns.hxx> +#include <dbase/DTable.hxx> +#include <connectivity/sdbcx/VColumn.hxx> + +using namespace connectivity::dbase; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; + +sdbcx::ObjectType ODbaseColumns::createObject(const OUString& _rName) +{ + ODbaseTable* pTable = static_cast<ODbaseTable*>(m_pTable); + + const ::rtl::Reference<OSQLColumns>& aCols = pTable->getTableColumns(); + OSQLColumns::const_iterator aIter = find(aCols->begin(),aCols->end(),_rName,::comphelper::UStringMixEqual(isCaseSensitive())); + + sdbcx::ObjectType xRet; + if(aIter != aCols->end()) + xRet = sdbcx::ObjectType(*aIter,UNO_QUERY); + return xRet; +} + + +void ODbaseColumns::impl_refresh() +{ + m_pTable->refreshColumns(); +} + +Reference< XPropertySet > ODbaseColumns::createDescriptor() +{ + return new sdbcx::OColumn(isCaseSensitive()); +} + + +// XAppend +sdbcx::ObjectType ODbaseColumns::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + if ( m_pTable->isNew() ) + return cloneDescriptor( descriptor ); + + m_pTable->addColumn( descriptor ); + return createObject( _rForName ); +} + + +// XDrop +void ODbaseColumns::dropObject(sal_Int32 _nPos, const OUString& /*_sElementName*/) +{ + if(!m_pTable->isNew()) + m_pTable->dropColumn(_nPos); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DConnection.cxx b/connectivity/source/drivers/dbase/DConnection.cxx new file mode 100644 index 000000000..7c577b7c9 --- /dev/null +++ b/connectivity/source/drivers/dbase/DConnection.cxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DConnection.hxx> +#include <dbase/DDatabaseMetaData.hxx> +#include <dbase/DCatalog.hxx> +#include <dbase/DDriver.hxx> +#include <dbase/DPreparedStatement.hxx> +#include <dbase/DStatement.hxx> +#include <connectivity/dbexception.hxx> + +using namespace connectivity::dbase; +using namespace connectivity::file; + +typedef connectivity::file::OConnection OConnection_BASE; + + +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::lang; + +ODbaseConnection::ODbaseConnection(ODriver* _pDriver) : OConnection(_pDriver) +{ + m_aFilenameExtension = "dbf"; +} + +ODbaseConnection::~ODbaseConnection() +{ +} + +// XServiceInfo + +IMPLEMENT_SERVICE_INFO(ODbaseConnection, "com.sun.star.sdbc.drivers.dbase.Connection", "com.sun.star.sdbc.Connection") + + +Reference< XDatabaseMetaData > SAL_CALL ODbaseConnection::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + xMetaData = new ODbaseDatabaseMetaData(this); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +css::uno::Reference< XTablesSupplier > ODbaseConnection::createCatalog() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XTablesSupplier > xTab = m_xCatalog; + if(!xTab.is()) + { + ODbaseCatalog *pCat = new ODbaseCatalog(this); + xTab = pCat; + m_xCatalog = xTab; + } + return xTab; +} + +Reference< XStatement > SAL_CALL ODbaseConnection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + Reference< XStatement > xReturn = new ODbaseStatement(this); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL ODbaseConnection::prepareStatement( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + ODbasePreparedStatement* pStmt = new ODbasePreparedStatement(this); + Reference< XPreparedStatement > xHoldAlive = pStmt; + pStmt->construct(sql); + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return pStmt; +} + +Reference< XPreparedStatement > SAL_CALL ODbaseConnection::prepareCall( const OUString& /*sql*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this ); + return nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DDatabaseMetaData.cxx b/connectivity/source/drivers/dbase/DDatabaseMetaData.cxx new file mode 100644 index 000000000..b896e942c --- /dev/null +++ b/connectivity/source/drivers/dbase/DDatabaseMetaData.cxx @@ -0,0 +1,388 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DDatabaseMetaData.hxx> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XIndexesSupplier.hpp> +#include <FDatabaseMetaDataResultSet.hxx> +#include <dbase/DIndex.hxx> +#include <connectivity/FValue.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <ucbhelper/content.hxx> + +using namespace ::comphelper; +using namespace connectivity::dbase; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::lang; + +ODbaseDatabaseMetaData::ODbaseDatabaseMetaData(::connectivity::file::OConnection* _pCon) :ODatabaseMetaData(_pCon) +{ +} + +ODbaseDatabaseMetaData::~ODbaseDatabaseMetaData() +{ +} + +Reference< XResultSet > ODbaseDatabaseMetaData::impl_getTypeInfo_throw( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTypeInfo); + Reference< XResultSet > xRef = pResult; + + static ODatabaseMetaDataResultSet::ORows aRows; + if(aRows.empty()) + { + ODatabaseMetaDataResultSet::ORow aRow; + aRow.reserve(19); + + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(OUString("VARCHAR"))); + aRow.push_back(new ORowSetValueDecorator(DataType::VARCHAR)); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(254))); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(new ORowSetValueDecorator(OUString("length"))); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(ColumnValue::NULLABLE))); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(ColumnSearch::FULL))); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(new ORowSetValueDecorator(OUString("C"))); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(10))); + + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("LONGVARCHAR")); + aRow[2] = new ORowSetValueDecorator(DataType::LONGVARCHAR); + aRow[3] = new ORowSetValueDecorator(sal_Int32(2147483647)); + aRow[6] = new ORowSetValueDecorator(); + aRow[13] = new ORowSetValueDecorator(OUString("M")); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("DATE")); + aRow[2] = new ORowSetValueDecorator(DataType::DATE); + aRow[3] = new ORowSetValueDecorator(sal_Int32(10)); + aRow[13] = new ORowSetValueDecorator(OUString("D")); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("BOOLEAN")); + aRow[2] = new ORowSetValueDecorator(DataType::BIT); + aRow[3] = ODatabaseMetaDataResultSet::get1Value(); + aRow[4] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[5] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[6] = new ORowSetValueDecorator(OUString()); + aRow[9] = ODatabaseMetaDataResultSet::getBasicValue(); + aRow[13] = new ORowSetValueDecorator(OUString("L")); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("DOUBLE")); + aRow[2] = new ORowSetValueDecorator(DataType::DOUBLE); + aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); + aRow[13] = new ORowSetValueDecorator(OUString("B")); + aRows.push_back(aRow); + + aRow[11] = new ORowSetValueDecorator(true); + aRow[13] = new ORowSetValueDecorator(OUString("Y")); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("TIMESTAMP")); + aRow[2] = new ORowSetValueDecorator(DataType::TIMESTAMP); + aRow[11] = new ORowSetValueDecorator(false); + aRow[13] = new ORowSetValueDecorator(OUString("T")); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("INTEGER")); + aRow[2] = new ORowSetValueDecorator(DataType::INTEGER); + aRow[3] = new ORowSetValueDecorator(sal_Int32(10)); + aRow[13] = new ORowSetValueDecorator(OUString("I")); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("DECIMAL")); + aRow[2] = new ORowSetValueDecorator(DataType::DECIMAL); + aRow[3] = new ORowSetValueDecorator(sal_Int32(20)); + aRow[6] = new ORowSetValueDecorator(OUString("length,scale")); + aRow[13] = new ORowSetValueDecorator(OUString("F")); + aRows.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("NUMERIC")); + aRow[2] = new ORowSetValueDecorator(DataType::DECIMAL); + aRow[3] = new ORowSetValueDecorator(sal_Int32(16)); + aRow[13] = new ORowSetValueDecorator(OUString("N")); + aRow[15] = new ORowSetValueDecorator(sal_Int32(16)); + aRows.push_back(aRow); + } + + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > SAL_CALL ODbaseDatabaseMetaData::getColumns( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XTablesSupplier > xTables = m_pConnection->createCatalog(); + if(!xTables.is()) + throw SQLException(); + + Reference< XNameAccess> xNames = xTables->getTables(); + if(!xNames.is()) + throw SQLException(); + + ODatabaseMetaDataResultSet::ORows aRows; + ODatabaseMetaDataResultSet::ORow aRow(19); + + try + { + aRow[10] = new ORowSetValueDecorator(sal_Int32(10)); + Sequence< OUString> aTabNames(xNames->getElementNames()); + const OUString* pTabBegin = aTabNames.getConstArray(); + const OUString* pTabEnd = pTabBegin + aTabNames.getLength(); + for(;pTabBegin != pTabEnd;++pTabBegin) + { + if(match(tableNamePattern,*pTabBegin,'\0')) + { + Reference< XColumnsSupplier> xTable( + xNames->getByName(*pTabBegin), css::uno::UNO_QUERY); + OSL_ENSURE(xTable.is(),"Table not found! Normally an exception had to be thrown here!"); + aRow[3] = new ORowSetValueDecorator(*pTabBegin); + + Reference< XNameAccess> xColumns = xTable->getColumns(); + if(!xColumns.is()) + throw SQLException(); + + Sequence< OUString> aColNames(xColumns->getElementNames()); + + const OUString* pBegin = aColNames.getConstArray(); + const OUString* pEnd = pBegin + aColNames.getLength(); + Reference< XPropertySet> xColumn; + for(sal_Int32 i=1;pBegin != pEnd;++pBegin,++i) + { + if(match(columnNamePattern,*pBegin,'\0')) + { + aRow[4] = new ORowSetValueDecorator(*pBegin); + + xColumn.set( + xColumns->getByName(*pBegin), css::uno::UNO_QUERY); + OSL_ENSURE(xColumn.is(),"Columns contains a column who isn't a fastpropertyset!"); + aRow[5] = new ORowSetValueDecorator(getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)))); + aRow[6] = new ORowSetValueDecorator(getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME)))); + aRow[7] = new ORowSetValueDecorator(getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)))); + aRow[9] = new ORowSetValueDecorator(getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE)))); + aRow[11] = new ORowSetValueDecorator(getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE)))); + aRow[13] = new ORowSetValueDecorator(getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE)))); + switch(static_cast<sal_Int32>(aRow[5]->getValue())) + { + case DataType::CHAR: + case DataType::VARCHAR: + aRow[16] = new ORowSetValueDecorator(sal_Int32(254)); + break; + case DataType::LONGVARCHAR: + aRow[16] = new ORowSetValueDecorator(sal_Int32(65535)); + break; + default: + aRow[16] = new ORowSetValueDecorator(sal_Int32(0)); + } + aRow[17] = new ORowSetValueDecorator(i); + switch(sal_Int32(aRow[11]->getValue())) + { + case ColumnValue::NO_NULLS: + aRow[18] = new ORowSetValueDecorator(OUString("NO")); + break; + case ColumnValue::NULLABLE: + aRow[18] = new ORowSetValueDecorator(OUString("YES")); + break; + default: + aRow[18] = new ORowSetValueDecorator(OUString()); + } + aRows.push_back(aRow); + } + } + } + } + } + catch (const WrappedTargetException& e) + { + SQLException aSql; + if (e.TargetException >>= aSql) + throw aSql; + throw WrappedTargetRuntimeException(e.Message, e.Context, e.TargetException); + } + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eColumns); + Reference< XResultSet > xRef = pResult; + pResult->setRows(aRows); + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODbaseDatabaseMetaData::getIndexInfo( + const Any& /*catalog*/, const OUString& /*schema*/, const OUString& table, + sal_Bool unique, sal_Bool /*approximate*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XTablesSupplier > xTables = m_pConnection->createCatalog(); + if(!xTables.is()) + throw SQLException(); + + Reference< XNameAccess> xNames = xTables->getTables(); + if(!xNames.is()) + throw SQLException(); + + ODatabaseMetaDataResultSet::ORows aRows; + ODatabaseMetaDataResultSet::ORow aRow(14); + + aRow[5] = new ORowSetValueDecorator(OUString()); + aRow[10] = new ORowSetValueDecorator(OUString("A")); + + Reference< XIndexesSupplier> xTable( + xNames->getByName(table), css::uno::UNO_QUERY); + aRow[3] = new ORowSetValueDecorator(table); + aRow[7] = new ORowSetValueDecorator(sal_Int32(3)); + + Reference< XNameAccess> xIndexes = xTable->getIndexes(); + if(!xIndexes.is()) + throw SQLException(); + + Sequence< OUString> aIdxNames(xIndexes->getElementNames()); + + const OUString* pBegin = aIdxNames.getConstArray(); + const OUString* pEnd = pBegin + aIdxNames.getLength(); + Reference< XPropertySet> xIndex; + for(;pBegin != pEnd;++pBegin) + { + xIndex.set(xIndexes->getByName(*pBegin), css::uno::UNO_QUERY); + OSL_ENSURE(xIndex.is(),"Indexes contains a column who isn't a fastpropertyset!"); + + if(unique && !getBOOL(xIndex->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISUNIQUE)))) + continue; + aRow[4] = new ORowSetValueDecorator(getBOOL(xIndex->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISUNIQUE)))); + aRow[6] = new ORowSetValueDecorator(*pBegin); + + auto pIndex = comphelper::getUnoTunnelImplementation<ODbaseIndex>(xIndex); + if(pIndex) + { + aRow[11] = new ORowSetValueDecorator(static_cast<sal_Int32>(pIndex->getHeader().db_maxkeys)); + aRow[12] = new ORowSetValueDecorator(static_cast<sal_Int32>(pIndex->getHeader().db_pagecount)); + } + + Reference<XColumnsSupplier> xColumnsSup(xIndex,UNO_QUERY); + Reference< XNameAccess> xColumns = xColumnsSup->getColumns(); + Sequence< OUString> aColNames(xColumns->getElementNames()); + + const OUString* pColBegin = aColNames.getConstArray(); + const OUString* pColEnd = pColBegin + aColNames.getLength(); + for(sal_Int32 j=1;pColBegin != pColEnd;++pColBegin,++j) + { + aRow[8] = new ORowSetValueDecorator(j); + aRow[9] = new ORowSetValueDecorator(*pColBegin); + aRows.push_back(aRow); + } + } + + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eIndexInfo); + Reference< XResultSet > xRef = pResult; + pResult->setRows(aRows); + return xRef; +} + +OUString SAL_CALL ODbaseDatabaseMetaData::getURL( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return "sdbc:dbase:" + m_pConnection->getURL(); +} + +sal_Int32 SAL_CALL ODbaseDatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + return SAL_MAX_INT32; +} + +sal_Int32 SAL_CALL ODbaseDatabaseMetaData::getMaxCharLiteralLength( ) +{ + return 254; +} + +sal_Int32 SAL_CALL ODbaseDatabaseMetaData::getMaxColumnNameLength( ) +{ + return 10; +} + +sal_Int32 SAL_CALL ODbaseDatabaseMetaData::getMaxColumnsInIndex( ) +{ + return 1; +} + +sal_Int32 SAL_CALL ODbaseDatabaseMetaData::getMaxColumnsInTable( ) +{ + return 128; +} + +sal_Bool SAL_CALL ODbaseDatabaseMetaData::supportsAlterTableWithAddColumn( ) +{ + return true; +} + +sal_Bool SAL_CALL ODbaseDatabaseMetaData::supportsAlterTableWithDropColumn( ) +{ + return false; +} + +sal_Bool SAL_CALL ODbaseDatabaseMetaData::isReadOnly( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + bool bReadOnly = false; + ::ucbhelper::Content aFile(m_pConnection->getContent(),Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext()); + aFile.getPropertyValue("IsReadOnly") >>= bReadOnly; + + return bReadOnly; +} + +bool ODbaseDatabaseMetaData::impl_storesMixedCaseQuotedIdentifiers_throw( ) +{ + return true; +} + +bool ODbaseDatabaseMetaData::impl_supportsMixedCaseQuotedIdentifiers_throw( ) +{ + return true; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DDriver.cxx b/connectivity/source/drivers/dbase/DDriver.cxx new file mode 100644 index 000000000..ab2d52413 --- /dev/null +++ b/connectivity/source/drivers/dbase/DDriver.cxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DDriver.hxx> +#include <dbase/DConnection.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> +#include <comphelper/processfactory.hxx> + +using namespace connectivity::dbase; +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::lang; + + +// static ServiceInfo + +OUString ODriver::getImplementationName_Static( ) +{ + return "com.sun.star.comp.sdbc.dbase.ODriver"; +} + + +OUString SAL_CALL ODriver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + + +css::uno::Reference< css::uno::XInterface > connectivity::dbase::ODriver_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory) +{ + return *(new ODriver( comphelper::getComponentContext(_rxFactory) )); +} + +Reference< XConnection > SAL_CALL ODriver::connect( const OUString& url, const Sequence< PropertyValue >& info ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if (ODriver_BASE::rBHelper.bDisposed) + throw DisposedException(); + + if ( ! acceptsURL(url) ) + return nullptr; + + ODbaseConnection* pCon = new ODbaseConnection(this); + pCon->construct(url,info); + Reference< XConnection > xCon = pCon; + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return xCon; +} + +sal_Bool SAL_CALL ODriver::acceptsURL( const OUString& url ) +{ + return url.startsWith("sdbc:dbase:"); +} + +Sequence< DriverPropertyInfo > SAL_CALL ODriver::getPropertyInfo( const OUString& url, const Sequence< PropertyValue >& /*info*/ ) +{ + if ( acceptsURL(url) ) + { + Sequence< OUString > aBoolean(2); + aBoolean[0] = "0"; + aBoolean[1] = "1"; + + DriverPropertyInfo aDriverInfo[] = { + { + "CharSet" + ,"CharSet of the database." + ,false + ,OUString() + ,Sequence< OUString >() + }, + { + "ShowDeleted" + ,"Display inactive records." + ,false + ,"0" + ,aBoolean + }, + { + "EnableSQL92Check" + ,"Use SQL92 naming constraints." + ,false + ,"0" + ,aBoolean + } + }; + + return Sequence< DriverPropertyInfo >(aDriverInfo, std::size(aDriverInfo)); + } + + SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + return Sequence< DriverPropertyInfo >(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DIndex.cxx b/connectivity/source/drivers/dbase/DIndex.cxx new file mode 100644 index 000000000..d168de39e --- /dev/null +++ b/connectivity/source/drivers/dbase/DIndex.cxx @@ -0,0 +1,611 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DIndex.hxx> +#include <dbase/DIndexColumns.hxx> +#include <dbase/DTable.hxx> +#include <dbase/DIndexIter.hxx> +#include <osl/file.hxx> +#include <sal/log.hxx> +#include <tools/config.hxx> +#include <connectivity/CommonTools.hxx> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <unotools/ucbhelper.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <connectivity/dbexception.hxx> +#include <dbase/DResultSet.hxx> +#include <strings.hrc> +#include <unotools/sharedunocomponent.hxx> + +using namespace ::comphelper; + +using namespace connectivity; +using namespace utl; +using namespace ::cppu; +using namespace connectivity::file; +using namespace connectivity::sdbcx; +using namespace connectivity::dbase; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::sdbcx; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; + +IMPLEMENT_SERVICE_INFO(ODbaseIndex,"com.sun.star.sdbcx.driver.dbase.Index","com.sun.star.sdbcx.Index"); + +ODbaseIndex::ODbaseIndex(ODbaseTable* _pTable) + : OIndex(true/*_pTable->getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers()*/) + , m_nCurNode(NODE_NOTFOUND) + , m_nPageCount(0) + , m_nRootPage(0) + , m_pTable(_pTable) + , m_bUseCollector(false) +{ + construct(); +} + +ODbaseIndex::ODbaseIndex( ODbaseTable* _pTable, + const NDXHeader& _rHeader, + const OUString& _rName) + : OIndex(_rName, OUString(), _rHeader.db_unique, false, false, true) + , m_aHeader(_rHeader) + , m_nCurNode(NODE_NOTFOUND) + , m_nPageCount(0) + , m_nRootPage(0) + , m_pTable(_pTable) + , m_bUseCollector(false) +{ + construct(); +} + +ODbaseIndex::~ODbaseIndex() +{ + closeImpl(); +} + +void ODbaseIndex::refreshColumns() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ::std::vector< OUString> aVector; + if(!isNew()) + { + OSL_ENSURE(m_pFileStream,"FileStream is not opened!"); + OSL_ENSURE(m_aHeader.db_name[0] != '\0',"Invalid name for the column!"); + aVector.push_back(OUString::createFromAscii(m_aHeader.db_name)); + } + + if(m_pColumns) + m_pColumns->reFill(aVector); + else + m_pColumns.reset(new ODbaseIndexColumns(this,m_aMutex,aVector)); +} + +Sequence< sal_Int8 > ODbaseIndex::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// XUnoTunnel + +sal_Int64 ODbaseIndex::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return (isUnoTunnelId<ODbaseIndex>(rId)) + ? reinterpret_cast< sal_Int64 >( this ) + : ODbaseIndex_BASE::getSomething(rId); +} + +ONDXPagePtr const & ODbaseIndex::getRoot() +{ + openIndexFile(); + if (!m_aRoot.Is()) + { + m_nRootPage = m_aHeader.db_rootpage; + m_nPageCount = m_aHeader.db_pagecount; + m_aRoot = CreatePage(m_nRootPage,nullptr,true); + } + return m_aRoot; +} + +void ODbaseIndex::openIndexFile() +{ + if(m_pFileStream) + return; + + OUString sFile = getCompletePath(); + if(UCBContentHelper::Exists(sFile)) + { + m_pFileStream = OFileTable::createStream_simpleError(sFile, StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE); + if (!m_pFileStream) + m_pFileStream = OFileTable::createStream_simpleError(sFile, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE); + if(m_pFileStream) + { + m_pFileStream->SetEndian(SvStreamEndian::LITTLE); + m_pFileStream->SetBufferSize(DINDEX_PAGE_SIZE); + (*m_pFileStream) >> *this; + } + } + if(!m_pFileStream) + { + const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, + "$filename$", sFile + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } +} + +std::unique_ptr<OIndexIterator> ODbaseIndex::createIterator() +{ + openIndexFile(); + return std::make_unique<OIndexIterator>(this); +} + +bool ODbaseIndex::ConvertToKey(ONDXKey* rKey, sal_uInt32 nRec, const ORowSetValue& rValue) +{ + OSL_ENSURE(m_pFileStream,"FileStream is not opened!"); + // Search a specific value in Index + // If the Index is unique, the key doesn't matter + try + { + if (m_aHeader.db_keytype == 0) + { + *rKey = ONDXKey(rValue.getString(), nRec ); + } + else + { + if (rValue.isNull()) + *rKey = ONDXKey(rValue.getDouble(), DataType::DOUBLE, nRec ); + else + *rKey = ONDXKey(rValue.getDouble(), nRec ); + } + } + catch (Exception&) + { + OSL_ASSERT(false); + return false; + } + return true; +} + + +bool ODbaseIndex::Find(sal_uInt32 nRec, const ORowSetValue& rValue) +{ + openIndexFile(); + OSL_ENSURE(m_pFileStream,"FileStream is not opened!"); + // Search a specific value in Index + // If the Index is unique, the key doesn't matter + ONDXKey aKey; + return ConvertToKey(&aKey, nRec, rValue) && getRoot()->Find(aKey); +} + + +bool ODbaseIndex::Insert(sal_uInt32 nRec, const ORowSetValue& rValue) +{ + openIndexFile(); + OSL_ENSURE(m_pFileStream,"FileStream is not opened!"); + ONDXKey aKey; + + // Does the value already exist + // Use Find() always to determine the actual leaf + if (!ConvertToKey(&aKey, nRec, rValue) || (getRoot()->Find(aKey) && isUnique())) + return false; + + ONDXNode aNewNode(aKey); + + // insert in the current leaf + if (!m_aCurLeaf.Is()) + return false; + + bool bResult = m_aCurLeaf->Insert(aNewNode); + Release(bResult); + + return bResult; +} + + +bool ODbaseIndex::Update(sal_uInt32 nRec, const ORowSetValue& rOldValue, + const ORowSetValue& rNewValue) +{ + openIndexFile(); + OSL_ENSURE(m_pFileStream,"FileStream is not opened!"); + ONDXKey aKey; + if (!ConvertToKey(&aKey, nRec, rNewValue) || (isUnique() && getRoot()->Find(aKey))) + return false; + else + return Delete(nRec, rOldValue) && Insert(nRec,rNewValue); +} + + +bool ODbaseIndex::Delete(sal_uInt32 nRec, const ORowSetValue& rValue) +{ + openIndexFile(); + OSL_ENSURE(m_pFileStream,"FileStream is not opened!"); + // Does the value already exist + // Always use Find() to determine the actual leaf + ONDXKey aKey; + if (!ConvertToKey(&aKey, nRec, rValue) || !getRoot()->Find(aKey)) + return false; + + // insert in the current leaf + if (!m_aCurLeaf.Is()) + return false; +#if OSL_DEBUG_LEVEL > 1 + m_aRoot->PrintPage(); +#endif + + m_aCurLeaf->Delete(m_nCurNode); + return true; +} + +void ODbaseIndex::Collect(ONDXPage* pPage) +{ + if (pPage) + m_aCollector.push_back(pPage); +} + +void ODbaseIndex::Release(bool bSave) +{ + // Release the Index-resources + m_bUseCollector = false; + + if (m_aCurLeaf.Is()) + { + m_aCurLeaf->Release(bSave); + m_aCurLeaf.Clear(); + } + + // Release the root + if (m_aRoot.Is()) + { + m_aRoot->Release(bSave); + m_aRoot.Clear(); + } + // Release all references, before the FileStream will be closed + for (auto& i : m_aCollector) + i->QueryDelete(); + + m_aCollector.clear(); + + // Header modified? + if (bSave && (m_aHeader.db_rootpage != m_nRootPage || + m_aHeader.db_pagecount != m_nPageCount)) + { + m_aHeader.db_rootpage = m_nRootPage; + m_aHeader.db_pagecount = m_nPageCount; + WriteODbaseIndex( *m_pFileStream, *this ); + } + m_nRootPage = m_nPageCount = 0; + m_nCurNode = NODE_NOTFOUND; + + closeImpl(); +} + +void ODbaseIndex::closeImpl() +{ + m_pFileStream.reset(); +} + +ONDXPage* ODbaseIndex::CreatePage(sal_uInt32 nPagePos, ONDXPage* pParent, bool bLoad) +{ + OSL_ENSURE(m_pFileStream,"FileStream is not opened!"); + + ONDXPage* pPage; + if ( !m_aCollector.empty() ) + { + pPage = *(m_aCollector.rbegin()); + m_aCollector.pop_back(); + pPage->SetPagePos(nPagePos); + pPage->SetParent(pParent); + } + else + pPage = new ONDXPage(*this, nPagePos, pParent); + + if (bLoad) + (*m_pFileStream) >> *pPage; + + return pPage; +} + +void connectivity::dbase::ReadHeader( + SvStream & rStream, ODbaseIndex::NDXHeader & rHeader) +{ +#if !defined(NDEBUG) + sal_uInt64 const nOldPos(rStream.Tell()); +#endif + rStream.ReadUInt32(rHeader.db_rootpage); + rStream.ReadUInt32(rHeader.db_pagecount); + rStream.ReadBytes(&rHeader.db_free, 4); + rStream.ReadUInt16(rHeader.db_keylen); + rStream.ReadUInt16(rHeader.db_maxkeys); + rStream.ReadUInt16(rHeader.db_keytype); + rStream.ReadUInt16(rHeader.db_keyrec); + rStream.ReadBytes(&rHeader.db_free1, 3); + rStream.ReadUChar(rHeader.db_unique); + rStream.ReadBytes(&rHeader.db_name, 488); + assert(rStream.GetError() || rStream.Tell() == nOldPos + DINDEX_PAGE_SIZE); +} + +SvStream& connectivity::dbase::operator >> (SvStream &rStream, ODbaseIndex& rIndex) +{ + rStream.Seek(0); + ReadHeader(rStream, rIndex.m_aHeader); + + rIndex.m_nRootPage = rIndex.m_aHeader.db_rootpage; + rIndex.m_nPageCount = rIndex.m_aHeader.db_pagecount; + return rStream; +} + +SvStream& connectivity::dbase::WriteODbaseIndex(SvStream &rStream, ODbaseIndex& rIndex) +{ + rStream.Seek(0); + rStream.WriteUInt32(rIndex.m_aHeader.db_rootpage); + rStream.WriteUInt32(rIndex.m_aHeader.db_pagecount); + rStream.WriteBytes(&rIndex.m_aHeader.db_free, 4); + rStream.WriteUInt16(rIndex.m_aHeader.db_keylen); + rStream.WriteUInt16(rIndex.m_aHeader.db_maxkeys); + rStream.WriteUInt16(rIndex.m_aHeader.db_keytype); + rStream.WriteUInt16(rIndex.m_aHeader.db_keyrec); + rStream.WriteBytes(&rIndex.m_aHeader.db_free1, 3); + rStream.WriteUChar(rIndex.m_aHeader.db_unique); + rStream.WriteBytes(&rIndex.m_aHeader.db_name, 488); + assert(rStream.GetError() || rStream.Tell() == DINDEX_PAGE_SIZE); + SAL_WARN_IF(rStream.GetError(), "connectivity.dbase", "write error"); + return rStream; +} + +OUString ODbaseIndex::getCompletePath() const +{ + OUString sDir = m_pTable->getConnection()->getURL() + + OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) + + m_Name + ".ndx"; + return sDir; +} + +void ODbaseIndex::createINFEntry() +{ + // synchronize inf-file + const OUString sEntry(m_Name + ".ndx"); + + OUString sCfgFile(m_pTable->getConnection()->getURL() + + OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) + + m_pTable->getName() + + ".inf"); + + OUString sPhysicalPath; + osl::FileBase::getSystemPathFromFileURL(sCfgFile, sPhysicalPath); + + Config aInfFile(sPhysicalPath); + aInfFile.SetGroup(dBASE_III_GROUP); + + sal_uInt16 nSuffix = aInfFile.GetKeyCount(); + OString aNewEntry,aKeyName; + bool bCase = isCaseSensitive(); + while (aNewEntry.isEmpty()) + { + aNewEntry = OString("NDX"); + aNewEntry += OString::number(++nSuffix); + for (sal_uInt16 i = 0; i < aInfFile.GetKeyCount(); i++) + { + aKeyName = aInfFile.GetKeyName(i); + if (bCase ? aKeyName == aNewEntry : aKeyName.equalsIgnoreAsciiCase(aNewEntry)) + { + aNewEntry.clear(); + break; + } + } + } + aInfFile.WriteKey(aNewEntry, OUStringToOString(sEntry, m_pTable->getConnection()->getTextEncoding())); +} + +void ODbaseIndex::DropImpl() +{ + closeImpl(); + + OUString sPath = getCompletePath(); + if(UCBContentHelper::Exists(sPath)) + { + if(!UCBContentHelper::Kill(sPath)) + m_pTable->getConnection()->throwGenericSQLException(STR_COULD_NOT_DELETE_INDEX,*m_pTable); + } + + // synchronize inf-file + OUString sCfgFile = m_pTable->getConnection()->getURL() + + OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) + + m_pTable->getName() + ".inf"; + + OUString sPhysicalPath; + OSL_VERIFY( osl::FileBase::getSystemPathFromFileURL(sCfgFile, sPhysicalPath) + == osl::FileBase::E_None ); + + Config aInfFile(sPhysicalPath); + aInfFile.SetGroup(dBASE_III_GROUP); + sal_uInt16 nKeyCnt = aInfFile.GetKeyCount(); + OString aKeyName; + OUString sEntry = m_Name + ".ndx"; + + // delete entries from the inf file + for (sal_uInt16 nKey = 0; nKey < nKeyCnt; nKey++) + { + // References the Key to an Index-file? + aKeyName = aInfFile.GetKeyName( nKey ); + if (aKeyName.startsWith("NDX")) + { + if(sEntry == OStringToOUString(aInfFile.ReadKey(aKeyName),m_pTable->getConnection()->getTextEncoding())) + { + aInfFile.DeleteKey(aKeyName); + break; + } + } + } +} + +void ODbaseIndex::impl_killFileAndthrowError_throw(const char* pErrorId, const OUString& _sFile) +{ + closeImpl(); + if(UCBContentHelper::Exists(_sFile)) + UCBContentHelper::Kill(_sFile); + m_pTable->getConnection()->throwGenericSQLException(pErrorId, *this); +} + +void ODbaseIndex::CreateImpl() +{ + // Create the Index + const OUString sFile = getCompletePath(); + if(UCBContentHelper::Exists(sFile)) + { + const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_CREATE_INDEX_NAME, + "$filename$", sFile + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + // Index comprises only one column + if (m_pColumns->getCount() > 1) + m_pTable->getConnection()->throwGenericSQLException(STR_ONL_ONE_COLUMN_PER_INDEX,*this); + + Reference<XFastPropertySet> xCol(m_pColumns->getByIndex(0),UNO_QUERY); + + // Is the column already indexed? + if ( !xCol.is() ) + ::dbtools::throwFunctionSequenceException(*this); + + // create the index file + m_pFileStream = OFileTable::createStream_simpleError(sFile,StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC); + if (!m_pFileStream) + { + const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, + "$filename$", sFile + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + + m_pFileStream->SetEndian(SvStreamEndian::LITTLE); + m_pFileStream->SetBufferSize(DINDEX_PAGE_SIZE); + + // firstly the result must be sorted + utl::SharedUNOComponent<XStatement> xStmt; + utl::SharedUNOComponent<XResultSet> xSet; + OUString aName; + try + { + xStmt.set( m_pTable->getConnection()->createStatement(), UNO_SET_THROW); + + aName = getString(xCol->getFastPropertyValue(PROPERTY_ID_NAME)); + + const OUString aQuote(m_pTable->getConnection()->getMetaData()->getIdentifierQuoteString()); + OUString aStatement( "SELECT " + aQuote + aName + aQuote +" FROM " + aQuote + m_pTable->getName() + aQuote + " ORDER BY " + aQuote + aName + aQuote); + + xSet.set( xStmt->executeQuery(aStatement),UNO_SET_THROW ); + } + catch(const Exception& ) + { + impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile); + } + if (!xSet.is()) + { + impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile); + } + + // Set the header info + memset(&m_aHeader,0,sizeof(m_aHeader)); + sal_Int32 nType = 0; + ::rtl::Reference<OSQLColumns> aCols = m_pTable->getTableColumns(); + const Reference< XPropertySet > xTableCol(*find(aCols->begin(),aCols->end(),aName,::comphelper::UStringMixEqual(isCaseSensitive()))); + + xTableCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType; + + m_aHeader.db_keytype = (nType == DataType::VARCHAR || nType == DataType::CHAR) ? 0 : 1; + m_aHeader.db_keylen = (m_aHeader.db_keytype) ? 8 : static_cast<sal_uInt16>(getINT32(xTableCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)))); + m_aHeader.db_keylen = (( m_aHeader.db_keylen - 1) / 4 + 1) * 4; + m_aHeader.db_maxkeys = (DINDEX_PAGE_SIZE - 4) / (8 + m_aHeader.db_keylen); + if ( m_aHeader.db_maxkeys < 3 ) + { + impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX_KEYSIZE,sFile); + } + + m_pFileStream->SetStreamSize(DINDEX_PAGE_SIZE); + + OString aCol(OUStringToOString(aName, m_pTable->getConnection()->getTextEncoding())); + strncpy(m_aHeader.db_name, aCol.getStr(), std::min<size_t>(sizeof(m_aHeader.db_name), aCol.getLength())); + m_aHeader.db_unique = m_IsUnique ? 1: 0; + m_aHeader.db_keyrec = m_aHeader.db_keylen + 8; + + // modifications of the header are detected by differences between + // the HeaderInfo and nRootPage or nPageCount respectively + m_nRootPage = 1; + m_nPageCount = 2; + + m_aCurLeaf = m_aRoot = CreatePage(m_nRootPage); + m_aRoot->SetModified(true); + + m_bUseCollector = true; + + sal_Int32 nRowsLeft = 0; + Reference<XRow> xRow(xSet,UNO_QUERY); + + if(xSet->last()) + { + Reference< XUnoTunnel> xTunnel(xSet, UNO_QUERY_THROW); + ODbaseResultSet* pDbaseRes = reinterpret_cast< ODbaseResultSet* >( xTunnel->getSomething(ODbaseResultSet::getUnoTunnelId()) ); + assert(pDbaseRes); //"No dbase resultset found? What's going on here! + nRowsLeft = xSet->getRow(); + + xSet->beforeFirst(); + ORowSetValue atmpValue; + ONDXKey aKey(atmpValue, nType, 0); + ONDXKey aInsertKey(atmpValue, nType, 0); + // Create the index structure + while (xSet->next()) + { + ORowSetValue aValue(m_aHeader.db_keytype ? ORowSetValue(xRow->getDouble(1)) : ORowSetValue(xRow->getString(1))); + // checking for duplicate entries + if (m_IsUnique && m_nCurNode != NODE_NOTFOUND) + { + aKey.setValue(aValue); + if (aKey == (*m_aCurLeaf)[m_nCurNode].GetKey()) + { + impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX_NOT_UNIQUE,sFile); + } + } + aInsertKey.setValue(aValue); + aInsertKey.setRecord(pDbaseRes->getCurrentFilePos()); + + ONDXNode aNewNode(aInsertKey); + if (!m_aCurLeaf->Insert(aNewNode, --nRowsLeft)) + break; + } + } + + if(nRowsLeft) + { + impl_killFileAndthrowError_throw(STR_COULD_NOT_CREATE_INDEX,sFile); + } + Release(); + createINFEntry(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DIndexColumns.cxx b/connectivity/source/drivers/dbase/DIndexColumns.cxx new file mode 100644 index 000000000..886c7273d --- /dev/null +++ b/connectivity/source/drivers/dbase/DIndexColumns.cxx @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DIndexColumns.hxx> +#include <dbase/DTable.hxx> +#include <sdbcx/VIndexColumn.hxx> +#include <comphelper/types.hxx> + +using namespace ::comphelper; + +using namespace connectivity::dbase; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; + + +sdbcx::ObjectType ODbaseIndexColumns::createObject(const OUString& _rName) +{ + const ODbaseTable* pTable = m_pIndex->getTable(); + + const ::rtl::Reference<OSQLColumns>& aCols = pTable->getTableColumns(); + OSQLColumns::const_iterator aIter = find(aCols->begin(),aCols->end(),_rName,::comphelper::UStringMixEqual(isCaseSensitive())); + + Reference< XPropertySet > xCol; + if(aIter != aCols->end()) + xCol = *aIter; + + if(!xCol.is()) + return sdbcx::ObjectType(); + + sdbcx::ObjectType xRet = new sdbcx::OIndexColumn(true,_rName + ,getString(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME))) + ,OUString() + ,getINT32(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE))) + ,getINT32(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))) + ,getINT32(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))) + ,getINT32(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))) + ,pTable->getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers() + ,getString(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CATALOGNAME))) + ,getString(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCHEMANAME))) + ,getString(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TABLENAME)))); + + return xRet; +} + + +void ODbaseIndexColumns::impl_refresh() +{ + m_pIndex->refreshColumns(); +} + +Reference< XPropertySet > ODbaseIndexColumns::createDescriptor() +{ + return new sdbcx::OIndexColumn(m_pIndex->getTable()->getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers()); +} + +sdbcx::ObjectType ODbaseIndexColumns::appendObject( const OUString& /*_rForName*/, const Reference< XPropertySet >& descriptor ) +{ + return cloneDescriptor( descriptor ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DIndexIter.cxx b/connectivity/source/drivers/dbase/DIndexIter.cxx new file mode 100644 index 000000000..62cc6601a --- /dev/null +++ b/connectivity/source/drivers/dbase/DIndexIter.cxx @@ -0,0 +1,302 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DIndexIter.hxx> +#include <com/sun/star/sdb/SQLFilterOperator.hpp> + +using namespace ::com::sun::star::sdb; +using namespace connectivity; +using namespace connectivity::dbase; +using namespace connectivity::file; + +// OIndexIterator + + +OIndexIterator::~OIndexIterator() +{ +} + + +sal_uInt32 OIndexIterator::First() +{ + return Find(true); +} + + +sal_uInt32 OIndexIterator::Next() +{ + return Find(false); +} + +sal_uInt32 OIndexIterator::Find(bool bFirst) +{ + sal_uInt32 nRes = NODE_NOTFOUND; + + if (bFirst) + { + m_aRoot = m_xIndex->getRoot(); + m_aCurLeaf.Clear(); + } + + if (!m_pOperator) + { + // Preparation, position on the smallest element + if (bFirst) + { + ONDXPage* pPage = m_aRoot; + while (pPage && !pPage->IsLeaf()) + pPage = pPage->GetChild(m_xIndex.get()); + + m_aCurLeaf = pPage; + m_nCurNode = NODE_NOTFOUND; + } + ONDXKey* pKey = GetNextKey(); + nRes = pKey ? pKey->GetRecord() : NODE_NOTFOUND; + } + else if (dynamic_cast<const OOp_ISNOTNULL *>(m_pOperator) != nullptr) + nRes = GetNotNull(bFirst); + else if (dynamic_cast<const OOp_ISNULL *>(m_pOperator) != nullptr) + nRes = GetNull(bFirst); + else if (dynamic_cast<const OOp_LIKE *>(m_pOperator) != nullptr) + nRes = GetLike(bFirst); + else if (dynamic_cast<const OOp_COMPARE *>(m_pOperator) != nullptr) + nRes = GetCompare(bFirst); + + return nRes; +} + + +ONDXKey* OIndexIterator::GetFirstKey(ONDXPage* pPage, const OOperand& rKey) +{ + // searches a given key + // Speciality: At the end of the algorithm + // the actual page and the position of the node which fulfil the + // '<='-condition are saved. this is considered for inserts. + // ONDXIndex* m_pIndex = GetNDXIndex(); + OOp_COMPARE aTempOp(SQLFilterOperator::GREATER); + sal_uInt16 i = 0; + + if (pPage->IsLeaf()) + { + // in the leaf the actual operation is run, otherwise temp. (>) + while (i < pPage->Count() && !m_pOperator->operate(&((*pPage)[i]).GetKey(),&rKey)) + i++; + } + else + while (i < pPage->Count() && !aTempOp.operate(&((*pPage)[i]).GetKey(),&rKey)) + i++; + + + ONDXKey* pFoundKey = nullptr; + if (!pPage->IsLeaf()) + { + // descend further + ONDXPagePtr aPage = (i==0) ? pPage->GetChild(m_xIndex.get()) + : ((*pPage)[i-1]).GetChild(m_xIndex.get(), pPage); + pFoundKey = aPage.Is() ? GetFirstKey(aPage, rKey) : nullptr; + } + else if (i == pPage->Count()) + { + pFoundKey = nullptr; + } + else + { + pFoundKey = &(*pPage)[i].GetKey(); + if (!m_pOperator->operate(pFoundKey,&rKey)) + pFoundKey = nullptr; + + m_aCurLeaf = pPage; + m_nCurNode = pFoundKey ? i : i - 1; + } + return pFoundKey; +} + + +sal_uInt32 OIndexIterator::GetCompare(bool bFirst) +{ + ONDXKey* pKey = nullptr; + sal_Int32 ePredicateType = dynamic_cast<file::OOp_COMPARE&>(*m_pOperator).getPredicateType(); + + if (bFirst) + { + // Preparation, position on the smallest element + ONDXPage* pPage = m_aRoot; + switch (ePredicateType) + { + case SQLFilterOperator::NOT_EQUAL: + case SQLFilterOperator::LESS: + case SQLFilterOperator::LESS_EQUAL: + while (pPage && !pPage->IsLeaf()) + pPage = pPage->GetChild(m_xIndex.get()); + + m_aCurLeaf = pPage; + m_nCurNode = NODE_NOTFOUND; + } + + + switch (ePredicateType) + { + case SQLFilterOperator::NOT_EQUAL: + while ( ( pKey = GetNextKey() ) != nullptr ) + if (m_pOperator->operate(pKey,m_pOperand)) + break; + break; + case SQLFilterOperator::LESS: + while ( ( pKey = GetNextKey() ) != nullptr ) + if (!pKey->getValue().isNull()) + break; + break; + case SQLFilterOperator::LESS_EQUAL: + while ( ( pKey = GetNextKey() ) != nullptr ) ; + break; + case SQLFilterOperator::GREATER_EQUAL: + case SQLFilterOperator::EQUAL: + pKey = GetFirstKey(m_aRoot,*m_pOperand); + break; + case SQLFilterOperator::GREATER: + pKey = GetFirstKey(m_aRoot,*m_pOperand); + if ( !pKey ) + while ( ( pKey = GetNextKey() ) != nullptr ) + if (m_pOperator->operate(pKey,m_pOperand)) + break; + } + } + else + { + switch (ePredicateType) + { + case SQLFilterOperator::NOT_EQUAL: + while ( ( pKey = GetNextKey() ) != nullptr ) + if (m_pOperator->operate(pKey,m_pOperand)) + break; + break; + case SQLFilterOperator::LESS: + case SQLFilterOperator::LESS_EQUAL: + case SQLFilterOperator::EQUAL: + pKey = GetNextKey(); + if ( pKey == nullptr || !m_pOperator->operate(pKey,m_pOperand)) + { + pKey = nullptr; + m_aCurLeaf.Clear(); + } + break; + case SQLFilterOperator::GREATER_EQUAL: + case SQLFilterOperator::GREATER: + pKey = GetNextKey(); + } + } + + return pKey ? pKey->GetRecord() : NODE_NOTFOUND; +} + + +sal_uInt32 OIndexIterator::GetLike(bool bFirst) +{ + if (bFirst) + { + ONDXPage* pPage = m_aRoot; + + while (pPage && !pPage->IsLeaf()) + pPage = pPage->GetChild(m_xIndex.get()); + + m_aCurLeaf = pPage; + m_nCurNode = NODE_NOTFOUND; + } + + ONDXKey* pKey; + while ( ( pKey = GetNextKey() ) != nullptr ) + if (m_pOperator->operate(pKey,m_pOperand)) + break; + return pKey ? pKey->GetRecord() : NODE_NOTFOUND; +} + + +sal_uInt32 OIndexIterator::GetNull(bool bFirst) +{ + if (bFirst) + { + ONDXPage* pPage = m_aRoot; + while (pPage && !pPage->IsLeaf()) + pPage = pPage->GetChild(m_xIndex.get()); + + m_aCurLeaf = pPage; + m_nCurNode = NODE_NOTFOUND; + } + + ONDXKey* pKey = GetNextKey(); + if ( pKey == nullptr || !pKey->getValue().isNull()) + { + pKey = nullptr; + m_aCurLeaf.Clear(); + } + return pKey ? pKey->GetRecord() : NODE_NOTFOUND; +} + + +sal_uInt32 OIndexIterator::GetNotNull(bool bFirst) +{ + ONDXKey* pKey; + if (bFirst) + { + // go through all NULL values first + for (sal_uInt32 nRec = GetNull(bFirst); + nRec != NODE_NOTFOUND; + nRec = GetNull(false)) + ; + pKey = m_aCurLeaf.Is() ? &(*m_aCurLeaf)[m_nCurNode].GetKey() : nullptr; + } + else + pKey = GetNextKey(); + + return pKey ? pKey->GetRecord() : NODE_NOTFOUND; +} + + +ONDXKey* OIndexIterator::GetNextKey() +{ + if (m_aCurLeaf.Is() && ((++m_nCurNode) >= m_aCurLeaf->Count())) + { + ONDXPage* pPage = m_aCurLeaf; + // search next page + while (pPage) + { + ONDXPage* pParentPage = pPage->GetParent(); + if (pParentPage) + { + sal_uInt16 nPos = pParentPage->Search(pPage); + if (nPos != pParentPage->Count() - 1) + { // page found + pPage = (*pParentPage)[nPos+1].GetChild(m_xIndex.get(),pParentPage); + break; + } + } + pPage = pParentPage; + } + + // now go on with leaf + while (pPage && !pPage->IsLeaf()) + pPage = pPage->GetChild(m_xIndex.get()); + + m_aCurLeaf = pPage; + m_nCurNode = 0; + } + return m_aCurLeaf.Is() ? &(*m_aCurLeaf)[m_nCurNode].GetKey() : nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DIndexes.cxx b/connectivity/source/drivers/dbase/DIndexes.cxx new file mode 100644 index 000000000..1ebce2612 --- /dev/null +++ b/connectivity/source/drivers/dbase/DIndexes.cxx @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DIndexes.hxx> +#include <dbase/DIndex.hxx> +#include <comphelper/servicehelper.hxx> +#include <connectivity/dbexception.hxx> +#include <unotools/ucbhelper.hxx> +#include <strings.hrc> + +using namespace ::comphelper; + +using namespace utl; +using namespace ::connectivity; +using namespace ::dbtools; +using namespace ::connectivity::dbase; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +sdbcx::ObjectType ODbaseIndexes::createObject(const OUString& _rName) +{ + OUString sFile = m_pTable->getConnection()->getURL() + + OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DELIMITER) + + _rName + ".ndx"; + if ( !UCBContentHelper::Exists(sFile) ) + { + const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, + "$filename$", sFile + ) ); + ::dbtools::throwGenericSQLException( sError, *m_pTable ); + } + + sdbcx::ObjectType xRet; + std::unique_ptr<SvStream> pFileStream = ::connectivity::file::OFileTable::createStream_simpleError(sFile, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE); + if(pFileStream) + { + pFileStream->SetEndian(SvStreamEndian::LITTLE); + pFileStream->SetBufferSize(DINDEX_PAGE_SIZE); + ODbaseIndex::NDXHeader aHeader; + + pFileStream->Seek(0); + ReadHeader(*pFileStream, aHeader); + pFileStream.reset(); + + ODbaseIndex* pIndex = new ODbaseIndex(m_pTable,aHeader,_rName); + xRet = pIndex; + pIndex->openIndexFile(); + } + else + { + const OUString sError( m_pTable->getConnection()->getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, + "$filename$", sFile + ) ); + ::dbtools::throwGenericSQLException( sError, *m_pTable ); + } + return xRet; +} + +void ODbaseIndexes::impl_refresh( ) +{ + if(m_pTable) + m_pTable->refreshIndexes(); +} + +Reference< XPropertySet > ODbaseIndexes::createDescriptor() +{ + return new ODbaseIndex(m_pTable); +} + +// XAppend +sdbcx::ObjectType ODbaseIndexes::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + Reference<XUnoTunnel> xTunnel(descriptor,UNO_QUERY); + if(xTunnel.is()) + { + ODbaseIndex* pIndex = reinterpret_cast< ODbaseIndex* >( xTunnel->getSomething(ODbaseIndex::getUnoTunnelId()) ); + if(!pIndex) + throw SQLException(); + pIndex->CreateImpl(); + } + + return createObject( _rForName ); +} + +// XDrop +void ODbaseIndexes::dropObject(sal_Int32 _nPos, const OUString& /*_sElementName*/) +{ + auto pIndex = comphelper::getUnoTunnelImplementation<ODbaseIndex>(getObject(_nPos)); + if ( pIndex ) + pIndex->DropImpl(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DPreparedStatement.cxx b/connectivity/source/drivers/dbase/DPreparedStatement.cxx new file mode 100644 index 000000000..60e7a6a7f --- /dev/null +++ b/connectivity/source/drivers/dbase/DPreparedStatement.cxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DPreparedStatement.hxx> +#include <dbase/DResultSet.hxx> + +using namespace connectivity::dbase; +using namespace connectivity::file; +using namespace com::sun::star::uno; + +OResultSet* ODbasePreparedStatement::createResultSet() +{ + return new ODbaseResultSet(this,m_aSQLIterator); +} + +IMPLEMENT_SERVICE_INFO(ODbasePreparedStatement,"com.sun.star.sdbc.driver.dbase.PreparedStatement","com.sun.star.sdbc.PreparedStatement"); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DResultSet.cxx b/connectivity/source/drivers/dbase/DResultSet.cxx new file mode 100644 index 000000000..f9395a359 --- /dev/null +++ b/connectivity/source/drivers/dbase/DResultSet.cxx @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <dbase/DResultSet.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <dbase/DIndex.hxx> +#include <dbase/DIndexIter.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> + +using namespace ::comphelper; + +using namespace connectivity::dbase; +using namespace connectivity::file; +using namespace ::cppu; +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; + +ODbaseResultSet::ODbaseResultSet( OStatement_Base* pStmt,connectivity::OSQLParseTreeIterator& _aSQLIterator) + : file::OResultSet(pStmt,_aSQLIterator) + ,m_bBookmarkable(true) +{ + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), PROPERTY_ID_ISBOOKMARKABLE, PropertyAttribute::READONLY,&m_bBookmarkable, cppu::UnoType<bool>::get()); +} + +OUString SAL_CALL ODbaseResultSet::getImplementationName( ) +{ + return "com.sun.star.sdbcx.dbase.ResultSet"; +} + +Sequence< OUString > SAL_CALL ODbaseResultSet::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdbc.ResultSet", "com.sun.star.sdbcx.ResultSet" }; +} + +sal_Bool SAL_CALL ODbaseResultSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Any SAL_CALL ODbaseResultSet::queryInterface( const Type & rType ) +{ + Any aRet = ODbaseResultSet_BASE::queryInterface(rType); + return aRet.hasValue() ? aRet : OResultSet::queryInterface(rType); +} + + Sequence< Type > SAL_CALL ODbaseResultSet::getTypes( ) +{ + return ::comphelper::concatSequences(OResultSet::getTypes(),ODbaseResultSet_BASE::getTypes()); +} + + +// XRowLocate +Any SAL_CALL ODbaseResultSet::getBookmark( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + OSL_ENSURE((m_bShowDeleted || !m_aRow->isDeleted()),"getBookmark called for deleted row"); + + return makeAny(static_cast<sal_Int32>((*m_aRow)[0]->getValue())); +} + +sal_Bool SAL_CALL ODbaseResultSet::moveToBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_bRowDeleted = m_bRowInserted = m_bRowUpdated = false; + + return m_pTable.is() && Move(IResultSetHelper::BOOKMARK,comphelper::getINT32(bookmark),true); +} + +sal_Bool SAL_CALL ODbaseResultSet::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + if(!m_pTable.is()) + return false; + + + Move(IResultSetHelper::BOOKMARK,comphelper::getINT32(bookmark),false); + + return relative(rows); +} + + +sal_Int32 SAL_CALL ODbaseResultSet::compareBookmarks( const Any& lhs, const Any& rhs ) +{ + sal_Int32 nFirst(0),nSecond(0),nResult(0); + if ( !( lhs >>= nFirst ) || !( rhs >>= nSecond ) ) + { + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_INVALID_BOOKMARK); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } // if ( !( lhs >>= nFirst ) || !( rhs >>= nSecond ) ) + + if(nFirst < nSecond) + nResult = CompareBookmark::LESS; + else if(nFirst > nSecond) + nResult = CompareBookmark::GREATER; + else + nResult = CompareBookmark::EQUAL; + + return nResult; +} + +sal_Bool SAL_CALL ODbaseResultSet::hasOrderedBookmarks( ) +{ + return true; +} + +sal_Int32 SAL_CALL ODbaseResultSet::hashBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return comphelper::getINT32(bookmark); +} + +// XDeleteRows +Sequence< sal_Int32 > SAL_CALL ODbaseResultSet::deleteRows( const Sequence< Any >& /*rows*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFeatureNotImplementedSQLException( "XDeleteRows::deleteRows", *this ); + return Sequence< sal_Int32 >(); +} + +bool ODbaseResultSet::fillIndexValues(const Reference< XColumnsSupplier> &_xIndex) +{ + auto pIndex = comphelper::getUnoTunnelImplementation<dbase::ODbaseIndex>(_xIndex); + if(pIndex) + { + std::unique_ptr<dbase::OIndexIterator> pIter = pIndex->createIterator(); + + if (pIter) + { + sal_uInt32 nRec = pIter->First(); + while (nRec != NODE_NOTFOUND) + { + m_pFileSet->push_back(nRec); + nRec = pIter->Next(); + } + m_pFileSet->setFrozen(); + return true; + } + } + return false; +} + +::cppu::IPropertyArrayHelper & ODbaseResultSet::getInfoHelper() +{ + return *ODbaseResultSet_BASE3::getArrayHelper(); +} + +::cppu::IPropertyArrayHelper* ODbaseResultSet::createArrayHelper() const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +void SAL_CALL ODbaseResultSet::acquire() throw() +{ + ODbaseResultSet_BASE2::acquire(); +} + +void SAL_CALL ODbaseResultSet::release() throw() +{ + ODbaseResultSet_BASE2::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL ODbaseResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +sal_Int32 ODbaseResultSet::getCurrentFilePos() const +{ + return m_pTable->getFilePos(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DStatement.cxx b/connectivity/source/drivers/dbase/DStatement.cxx new file mode 100644 index 000000000..096878e3e --- /dev/null +++ b/connectivity/source/drivers/dbase/DStatement.cxx @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DStatement.hxx> +#include <dbase/DResultSet.hxx> + +using namespace connectivity::dbase; +using namespace connectivity::file; +using namespace com::sun::star::uno; + + +OResultSet* ODbaseStatement::createResultSet() +{ + return new ODbaseResultSet(this,m_aSQLIterator); +} + +IMPLEMENT_SERVICE_INFO(ODbaseStatement,"com.sun.star.sdbc.driver.dbase.Statement","com.sun.star.sdbc.Statement"); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DTable.cxx b/connectivity/source/drivers/dbase/DTable.cxx new file mode 100644 index 000000000..ff01f3b6b --- /dev/null +++ b/connectivity/source/drivers/dbase/DTable.cxx @@ -0,0 +1,2732 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/DTable.hxx> +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <o3tl/safeint.hxx> +#include <svl/converter.hxx> +#include <dbase/DConnection.hxx> +#include <dbase/DColumns.hxx> +#include <tools/config.hxx> +#include <dbase/DIndex.hxx> +#include <dbase/DIndexes.hxx> +#include <comphelper/processfactory.hxx> +#include <rtl/math.hxx> +#include <ucbhelper/content.hxx> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <connectivity/dbexception.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/property.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/string.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/ucbhelper.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/FValue.hxx> +#include <connectivity/dbconversion.hxx> +#include <connectivity/sdbcx/VColumn.hxx> +#include <strings.hrc> +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> +#include <tools/date.hxx> + +#include <algorithm> +#include <cassert> +#include <memory> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::sdbcx; +using namespace connectivity::dbase; +using namespace connectivity::file; +using namespace ::ucbhelper; +using namespace ::utl; +using namespace ::cppu; +using namespace ::dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::i18n; + +// stored as the Field Descriptor terminator +#define FIELD_DESCRIPTOR_TERMINATOR 0x0D +#define DBF_EOL 0x1A + +namespace +{ +std::size_t lcl_getFileSize(SvStream& _rStream) +{ + std::size_t nFileSize = 0; + _rStream.Seek(STREAM_SEEK_TO_END); + _rStream.SeekRel(-1); + char cEOL; + _rStream.ReadChar( cEOL ); + nFileSize = _rStream.Tell(); + if ( cEOL == DBF_EOL ) + nFileSize -= 1; + return nFileSize; +} +/** + calculates the Julian date +*/ +void lcl_CalcJulDate(sal_Int32& _nJulianDate,sal_Int32& _nJulianTime, const css::util::DateTime& rDateTime) +{ + css::util::DateTime aDateTime = rDateTime; + // weird: months fix + if (aDateTime.Month > 12) + { + aDateTime.Month--; + sal_uInt16 delta = rDateTime.Month / 12; + aDateTime.Year += delta; + aDateTime.Month -= delta * 12; + aDateTime.Month++; + } + + _nJulianTime = ((aDateTime.Hours*3600000)+(aDateTime.Minutes*60000)+(aDateTime.Seconds*1000)+(aDateTime.NanoSeconds/1000000)); + /* conversion factors */ + sal_uInt16 iy0; + sal_uInt16 im0; + if ( aDateTime.Month <= 2 ) + { + iy0 = aDateTime.Year - 1; + im0 = aDateTime.Month + 12; + } + else + { + iy0 = aDateTime.Year; + im0 = aDateTime.Month; + } + sal_Int32 ia = iy0 / 100; + sal_Int32 ib = 2 - ia + (ia >> 2); + /* calculate julian date */ + if ( aDateTime.Year <= 0 ) + { + _nJulianDate = static_cast<sal_Int32>((365.25 * iy0) - 0.75) + + static_cast<sal_Int32>(30.6001 * (im0 + 1) ) + + aDateTime.Day + 1720994; + } // if ( rDateTime.Year <= 0 ) + else + { + _nJulianDate = static_cast<sal_Int32>(365.25 * iy0) + + static_cast<sal_Int32>(30.6001 * (im0 + 1)) + + aDateTime.Day + 1720994; + } + double JD = _nJulianDate + 0.5; + _nJulianDate = static_cast<sal_Int32>( JD + 0.5); + const double gyr = aDateTime.Year + (0.01 * aDateTime.Month) + (0.0001 * aDateTime.Day); + if ( gyr >= 1582.1015 ) /* on or after 15 October 1582 */ + _nJulianDate += ib; +} + +/** + calculates date time from the Julian Date +*/ +void lcl_CalDate(sal_Int32 _nJulianDate,sal_Int32 _nJulianTime,css::util::DateTime& _rDateTime) +{ + if ( _nJulianDate ) + { + sal_Int32 ialp; + sal_Int32 ka = _nJulianDate; + if ( _nJulianDate >= 2299161 ) + { + ialp = static_cast<sal_Int32>( (static_cast<double>(_nJulianDate) - 1867216.25 ) / 36524.25 ); + ka = _nJulianDate + 1 + ialp - ( ialp >> 2 ); + } + sal_Int32 kb = ka + 1524; + sal_Int32 kc = static_cast<sal_Int32>( (static_cast<double>(kb) - 122.1 ) / 365.25 ); + sal_Int32 kd = static_cast<sal_Int32>(static_cast<double>(kc) * 365.25); + sal_Int32 ke = static_cast<sal_Int32>(static_cast<double>( kb - kd ) / 30.6001 ); + _rDateTime.Day = static_cast<sal_uInt16>(kb - kd - static_cast<sal_Int32>( static_cast<double>(ke) * 30.6001 )); + if ( ke > 13 ) + _rDateTime.Month = static_cast<sal_uInt16>(ke - 13); + else + _rDateTime.Month = static_cast<sal_uInt16>(ke - 1); + if ( (_rDateTime.Month == 2) && (_rDateTime.Day > 28) ) + _rDateTime.Day = 29; + if ( (_rDateTime.Month == 2) && (_rDateTime.Day == 29) && (ke == 3) ) + _rDateTime.Year = static_cast<sal_uInt16>(kc - 4716); + else if ( _rDateTime.Month > 2 ) + _rDateTime.Year = static_cast<sal_uInt16>(kc - 4716); + else + _rDateTime.Year = static_cast<sal_uInt16>(kc - 4715); + } + + if ( _nJulianTime ) + { + double d_s = _nJulianTime / 1000.0; + double d_m = d_s / 60.0; + double d_h = d_m / 60.0; + _rDateTime.Hours = static_cast<sal_uInt16>(d_h); + _rDateTime.Minutes = static_cast<sal_uInt16>((d_h - static_cast<double>(_rDateTime.Hours)) * 60.0); + _rDateTime.Seconds = static_cast<sal_uInt16>(((d_m - static_cast<double>(_rDateTime.Minutes)) * 60.0) + - (static_cast<double>(_rDateTime.Hours) * 3600.0)); + } +} + +} + + +void ODbaseTable::readHeader() +{ + OSL_ENSURE(m_pFileStream,"No Stream available!"); + if(!m_pFileStream) + return; + m_pFileStream->RefreshBuffer(); // Make sure, that the header information actually is read again + m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN); + + sal_uInt8 nType=0; + m_pFileStream->ReadUChar( nType ); + if(ERRCODE_NONE != m_pFileStream->GetErrorCode()) + throwInvalidDbaseFormat(); + + m_pFileStream->ReadBytes(m_aHeader.dateElems, 3); + if(ERRCODE_NONE != m_pFileStream->GetErrorCode()) + throwInvalidDbaseFormat(); + + m_pFileStream->ReadUInt32( m_aHeader.nbRecords); + if(ERRCODE_NONE != m_pFileStream->GetErrorCode()) + throwInvalidDbaseFormat(); + + m_pFileStream->ReadUInt16( m_aHeader.headerLength); + if(ERRCODE_NONE != m_pFileStream->GetErrorCode()) + throwInvalidDbaseFormat(); + + m_pFileStream->ReadUInt16( m_aHeader.recordLength); + if(ERRCODE_NONE != m_pFileStream->GetErrorCode()) + throwInvalidDbaseFormat(); + if (m_aHeader.recordLength == 0) + throwInvalidDbaseFormat(); + + m_pFileStream->ReadBytes(m_aHeader.trailer, 20); + if(ERRCODE_NONE != m_pFileStream->GetErrorCode()) + throwInvalidDbaseFormat(); + + + if ( ( ( m_aHeader.headerLength - 1 ) / 32 - 1 ) <= 0 ) // number of fields + { + // no dBASE file + throwInvalidDbaseFormat(); + } + else + { + // Consistency check of the header: + m_aHeader.type = static_cast<DBFType>(nType); + switch (m_aHeader.type) + { + case dBaseIII: + case dBaseIV: + case dBaseV: + case VisualFoxPro: + case VisualFoxProAuto: + case dBaseFS: + case dBaseFSMemo: + case dBaseIVMemoSQL: + case dBaseIIIMemo: + case FoxProMemo: + m_pFileStream->SetEndian(SvStreamEndian::LITTLE); + if( getConnection()->isTextEncodingDefaulted() && + !dbfDecodeCharset(m_eEncoding, nType, m_aHeader.trailer[17])) + { + m_eEncoding = RTL_TEXTENCODING_IBM_850; + } + break; + case dBaseIVMemo: + m_pFileStream->SetEndian(SvStreamEndian::LITTLE); + break; + default: + { + throwInvalidDbaseFormat(); + } + } + } +} + +void ODbaseTable::fillColumns() +{ + m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN); + m_pFileStream->Seek(32); + + if(!m_aColumns.is()) + m_aColumns = new OSQLColumns(); + else + m_aColumns->clear(); + + m_aTypes.clear(); + m_aPrecisions.clear(); + m_aScales.clear(); + + // Number of fields: + const sal_Int32 nFieldCount = (m_aHeader.headerLength - 1) / 32 - 1; + OSL_ENSURE(nFieldCount,"No columns in table!"); + + m_aColumns->reserve(nFieldCount); + m_aTypes.reserve(nFieldCount); + m_aPrecisions.reserve(nFieldCount); + m_aScales.reserve(nFieldCount); + + OUString aTypeName; + const bool bCase = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(); + const bool bFoxPro = m_aHeader.type == VisualFoxPro || m_aHeader.type == VisualFoxProAuto || m_aHeader.type == FoxProMemo; + + sal_Int32 i = 0; + for (; i < nFieldCount; i++) + { + DBFColumn aDBFColumn; +#if !defined(NDEBUG) + sal_uInt64 const nOldPos(m_pFileStream->Tell()); +#endif + m_pFileStream->ReadBytes(aDBFColumn.db_fnm, 11); + m_pFileStream->ReadUChar(aDBFColumn.db_typ); + m_pFileStream->ReadUInt32(aDBFColumn.db_adr); + m_pFileStream->ReadUChar(aDBFColumn.db_flng); + m_pFileStream->ReadUChar(aDBFColumn.db_dez); + m_pFileStream->ReadBytes(aDBFColumn.db_free2, 14); + assert(m_pFileStream->GetError() || m_pFileStream->Tell() == nOldPos + sizeof(aDBFColumn)); + if (m_pFileStream->GetError()) + { + SAL_WARN("connectivity.drivers", "ODbaseTable::fillColumns: short read!"); + break; + } + if ( FIELD_DESCRIPTOR_TERMINATOR == aDBFColumn.db_fnm[0] ) // 0x0D stored as the Field Descriptor terminator. + break; + + aDBFColumn.db_fnm[sizeof(aDBFColumn.db_fnm)-1] = 0; //ensure null termination for broken input + const OUString aColumnName(reinterpret_cast<char *>(aDBFColumn.db_fnm), strlen(reinterpret_cast<char *>(aDBFColumn.db_fnm)), m_eEncoding); + + bool bIsRowVersion = bFoxPro && ( aDBFColumn.db_free2[0] & 0x01 ) == 0x01; + + m_aRealFieldLengths.push_back(aDBFColumn.db_flng); + sal_Int32 nPrecision = aDBFColumn.db_flng; + sal_Int32 eType; + bool bIsCurrency = false; + + char cType[2]; + cType[0] = aDBFColumn.db_typ; + cType[1] = 0; + aTypeName = OUString(cType, 1, RTL_TEXTENCODING_ASCII_US); + SAL_INFO( "connectivity.drivers","column type: " << aDBFColumn.db_typ); + + switch (aDBFColumn.db_typ) + { + case 'C': + eType = DataType::VARCHAR; + aTypeName = "VARCHAR"; + break; + case 'F': + case 'N': + aTypeName = "DECIMAL"; + if ( aDBFColumn.db_typ == 'N' ) + aTypeName = "NUMERIC"; + eType = DataType::DECIMAL; + + // for numeric fields two characters more are written, then the precision of the column description predescribes, + // to keep room for the possible sign and the comma. This has to be considered... + nPrecision = SvDbaseConverter::ConvertPrecisionToOdbc(nPrecision,aDBFColumn.db_dez); + // This is not true for older versions... + break; + case 'L': + eType = DataType::BIT; + aTypeName = "BOOLEAN"; + break; + case 'Y': + bIsCurrency = true; + eType = DataType::DOUBLE; + aTypeName = "DOUBLE"; + break; + case 'D': + eType = DataType::DATE; + aTypeName = "DATE"; + break; + case 'T': + eType = DataType::TIMESTAMP; + aTypeName = "TIMESTAMP"; + break; + case 'I': + eType = DataType::INTEGER; + aTypeName = "INTEGER"; + break; + case 'M': + if ( bFoxPro && ( aDBFColumn.db_free2[0] & 0x04 ) == 0x04 ) + { + eType = DataType::LONGVARBINARY; + aTypeName = "LONGVARBINARY"; + } + else + { + aTypeName = "LONGVARCHAR"; + eType = DataType::LONGVARCHAR; + } + nPrecision = 2147483647; + break; + case 'P': + aTypeName = "LONGVARBINARY"; + eType = DataType::LONGVARBINARY; + nPrecision = 2147483647; + break; + case '0': + case 'B': + if ( m_aHeader.type == VisualFoxPro || m_aHeader.type == VisualFoxProAuto ) + { + aTypeName = "DOUBLE"; + eType = DataType::DOUBLE; + } + else + { + aTypeName = "LONGVARBINARY"; + eType = DataType::LONGVARBINARY; + nPrecision = 2147483647; + } + break; + default: + eType = DataType::OTHER; + } + + m_aTypes.push_back(eType); + m_aPrecisions.push_back(nPrecision); + m_aScales.push_back(aDBFColumn.db_dez); + + Reference< XPropertySet> xCol = new sdbcx::OColumn(aColumnName, + aTypeName, + OUString(), + OUString(), + ColumnValue::NULLABLE, + nPrecision, + aDBFColumn.db_dez, + eType, + false, + bIsRowVersion, + bIsCurrency, + bCase, + m_CatalogName, getSchema(), getName()); + m_aColumns->push_back(xCol); + } // for (; i < nFieldCount; i++) + OSL_ENSURE(i,"No columns in table!"); +} + +ODbaseTable::ODbaseTable(sdbcx::OCollection* _pTables, ODbaseConnection* _pConnection) + : ODbaseTable_BASE(_pTables,_pConnection) +{ + // initialize the header + m_aHeader.type = dBaseIII; + m_eEncoding = getConnection()->getTextEncoding(); +} + +ODbaseTable::ODbaseTable(sdbcx::OCollection* _pTables, ODbaseConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName ) + : ODbaseTable_BASE(_pTables,_pConnection,Name, + Type, + Description, + SchemaName, + CatalogName) +{ + m_eEncoding = getConnection()->getTextEncoding(); +} + + +void ODbaseTable::construct() +{ + // initialize the header + m_aHeader.type = dBaseIII; + m_aHeader.nbRecords = 0; + m_aHeader.headerLength = 0; + m_aHeader.recordLength = 0; + m_aMemoHeader.db_size = 0; + + OUString sFileName(getEntry(m_pConnection, m_Name)); + + INetURLObject aURL; + aURL.SetURL(sFileName); + + OSL_ENSURE( m_pConnection->matchesExtension( aURL.getExtension() ), + "ODbaseTable::ODbaseTable: invalid extension!"); + // getEntry is expected to ensure the correct file name + + m_pFileStream = createStream_simpleError( sFileName, StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE); + m_bWriteable = ( m_pFileStream != nullptr ); + + if ( !m_pFileStream ) + { + m_bWriteable = false; + m_pFileStream = createStream_simpleError( sFileName, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE); + } + + if(!m_pFileStream) + return; + + readHeader(); + if (HasMemoFields()) + { + // Create Memo-Filename (.DBT): + // nyi: Ugly for Unix and Mac! + + if ( m_aHeader.type == FoxProMemo || m_aHeader.type == VisualFoxPro || m_aHeader.type == VisualFoxProAuto) // foxpro uses another extension + aURL.SetExtension("fpt"); + else + aURL.SetExtension("dbt"); + + // If the memo file isn't found, the data will be displayed anyhow. + // However, updates can't be done + // but the operation is executed + m_pMemoStream = createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE); + if ( !m_pMemoStream ) + { + m_pMemoStream = createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE); + } + if (m_pMemoStream) + ReadMemoHeader(); + } + fillColumns(); + + std::size_t nFileSize = lcl_getFileSize(*m_pFileStream); + m_pFileStream->Seek(STREAM_SEEK_TO_BEGIN); + // seems to be empty or someone wrote bullshit into the dbase file + // try and recover if m_aHeader.db_slng is sane + if (m_aHeader.nbRecords == 0 && m_aHeader.recordLength) + { + std::size_t nRecords = (nFileSize-m_aHeader.headerLength)/m_aHeader.recordLength; + if (nRecords > 0) + m_aHeader.nbRecords = nRecords; + } + + // Buffersize dependent on the file size + m_pFileStream->SetBufferSize(nFileSize > 1000000 ? 32768 : + nFileSize > 100000 ? 16384 : + nFileSize > 10000 ? 4096 : 1024); + + if (m_pMemoStream) + { + // set the buffer exactly to the length of a record + nFileSize = m_pMemoStream->TellEnd(); + m_pMemoStream->Seek(STREAM_SEEK_TO_BEGIN); + + // Buffersize dependent on the file size + m_pMemoStream->SetBufferSize(nFileSize > 1000000 ? 32768 : + nFileSize > 100000 ? 16384 : + nFileSize > 10000 ? 4096 : + m_aMemoHeader.db_size); + } + + AllocBuffer(); +} + +void ODbaseTable::ReadMemoHeader() +{ + m_pMemoStream->SetEndian(SvStreamEndian::LITTLE); + m_pMemoStream->RefreshBuffer(); // make sure that the header information is actually read again + m_pMemoStream->Seek(0); + + (*m_pMemoStream).ReadUInt32( m_aMemoHeader.db_next ); + switch (m_aHeader.type) + { + case dBaseIIIMemo: // dBase III: fixed block size + case dBaseIVMemo: + // sometimes dBase3 is attached to dBase4 memo + m_pMemoStream->Seek(20); + (*m_pMemoStream).ReadUInt16( m_aMemoHeader.db_size ); + if (m_aMemoHeader.db_size > 1 && m_aMemoHeader.db_size != 512) // 1 is also for dBase 3 + m_aMemoHeader.db_typ = MemodBaseIV; + else if (m_aMemoHeader.db_size == 512) + { + // There are files using size specification, though they are dBase-files + char sHeader[4]; + m_pMemoStream->Seek(m_aMemoHeader.db_size); + m_pMemoStream->ReadBytes(sHeader, 4); + + if ((m_pMemoStream->GetErrorCode() != ERRCODE_NONE) || static_cast<sal_uInt8>(sHeader[0]) != 0xFF || static_cast<sal_uInt8>(sHeader[1]) != 0xFF || static_cast<sal_uInt8>(sHeader[2]) != 0x08) + m_aMemoHeader.db_typ = MemodBaseIII; + else + m_aMemoHeader.db_typ = MemodBaseIV; + } + else + { + m_aMemoHeader.db_typ = MemodBaseIII; + m_aMemoHeader.db_size = 512; + } + break; + case VisualFoxPro: + case VisualFoxProAuto: + case FoxProMemo: + m_aMemoHeader.db_typ = MemoFoxPro; + m_pMemoStream->Seek(6); + m_pMemoStream->SetEndian(SvStreamEndian::BIG); + (*m_pMemoStream).ReadUInt16( m_aMemoHeader.db_size ); + break; + default: + SAL_WARN( "connectivity.drivers", "ODbaseTable::ReadMemoHeader: unsupported memo type!" ); + break; + } +} + +OUString ODbaseTable::getEntry(file::OConnection const * _pConnection,const OUString& _sName ) +{ + OUString sURL; + try + { + Reference< XResultSet > xDir = _pConnection->getDir()->getStaticResultSet(); + Reference< XRow> xRow(xDir,UNO_QUERY); + OUString sName; + OUString sExt; + INetURLObject aURL; + xDir->beforeFirst(); + while(xDir->next()) + { + sName = xRow->getString(1); + aURL.SetSmartProtocol(INetProtocol::File); + OUString sUrl = _pConnection->getURL() + "/" + sName; + aURL.SetSmartURL( sUrl ); + + // cut the extension + sExt = aURL.getExtension(); + + // name and extension have to coincide + if ( _pConnection->matchesExtension( sExt ) ) + { + sName = sName.replaceAt(sName.getLength() - (sExt.getLength() + 1), sExt.getLength() + 1, OUString()); + if ( sName == _sName ) + { + Reference< XContentAccess > xContentAccess( xDir, UNO_QUERY ); + sURL = xContentAccess->queryContentIdentifierString(); + break; + } + } + } + xDir->beforeFirst(); // move back to before first record + } + catch(const Exception&) + { + OSL_ASSERT(false); + } + return sURL; +} + +void ODbaseTable::refreshColumns() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ::std::vector< OUString> aVector; + aVector.reserve(m_aColumns->size()); + + for (auto const& column : *m_aColumns) + aVector.push_back(Reference< XNamed>(column,UNO_QUERY_THROW)->getName()); + + if(m_xColumns) + m_xColumns->reFill(aVector); + else + m_xColumns = new ODbaseColumns(this,m_aMutex,aVector); +} + +void ODbaseTable::refreshIndexes() +{ + ::std::vector< OUString> aVector; + if(m_pFileStream && (!m_xIndexes || m_xIndexes->getCount() == 0)) + { + INetURLObject aURL; + aURL.SetURL(getEntry(m_pConnection,m_Name)); + + aURL.setExtension("inf"); + Config aInfFile(aURL.getFSysPath(FSysStyle::Detect)); + aInfFile.SetGroup(dBASE_III_GROUP); + sal_uInt16 nKeyCnt = aInfFile.GetKeyCount(); + OString aKeyName; + + for (sal_uInt16 nKey = 0; nKey < nKeyCnt; nKey++) + { + // References the key an index-file? + aKeyName = aInfFile.GetKeyName( nKey ); + //...if yes, add the index list of the table + if (aKeyName.startsWith("NDX")) + { + OString aIndexName = aInfFile.ReadKey(aKeyName); + aURL.setName(OStringToOUString(aIndexName, m_eEncoding)); + try + { + Content aCnt(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext()); + if (aCnt.isDocument()) + { + aVector.push_back(aURL.getBase()); + } + } + catch(const Exception&) // an exception is thrown when no file exists + { + } + } + } + } + if(m_xIndexes) + m_xIndexes->reFill(aVector); + else + m_xIndexes = new ODbaseIndexes(this,m_aMutex,aVector); +} + + +void SAL_CALL ODbaseTable::disposing() +{ + OFileTable::disposing(); + ::osl::MutexGuard aGuard(m_aMutex); + m_aColumns = nullptr; +} + +Sequence< Type > SAL_CALL ODbaseTable::getTypes( ) +{ + Sequence< Type > aTypes = OTable_TYPEDEF::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<XKeysSupplier>::get()|| + *pBegin == cppu::UnoType<XDataDescriptorFactory>::get())) + { + aOwnTypes.push_back(*pBegin); + } + } + aOwnTypes.push_back(cppu::UnoType<css::lang::XUnoTunnel>::get()); + return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size()); +} + + +Any SAL_CALL ODbaseTable::queryInterface( const Type & rType ) +{ + if( rType == cppu::UnoType<XKeysSupplier>::get()|| + rType == cppu::UnoType<XDataDescriptorFactory>::get()) + return Any(); + + Any aRet = OTable_TYPEDEF::queryInterface(rType); + return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< css::lang::XUnoTunnel*> (this)); +} + + +Sequence< sal_Int8 > ODbaseTable::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 ODbaseTable::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return (isUnoTunnelId<ODbaseTable>(rId)) + ? reinterpret_cast< sal_Int64 >( this ) + : ODbaseTable_BASE::getSomething(rId); +} + +bool ODbaseTable::fetchRow(OValueRefRow& _rRow, const OSQLColumns & _rCols, bool bRetrieveData) +{ + if (!m_pBuffer) + return false; + + // Read the data + bool bIsCurRecordDeleted = static_cast<char>(m_pBuffer[0]) == '*'; + + // only read the bookmark + + // Mark record as deleted + _rRow->setDeleted(bIsCurRecordDeleted); + *(*_rRow)[0] = m_nFilePos; + + if (!bRetrieveData) + return true; + + std::size_t nByteOffset = 1; + // Fields: + OSQLColumns::const_iterator aIter = _rCols.begin(); + OSQLColumns::const_iterator aEnd = _rCols.end(); + const std::size_t nCount = _rRow->size(); + for (std::size_t i = 1; aIter != aEnd && nByteOffset <= m_nBufferSize && i < nCount;++aIter, i++) + { + // Lengths depending on data type: + sal_Int32 nLen = 0; + sal_Int32 nType = 0; + nLen = m_aPrecisions[i-1]; + nType = m_aTypes[i-1]; + + switch(nType) + { + case DataType::INTEGER: + case DataType::DOUBLE: + case DataType::TIMESTAMP: + case DataType::DATE: + case DataType::BIT: + case DataType::LONGVARCHAR: + case DataType::LONGVARBINARY: + nLen = m_aRealFieldLengths[i-1]; + break; + case DataType::DECIMAL: + nLen = SvDbaseConverter::ConvertPrecisionToDbase(nLen,m_aScales[i-1]); + break; // the sign and the comma + + case DataType::BINARY: + case DataType::OTHER: + nByteOffset += nLen; + continue; + } + + // Is the variable bound? + if ( !(*_rRow)[i]->isBound() ) + { + // No - next field. + nByteOffset += nLen; + OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!"); + continue; + } // if ( !(_rRow->get())[i]->isBound() ) + if ( ( nByteOffset + nLen) > m_nBufferSize ) + break; // length doesn't match buffer size. + + char *pData = reinterpret_cast<char *>(m_pBuffer.get() + nByteOffset); + + if (nType == DataType::CHAR || nType == DataType::VARCHAR) + { + sal_Int32 nLastPos = -1; + for (sal_Int32 k = 0; k < nLen; ++k) + { + if (pData[k] != ' ') + // Record last non-empty position. + nLastPos = k; + } + if (nLastPos < 0) + { + // Empty string. Skip it. + (*_rRow)[i]->setNull(); + } + else + { + // Commit the string. Use intern() to ref-count it. + *(*_rRow)[i] = OUString::intern(pData, static_cast<sal_Int32>(nLastPos+1), m_eEncoding); + } + } // if (nType == DataType::CHAR || nType == DataType::VARCHAR) + else if ( DataType::TIMESTAMP == nType ) + { + sal_Int32 nDate = 0,nTime = 0; + memcpy(&nDate, pData, 4); + memcpy(&nTime, pData+ 4, 4); + if ( !nDate && !nTime ) + { + (*_rRow)[i]->setNull(); + } + else + { + css::util::DateTime aDateTime; + lcl_CalDate(nDate,nTime,aDateTime); + *(*_rRow)[i] = aDateTime; + } + } + else if ( DataType::INTEGER == nType ) + { + sal_Int32 nValue = 0; + if (o3tl::make_unsigned(nLen) > sizeof(nValue)) + return false; + memcpy(&nValue, pData, nLen); + *(*_rRow)[i] = nValue; + } + else if ( DataType::DOUBLE == nType ) + { + double d = 0.0; + if (getBOOL((*aIter)->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency is treated separately + { + sal_Int64 nValue = 0; + if (o3tl::make_unsigned(nLen) > sizeof(nValue)) + return false; + memcpy(&nValue, pData, nLen); + + if ( m_aScales[i-1] ) + d = (nValue / pow(10.0,static_cast<int>(m_aScales[i-1]))); + else + d = static_cast<double>(nValue); + } + else + { + if (o3tl::make_unsigned(nLen) > sizeof(d)) + return false; + memcpy(&d, pData, nLen); + } + + *(*_rRow)[i] = d; + } + else + { + sal_Int32 nPos1 = -1, nPos2 = -1; + // If the string contains Nul-characters, then convert them to blanks! + for (sal_Int32 k = 0; k < nLen; k++) + { + if (pData[k] == '\0') + pData[k] = ' '; + + if (pData[k] != ' ') + { + if (nPos1 < 0) + // first non-empty char position. + nPos1 = k; + + // last non-empty char position. + nPos2 = k; + } + } + + if (nPos1 < 0) + { + // Empty string. Skip it. + nByteOffset += nLen; + (*_rRow)[i]->setNull(); // no values -> done + continue; + } + + OUString aStr = OUString::intern(pData+nPos1, nPos2-nPos1+1, m_eEncoding); + + switch (nType) + { + case DataType::DATE: + { + if (aStr.getLength() != nLen) + { + (*_rRow)[i]->setNull(); + break; + } + const sal_uInt16 nYear = static_cast<sal_uInt16>(aStr.copy( 0, 4 ).toInt32()); + const sal_uInt16 nMonth = static_cast<sal_uInt16>(aStr.copy( 4, 2 ).toInt32()); + const sal_uInt16 nDay = static_cast<sal_uInt16>(aStr.copy( 6, 2 ).toInt32()); + + const css::util::Date aDate(nDay,nMonth,nYear); + *(*_rRow)[i] = aDate; + } + break; + case DataType::DECIMAL: + *(*_rRow)[i] = ORowSetValue(aStr); + break; + case DataType::BIT: + { + bool b; + switch (*pData) + { + case 'T': + case 'Y': + case 'J': b = true; break; + default: b = false; break; + } + *(*_rRow)[i] = b; + } + break; + case DataType::LONGVARBINARY: + case DataType::BINARY: + case DataType::LONGVARCHAR: + { + const long nBlockNo = aStr.toInt32(); // read blocknumber + if (nBlockNo > 0 && m_pMemoStream) // Read data from memo-file, only if + { + if ( !ReadMemo(nBlockNo, (*_rRow)[i]->get()) ) + break; + } + else + (*_rRow)[i]->setNull(); + } break; + default: + SAL_WARN( "connectivity.drivers","Wrong type"); + } + (*_rRow)[i]->setTypeKind(nType); + } + + nByteOffset += nLen; + OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!"); + } + return true; +} + + +void ODbaseTable::FileClose() +{ + ::osl::MutexGuard aGuard(m_aMutex); + // if not everything has been written yet + if (m_pMemoStream && m_pMemoStream->IsWritable()) + m_pMemoStream->Flush(); + + m_pMemoStream.reset(); + + ODbaseTable_BASE::FileClose(); +} + +bool ODbaseTable::CreateImpl() +{ + OSL_ENSURE(!m_pFileStream, "SequenceError"); + + if ( m_pConnection->isCheckEnabled() && ::dbtools::convertName2SQLName(m_Name,OUString()) != m_Name ) + { + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_SQL_NAME_ERROR, + "$name$", m_Name + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + + INetURLObject aURL; + aURL.SetSmartProtocol(INetProtocol::File); + OUString aName = getEntry(m_pConnection, m_Name); + if(aName.isEmpty()) + { + OUString aIdent = m_pConnection->getContent()->getIdentifier()->getContentIdentifier(); + if ( aIdent.lastIndexOf('/') != (aIdent.getLength()-1) ) + aIdent += "/"; + aIdent += m_Name; + aName = aIdent; + } + aURL.SetURL(aName); + + if ( !m_pConnection->matchesExtension( aURL.getExtension() ) ) + aURL.setExtension(m_pConnection->getExtension()); + + try + { + Content aContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext()); + if (aContent.isDocument()) + { + // Only if the file exists with length > 0 raise an error + std::unique_ptr<SvStream> pFileStream(createStream_simpleError( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ)); + + if (pFileStream && pFileStream->TellEnd()) + return false; + } + } + catch(const Exception&) // an exception is thrown when no file exists + { + } + + bool bMemoFile = false; + + bool bOk = CreateFile(aURL, bMemoFile); + + FileClose(); + + if (!bOk) + { + try + { + Content aContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext()); + aContent.executeCommand( "delete", css::uno::Any( true ) ); + } + catch(const Exception&) // an exception is thrown when no file exists + { + } + return false; + } + + if (bMemoFile) + { + OUString aExt = aURL.getExtension(); + aURL.setExtension("dbt"); // extension for memo file + + bool bMemoAlreadyExists = false; + try + { + Content aMemo1Content(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext()); + bMemoAlreadyExists = aMemo1Content.isDocument(); + } + catch(const Exception&) // an exception is thrown when no file exists + { + } + if (bMemoAlreadyExists) + { + aURL.setExtension(aExt); // kill dbf file + try + { + Content aMemoContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext()); + aMemoContent.executeCommand( "delete", css::uno::Any( true ) ); + } + catch(const Exception&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_DELETE_FILE, + "$name$", aName + ) ); + ::dbtools::throwGenericSQLException( sError, *this, anyEx ); + } + } + if (!CreateMemoFile(aURL)) + { + aURL.setExtension(aExt); // kill dbf file + try + { + Content aMemoContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext()); + aMemoContent.executeCommand( "delete", css::uno::Any( true ) ); + } + catch(const ContentCreationException&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_DELETE_FILE, + "$name$", aName + ) ); + ::dbtools::throwGenericSQLException( sError, *this, anyEx ); + } + return false; + } + m_aHeader.type = dBaseIIIMemo; + } + else + m_aHeader.type = dBaseIII; + + return true; +} + +void ODbaseTable::throwInvalidColumnType(const char* pErrorId, const OUString& _sColumnName) +{ + try + { + // we have to drop the file because it is corrupted now + DropImpl(); + } + catch(const Exception&) + { + } + + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + pErrorId, + "$columnname$", _sColumnName + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); +} + +// creates in principle dBase IV file format +bool ODbaseTable::CreateFile(const INetURLObject& aFile, bool& bCreateMemo) +{ + bCreateMemo = false; + Date aDate( Date::SYSTEM ); // current date + + m_pFileStream = createStream_simpleError( aFile.GetMainURL(INetURLObject::DecodeMechanism::NONE),StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC ); + + if (!m_pFileStream) + return false; + + sal_uInt8 nDbaseType = dBaseIII; + Reference<XIndexAccess> xColumns(getColumns(),UNO_QUERY); + Reference<XPropertySet> xCol; + const OUString sPropType = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE); + + try + { + const sal_Int32 nCount = xColumns->getCount(); + for(sal_Int32 i=0;i<nCount;++i) + { + xColumns->getByIndex(i) >>= xCol; + OSL_ENSURE(xCol.is(),"This should be a column!"); + + switch (getINT32(xCol->getPropertyValue(sPropType))) + { + case DataType::DOUBLE: + case DataType::INTEGER: + case DataType::TIMESTAMP: + case DataType::LONGVARBINARY: + nDbaseType = VisualFoxPro; + i = nCount; // no more columns need to be checked + break; + } // switch (getINT32(xCol->getPropertyValue(sPropType))) + } + } + catch ( const Exception& ) + { + try + { + // we have to drop the file because it is corrupted now + DropImpl(); + } + catch(const Exception&) { } + throw; + } + + char aBuffer[21] = {}; // write buffer + + m_pFileStream->Seek(0); + (*m_pFileStream).WriteUChar( nDbaseType ); // dBase format + (*m_pFileStream).WriteUChar( aDate.GetYearUnsigned() % 100 ); // current date + + + (*m_pFileStream).WriteUChar( aDate.GetMonth() ); + (*m_pFileStream).WriteUChar( aDate.GetDay() ); + (*m_pFileStream).WriteUInt32( 0 ); // number of data records + (*m_pFileStream).WriteUInt16( (m_xColumns->getCount()+1) * 32 + 1 ); // header information, + // pColumns contains always an additional column + (*m_pFileStream).WriteUInt16( 0 ); // record length will be determined later + m_pFileStream->WriteBytes(aBuffer, 20); + + sal_uInt16 nRecLength = 1; // Length 1 for deleted flag + sal_Int32 nMaxFieldLength = m_pConnection->getMetaData()->getMaxColumnNameLength(); + OUString aName; + const OUString sPropName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME); + const OUString sPropPrec = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION); + const OUString sPropScale = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE); + + try + { + const sal_Int32 nCount = xColumns->getCount(); + for(sal_Int32 i=0;i<nCount;++i) + { + xColumns->getByIndex(i) >>= xCol; + OSL_ENSURE(xCol.is(),"This should be a column!"); + + char cTyp( 'C' ); + + xCol->getPropertyValue(sPropName) >>= aName; + + OString aCol; + if ( DBTypeConversion::convertUnicodeString( aName, aCol, m_eEncoding ) > nMaxFieldLength) + { + throwInvalidColumnType( STR_INVALID_COLUMN_NAME_LENGTH, aName ); + } + + m_pFileStream->WriteOString( aCol ); + m_pFileStream->WriteBytes(aBuffer, 11 - aCol.getLength()); + + sal_Int32 nPrecision = 0; + xCol->getPropertyValue(sPropPrec) >>= nPrecision; + sal_Int32 nScale = 0; + xCol->getPropertyValue(sPropScale) >>= nScale; + + bool bBinary = false; + + switch (getINT32(xCol->getPropertyValue(sPropType))) + { + case DataType::CHAR: + case DataType::VARCHAR: + cTyp = 'C'; + break; + case DataType::DOUBLE: + if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency will be treated separately + cTyp = 'Y'; + else + cTyp = 'B'; + break; + case DataType::INTEGER: + cTyp = 'I'; + break; + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::BIGINT: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::REAL: + cTyp = 'N'; // only dBase 3 format + break; + case DataType::TIMESTAMP: + cTyp = 'T'; + break; + case DataType::DATE: + cTyp = 'D'; + break; + case DataType::BIT: + cTyp = 'L'; + break; + case DataType::LONGVARBINARY: + bBinary = true; + [[fallthrough]]; + case DataType::LONGVARCHAR: + cTyp = 'M'; + break; + default: + { + throwInvalidColumnType(STR_INVALID_COLUMN_TYPE, aName); + } + } + + (*m_pFileStream).WriteChar( cTyp ); + if ( nDbaseType == VisualFoxPro ) + (*m_pFileStream).WriteUInt32( nRecLength-1 ); + else + m_pFileStream->WriteBytes(aBuffer, 4); + + switch(cTyp) + { + case 'C': + OSL_ENSURE(nPrecision < 255, "ODbaseTable::Create: Column too long!"); + if (nPrecision > 254) + { + throwInvalidColumnType(STR_INVALID_COLUMN_PRECISION, aName); + } + (*m_pFileStream).WriteUChar( std::min(static_cast<unsigned>(nPrecision), 255U) ); // field length + nRecLength = nRecLength + static_cast<sal_uInt16>(std::min(static_cast<sal_uInt16>(nPrecision), sal_uInt16(255UL))); + (*m_pFileStream).WriteUChar( 0 ); // decimals + break; + case 'F': + case 'N': + OSL_ENSURE(nPrecision >= nScale, + "ODbaseTable::Create: Field length must be larger than decimal places!"); + if (nPrecision < nScale) + { + throwInvalidColumnType(STR_INVALID_PRECISION_SCALE, aName); + } + if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency will be treated separately + { + (*m_pFileStream).WriteUChar( 10 ); // standard length + (*m_pFileStream).WriteUChar( 4 ); + nRecLength += 10; + } + else + { + sal_Int32 nPrec = SvDbaseConverter::ConvertPrecisionToDbase(nPrecision,nScale); + + (*m_pFileStream).WriteUChar( nPrec ); + (*m_pFileStream).WriteUChar( nScale ); + nRecLength += static_cast<sal_uInt16>(nPrec); + } + break; + case 'L': + (*m_pFileStream).WriteUChar( 1 ); + (*m_pFileStream).WriteUChar( 0 ); + ++nRecLength; + break; + case 'I': + (*m_pFileStream).WriteUChar( 4 ); + (*m_pFileStream).WriteUChar( 0 ); + nRecLength += 4; + break; + case 'Y': + case 'B': + case 'T': + case 'D': + (*m_pFileStream).WriteUChar( 8 ); + (*m_pFileStream).WriteUChar( 0 ); + nRecLength += 8; + break; + case 'M': + bCreateMemo = true; + (*m_pFileStream).WriteUChar( 10 ); + (*m_pFileStream).WriteUChar( 0 ); + nRecLength += 10; + if ( bBinary ) + aBuffer[0] = 0x06; + break; + default: + throwInvalidColumnType(STR_INVALID_COLUMN_TYPE, aName); + } + m_pFileStream->WriteBytes(aBuffer, 14); + aBuffer[0] = 0x00; + } + + (*m_pFileStream).WriteUChar( FIELD_DESCRIPTOR_TERMINATOR ); // end of header + (*m_pFileStream).WriteChar( char(DBF_EOL) ); + m_pFileStream->Seek(10); + (*m_pFileStream).WriteUInt16( nRecLength ); // set record length afterwards + + if (bCreateMemo) + { + m_pFileStream->Seek(0); + if (nDbaseType == VisualFoxPro) + (*m_pFileStream).WriteUChar( FoxProMemo ); + else + (*m_pFileStream).WriteUChar( dBaseIIIMemo ); + } // if (bCreateMemo) + } + catch ( const Exception& ) + { + try + { + // we have to drop the file because it is corrupted now + DropImpl(); + } + catch(const Exception&) { } + throw; + } + return true; +} + + +// creates in principle dBase III file format +bool ODbaseTable::CreateMemoFile(const INetURLObject& aFile) +{ + // filehandling macro for table creation + m_pMemoStream = createStream_simpleError( aFile.GetMainURL(INetURLObject::DecodeMechanism::NONE),StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE); + + if (!m_pMemoStream) + return false; + + m_pMemoStream->SetStreamSize(512); + + m_pMemoStream->Seek(0); + (*m_pMemoStream).WriteUInt32( 1 ); // pointer to the first free block + + m_pMemoStream->Flush(); + m_pMemoStream.reset(); + return true; +} + +bool ODbaseTable::Drop_Static(const OUString& _sUrl, bool _bHasMemoFields, OCollection* _pIndexes ) +{ + INetURLObject aURL; + aURL.SetURL(_sUrl); + + bool bDropped = ::utl::UCBContentHelper::Kill(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + + if(bDropped) + { + if (_bHasMemoFields) + { // delete the memo fields + aURL.setExtension("dbt"); + bDropped = ::utl::UCBContentHelper::Kill(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + } + + if(bDropped) + { + if(_pIndexes) + { + try + { + sal_Int32 i = _pIndexes->getCount(); + while (i) + { + _pIndexes->dropByIndex(--i); + } + } + catch(const SQLException&) + { + } + } + aURL.setExtension("inf"); + + // as the inf file does not necessarily exist, we aren't allowed to use UCBContentHelper::Kill + try + { + ::ucbhelper::Content aDeleteContent( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + aDeleteContent.executeCommand( "delete", makeAny( true ) ); + } + catch(const Exception&) + { + // silently ignore this... + } + } + } + return bDropped; +} + +bool ODbaseTable::DropImpl() +{ + FileClose(); + + if(!m_xIndexes) + refreshIndexes(); // look for indexes which must be deleted as well + + bool bDropped = Drop_Static(getEntry(m_pConnection,m_Name),HasMemoFields(),m_xIndexes.get()); + if(!bDropped) + {// we couldn't drop the table so we have to reopen it + construct(); + if(m_xColumns) + m_xColumns->refresh(); + } + return bDropped; +} + + +bool ODbaseTable::InsertRow(OValueRefVector& rRow, const Reference<XIndexAccess>& _xCols) +{ + // fill buffer with blanks + if (!AllocBuffer()) + return false; + + memset(m_pBuffer.get(), 0, m_aHeader.recordLength); + m_pBuffer[0] = ' '; + + // Copy new row completely: + // ... and add at the end as new Record: + std::size_t nTempPos = m_nFilePos; + + m_nFilePos = static_cast<std::size_t>(m_aHeader.nbRecords) + 1; + bool bInsertRow = UpdateBuffer( rRow, nullptr, _xCols, true ); + if ( bInsertRow ) + { + std::size_t nFileSize = 0, nMemoFileSize = 0; + + nFileSize = lcl_getFileSize(*m_pFileStream); + + if (HasMemoFields() && m_pMemoStream) + { + m_pMemoStream->Seek(STREAM_SEEK_TO_END); + nMemoFileSize = m_pMemoStream->Tell(); + } + + if (!WriteBuffer()) + { + m_pFileStream->SetStreamSize(nFileSize); // restore old size + + if (HasMemoFields() && m_pMemoStream) + m_pMemoStream->SetStreamSize(nMemoFileSize); // restore old size + m_nFilePos = nTempPos; // restore file position + } + else + { + (*m_pFileStream).WriteChar( char(DBF_EOL) ); // write EOL + // raise number of datasets in the header: + m_pFileStream->Seek( 4 ); + (*m_pFileStream).WriteUInt32( m_aHeader.nbRecords + 1 ); + + m_pFileStream->Flush(); + + // raise number if successfully + m_aHeader.nbRecords++; + *rRow[0] = m_nFilePos; // set bookmark + m_nFilePos = nTempPos; + } + } + else + m_nFilePos = nTempPos; + + return bInsertRow; +} + + +bool ODbaseTable::UpdateRow(OValueRefVector& rRow, OValueRefRow& pOrgRow, const Reference<XIndexAccess>& _xCols) +{ + // fill buffer with blanks + if (!AllocBuffer()) + return false; + + // position on desired record: + std::size_t nPos = m_aHeader.headerLength + static_cast<long>(m_nFilePos-1) * m_aHeader.recordLength; + m_pFileStream->Seek(nPos); + m_pFileStream->ReadBytes(m_pBuffer.get(), m_aHeader.recordLength); + + std::size_t nMemoFileSize( 0 ); + if (HasMemoFields() && m_pMemoStream) + { + m_pMemoStream->Seek(STREAM_SEEK_TO_END); + nMemoFileSize = m_pMemoStream->Tell(); + } + if (!UpdateBuffer(rRow, pOrgRow, _xCols, false) || !WriteBuffer()) + { + if (HasMemoFields() && m_pMemoStream) + m_pMemoStream->SetStreamSize(nMemoFileSize); // restore old size + } + else + { + m_pFileStream->Flush(); + } + return true; +} + + +bool ODbaseTable::DeleteRow(const OSQLColumns& _rCols) +{ + // Set the Delete-Flag (be it set or not): + // Position on desired record: + std::size_t nFilePos = m_aHeader.headerLength + static_cast<long>(m_nFilePos-1) * m_aHeader.recordLength; + m_pFileStream->Seek(nFilePos); + + OValueRefRow aRow = new OValueRefVector(_rCols.size()); + + if (!fetchRow(aRow,_rCols,true)) + return false; + + Reference<XPropertySet> xCol; + OUString aColName; + ::comphelper::UStringMixEqual aCase(isCaseSensitive()); + for (sal_Int32 i = 0; i < m_xColumns->getCount(); i++) + { + Reference<XPropertySet> xIndex = isUniqueByColumnName(i); + if (xIndex.is()) + { + xCol.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY); + OSL_ENSURE(xCol.is(),"ODbaseTable::DeleteRow column is null!"); + if(xCol.is()) + { + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName; + + Reference<XUnoTunnel> xTunnel(xIndex,UNO_QUERY); + OSL_ENSURE(xTunnel.is(),"No TunnelImplementation!"); + ODbaseIndex* pIndex = reinterpret_cast< ODbaseIndex* >( xTunnel->getSomething(ODbaseIndex::getUnoTunnelId()) ); + OSL_ENSURE(pIndex,"ODbaseTable::DeleteRow: No Index returned!"); + + OSQLColumns::const_iterator aIter = std::find_if(_rCols.begin(), _rCols.end(), + [&aCase, &aColName](const OSQLColumns::value_type& rxCol) { + return aCase(getString(rxCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME))), aColName); }); + if (aIter == _rCols.end()) + continue; + + auto nPos = static_cast<sal_Int32>(std::distance(_rCols.begin(), aIter)) + 1; + pIndex->Delete(m_nFilePos,*(*aRow)[nPos]); + } + } + } + + m_pFileStream->Seek(nFilePos); + (*m_pFileStream).WriteUChar( '*' ); // mark the row in the table as deleted + m_pFileStream->Flush(); + return true; +} + +Reference<XPropertySet> ODbaseTable::isUniqueByColumnName(sal_Int32 _nColumnPos) +{ + if(!m_xIndexes) + refreshIndexes(); + if(m_xIndexes->hasElements()) + { + Reference<XPropertySet> xCol; + m_xColumns->getByIndex(_nColumnPos) >>= xCol; + OSL_ENSURE(xCol.is(),"ODbaseTable::isUniqueByColumnName column is null!"); + OUString sColName; + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= sColName; + + Reference<XPropertySet> xIndex; + for(sal_Int32 i=0;i<m_xIndexes->getCount();++i) + { + xIndex.set(m_xIndexes->getByIndex(i), css::uno::UNO_QUERY); + if(xIndex.is() && getBOOL(xIndex->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISUNIQUE)))) + { + Reference<XNameAccess> xCols(Reference<XColumnsSupplier>(xIndex,UNO_QUERY_THROW)->getColumns()); + if(xCols->hasByName(sColName)) + return xIndex; + + } + } + } + return Reference<XPropertySet>(); +} + +static double toDouble(const OString& rString) +{ + return ::rtl::math::stringToDouble( rString, '.', ',' ); +} + + +bool ODbaseTable::UpdateBuffer(OValueRefVector& rRow, const OValueRefRow& pOrgRow, const Reference<XIndexAccess>& _xCols, const bool bForceAllFields) +{ + OSL_ENSURE(m_pBuffer,"Buffer is NULL!"); + if ( !m_pBuffer ) + return false; + sal_Int32 nByteOffset = 1; + + // Update fields: + Reference<XPropertySet> xCol; + Reference<XPropertySet> xIndex; + OUString aColName; + const sal_Int32 nColumnCount = m_xColumns->getCount(); + std::vector< Reference<XPropertySet> > aIndexedCols(nColumnCount); + + ::comphelper::UStringMixEqual aCase(isCaseSensitive()); + + Reference<XIndexAccess> xColumns = m_xColumns.get(); + // first search a key that exist already in the table + for (sal_Int32 i = 0; i < nColumnCount; ++i) + { + sal_Int32 nPos = i; + if(_xCols != xColumns) + { + m_xColumns->getByIndex(i) >>= xCol; + OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!"); + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName; + + for(nPos = 0;nPos<_xCols->getCount();++nPos) + { + Reference<XPropertySet> xFindCol( + _xCols->getByIndex(nPos), css::uno::UNO_QUERY); + OSL_ENSURE(xFindCol.is(),"ODbaseTable::UpdateBuffer column is null!"); + if(aCase(getString(xFindCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),aColName)) + break; + } + if (nPos >= _xCols->getCount()) + continue; + } + + ++nPos; + xIndex = isUniqueByColumnName(i); + aIndexedCols[i] = xIndex; + if (xIndex.is()) + { + // first check if the value is different to the old one and when if it conform to the index + if(pOrgRow.is() && (rRow[nPos]->getValue().isNull() || rRow[nPos] == (*pOrgRow)[nPos])) + continue; + else + { + Reference<XUnoTunnel> xTunnel(xIndex,UNO_QUERY); + OSL_ENSURE(xTunnel.is(),"No TunnelImplementation!"); + ODbaseIndex* pIndex = reinterpret_cast< ODbaseIndex* >( xTunnel->getSomething(ODbaseIndex::getUnoTunnelId()) ); + OSL_ENSURE(pIndex,"ODbaseTable::UpdateBuffer: No Index returned!"); + + if (pIndex->Find(0,*rRow[nPos])) + { + // There is no unique value + if ( aColName.isEmpty() ) + { + m_xColumns->getByIndex(i) >>= xCol; + OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!"); + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName; + xCol.clear(); + } // if ( !aColName.getLength() ) + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_DUPLICATE_VALUE_IN_COLUMN + ,"$columnname$", aColName + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + } + } + } + + // when we are here there is no double key in the table + + for (sal_Int32 i = 0; i < nColumnCount && nByteOffset <= m_nBufferSize ; ++i) + { + // Lengths for each data type: + assert(i >= 0); + OSL_ENSURE(o3tl::make_unsigned(i) < m_aPrecisions.size(),"Illegal index!"); + sal_Int32 nLen = 0; + sal_Int32 nType = 0; + sal_Int32 nScale = 0; + if ( o3tl::make_unsigned(i) < m_aPrecisions.size() ) + { + nLen = m_aPrecisions[i]; + nType = m_aTypes[i]; + nScale = m_aScales[i]; + } + else + { + m_xColumns->getByIndex(i) >>= xCol; + if ( xCol.is() ) + { + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)) >>= nLen; + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType; + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE)) >>= nScale; + } + } + + bool bSetZero = false; + switch (nType) + { + case DataType::INTEGER: + case DataType::DOUBLE: + case DataType::TIMESTAMP: + bSetZero = true; + [[fallthrough]]; + case DataType::LONGVARBINARY: + case DataType::DATE: + case DataType::BIT: + case DataType::LONGVARCHAR: + nLen = m_aRealFieldLengths[i]; + break; + case DataType::DECIMAL: + nLen = SvDbaseConverter::ConvertPrecisionToDbase(nLen,nScale); + break; // The sign and the comma + default: + break; + + } // switch (nType) + + sal_Int32 nPos = i; + if(_xCols != xColumns) + { + m_xColumns->getByIndex(i) >>= xCol; + OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!"); + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName; + for(nPos = 0;nPos<_xCols->getCount();++nPos) + { + Reference<XPropertySet> xFindCol( + _xCols->getByIndex(nPos), css::uno::UNO_QUERY); + if(aCase(getString(xFindCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))),aColName)) + break; + } + if (nPos >= _xCols->getCount()) + { + nByteOffset += nLen; + continue; + } + } + + + ++nPos; // the row values start at 1 + const ORowSetValue &thisColVal = rRow[nPos]->get(); + const bool thisColIsBound = thisColVal.isBound(); + const bool thisColIsNull = !thisColIsBound || thisColVal.isNull(); + // don't overwrite non-bound columns + if ( ! (bForceAllFields || thisColIsBound) ) + { + // No - don't overwrite this field, it has not changed. + nByteOffset += nLen; + continue; + } + if (aIndexedCols[i].is()) + { + Reference<XUnoTunnel> xTunnel(aIndexedCols[i],UNO_QUERY); + OSL_ENSURE(xTunnel.is(),"No TunnelImplementation!"); + ODbaseIndex* pIndex = reinterpret_cast< ODbaseIndex* >( xTunnel->getSomething(ODbaseIndex::getUnoTunnelId()) ); + OSL_ENSURE(pIndex,"ODbaseTable::UpdateBuffer: No Index returned!"); + // Update !! + if (pOrgRow.is() && !thisColIsNull) + pIndex->Update(m_nFilePos, *(*pOrgRow)[nPos], thisColVal); + else + pIndex->Insert(m_nFilePos, thisColVal); + } + + char* pData = reinterpret_cast<char *>(m_pBuffer.get() + nByteOffset); + if (thisColIsNull) + { + if ( bSetZero ) + memset(pData,0,nLen); // Clear to NULL char ('\0') + else + memset(pData,' ',nLen); // Clear to space/blank ('\0x20') + nByteOffset += nLen; + OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!"); + continue; + } + + try + { + switch (nType) + { + case DataType::TIMESTAMP: + { + sal_Int32 nJulianDate = 0, nJulianTime = 0; + lcl_CalcJulDate(nJulianDate,nJulianTime, thisColVal); + // Exactly 8 bytes to copy: + memcpy(pData,&nJulianDate,4); + memcpy(pData+4,&nJulianTime,4); + } + break; + case DataType::DATE: + { + css::util::Date aDate; + if(thisColVal.getTypeKind() == DataType::DOUBLE) + aDate = ::dbtools::DBTypeConversion::toDate(thisColVal.getDouble()); + else + aDate = thisColVal; + char s[sizeof("-327686553565535")]; + // reserve enough space for hypothetical max length + snprintf(s, + sizeof(s), + "%04" SAL_PRIdINT32 "%02" SAL_PRIuUINT32 "%02" SAL_PRIuUINT32, + static_cast<sal_Int32>(aDate.Year), + static_cast<sal_uInt32>(aDate.Month), + static_cast<sal_uInt32>(aDate.Day)); + + // Exactly 8 bytes to copy (even if s could hypothetically be longer): + memcpy(pData,s,8); + } break; + case DataType::INTEGER: + { + sal_Int32 nValue = thisColVal; + if (o3tl::make_unsigned(nLen) > sizeof(nValue)) + return false; + memcpy(pData,&nValue,nLen); + } + break; + case DataType::DOUBLE: + { + const double d = thisColVal; + m_xColumns->getByIndex(i) >>= xCol; + + if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency is treated separately + { + sal_Int64 nValue = 0; + if ( m_aScales[i] ) + nValue = static_cast<sal_Int64>(d * pow(10.0,static_cast<int>(m_aScales[i]))); + else + nValue = static_cast<sal_Int64>(d); + if (o3tl::make_unsigned(nLen) > sizeof(nValue)) + return false; + memcpy(pData,&nValue,nLen); + } // if (getBOOL(xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY)))) // Currency is treated separately + else + { + if (o3tl::make_unsigned(nLen) > sizeof(d)) + return false; + memcpy(pData,&d,nLen); + } + } + break; + case DataType::DECIMAL: + { + memset(pData,' ',nLen); // Clear to NULL + + const double n = thisColVal; + + // one, because const_cast GetFormatPrecision on SvNumberFormat is not constant, + // even though it really could and should be + const OString aDefaultValue( ::rtl::math::doubleToString( n, rtl_math_StringFormat_F, nScale, '.', nullptr, 0)); + const sal_Int32 nValueLen = aDefaultValue.getLength(); + if ( nValueLen <= nLen ) + { + // Write value right-justified, padded with blanks to the left. + memcpy(pData+nLen-nValueLen,aDefaultValue.getStr(),nValueLen); + // write the resulting double back + *rRow[nPos] = toDouble(aDefaultValue); + } + else + { + m_xColumns->getByIndex(i) >>= xCol; + OSL_ENSURE(xCol.is(),"ODbaseTable::UpdateBuffer column is null!"); + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName; + std::vector< std::pair<const char* , OUString > > aStringToSubstitutes; + aStringToSubstitutes.push_back(std::pair<const char* , OUString >("$columnname$", aColName)); + aStringToSubstitutes.push_back(std::pair<const char* , OUString >("$precision$", OUString::number(nLen))); + aStringToSubstitutes.push_back(std::pair<const char* , OUString >("$scale$", OUString::number(nScale))); + aStringToSubstitutes.push_back(std::pair<const char* , OUString >("$value$", OStringToOUString(aDefaultValue,RTL_TEXTENCODING_UTF8))); + + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_INVALID_COLUMN_DECIMAL_VALUE + ,aStringToSubstitutes + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + } break; + case DataType::BIT: + *pData = thisColVal.getBool() ? 'T' : 'F'; + break; + case DataType::LONGVARBINARY: + case DataType::LONGVARCHAR: + { + char cNext = pData[nLen]; // Mark's scratch and replaced by 0 + pData[nLen] = '\0'; // This is because the buffer is always a sign of greater ... + + std::size_t nBlockNo = strtol(pData,nullptr,10); // Block number read + + // Next initial character restore again: + pData[nLen] = cNext; + if (!m_pMemoStream) + break; + WriteMemo(thisColVal, nBlockNo); + + OString aBlock(OString::number(nBlockNo)); + //align aBlock at the right of a nLen sequence, fill to the left with '0' + OStringBuffer aStr; + comphelper::string::padToLength(aStr, nLen - aBlock.getLength(), '0'); + aStr.append(aBlock); + + // Copy characters: + memcpy(pData, aStr.getStr(), nLen); + } break; + default: + { + memset(pData,' ',nLen); // Clear to NULL + + OUString sStringToWrite( thisColVal.getString() ); + + // convert the string, using the connection's encoding + OString sEncoded; + + DBTypeConversion::convertUnicodeStringToLength( sStringToWrite, sEncoded, nLen, m_eEncoding ); + memcpy( pData, sEncoded.getStr(), sEncoded.getLength() ); + + } + break; + } + } + catch( const SQLException& ) + { + throw; + } + catch ( const Exception& ) + { + m_xColumns->getByIndex(i) >>= xCol; + OSL_ENSURE( xCol.is(), "ODbaseTable::UpdateBuffer column is null!" ); + if ( xCol.is() ) + xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName; + + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_INVALID_COLUMN_VALUE, + "$columnname$", aColName + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + // And more ... + nByteOffset += nLen; + OSL_ENSURE( nByteOffset <= m_nBufferSize ,"ByteOffset > m_nBufferSize!"); + } + return true; +} + + +void ODbaseTable::WriteMemo(const ORowSetValue& aVariable, std::size_t& rBlockNr) +{ + // if the BlockNo 0 is given, the block will be appended at the end + std::size_t nSize = 0; + OString aStr; + css::uno::Sequence<sal_Int8> aValue; + sal_uInt8 nHeader[4]; + const bool bBinary = aVariable.getTypeKind() == DataType::LONGVARBINARY && m_aMemoHeader.db_typ == MemoFoxPro; + if ( bBinary ) + { + aValue = aVariable.getSequence(); + nSize = aValue.getLength(); + } + else + { + nSize = DBTypeConversion::convertUnicodeString( aVariable.getString(), aStr, m_eEncoding ); + } + + // append or overwrite + bool bAppend = rBlockNr == 0; + + if (!bAppend) + { + switch (m_aMemoHeader.db_typ) + { + case MemodBaseIII: // dBase III-Memofield, ends with 2 * Ctrl-Z + bAppend = nSize > (512 - 2); + break; + case MemoFoxPro: + case MemodBaseIV: // dBase IV-Memofield with length + { + char sHeader[4]; + m_pMemoStream->Seek(rBlockNr * m_aMemoHeader.db_size); + m_pMemoStream->SeekRel(4); + m_pMemoStream->ReadBytes(sHeader, 4); + + std::size_t nOldSize; + if (m_aMemoHeader.db_typ == MemoFoxPro) + nOldSize = ((static_cast<unsigned char>(sHeader[0]) * 256 + + static_cast<unsigned char>(sHeader[1])) * 256 + + static_cast<unsigned char>(sHeader[2])) * 256 + + static_cast<unsigned char>(sHeader[3]); + else + nOldSize = ((static_cast<unsigned char>(sHeader[3]) * 256 + + static_cast<unsigned char>(sHeader[2])) * 256 + + static_cast<unsigned char>(sHeader[1])) * 256 + + static_cast<unsigned char>(sHeader[0]) - 8; + + // fits the new length in the used blocks + std::size_t nUsedBlocks = ((nSize + 8) / m_aMemoHeader.db_size) + (((nSize + 8) % m_aMemoHeader.db_size > 0) ? 1 : 0), + nOldUsedBlocks = ((nOldSize + 8) / m_aMemoHeader.db_size) + (((nOldSize + 8) % m_aMemoHeader.db_size > 0) ? 1 : 0); + bAppend = nUsedBlocks > nOldUsedBlocks; + } + } + } + + if (bAppend) + { + sal_uInt64 const nStreamSize = m_pMemoStream->TellEnd(); + // fill last block + rBlockNr = (nStreamSize / m_aMemoHeader.db_size) + ((nStreamSize % m_aMemoHeader.db_size) > 0 ? 1 : 0); + + m_pMemoStream->SetStreamSize(rBlockNr * m_aMemoHeader.db_size); + m_pMemoStream->Seek(STREAM_SEEK_TO_END); + } + else + { + m_pMemoStream->Seek(rBlockNr * m_aMemoHeader.db_size); + } + + switch (m_aMemoHeader.db_typ) + { + case MemodBaseIII: // dBase III-Memofield, ends with Ctrl-Z + { + const char cEOF = char(DBF_EOL); + nSize++; + m_pMemoStream->WriteBytes(aStr.getStr(), aStr.getLength()); + m_pMemoStream->WriteChar( cEOF ).WriteChar( cEOF ); + } break; + case MemoFoxPro: + case MemodBaseIV: // dBase IV-Memofield with length + { + if ( MemodBaseIV == m_aMemoHeader.db_typ ) + (*m_pMemoStream).WriteUChar( 0xFF ) + .WriteUChar( 0xFF ) + .WriteUChar( 0x08 ); + else + (*m_pMemoStream).WriteUChar( 0x00 ) + .WriteUChar( 0x00 ) + .WriteUChar( 0x00 ); + + sal_uInt32 nWriteSize = nSize; + if (m_aMemoHeader.db_typ == MemoFoxPro) + { + if ( bBinary ) + (*m_pMemoStream).WriteUChar( 0x00 ); // Picture + else + (*m_pMemoStream).WriteUChar( 0x01 ); // Memo + for (int i = 4; i > 0; nWriteSize >>= 8) + nHeader[--i] = static_cast<sal_uInt8>(nWriteSize % 256); + } + else + { + (*m_pMemoStream).WriteUChar( 0x00 ); + nWriteSize += 8; + for (int i = 0; i < 4; nWriteSize >>= 8) + nHeader[i++] = static_cast<sal_uInt8>(nWriteSize % 256); + } + + m_pMemoStream->WriteBytes(nHeader, 4); + if ( bBinary ) + m_pMemoStream->WriteBytes(aValue.getConstArray(), aValue.getLength()); + else + m_pMemoStream->WriteBytes(aStr.getStr(), aStr.getLength()); + m_pMemoStream->Flush(); + } + } + + + // Write the new block number + if (bAppend) + { + sal_uInt64 const nStreamSize = m_pMemoStream->TellEnd(); + m_aMemoHeader.db_next = (nStreamSize / m_aMemoHeader.db_size) + ((nStreamSize % m_aMemoHeader.db_size) > 0 ? 1 : 0); + + // Write the new block number + m_pMemoStream->Seek(0); + (*m_pMemoStream).WriteUInt32( m_aMemoHeader.db_next ); + m_pMemoStream->Flush(); + } +} + + +// XAlterTable +void SAL_CALL ODbaseTable::alterColumnByName( const OUString& colName, const Reference< XPropertySet >& descriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + + + Reference<XDataDescriptorFactory> xOldColumn; + m_xColumns->getByName(colName) >>= xOldColumn; + + try + { + alterColumn(m_xColumns->findColumn(colName)-1,descriptor,xOldColumn); + } + catch (const css::lang::IndexOutOfBoundsException&) + { + throw NoSuchElementException(colName, *this); + } +} + +void SAL_CALL ODbaseTable::alterColumnByIndex( sal_Int32 index, const Reference< XPropertySet >& descriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + + if(index < 0 || index >= m_xColumns->getCount()) + throw IndexOutOfBoundsException(OUString::number(index),*this); + + Reference<XDataDescriptorFactory> xOldColumn; + m_xColumns->getByIndex(index) >>= xOldColumn; + alterColumn(index,descriptor,xOldColumn); +} + +void ODbaseTable::alterColumn(sal_Int32 index, + const Reference< XPropertySet >& descriptor , + const Reference< XDataDescriptorFactory >& xOldColumn ) +{ + if(index < 0 || index >= m_xColumns->getCount()) + throw IndexOutOfBoundsException(OUString::number(index),*this); + + ODbaseTable* pNewTable = nullptr; + try + { + OSL_ENSURE(descriptor.is(),"ODbaseTable::alterColumn: descriptor can not be null!"); + // creates a copy of the original column and copy all properties from descriptor in xCopyColumn + Reference<XPropertySet> xCopyColumn; + if(xOldColumn.is()) + xCopyColumn = xOldColumn->createDataDescriptor(); + else + xCopyColumn = new OColumn(getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers()); + + ::comphelper::copyProperties(descriptor,xCopyColumn); + + // creates a temp file + + OUString sTempName = createTempFile(); + + pNewTable = new ODbaseTable(m_pTables,static_cast<ODbaseConnection*>(m_pConnection)); + Reference<XPropertySet> xHoldTable = pNewTable; + pNewTable->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME),makeAny(sTempName)); + Reference<XAppend> xAppend(pNewTable->getColumns(),UNO_QUERY); + OSL_ENSURE(xAppend.is(),"ODbaseTable::alterColumn: No XAppend interface!"); + + // copy the structure + sal_Int32 i=0; + for(;i < index;++i) + { + Reference<XPropertySet> xProp; + m_xColumns->getByIndex(i) >>= xProp; + Reference<XDataDescriptorFactory> xColumn(xProp,UNO_QUERY); + Reference<XPropertySet> xCpy; + if(xColumn.is()) + xCpy = xColumn->createDataDescriptor(); + else + xCpy = new OColumn(getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers()); + ::comphelper::copyProperties(xProp,xCpy); + xAppend->appendByDescriptor(xCpy); + } + ++i; // now insert our new column + xAppend->appendByDescriptor(xCopyColumn); + + for(;i < m_xColumns->getCount();++i) + { + Reference<XPropertySet> xProp; + m_xColumns->getByIndex(i) >>= xProp; + Reference<XDataDescriptorFactory> xColumn(xProp,UNO_QUERY); + Reference<XPropertySet> xCpy; + if(xColumn.is()) + xCpy = xColumn->createDataDescriptor(); + else + xCpy = new OColumn(getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers()); + ::comphelper::copyProperties(xProp,xCpy); + xAppend->appendByDescriptor(xCpy); + } + + // construct the new table + if(!pNewTable->CreateImpl()) + { + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_COLUMN_NOT_ALTERABLE, + "$columnname$", ::comphelper::getString(descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))) + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + + pNewTable->construct(); + + // copy the data + copyData(pNewTable,0); + + // now drop the old one + if( DropImpl() ) // we don't want to delete the memo columns too + { + try + { + // rename the new one to the old one + pNewTable->renameImpl(m_Name); + } + catch(const css::container::ElementExistException&) + { + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_DELETE_FILE, + "$filename$", m_Name + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + // release the temp file + pNewTable = nullptr; + ::comphelper::disposeComponent(xHoldTable); + } + else + { + pNewTable = nullptr; + } + FileClose(); + construct(); + if(m_xColumns) + m_xColumns->refresh(); + + } + catch(const SQLException&) + { + throw; + } + catch(const Exception&) + { + SAL_WARN( "connectivity.drivers","ODbaseTable::alterColumn: Exception occurred!"); + throw; + } +} + +Reference< XDatabaseMetaData> ODbaseTable::getMetaData() const +{ + return getConnection()->getMetaData(); +} + +void SAL_CALL ODbaseTable::rename( const OUString& newName ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + if(m_pTables && m_pTables->hasByName(newName)) + throw ElementExistException(newName,*this); + + + renameImpl(newName); + + ODbaseTable_BASE::rename(newName); + + construct(); + if(m_xColumns) + m_xColumns->refresh(); +} +namespace +{ + void renameFile(file::OConnection const * _pConenction,const OUString& oldName, + const OUString& newName,const OUString& _sExtension) + { + OUString aName = ODbaseTable::getEntry(_pConenction,oldName); + if(aName.isEmpty()) + { + OUString aIdent = _pConenction->getContent()->getIdentifier()->getContentIdentifier(); + if ( aIdent.lastIndexOf('/') != (aIdent.getLength()-1) ) + aIdent += "/"; + aIdent += oldName; + aName = aIdent; + } + INetURLObject aURL; + aURL.SetURL(aName); + + aURL.setExtension( _sExtension ); + OUString sNewName(newName + "." + _sExtension); + + try + { + Content aContent(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),Reference<XCommandEnvironment>(), comphelper::getProcessComponentContext()); + + Sequence< PropertyValue > aProps( 1 ); + aProps[0].Name = "Title"; + aProps[0].Handle = -1; // n/a + aProps[0].Value <<= sNewName; + Sequence< Any > aValues; + aContent.executeCommand( "setPropertyValues",makeAny(aProps) ) >>= aValues; + if(aValues.hasElements() && aValues[0].hasValue()) + throw Exception("setPropertyValues returned non-zero", nullptr); + } + catch(const Exception&) + { + throw ElementExistException(newName); + } + } +} + +void ODbaseTable::renameImpl( const OUString& newName ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + + FileClose(); + + + renameFile(m_pConnection,m_Name,newName,m_pConnection->getExtension()); + if ( HasMemoFields() ) + { // delete the memo fields + renameFile(m_pConnection,m_Name,newName,"dbt"); + } +} + +void ODbaseTable::addColumn(const Reference< XPropertySet >& _xNewColumn) +{ + OUString sTempName = createTempFile(); + + rtl::Reference xNewTable(new ODbaseTable(m_pTables,static_cast<ODbaseConnection*>(m_pConnection))); + xNewTable->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME),makeAny(sTempName)); + { + Reference<XAppend> xAppend(xNewTable->getColumns(),UNO_QUERY); + bool bCase = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(); + // copy the structure + for(sal_Int32 i=0;i < m_xColumns->getCount();++i) + { + Reference<XPropertySet> xProp; + m_xColumns->getByIndex(i) >>= xProp; + Reference<XDataDescriptorFactory> xColumn(xProp,UNO_QUERY); + Reference<XPropertySet> xCpy; + if(xColumn.is()) + xCpy = xColumn->createDataDescriptor(); + else + { + xCpy = new OColumn(bCase); + ::comphelper::copyProperties(xProp,xCpy); + } + + xAppend->appendByDescriptor(xCpy); + } + Reference<XPropertySet> xCpy = new OColumn(bCase); + ::comphelper::copyProperties(_xNewColumn,xCpy); + xAppend->appendByDescriptor(xCpy); + } + + // construct the new table + if(!xNewTable->CreateImpl()) + { + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_COLUMN_NOT_ADDABLE, + "$columnname$", ::comphelper::getString(_xNewColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))) + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + + xNewTable->construct(); + // copy the data + copyData(xNewTable.get(),xNewTable->m_xColumns->getCount()); + // drop the old table + if(DropImpl()) + { + xNewTable->renameImpl(m_Name); + // release the temp file + } + xNewTable.clear(); + + FileClose(); + construct(); + if(m_xColumns) + m_xColumns->refresh(); +} + +void ODbaseTable::dropColumn(sal_Int32 _nPos) +{ + OUString sTempName = createTempFile(); + + rtl::Reference xNewTable(new ODbaseTable(m_pTables,static_cast<ODbaseConnection*>(m_pConnection))); + xNewTable->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME),makeAny(sTempName)); + { + Reference<XAppend> xAppend(xNewTable->getColumns(),UNO_QUERY); + bool bCase = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(); + // copy the structure + for(sal_Int32 i=0;i < m_xColumns->getCount();++i) + { + if(_nPos != i) + { + Reference<XPropertySet> xProp; + m_xColumns->getByIndex(i) >>= xProp; + Reference<XDataDescriptorFactory> xColumn(xProp,UNO_QUERY); + Reference<XPropertySet> xCpy; + if(xColumn.is()) + xCpy = xColumn->createDataDescriptor(); + else + { + xCpy = new OColumn(bCase); + ::comphelper::copyProperties(xProp,xCpy); + } + xAppend->appendByDescriptor(xCpy); + } + } + } + + // construct the new table + if(!xNewTable->CreateImpl()) + { + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_COLUMN_NOT_DROP, + "$position$", OUString::number(_nPos) + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + xNewTable->construct(); + // copy the data + copyData(xNewTable.get(),_nPos); + // drop the old table + if(DropImpl()) + xNewTable->renameImpl(m_Name); + // release the temp file + + xNewTable.clear(); + + FileClose(); + construct(); +} + +OUString ODbaseTable::createTempFile() +{ + OUString aIdent = m_pConnection->getContent()->getIdentifier()->getContentIdentifier(); + if ( aIdent.lastIndexOf('/') != (aIdent.getLength()-1) ) + aIdent += "/"; + + OUString sTempName(aIdent); + OUString sExt("." + m_pConnection->getExtension()); + OUString sName(m_Name); + TempFile aTempFile(sName, true, &sExt, &sTempName); + if(!aTempFile.IsValid()) + getConnection()->throwGenericSQLException(STR_COULD_NOT_ALTER_TABLE, *this); + + INetURLObject aURL; + aURL.SetSmartProtocol(INetProtocol::File); + aURL.SetURL(aTempFile.GetURL()); + + OUString sNewName(aURL.getName().copy(0, aURL.getName().getLength() - sExt.getLength())); + + return sNewName; +} + +void ODbaseTable::copyData(ODbaseTable* _pNewTable,sal_Int32 _nPos) +{ + sal_Int32 nPos = _nPos + 1; // +1 because we always have the bookmark column as well + OValueRefRow aRow = new OValueRefVector(m_xColumns->getCount()); + OValueRefRow aInsertRow; + if(_nPos) + { + aInsertRow = new OValueRefVector(_pNewTable->m_xColumns->getCount()); + std::for_each(aInsertRow->begin(),aInsertRow->end(),TSetRefBound(true)); + } + else + aInsertRow = aRow; + + // we only have to bind the values which we need to copy into the new table + std::for_each(aRow->begin(),aRow->end(),TSetRefBound(true)); + if(_nPos && (_nPos < static_cast<sal_Int32>(aRow->size()))) + (*aRow)[nPos]->setBound(false); + + + sal_Int32 nCurPos; + OValueRefVector::const_iterator aIter; + for(sal_uInt32 nRowPos = 0; nRowPos < m_aHeader.nbRecords;++nRowPos) + { + bool bOk = seekRow( IResultSetHelper::BOOKMARK, nRowPos+1, nCurPos ); + if ( bOk ) + { + bOk = fetchRow( aRow, *m_aColumns, true); + if ( bOk && !aRow->isDeleted() ) // copy only not deleted rows + { + // special handling when pos == 0 then we don't have to distinguish between the two rows + if(_nPos) + { + aIter = aRow->begin()+1; + sal_Int32 nCount = 1; + for(OValueRefVector::iterator aInsertIter = aInsertRow->begin()+1; aIter != aRow->end() && aInsertIter != aInsertRow->end();++aIter,++nCount) + { + if(nPos != nCount) + { + (*aInsertIter)->setValue( (*aIter)->getValue() ); + ++aInsertIter; + } + } + } + bOk = _pNewTable->InsertRow(*aInsertRow,_pNewTable->m_xColumns.get()); + SAL_WARN_IF(!bOk, "connectivity.drivers", "Row could not be inserted!"); + } + else + { + SAL_WARN_IF(!bOk, "connectivity.drivers", "Row could not be fetched!"); + } + } + else + { + OSL_ASSERT(false); + } + } // for(sal_uInt32 nRowPos = 0; nRowPos < m_aHeader.db_anz;++nRowPos) +} + +void ODbaseTable::throwInvalidDbaseFormat() +{ + FileClose(); + // no dbase file + + const OUString sError( getConnection()->getResources().getResourceStringWithSubstitution( + STR_INVALID_DBASE_FILE, + "$filename$", getEntry(m_pConnection,m_Name) + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); +} + +void ODbaseTable::refreshHeader() +{ + if ( m_aHeader.nbRecords == 0 ) + readHeader(); +} + +bool ODbaseTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos) +{ + // prepare positioning: + OSL_ENSURE(m_pFileStream,"ODbaseTable::seekRow: FileStream is NULL!"); + + sal_uInt32 nNumberOfRecords = m_aHeader.nbRecords; + sal_uInt32 nTempPos = m_nFilePos; + m_nFilePos = nCurPos; + + switch(eCursorPosition) + { + case IResultSetHelper::NEXT: + ++m_nFilePos; + break; + case IResultSetHelper::PRIOR: + if (m_nFilePos > 0) + --m_nFilePos; + break; + case IResultSetHelper::FIRST: + m_nFilePos = 1; + break; + case IResultSetHelper::LAST: + m_nFilePos = nNumberOfRecords; + break; + case IResultSetHelper::RELATIVE1: + m_nFilePos = (m_nFilePos + nOffset < 0) ? 0 + : static_cast<sal_uInt32>(m_nFilePos + nOffset); + break; + case IResultSetHelper::ABSOLUTE1: + case IResultSetHelper::BOOKMARK: + m_nFilePos = static_cast<sal_uInt32>(nOffset); + break; + } + + if (m_nFilePos > static_cast<sal_Int32>(nNumberOfRecords)) + m_nFilePos = static_cast<sal_Int32>(nNumberOfRecords) + 1; + + if (m_nFilePos == 0 || m_nFilePos == static_cast<sal_Int32>(nNumberOfRecords) + 1) + goto Error; + else + { + std::size_t nEntryLen = m_aHeader.recordLength; + + OSL_ENSURE(m_nFilePos >= 1,"SdbDBFCursor::FileFetchRow: invalid record position"); + std::size_t nPos = m_aHeader.headerLength + static_cast<std::size_t>(m_nFilePos-1) * nEntryLen; + + m_pFileStream->Seek(nPos); + if (m_pFileStream->GetError() != ERRCODE_NONE) + goto Error; + + std::size_t nRead = m_pFileStream->ReadBytes(m_pBuffer.get(), nEntryLen); + if (nRead != nEntryLen) + { + SAL_WARN("connectivity.drivers", "ODbaseTable::seekRow: short read!"); + goto Error; + } + if (m_pFileStream->GetError() != ERRCODE_NONE) + goto Error; + } + goto End; + +Error: + switch(eCursorPosition) + { + case IResultSetHelper::PRIOR: + case IResultSetHelper::FIRST: + m_nFilePos = 0; + break; + case IResultSetHelper::LAST: + case IResultSetHelper::NEXT: + case IResultSetHelper::ABSOLUTE1: + case IResultSetHelper::RELATIVE1: + if (nOffset > 0) + m_nFilePos = nNumberOfRecords + 1; + else if (nOffset < 0) + m_nFilePos = 0; + break; + case IResultSetHelper::BOOKMARK: + m_nFilePos = nTempPos; // last position + } + return false; + +End: + nCurPos = m_nFilePos; + return true; +} + +bool ODbaseTable::ReadMemo(std::size_t nBlockNo, ORowSetValue& aVariable) +{ + m_pMemoStream->Seek(nBlockNo * m_aMemoHeader.db_size); + switch (m_aMemoHeader.db_typ) + { + case MemodBaseIII: // dBase III-Memofield, ends with Ctrl-Z + { + const char cEOF = char(DBF_EOL); + OStringBuffer aBStr; + static char aBuf[514]; + aBuf[512] = 0; // avoid random value + bool bReady = false; + + do + { + m_pMemoStream->ReadBytes(&aBuf, 512); + + sal_uInt16 i = 0; + while (aBuf[i] != cEOF && ++i < 512) + ; + bReady = aBuf[i] == cEOF; + + aBuf[i] = 0; + aBStr.append(aBuf); + + } while (!bReady && !m_pMemoStream->eof()); + + aVariable = OStringToOUString(aBStr.makeStringAndClear(), + m_eEncoding); + + } break; + case MemoFoxPro: + case MemodBaseIV: // dBase IV-Memofield with length + { + bool bIsText = true; + char sHeader[4]; + m_pMemoStream->ReadBytes(sHeader, 4); + // Foxpro stores text and binary data + if (m_aMemoHeader.db_typ == MemoFoxPro) + { + bIsText = sHeader[3] != 0; + } + else if (static_cast<sal_uInt8>(sHeader[0]) != 0xFF || static_cast<sal_uInt8>(sHeader[1]) != 0xFF || static_cast<sal_uInt8>(sHeader[2]) != 0x08) + { + return false; + } + + sal_uInt32 nLength(0); + (*m_pMemoStream).ReadUInt32( nLength ); + + if (m_aMemoHeader.db_typ == MemodBaseIV) + nLength -= 8; + + if ( nLength ) + { + if ( bIsText ) + { + OStringBuffer aBuffer(read_uInt8s_ToOString(*m_pMemoStream, nLength)); + //pad it out with ' ' to expected length on short read + sal_Int32 nRequested = sal::static_int_cast<sal_Int32>(nLength); + comphelper::string::padToLength(aBuffer, nRequested, ' '); + aVariable = OStringToOUString(aBuffer.makeStringAndClear(), m_eEncoding); + } // if ( bIsText ) + else + { + css::uno::Sequence< sal_Int8 > aData(nLength); + m_pMemoStream->ReadBytes(aData.getArray(), nLength); + aVariable = aData; + } + } // if ( nLength ) + } + } + return true; +} + +bool ODbaseTable::AllocBuffer() +{ + sal_uInt16 nSize = m_aHeader.recordLength; + SAL_WARN_IF(nSize == 0, "connectivity.drivers", "Size too small"); + + if (m_nBufferSize != nSize) + { + m_pBuffer.reset(); + } + + // if there is no buffer available: allocate: + if (!m_pBuffer && nSize > 0) + { + m_nBufferSize = nSize; + m_pBuffer.reset(new sal_uInt8[m_nBufferSize+1]); + } + + return m_pBuffer != nullptr; +} + +bool ODbaseTable::WriteBuffer() +{ + OSL_ENSURE(m_nFilePos >= 1,"SdbDBFCursor::FileFetchRow: invalid record position"); + + // position on desired record: + std::size_t nPos = m_aHeader.headerLength + static_cast<long>(m_nFilePos-1) * m_aHeader.recordLength; + m_pFileStream->Seek(nPos); + return m_pFileStream->WriteBytes(m_pBuffer.get(), m_aHeader.recordLength) > 0; +} + +sal_Int32 ODbaseTable::getCurrentLastPos() const +{ + return m_aHeader.nbRecords; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/DTables.cxx b/connectivity/source/drivers/dbase/DTables.cxx new file mode 100644 index 000000000..1d9229694 --- /dev/null +++ b/connectivity/source/drivers/dbase/DTables.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <dbase/DConnection.hxx> +#include <dbase/DTables.hxx> +#include <dbase/DTable.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <file/FCatalog.hxx> +#include <file/FConnection.hxx> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <dbase/DCatalog.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <strings.hrc> +#include <connectivity/dbexception.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::dbase; +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::lang; +using namespace ::com::sun::star::container; + +sdbcx::ObjectType ODbaseTables::createObject(const OUString& _rName) +{ + ODbaseTable* pRet = new ODbaseTable(this, static_cast<ODbaseConnection*>(static_cast<OFileCatalog&>(m_rParent).getConnection()), + _rName,"TABLE"); + + sdbcx::ObjectType xRet = pRet; + pRet->construct(); + return xRet; +} + +void ODbaseTables::impl_refresh( ) +{ + static_cast<ODbaseCatalog*>(&m_rParent)->refreshTables(); +} + +Reference< XPropertySet > ODbaseTables::createDescriptor() +{ + return new ODbaseTable(this, static_cast<ODbaseConnection*>(static_cast<OFileCatalog&>(m_rParent).getConnection())); +} + +// XAppend +sdbcx::ObjectType ODbaseTables::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + auto pTable = comphelper::getUnoTunnelImplementation<ODbaseTable>(descriptor); + if(pTable) + { + pTable->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME),makeAny(_rForName)); + try + { + if(!pTable->CreateImpl()) + throw SQLException(); + } + catch(SQLException&) + { + throw; + } + catch(Exception& ex) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw SQLException( ex.Message, nullptr, "", 0, anyEx ); + } + } + return createObject( _rForName ); +} + +// XDrop +void ODbaseTables::dropObject(sal_Int32 _nPos, const OUString& _sElementName) +{ + Reference< XUnoTunnel> xTunnel; + try + { + xTunnel.set(getObject(_nPos),UNO_QUERY); + } + catch(const Exception&) + { + if(ODbaseTable::Drop_Static(ODbaseTable::getEntry(static_cast<OFileCatalog&>(m_rParent).getConnection(),_sElementName),false,nullptr)) + return; + } + + if ( xTunnel.is() ) + { + ODbaseTable* pTable = reinterpret_cast< ODbaseTable* >( xTunnel->getSomething(ODbaseTable::getUnoTunnelId()) ); + if(pTable) + pTable->DropImpl(); + } + else + { + const OUString sError( static_cast<OFileCatalog&>(m_rParent).getConnection()->getResources().getResourceStringWithSubstitution( + STR_TABLE_NOT_DROP, + "$tablename$", _sElementName + ) ); + ::dbtools::throwGenericSQLException( sError, nullptr ); + } +} + +Any SAL_CALL ODbaseTables::queryInterface( const Type & rType ) +{ + typedef sdbcx::OCollection OTables_BASE; + return OTables_BASE::queryInterface(rType); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/Dservices.cxx b/connectivity/source/drivers/dbase/Dservices.cxx new file mode 100644 index 000000000..e2d09d6c0 --- /dev/null +++ b/connectivity/source/drivers/dbase/Dservices.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbase/DDriver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::dbase; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* dbase_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + ODriver::getImplementationName_Static(), + ODriver::getSupportedServiceNames_Static(), + ODriver_CreateInstance, ::cppu::createSingleFactory) + ; + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/dbase/dbase.component b/connectivity/source/drivers/dbase/dbase.component new file mode 100644 index 000000000..3f2cea66b --- /dev/null +++ b/connectivity/source/drivers/dbase/dbase.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="dbase" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.dbase.ODriver"> + <service name="com.sun.star.sdbc.Driver"/> + <service name="com.sun.star.sdbcx.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/dbase/dindexnode.cxx b/connectivity/source/drivers/dbase/dindexnode.cxx new file mode 100644 index 000000000..1ac10800f --- /dev/null +++ b/connectivity/source/drivers/dbase/dindexnode.cxx @@ -0,0 +1,1046 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <dbase/dindexnode.hxx> +#include <dbase/DIndex.hxx> +#include <o3tl/safeint.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <sal/log.hxx> + +#include <algorithm> +#include <memory> + + +using namespace connectivity; +using namespace connectivity::dbase; +using namespace connectivity::file; +using namespace com::sun::star::sdbc; + +ONDXKey::ONDXKey() + :nRecord(0) +{ +} + +ONDXKey::ONDXKey(const ORowSetValue& rVal, sal_Int32 eType, sal_uInt32 nRec) + : ONDXKey_BASE(eType) + , nRecord(nRec) + , xValue(rVal) +{ +} + +ONDXKey::ONDXKey(const OUString& aStr, sal_uInt32 nRec) + : ONDXKey_BASE(css::sdbc::DataType::VARCHAR) + ,nRecord(nRec) +{ + if (!aStr.isEmpty()) + { + xValue = aStr; + xValue.setBound(true); + } +} + +ONDXKey::ONDXKey(double aVal, sal_uInt32 nRec) + : ONDXKey_BASE(css::sdbc::DataType::DOUBLE) + ,nRecord(nRec) + ,xValue(aVal) +{ +} + +// index page +ONDXPage::ONDXPage(ODbaseIndex& rInd, sal_uInt32 nPos, ONDXPage* pParent) + : nRefCount(0) + , bNoDelete(1) + , nPagePos(nPos) + , bModified(false) + , nCount(0) + , aParent(pParent) + , rIndex(rInd) +{ + sal_uInt16 nT = rIndex.getHeader().db_maxkeys; + ppNodes.reset( new ONDXNode[nT] ); +} + +ONDXPage::~ONDXPage() +{ +} + +void ONDXPage::ReleaseRef() +{ + assert( nRefCount >= 1); + if(--nRefCount == 0 && !bNoDelete) + { + QueryDelete(); + } +} + +void ONDXPage::QueryDelete() +{ + // Store in GarbageCollector + if (IsModified() && rIndex.m_pFileStream) + WriteONDXPage( *rIndex.m_pFileStream, *this ); + + bModified = false; + if (rIndex.UseCollector()) + { + if (aChild.Is()) + aChild->Release(false); + + for (sal_uInt16 i = 0; i < rIndex.getHeader().db_maxkeys;i++) + { + if (ppNodes[i].GetChild().Is()) + ppNodes[i].GetChild()->Release(false); + + ppNodes[i] = ONDXNode(); + } + bNoDelete = 1; + + nCount = 0; + aParent.Clear(); + rIndex.Collect(this); + } + else + { + // I'm not sure about the original purpose of this line, but right now + // it serves the purpose that anything that attempts to do an AddFirstRef() + // after an object is deleted will trip an assert. + nRefCount = 1 << 30; + delete this; + } +} + +ONDXPagePtr& ONDXPage::GetChild(ODbaseIndex const * pIndex) +{ + if (!aChild.Is() && pIndex) + { + aChild = rIndex.CreatePage(aChild.GetPagePos(),this,aChild.HasPage()); + } + return aChild; +} + + +sal_uInt16 ONDXPage::FindPos(const ONDXKey& rKey) const +{ + // searches the position for the given key in a page + sal_uInt16 i = 0; + while (i < nCount && rKey > ((*this)[i]).GetKey()) + i++; + + return i; +} + + +bool ONDXPage::Find(const ONDXKey& rKey) +{ + // searches the given key + // Speciality: At the end of the method + // the actual page and the position of the node, fulfilling the '<=' condition, are saved + // This is considered at insert. + sal_uInt16 i = 0; + while (i < nCount && rKey > ((*this)[i]).GetKey()) + i++; + + bool bResult = false; + + if (!IsLeaf()) + { + // descend further + ONDXPagePtr aPage = (i==0) ? GetChild(&rIndex) : ((*this)[i-1]).GetChild(&rIndex, this); + bResult = aPage.Is() && aPage->Find(rKey); + } + else if (i == nCount) + { + rIndex.m_aCurLeaf = this; + rIndex.m_nCurNode = i - 1; + bResult = false; + } + else + { + bResult = rKey == ((*this)[i]).GetKey(); + rIndex.m_aCurLeaf = this; + rIndex.m_nCurNode = bResult ? i : i - 1; + } + return bResult; +} + + +bool ONDXPage::Insert(ONDXNode& rNode, sal_uInt32 nRowsLeft) +{ + // When creating an index there can be multiple nodes added, + // these are sorted ascending + bool bAppend = nRowsLeft > 0; + if (IsFull()) + { + ONDXNode aSplitNode; + if (bAppend) + aSplitNode = rNode; + else + { + // Save the last node + aSplitNode = (*this)[nCount-1]; + if(rNode.GetKey() <= aSplitNode.GetKey()) + { + bool bResult = true; + // this practically reduces the number of nodes by 1 + if (IsLeaf() && this == rIndex.m_aCurLeaf) + { + // assumes, that the node, for which the condition (<=) holds, is stored in m_nCurNode + --nCount; // (otherwise we might get Assertions and GPFs - 60593) + bResult = Insert(rIndex.m_nCurNode + 1, rNode); + } + else // position unknown + { + sal_uInt16 nPos = NODE_NOTFOUND; + while (++nPos < nCount && rNode.GetKey() > ((*this)[nPos]).GetKey()) ; + + --nCount; // (otherwise we might get Assertions and GPFs - 60593) + bResult = Insert(nPos, rNode); + } + + // can the new node be inserted + if (!bResult) + { + nCount++; + aSplitNode = rNode; + } + } + else + aSplitNode = rNode; + } + + sal_uInt32 nNewPagePos = rIndex.GetPageCount(); + sal_uInt32 nNewPageCount = nNewPagePos + 1; + + // insert extracted node into parent node + if (!HasParent()) + { + // No parent, then new root + ONDXPagePtr aNewRoot = rIndex.CreatePage(nNewPagePos + 1); + aNewRoot->SetChild(this); + + rIndex.m_aRoot = aNewRoot; + rIndex.SetRootPos(nNewPagePos + 1); + rIndex.SetPageCount(++nNewPageCount); + } + + // create new leaf and divide page + ONDXPagePtr aNewPage = rIndex.CreatePage(nNewPagePos,aParent); + rIndex.SetPageCount(nNewPageCount); + + // How many nodes are being inserted? + // Enough, then we can fill the page to the brim + ONDXNode aInnerNode; + if (!IsLeaf() || nRowsLeft < o3tl::make_unsigned(rIndex.GetMaxNodes() / 2)) + aInnerNode = Split(*aNewPage); + else + { + aInnerNode = (*this)[nCount - 1]; + + // Node points to the new page + aInnerNode.SetChild(aNewPage); + + // Inner nodes have no record number + if (rIndex.isUnique()) + aInnerNode.GetKey().ResetRecord(); + + // new page points to the page of the extracted node + if (!IsLeaf()) + aNewPage->SetChild(aInnerNode.GetChild()); + } + + aNewPage->Append(aSplitNode); + ONDXPagePtr aTempParent = aParent; + if (IsLeaf()) + { + rIndex.m_aCurLeaf = aNewPage; + rIndex.m_nCurNode = rIndex.m_aCurLeaf->Count() - 1; + + // free not needed pages, there are no references to those on the page + // afterwards 'this' can't be valid anymore!!! + ReleaseFull(); + } + + // Insert extracted node + return aTempParent->Insert(aInnerNode); + } + else // Fill the page up + { + if (bAppend) + { + if (IsLeaf()) + rIndex.m_nCurNode = nCount - 1; + return Append(rNode); + } + else + { + sal_uInt16 nNodePos = FindPos(rNode.GetKey()); + if (IsLeaf()) + rIndex.m_nCurNode = nNodePos; + + return Insert(nNodePos, rNode); + } + } +} + + +bool ONDXPage::Insert(sal_uInt16 nPos, ONDXNode& rNode) +{ + sal_uInt16 nMaxCount = rIndex.getHeader().db_maxkeys; + if (nPos >= nMaxCount) + return false; + + if (nCount) + { + ++nCount; + // shift right + for (sal_uInt16 i = std::min(static_cast<sal_uInt16>(nMaxCount-1), static_cast<sal_uInt16>(nCount-1)); nPos < i; --i) + (*this)[i] = (*this)[i-1]; + } + else + if (nCount < nMaxCount) + nCount++; + + // insert at the position + ONDXNode& rInsertNode = (*this)[nPos]; + rInsertNode = rNode; + if (rInsertNode.GetChild().Is()) + { + rInsertNode.GetChild()->SetParent(this); + rNode.GetChild()->SetParent(this); + } + + bModified = true; + + return true; +} + + +bool ONDXPage::Append(ONDXNode& rNode) +{ + DBG_ASSERT(!IsFull(), "no Append possible"); + return Insert(nCount, rNode); +} + +void ONDXPage::Release(bool bSave) +{ + // free pages + if (aChild.Is()) + aChild->Release(bSave); + + // free pointer + aChild.Clear(); + + for (sal_uInt16 i = 0; i < rIndex.getHeader().db_maxkeys;i++) + { + if (ppNodes[i].GetChild()) + ppNodes[i].GetChild()->Release(bSave); + + ppNodes[i].GetChild().Clear(); + } + aParent.Clear(); +} + +void ONDXPage::ReleaseFull() +{ + ONDXPagePtr aTempParent = aParent; + Release(); + + if (aTempParent.Is()) + { + // Free pages not needed, there will be no reference anymore to the pages + // afterwards 'this' can't be valid anymore!!! + sal_uInt16 nParentPos = aTempParent->Search(this); + if (nParentPos != NODE_NOTFOUND) + (*aTempParent)[nParentPos].GetChild().Clear(); + else + aTempParent->GetChild().Clear(); + } +} + +void ONDXPage::Delete(sal_uInt16 nNodePos) +{ + if (IsLeaf()) + { + // The last element will not be deleted + if (nNodePos == (nCount - 1)) + { + ONDXNode aNode = (*this)[nNodePos]; + + // parent's KeyValue has to be replaced + if (HasParent()) + aParent->SearchAndReplace(aNode.GetKey(), + (*this)[nNodePos-1].GetKey()); + } + } + + // Delete the node + Remove(nNodePos); + + // Underflow + if (HasParent() && nCount < (rIndex.GetMaxNodes() / 2)) + { + // determine, which node points to the page + sal_uInt16 nParentNodePos = aParent->Search(this); + // last element on parent-page -> merge with secondlast page + if (nParentNodePos == (aParent->Count() - 1)) + { + if (!nParentNodePos) + // merge with left neighbour + Merge(nParentNodePos,aParent->GetChild(&rIndex)); + else + Merge(nParentNodePos,(*aParent)[nParentNodePos-1].GetChild(&rIndex,aParent)); + } + // otherwise merge page with next page + else + { + // merge with right neighbour + Merge(nParentNodePos + 1,((*aParent)[nParentNodePos + 1].GetChild(&rIndex,aParent))); + nParentNodePos++; + } + if (HasParent() && !(*aParent)[nParentNodePos].HasChild()) + aParent->Delete(nParentNodePos); + } + else if (IsRoot()) + // make sure that the position of the root is kept + rIndex.SetRootPos(nPagePos); +} + + +ONDXNode ONDXPage::Split(ONDXPage& rPage) +{ + DBG_ASSERT(IsFull(), "Incorrect Splitting"); + /* divide one page into two + leaf: + Page 1 is (n - (n/2)) + Page 2 is (n/2) + Node n/2 will be duplicated + inner node: + Page 1 is (n+1)/2 + Page 2 is (n/2-1) + Node ((n+1)/2 + 1) : will be taken out + */ + ONDXNode aResultNode; + if (IsLeaf()) + { + for (sal_uInt16 i = nCount - (nCount / 2), j = 0 ; i < nCount; i++) + rPage.Insert(j++,(*this)[i]); + + // this node contains a key that already exists in the tree and must be replaced + ONDXNode aLastNode = (*this)[nCount - 1]; + nCount = nCount - (nCount / 2); + aResultNode = (*this)[nCount - 1]; + + if (HasParent()) + aParent->SearchAndReplace(aLastNode.GetKey(), + aResultNode.GetKey()); + } + else + { + for (sal_uInt16 i = (nCount + 1) / 2 + 1, j = 0 ; i < nCount; i++) + rPage.Insert(j++,(*this)[i]); + + aResultNode = (*this)[(nCount + 1) / 2]; + nCount = (nCount + 1) / 2; + + // new page points to page with extracted node + rPage.SetChild(aResultNode.GetChild()); + } + // node points to new page + aResultNode.SetChild(&rPage); + + // inner nodes have no record number + if (rIndex.isUnique()) + aResultNode.GetKey().ResetRecord(); + bModified = true; + return aResultNode; +} + + +void ONDXPage::Merge(sal_uInt16 nParentNodePos, const ONDXPagePtr& xPage) +{ + DBG_ASSERT(HasParent(), "no parent existing"); + DBG_ASSERT(nParentNodePos != NODE_NOTFOUND, "Wrong index setup"); + + /* Merge 2 pages */ + sal_uInt16 nMaxNodes = rIndex.GetMaxNodes(), + nMaxNodes_2 = nMaxNodes / 2; + + // Determine if page is right or left neighbour + bool bRight = ((*xPage)[0].GetKey() > (*this)[0].GetKey()); // sal_True, whenn xPage the right side is + sal_uInt16 nNewCount = (*xPage).Count() + Count(); + + if (IsLeaf()) + { + // Condition for merge + if (nNewCount < (nMaxNodes_2 * 2)) + { + sal_uInt16 nLastNode = bRight ? Count() - 1 : xPage->Count() - 1; + if (bRight) + { + DBG_ASSERT(xPage != this,"xPage and THIS must not be the same: infinite loop"); + // shift all nodes from xPage to the left node (append) + while (xPage->Count()) + { + Append((*xPage)[0]); + xPage->Remove(0); + } + } + else + { + DBG_ASSERT(xPage != this,"xPage and THIS must not be the same: infinite loop"); + // xPage is the left page and THIS the right one + while (xPage->Count()) + { + Insert(0,(*xPage)[xPage->Count()-1]); + xPage->Remove(xPage->Count()-1); + } + // replace old position of xPage in parent with this + if (nParentNodePos) + (*aParent)[nParentNodePos-1].SetChild(this,aParent); + else // or set as right node + aParent->SetChild(this); + aParent->SetModified(true); + + } + + // cancel Child-relationship at parent node + (*aParent)[nParentNodePos].SetChild(); + // replace the Node-value, only if changed page is the left one, otherwise become + if(aParent->IsRoot() && aParent->Count() == 1) + { + (*aParent)[0].SetChild(); + aParent->ReleaseFull(); + aParent.Clear(); + rIndex.SetRootPos(nPagePos); + rIndex.m_aRoot = this; + SetModified(true); + } + else + aParent->SearchAndReplace((*this)[nLastNode].GetKey(),(*this)[nCount-1].GetKey()); + + xPage->SetModified(false); + xPage->ReleaseFull(); // is not needed anymore + } + // balance the elements nNewCount >= (nMaxNodes_2 * 2) + else + { + if (bRight) + { + // shift all nodes from xPage to the left node (append) + ONDXNode aReplaceNode = (*this)[nCount - 1]; + while (nCount < nMaxNodes_2) + { + Append((*xPage)[0]); + xPage->Remove(0); + } + // Replace the node values: replace old last value by the last of xPage + aParent->SearchAndReplace(aReplaceNode.GetKey(),(*this)[nCount-1].GetKey()); + } + else + { + // insert all nodes from this in front of the xPage nodes + ONDXNode aReplaceNode = (*this)[nCount - 1]; + while (xPage->Count() < nMaxNodes_2) + { + xPage->Insert(0,(*this)[nCount-1]); + Remove(nCount-1); + } + // Replace the node value + aParent->SearchAndReplace(aReplaceNode.GetKey(),(*this)[Count()-1].GetKey()); + } + } + } + else // !IsLeaf() + { + // Condition for merge + if (nNewCount < nMaxNodes_2 * 2) + { + if (bRight) + { + DBG_ASSERT(xPage != this,"xPage and THIS must not be the same: infinite loop"); + // Parent node will be integrated; is initialized with Child from xPage + (*aParent)[nParentNodePos].SetChild(xPage->GetChild(),aParent); + Append((*aParent)[nParentNodePos]); + for (sal_uInt16 i = 0 ; i < xPage->Count(); i++) + Append((*xPage)[i]); + } + else + { + DBG_ASSERT(xPage != this,"xPage and THIS must not be the same: infinite loop"); + // Parent-node will be integrated; is initialized with child + (*aParent)[nParentNodePos].SetChild(GetChild(),aParent); // Parent memorizes my child + Insert(0,(*aParent)[nParentNodePos]); // insert parent node into myself + while (xPage->Count()) + { + Insert(0,(*xPage)[xPage->Count()-1]); + xPage->Remove(xPage->Count()-1); + } + SetChild(xPage->GetChild()); + + if (nParentNodePos) + (*aParent)[nParentNodePos-1].SetChild(this,aParent); + else + aParent->SetChild(this); + } + + // afterwards parent node will be reset + (*aParent)[nParentNodePos].SetChild(); + aParent->SetModified(true); + + if(aParent->IsRoot() && aParent->Count() == 1) + { + (*aParent).SetChild(); + aParent->ReleaseFull(); + aParent.Clear(); + rIndex.SetRootPos(nPagePos); + rIndex.m_aRoot = this; + SetModified(true); + } + else if(nParentNodePos) + // replace the node value + // for Append the range will be enlarged, for Insert the old node from xPage will reference to this + // that's why the node must be updated here + aParent->SearchAndReplace((*aParent)[nParentNodePos-1].GetKey(),(*aParent)[nParentNodePos].GetKey()); + + xPage->SetModified(false); + xPage->ReleaseFull(); + } + // balance the elements + else + { + if (bRight) + { + while (nCount < nMaxNodes_2) + { + (*aParent)[nParentNodePos].SetChild(xPage->GetChild(),aParent); + Append((*aParent)[nParentNodePos]); + (*aParent)[nParentNodePos] = (*xPage)[0]; + xPage->Remove(0); + } + xPage->SetChild((*aParent)[nParentNodePos].GetChild()); + (*aParent)[nParentNodePos].SetChild(xPage,aParent); + } + else + { + while (nCount < nMaxNodes_2) + { + (*aParent)[nParentNodePos].SetChild(GetChild(),aParent); + Insert(0,(*aParent)[nParentNodePos]); + (*aParent)[nParentNodePos] = (*xPage)[xPage->Count()-1]; + xPage->Remove(xPage->Count()-1); + } + SetChild((*aParent)[nParentNodePos].GetChild()); + (*aParent)[nParentNodePos].SetChild(this,aParent); + + } + aParent->SetModified(true); + } + } +} + +// ONDXNode + + +void ONDXNode::Read(SvStream &rStream, ODbaseIndex const & rIndex) +{ + rStream.ReadUInt32( aKey.nRecord ); // key + + if (rIndex.getHeader().db_keytype) + { + double aDbl; + rStream.ReadDouble( aDbl ); + aKey = ONDXKey(aDbl,aKey.nRecord); + } + else + { + sal_uInt16 nLen = rIndex.getHeader().db_keylen; + OString aBuf = read_uInt8s_ToOString(rStream, nLen); + //get length minus trailing whitespace + sal_Int32 nContentLen = aBuf.getLength(); + while (nContentLen && aBuf[nContentLen-1] == ' ') + --nContentLen; + aKey = ONDXKey(OUString(aBuf.getStr(), nContentLen, rIndex.m_pTable->getConnection()->getTextEncoding()) ,aKey.nRecord); + } + rStream >> aChild; +} + + +void ONDXNode::Write(SvStream &rStream, const ONDXPage& rPage) const +{ + const ODbaseIndex& rIndex = rPage.GetIndex(); + if (!rIndex.isUnique() || rPage.IsLeaf()) + rStream.WriteUInt32( aKey.nRecord ); // key + else + rStream.WriteUInt32( 0 ); // key + + if (rIndex.getHeader().db_keytype) // double + { + if (sizeof(double) != rIndex.getHeader().db_keylen) + { + SAL_WARN("connectivity.dbase", "this key length cannot possibly be right?"); + } + if (aKey.getValue().isNull()) + { + sal_uInt8 buf[sizeof(double)] = {}; + rStream.WriteBytes(&buf[0], sizeof(double)); + } + else + rStream.WriteDouble( static_cast<double>(aKey.getValue()) ); + } + else + { + sal_uInt16 const nLen(rIndex.getHeader().db_keylen); + std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[nLen]); + memset(&pBuf[0], 0x20, nLen); + if (!aKey.getValue().isNull()) + { + OUString sValue = aKey.getValue(); + OString aText(OUStringToOString(sValue, rIndex.m_pTable->getConnection()->getTextEncoding())); + strncpy(reinterpret_cast<char *>(&pBuf[0]), aText.getStr(), + std::min<size_t>(nLen, aText.getLength())); + } + rStream.WriteBytes(&pBuf[0], nLen); + } + WriteONDXPagePtr( rStream, aChild ); +} + + +ONDXPagePtr& ONDXNode::GetChild(ODbaseIndex* pIndex, ONDXPage* pParent) +{ + if (!aChild.Is() && pIndex) + { + aChild = pIndex->CreatePage(aChild.GetPagePos(),pParent,aChild.HasPage()); + } + return aChild; +} + + +// ONDXKey + + +bool ONDXKey::IsText(sal_Int32 eType) +{ + return eType == DataType::VARCHAR || eType == DataType::CHAR; +} + + +int ONDXKey::Compare(const ONDXKey& rKey) const +{ + sal_Int32 nRes; + + if (getValue().isNull()) + { + if (rKey.getValue().isNull() || (IsText(getDBType()) && rKey.getValue().getString().isEmpty())) + nRes = 0; + else + nRes = -1; + } + else if (rKey.getValue().isNull()) + { + if (getValue().isNull() || (IsText(getDBType()) && getValue().getString().isEmpty())) + nRes = 0; + else + nRes = 1; + } + else if (IsText(getDBType())) + { + nRes = getValue().getString().compareTo(rKey.getValue()); + } + else + { + double m = getValue(); + double n = rKey.getValue(); + nRes = (m > n) ? 1 : ( m < n) ? -1 : 0; + } + + // compare record, if index !Unique + if (nRes == 0 && nRecord && rKey.nRecord) + { + nRes = (nRecord > rKey.nRecord) ? 1 : + (nRecord == rKey.nRecord) ? 0 : -1; + } + return nRes; +} + +void ONDXKey::setValue(const ORowSetValue& _rVal) +{ + xValue = _rVal; +} + +const ORowSetValue& ONDXKey::getValue() const +{ + return xValue; +} + +SvStream& connectivity::dbase::operator >> (SvStream &rStream, ONDXPagePtr& rPage) +{ + rStream.ReadUInt32( rPage.nPagePos ); + return rStream; +} + +SvStream& connectivity::dbase::WriteONDXPagePtr(SvStream &rStream, const ONDXPagePtr& rPage) +{ + rStream.WriteUInt32( rPage.nPagePos ); + return rStream; +} + +// ONDXPagePtr +ONDXPagePtr::ONDXPagePtr() + : mpPage(nullptr) + , nPagePos(0) +{ +} + +ONDXPagePtr::ONDXPagePtr(ONDXPagePtr&& rRef) noexcept +{ + mpPage = rRef.mpPage; + rRef.mpPage = nullptr; + nPagePos = rRef.nPagePos; +} + +ONDXPagePtr::ONDXPagePtr(ONDXPagePtr const & rRef) + : mpPage(rRef.mpPage) + , nPagePos(rRef.nPagePos) +{ + if (mpPage != nullptr) + mpPage->AddNextRef(); +} + +ONDXPagePtr::ONDXPagePtr(ONDXPage* pRefPage) + : mpPage(pRefPage) + , nPagePos(0) +{ + if (mpPage != nullptr) + mpPage->AddFirstRef(); + if (pRefPage) + nPagePos = pRefPage->GetPagePos(); +} + +ONDXPagePtr::~ONDXPagePtr() +{ + if (mpPage != nullptr) mpPage->ReleaseRef(); +} + +void ONDXPagePtr::Clear() +{ + if (mpPage != nullptr) { + ONDXPage * pRefObj = mpPage; + mpPage = nullptr; + pRefObj->ReleaseRef(); + } +} + +ONDXPagePtr& ONDXPagePtr::operator=(ONDXPagePtr const & rOther) +{ + ONDXPagePtr aTemp(rOther); + *this = std::move(aTemp); + return *this; +} + +ONDXPagePtr& ONDXPagePtr::operator=(ONDXPagePtr && rOther) +{ + if (mpPage != nullptr) { + mpPage->ReleaseRef(); + } + mpPage = rOther.mpPage; + nPagePos = rOther.nPagePos; + rOther.mpPage = nullptr; + return *this; +} + +static sal_uInt32 nValue; + +SvStream& connectivity::dbase::operator >> (SvStream &rStream, ONDXPage& rPage) +{ + rStream.Seek(rPage.GetPagePos() * DINDEX_PAGE_SIZE); + rStream.ReadUInt32( nValue ) >> rPage.aChild; + rPage.nCount = sal_uInt16(nValue); + + for (sal_uInt16 i = 0; i < rPage.nCount; i++) + rPage[i].Read(rStream, rPage.GetIndex()); + return rStream; +} + + +SvStream& connectivity::dbase::WriteONDXPage(SvStream &rStream, const ONDXPage& rPage) +{ + // Page doesn't exist yet + std::size_t nSize = rPage.GetPagePos() + 1; + nSize *= DINDEX_PAGE_SIZE; + if (nSize > rStream.TellEnd()) + { + rStream.SetStreamSize(nSize); + rStream.Seek(rPage.GetPagePos() * DINDEX_PAGE_SIZE); + + char aEmptyData[DINDEX_PAGE_SIZE] = {}; + rStream.WriteBytes(aEmptyData, DINDEX_PAGE_SIZE); + } + rStream.Seek(rPage.GetPagePos() * DINDEX_PAGE_SIZE); + + nValue = rPage.nCount; + rStream.WriteUInt32( nValue ); + WriteONDXPagePtr( rStream, rPage.aChild ); + + sal_uInt16 i = 0; + for (; i < rPage.nCount; i++) + rPage[i].Write(rStream, rPage); + + // check if we have to fill the stream with '\0' + if(i < rPage.rIndex.getHeader().db_maxkeys) + { + std::size_t nTell = rStream.Tell() % DINDEX_PAGE_SIZE; + sal_uInt16 nBufferSize = rStream.GetBufferSize(); + std::size_t nRemainSize = nBufferSize - nTell; + if ( nRemainSize <= nBufferSize ) + { + std::unique_ptr<char[]> pEmptyData( new char[nRemainSize] ); + memset(pEmptyData.get(), 0x00, nRemainSize); + rStream.WriteBytes(pEmptyData.get(), nRemainSize); + rStream.Seek(nTell); + } + } + return rStream; +} + +#if OSL_DEBUG_LEVEL > 1 + +void ONDXPage::PrintPage() +{ + SAL_WARN("connectivity.dbase", "SDB: -----------Page: " << nPagePos << " Parent: " << (HasParent() ? aParent->GetPagePos() : 0) + << " Count: " << nCount << " Child: " << aChild.GetPagePos() << "-----"); + + for (sal_uInt16 i = 0; i < nCount; i++) + { + ONDXNode rNode = (*this)[i]; + ONDXKey& rKey = rNode.GetKey(); + if (!IsLeaf()) + rNode.GetChild(&rIndex, this); + + if (rKey.getValue().isNull()) + { + SAL_WARN("connectivity.dbase", "SDB: [" << rKey.GetRecord() << ",NULL," << rNode.GetChild().GetPagePos() << "]"); + } + else if (rIndex.getHeader().db_keytype) + { + SAL_WARN("connectivity.dbase", "SDB: [" << rKey.GetRecord() << "," << rKey.getValue().getDouble() + << "," << rNode.GetChild().GetPagePos() << "]"); + } + else + { + SAL_WARN("connectivity.dbase", "SDB: [" << rKey.GetRecord() << "," << rKey.getValue().getString() + << "," << rNode.GetChild().GetPagePos() << "]" ); + } + } + SAL_WARN("connectivity.dbase", "SDB: -----------------------------------------------"); + if (!IsLeaf()) + { +#if OSL_DEBUG_LEVEL > 1 + GetChild(&rIndex)->PrintPage(); + for (sal_uInt16 i = 0; i < nCount; i++) + { + ONDXNode rNode = (*this)[i]; + rNode.GetChild(&rIndex,this)->PrintPage(); + } +#endif + } + SAL_WARN("connectivity.dbase", "SDB: ==============================================="); +} +#endif + +bool ONDXPage::IsFull() const +{ + return Count() == rIndex.getHeader().db_maxkeys; +} + + +sal_uInt16 ONDXPage::Search(const ONDXKey& rSearch) +{ + // binary search later + sal_uInt16 i = NODE_NOTFOUND; + while (++i < Count()) + if ((*this)[i].GetKey() == rSearch) + break; + + return (i < Count()) ? i : NODE_NOTFOUND; +} + + +sal_uInt16 ONDXPage::Search(const ONDXPage* pPage) +{ + sal_uInt16 i = NODE_NOTFOUND; + while (++i < Count()) + if (((*this)[i]).GetChild() == pPage) + break; + + // if not found, then we assume, that the page itself points to the page + return (i < Count()) ? i : NODE_NOTFOUND; +} + +// runs recursively +void ONDXPage::SearchAndReplace(const ONDXKey& rSearch, + ONDXKey const & rReplace) +{ + OSL_ENSURE(rSearch != rReplace,"Invalid here:rSearch == rReplace"); + if (rSearch == rReplace) + return; + + sal_uInt16 nPos = NODE_NOTFOUND; + ONDXPage* pPage = this; + + while (pPage) + { + nPos = pPage->Search(rSearch); + if (nPos != NODE_NOTFOUND) + break; + pPage = pPage->aParent; + } + + if (pPage) + { + (*pPage)[nPos].GetKey() = rReplace; + pPage->SetModified(true); + } +} + +ONDXNode& ONDXPage::operator[] (sal_uInt16 nPos) +{ + DBG_ASSERT(nCount > nPos, "incorrect index access"); + return ppNodes[nPos]; +} + + +const ONDXNode& ONDXPage::operator[] (sal_uInt16 nPos) const +{ + DBG_ASSERT(nCount > nPos, "incorrect index access"); + return ppNodes[nPos]; +} + +void ONDXPage::Remove(sal_uInt16 nPos) +{ + DBG_ASSERT(nCount > nPos, "incorrect index access"); + + for (sal_uInt16 i = nPos; i < (nCount-1); i++) + (*this)[i] = (*this)[i+1]; + + nCount--; + bModified = true; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/EApi.cxx b/connectivity/source/drivers/evoab2/EApi.cxx new file mode 100644 index 000000000..12096bdad --- /dev/null +++ b/connectivity/source/drivers/evoab2/EApi.cxx @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <rtl/ustring.hxx> +#include <osl/module.hxx> +#define DECLARE_FN_POINTERS 1 +#include "EApi.h" +static const char *eBookLibNames[] = { + "libebook-1.2.so.20", // evolution-data-server 3.33.2+ + "libebook-1.2.so.19", // evolution-data-server 3.24+ + "libebook-1.2.so.16", + "libebook-1.2.so.15", + "libebook-1.2.so.14", // bumped again (evolution-3.6) + "libebook-1.2.so.13", // bumped again (evolution-3.4) + "libebook-1.2.so.12", // bumped again + "libebook-1.2.so.10", // bumped again + "libebook-1.2.so.9", // evolution-2.8 + "libebook-1.2.so.5", // evolution-2.4 and 2.6+ + "libebook-1.2.so.3", // evolution-2.2 + "libebook.so.8" // evolution-2.0 +}; + +typedef void (*SymbolFunc) (); + +#define SYM_MAP(a) { #a, reinterpret_cast<SymbolFunc *>(&a) } + +namespace { + +struct ApiMap +{ + const char *sym_name; + SymbolFunc *ref_value; +}; + +} + +static const ApiMap aCommonApiMap[] = +{ + SYM_MAP( eds_check_version ), + SYM_MAP( e_contact_field_name ), + SYM_MAP( e_contact_get ), + SYM_MAP( e_contact_get_type ), + SYM_MAP( e_contact_field_id ), + SYM_MAP( e_book_new ), + SYM_MAP( e_book_open ), + SYM_MAP( e_book_get_source ), + SYM_MAP( e_book_get_contacts ), + SYM_MAP( e_book_query_field_test ), + SYM_MAP( e_book_query_and ), + SYM_MAP( e_book_query_or ), + SYM_MAP( e_book_query_not ), + SYM_MAP( e_book_query_ref ), + SYM_MAP( e_book_query_unref ), + SYM_MAP( e_book_query_from_string ), + SYM_MAP( e_book_query_to_string ), + SYM_MAP( e_book_query_field_exists ) +}; + +//< 3.6 api +static const ApiMap aOldApiMap[] = +{ + SYM_MAP( e_book_get_addressbooks ), + SYM_MAP( e_book_get_uri ), + SYM_MAP( e_book_authenticate_user ), + SYM_MAP( e_source_group_peek_base_uri), + SYM_MAP( e_source_peek_name ), + SYM_MAP( e_source_get_property ), + SYM_MAP( e_source_list_peek_groups ), + SYM_MAP( e_source_group_peek_sources ) +}; + +//>= 3.6 api +static const ApiMap aNewApiMap[] = +{ + SYM_MAP( e_source_registry_list_sources ), + SYM_MAP( e_source_registry_new_sync ), + SYM_MAP( e_source_has_extension ), + SYM_MAP( e_source_get_extension ), + SYM_MAP( e_source_backend_get_backend_name ), + SYM_MAP( e_source_get_display_name ), + SYM_MAP( e_source_get_uid ), + SYM_MAP( e_source_registry_ref_source), + SYM_MAP( e_client_open_sync ), + SYM_MAP( e_client_get_source ), + SYM_MAP( e_book_client_get_contacts_sync ), + SYM_MAP( e_client_util_free_object_slist ) +}; + +//== indirect read access (3.6 only) +static const ApiMap aClientApiMap36[] = +{ + SYM_MAP( e_book_client_new ) +}; + +//>= direct read access API (>= 3.8) +static const ApiMap aClientApiMap38[] = +{ + SYM_MAP( e_book_client_connect_direct_sync ) +}; + +#undef SYM_MAP + +template<size_t N> static bool +tryLink( osl::Module &rModule, const char *pName, const ApiMap (&pMap)[N]) +{ + for (size_t i = 0; i < N; ++i) + { + SymbolFunc aMethod = reinterpret_cast<SymbolFunc>( + rModule.getFunctionSymbol(OUString::createFromAscii(pMap[i].sym_name))); + if( !aMethod ) + { + fprintf( stderr, "Warning: missing symbol '%s' in '%s'\n", + pMap[ i ].sym_name, pName ); + return false; + } + *pMap[ i ].ref_value = aMethod; + } + return true; +} + +bool EApiInit() +{ + for( guint j = 0; j < G_N_ELEMENTS( eBookLibNames ); j++ ) + { + osl::Module aModule(OUString::createFromAscii(eBookLibNames[j]), SAL_LOADMODULE_DEFAULT); + + if (!aModule.is()) + continue; + + if (tryLink( aModule, eBookLibNames[ j ], aCommonApiMap)) + { + if (eds_check_version( 3, 6, 0 ) != nullptr) + { + if (tryLink( aModule, eBookLibNames[ j ], aOldApiMap)) + { + aModule.release(); + return true; + } + } + else if (tryLink( aModule, eBookLibNames[ j ], aNewApiMap)) + { + if (eds_check_version( 3, 7, 6 ) != nullptr) + { + if (tryLink( aModule, eBookLibNames[ j ], aClientApiMap36)) + { + aModule.release(); + return true; + } + } + else + { + if (tryLink( aModule, eBookLibNames[ j ], aClientApiMap38)) + { + aModule.release(); + return true; + } + } + } + } + } + fprintf( stderr, "Can find no compliant libebook client libraries\n" ); + return false; +} + +ESourceRegistry *get_e_source_registry() +{ + static ESourceRegistry *theInstance = e_source_registry_new_sync(nullptr, nullptr); + return theInstance; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/EApi.h b/connectivity/source/drivers/evoab2/EApi.h new file mode 100644 index 000000000..8c05f95fa --- /dev/null +++ b/connectivity/source/drivers/evoab2/EApi.h @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_EAPI_H +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_EAPI_H +#include <glib-object.h> + +// Initializes the API below, returns false if not available +bool EApiInit(); + +G_BEGIN_DECLS + +// This header defined all the API methods as +// function pointers instead of real functions +// this will all evaporate as it is compiled generating +// no symbol lookups or relocations, but giving code +// clarity. + +// We attempt to define a minimum API that we use: + +// e-contact.h +#ifdef DECLARE_FN_POINTERS +#define EAPI_EXTERN +#else +#define EAPI_EXTERN extern +#endif + + +typedef void EContact; +#define E_CONTACT(a) ((EContact *)(a)) +#define E_TYPE_CONTACT (e_contact_get_type()) +typedef int EContactField; + +EAPI_EXTERN const char *(*e_contact_field_name) ( EContactField field_id); +EAPI_EXTERN gpointer (*e_contact_get) (EContact *contact, EContactField field_id); +EAPI_EXTERN gconstpointer (*e_contact_get_const) (EContact *contact, EContactField field_id); +// e-source.h +typedef void ESource; +#define E_SOURCE(a) ((ESource *)(a)) +EAPI_EXTERN const char *(*e_source_peek_name) (ESource *source); +EAPI_EXTERN const gchar *(*e_source_get_property) (ESource *source, + const gchar *property); + +EAPI_EXTERN GType (*e_contact_get_type) (void); +EAPI_EXTERN EContactField (*e_contact_field_id) (const char *field_name); + +// e-source-list.h +typedef void ESourceList; +EAPI_EXTERN GSList *(*e_source_list_peek_groups) (ESourceList *list); + +// e-source-group.h +typedef void ESourceGroup; +#define E_SOURCE_GROUP(a) ((ESourceGroup *)(a)) + +EAPI_EXTERN GSList *(*e_source_group_peek_sources) (ESourceGroup *group); +EAPI_EXTERN const char *(*e_source_group_peek_base_uri) (ESourceGroup *group); +// e-book.h +typedef enum { + E_BOOK_QUERY_IS, + E_BOOK_QUERY_CONTAINS, + E_BOOK_QUERY_BEGINS_WITH, + E_BOOK_QUERY_ENDS_WITH, +} EBookQueryTest; + +typedef void EBook; +typedef void EBookQuery; + +EAPI_EXTERN EBook *(*e_book_new) (ESource *source, + GError **error); + +EAPI_EXTERN gboolean (*e_book_open) (EBook *book, + gboolean only_if_exists, + GError **error); + +EAPI_EXTERN const char *(*e_book_get_uri) (EBook *book); +EAPI_EXTERN ESource *(*e_book_get_source)(EBook *book); + +EAPI_EXTERN gboolean (*e_book_get_addressbooks) (ESourceList **addressbook_sources, + GError **error); + +EAPI_EXTERN gboolean (*e_book_get_contacts) (EBook *book, + EBookQuery *query, + GList **contacts, + GError **error); + +EAPI_EXTERN gboolean (*e_book_authenticate_user) (EBook *book, + const char *user, + const char *passwd, + const char *auth_method, + GError **error); + +// e-book-query.h +EAPI_EXTERN EBookQuery* (*e_book_query_field_exists) (EContactField field); +EAPI_EXTERN EBookQuery* (*e_book_query_field_test) (EContactField field, + EBookQueryTest test, + const char *value); +EAPI_EXTERN EBookQuery* (*e_book_query_and) (int nqs, EBookQuery **qs, gboolean unref); +EAPI_EXTERN EBookQuery* (*e_book_query_or) (int nqs, EBookQuery **qs, gboolean unref); +EAPI_EXTERN EBookQuery* (*e_book_query_not) (EBookQuery *q, gboolean unref); +EAPI_EXTERN EBookQuery* (*e_book_query_ref) (EBookQuery *q); +EAPI_EXTERN void (*e_book_query_unref) (EBookQuery *q); +EAPI_EXTERN char* (*e_book_query_to_string) (EBookQuery *q); +EAPI_EXTERN EBookQuery* (*e_book_query_from_string) (const char *query_string); + +typedef struct { + char *address_format; /* the two letter country code that + determines the format/meaning of the + following fields */ + char *po; + char *ext; + char *street; + char *locality; + char *region; + char *code; + char *country; +} EContactAddress; + +#define E_SOURCE_EXTENSION_ADDRESS_BOOK "Address Book" +typedef void ESourceRegistry; +typedef void GCancellable; +typedef void ESourceBackend; +typedef void EClient; +typedef EClient EBookClient; +EAPI_EXTERN ESourceRegistry* (*e_source_registry_new_sync) (GCancellable *cancellable, GError **error); +EAPI_EXTERN GList* (*e_source_registry_list_sources) (ESourceRegistry *registry, const gchar *extension_name); +EAPI_EXTERN gboolean (*e_source_has_extension) (ESource *source, const gchar *extension_name); +EAPI_EXTERN gpointer (*e_source_get_extension) (ESource *source, const gchar *extension_name); +EAPI_EXTERN const gchar* (*e_source_backend_get_backend_name) (ESourceBackend *extension); +EAPI_EXTERN const gchar* (*e_source_get_display_name) (ESource *source); +EAPI_EXTERN const gchar* (*eds_check_version) (guint required_major, guint required_minor, guint required_micro); +EAPI_EXTERN const gchar* (*e_source_get_uid) (ESource *source); +EAPI_EXTERN ESource* (*e_source_registry_ref_source) (ESourceRegistry *registry, const gchar *uid); +EAPI_EXTERN EBookClient* (*e_book_client_new) (ESource *source, GError **error); +EAPI_EXTERN EBookClient* (*e_book_client_connect_direct_sync) (ESourceRegistry *registry, ESource *source, GCancellable *cancellable, GError **error); +EAPI_EXTERN gboolean (*e_client_open_sync) (EClient *client, gboolean only_if_exists, GCancellable *cancellable, GError **error); +EAPI_EXTERN ESource* (*e_client_get_source) (EClient *client); +EAPI_EXTERN gboolean (*e_book_client_get_contacts_sync) (EBookClient *client, const gchar *sexp, GSList **contacts, GCancellable *cancellable, GError **error); +EAPI_EXTERN void (*e_client_util_free_object_slist) (GSList *objects); + +ESourceRegistry *get_e_source_registry(); +bool isSourceBackend(ESource *pSource, const char *backendname); + +G_END_DECLS +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NCatalog.cxx b/connectivity/source/drivers/evoab2/NCatalog.cxx new file mode 100644 index 000000000..fb010b8dc --- /dev/null +++ b/connectivity/source/drivers/evoab2/NCatalog.cxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "NCatalog.hxx" +#include "NConnection.hxx" +#include "NTables.hxx" +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> + + +using namespace connectivity::evoab; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OEvoabCatalog::OEvoabCatalog(OEvoabConnection* _pCon) : + connectivity::sdbcx::OCatalog(_pCon) + ,m_pConnection(_pCon) +{ +} +void OEvoabCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + Sequence< OUString > aTypes { "TABLE" }; + Reference< XResultSet > xResult = m_xMetaData->getTables( + Any(), "%", "%", aTypes); + + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + OUString aName; + + while(xResult->next()) + { + aName = xRow->getString(3); + aVector.push_back(aName); + } + } + if(m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset( new OEvoabTables(m_xMetaData,*this,m_aMutex,aVector) ); +} +// XTablesSupplier +Reference< XNameAccess > SAL_CALL OEvoabCatalog::getTables( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + + try + { + if (!m_pTables) { + refreshTables(); + } + } + catch( const RuntimeException& ) + { + // allowed to leave this method + throw; + } + catch( const Exception& ) + { + // allowed + } + + return m_pTables.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NCatalog.hxx b/connectivity/source/drivers/evoab2/NCatalog.hxx new file mode 100644 index 000000000..d883d53c2 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NCatalog.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCATALOG_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCATALOG_HXX + +#include <sdbcx/VCatalog.hxx> + +namespace connectivity +{ + namespace evoab + { + class OEvoabConnection; + class OEvoabCatalog : public connectivity::sdbcx::OCatalog + { + OEvoabConnection *m_pConnection; + public: + explicit OEvoabCatalog(OEvoabConnection *_pCon); + OEvoabConnection* getConnection() const { return m_pConnection; } + virtual void refreshTables() override; + virtual void refreshViews() override {} + virtual void refreshGroups() override {} + virtual void refreshUsers() override {} + // XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( + ) override; + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCATALOG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NColumns.cxx b/connectivity/source/drivers/evoab2/NColumns.cxx new file mode 100644 index 000000000..378e2af77 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NColumns.cxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "NColumns.hxx" +#include "NTable.hxx" +#include <connectivity/sdbcx/VColumn.hxx> +#include <com/sun/star/sdbc/XRow.hpp> + +using namespace connectivity::sdbcx; +using namespace connectivity; +using namespace ::comphelper; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace connectivity::evoab; + + +sdbcx::ObjectType OEvoabColumns::createObject(const OUString& _rName) +{ + const Any aCatalog; + const OUString sCatalogName; + const OUString sSchemaName(m_pTable->getSchema()); + const OUString sTableName(m_pTable->getTableName()); + Reference< XResultSet > xResult = m_pTable->getConnection()->getMetaData()->getColumns( + aCatalog, + sSchemaName, + sTableName, + _rName); + + sdbcx::ObjectType xRet; + if (xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + + while (xResult->next()) + { + if (xRow->getString(4) == _rName) + { + OColumn* pRet = new 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, + true, + sCatalogName, + sSchemaName, + sTableName); + xRet = pRet; + break; + } + } + } + + return xRet; +} + +void OEvoabColumns::impl_refresh() +{ + m_pTable->refreshColumns(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NColumns.hxx b/connectivity/source/drivers/evoab2/NColumns.hxx new file mode 100644 index 000000000..d3038aaa6 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NColumns.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCOLUMNS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCOLUMNS_HXX + +#include "NTable.hxx" +#include <connectivity/sdbcx/VCollection.hxx> + +namespace connectivity +{ + namespace evoab + { + class OEvoabColumns final : public sdbcx::OCollection + { + OEvoabTable* m_pTable; + + virtual sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override; + + public: + OEvoabColumns( OEvoabTable* _pTable, + ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector + ) : sdbcx::OCollection(*_pTable,true,_rMutex,_rVector), + m_pTable(_pTable) + { } + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCOLUMNS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NConnection.cxx b/connectivity/source/drivers/evoab2/NConnection.cxx new file mode 100644 index 000000000..d586d6ba2 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NConnection.cxx @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "NConnection.hxx" +#include "NDatabaseMetaData.hxx" +#include "NCatalog.hxx" +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include "NPreparedStatement.hxx" +#include "NStatement.hxx" +#include <connectivity/dbexception.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +using namespace connectivity::evoab; +using namespace dbtools; + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; + +OEvoabConnection::OEvoabConnection(OEvoabDriver const & _rDriver) + : m_rDriver(_rDriver) + , m_eSDBCAddressType(SDBCAddress::EVO_LOCAL) +{ +} + +OEvoabConnection::~OEvoabConnection() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if(!isClosed()) { + acquire(); + close(); + } +} + + +// XServiceInfo + +IMPLEMENT_SERVICE_INFO(OEvoabConnection, "com.sun.star.sdbc.drivers.evoab.Connection", "com.sun.star.sdbc.Connection") + + +void OEvoabConnection::construct(const OUString& url, const Sequence< PropertyValue >& info) +{ + osl_atomic_increment( &m_refCount ); + SAL_INFO("connectivity.evoab2", "OEvoabConnection::construct()::url = " << url ); + + OUString sPassword; + const char pPwd[] = "password"; + + const PropertyValue *pIter = info.getConstArray(); + const PropertyValue *pEnd = pIter + info.getLength(); + for(;pIter != pEnd;++pIter) + { + if(pIter->Name == pPwd) + { + pIter->Value >>= sPassword; + break; + } + } + + if ( url == "sdbc:address:evolution:groupwise" ) + setSDBCAddressType(SDBCAddress::EVO_GWISE); + else if ( url == "sdbc:address:evolution:ldap" ) + setSDBCAddressType(SDBCAddress::EVO_LDAP); + else + setSDBCAddressType(SDBCAddress::EVO_LOCAL); + setURL(url); + setPassword(OUStringToOString(sPassword,RTL_TEXTENCODING_UTF8)); + osl_atomic_decrement( &m_refCount ); +} + + +OUString SAL_CALL OEvoabConnection::nativeSQL( const OUString& _sSql ) +{ + // when you need to transform SQL92 to you driver specific you can do it here + return _sSql; +} + +Reference< XDatabaseMetaData > SAL_CALL OEvoabConnection::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + xMetaData = new OEvoabDatabaseMetaData(this); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +css::uno::Reference< XTablesSupplier > OEvoabConnection::createCatalog() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XTablesSupplier > xTab = m_xCatalog; + if(!xTab.is()) + { + OEvoabCatalog *pCat = new OEvoabCatalog(this); + xTab = pCat; + m_xCatalog = xTab; + } + return xTab; +} + +Reference< XStatement > SAL_CALL OEvoabConnection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + OStatement* pStmt = new OStatement(this); + + Reference< XStatement > xStmt = pStmt; + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return xStmt; +} + +Reference< XPreparedStatement > SAL_CALL OEvoabConnection::prepareStatement( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + OEvoabPreparedStatement* pStmt = new OEvoabPreparedStatement( this ); + Reference< XPreparedStatement > xStmt = pStmt; + pStmt->construct( sql ); + + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return xStmt; +} + +Reference< XPreparedStatement > SAL_CALL OEvoabConnection::prepareCall( const OUString& /*sql*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this ); + return nullptr; +} +sal_Bool SAL_CALL OEvoabConnection::isClosed( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return OConnection_BASE::rBHelper.bDisposed; +} + + +// XCloseable +void SAL_CALL OEvoabConnection::close( ) +{ + { // we just dispose us + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + } + dispose(); +} + + +// XWarningsSupplier +Any SAL_CALL OEvoabConnection::getWarnings( ) +{ + return m_aWarnings.getWarnings(); +} +void SAL_CALL OEvoabConnection::clearWarnings( ) +{ + m_aWarnings.clearWarnings(); +} + + +void OEvoabConnection::disposing() +{ + // we noticed that we should be destroyed in near future so we have to dispose our statements + ::osl::MutexGuard aGuard(m_aMutex); + OConnection_BASE::disposing(); +} + +// -------------------------------- stubbed methods ------------------------------------------------ +void SAL_CALL OEvoabConnection::setAutoCommit( sal_Bool /*autoCommit*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setAutoCommit", *this ); +} +sal_Bool SAL_CALL OEvoabConnection::getAutoCommit( ) +{ + return true; +} +void SAL_CALL OEvoabConnection::commit( ) +{ +} +void SAL_CALL OEvoabConnection::rollback( ) +{ +} +void SAL_CALL OEvoabConnection::setReadOnly( sal_Bool /*readOnly*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setReadOnly", *this ); +} +sal_Bool SAL_CALL OEvoabConnection::isReadOnly( ) +{ + return false; +} +void SAL_CALL OEvoabConnection::setCatalog( const OUString& /*catalog*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setCatalog", *this ); +} + +OUString SAL_CALL OEvoabConnection::getCatalog( ) +{ + return OUString(); +} +void SAL_CALL OEvoabConnection::setTransactionIsolation( sal_Int32 /*level*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTransactionIsolation", *this ); +} + +sal_Int32 SAL_CALL OEvoabConnection::getTransactionIsolation( ) +{ + return TransactionIsolation::NONE; +} + +Reference< css::container::XNameAccess > SAL_CALL OEvoabConnection::getTypeMap( ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::getTypeMap", *this ); + return nullptr; +} +void SAL_CALL OEvoabConnection::setTypeMap( const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NConnection.hxx b/connectivity/source/drivers/evoab2/NConnection.hxx new file mode 100644 index 000000000..2ba2a8db7 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NConnection.hxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCONNECTION_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCONNECTION_HXX + +#include "NDriver.hxx" +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <connectivity/CommonTools.hxx> +#include <connectivity/warningscontainer.hxx> +#include <TConnection.hxx> +#include <cppuhelper/weakref.hxx> +#include <osl/module.h> +#include "EApi.h" + +namespace connectivity +{ + namespace evoab + { + + namespace SDBCAddress { + typedef enum { + Unknown = 0, + EVO_LOCAL = 1, + EVO_LDAP = 2, + EVO_GWISE = 3 + } sdbc_address_type; + } + + typedef connectivity::OMetaConnection OConnection_BASE; // implements basics and text encoding + + class OEvoabConnection final :public OConnection_BASE + { + private: + const OEvoabDriver& m_rDriver; + SDBCAddress::sdbc_address_type m_eSDBCAddressType; + css::uno::Reference< css::sdbcx::XTablesSupplier > + m_xCatalog; + OString m_aPassword; + ::dbtools::WarningsContainer m_aWarnings; + + virtual ~OEvoabConnection() override; + + public: + explicit OEvoabConnection( OEvoabDriver const & _rDriver ); + /// @throws css::sdbc::SQLException + void construct(const OUString& _rUrl,const css::uno::Sequence< css::beans::PropertyValue >& _rInfo ); + + OString const & getPassword() const { return m_aPassword; } + void setPassword( OString const & aStr ) { m_aPassword = aStr; } + // own methods + const OEvoabDriver& getDriver() const { return m_rDriver; } + + SDBCAddress::sdbc_address_type getSDBCAddressType() const { return m_eSDBCAddressType;} + void setSDBCAddressType(SDBCAddress::sdbc_address_type _eSDBCAddressType) {m_eSDBCAddressType = _eSDBCAddressType;} + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XServiceInfo + DECLARE_SERVICE_INFO(); + + // XConnection + css::uno::Reference< css::sdbcx::XTablesSupplier > createCatalog(); + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual void SAL_CALL setAutoCommit( sal_Bool autoCommit ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual void SAL_CALL setReadOnly( sal_Bool readOnly ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL setCatalog( const OUString& catalog ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual void SAL_CALL setTransactionIsolation( sal_Int32 level ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + + // XCloseable + virtual void SAL_CALL close( ) override; + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NCONNECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NDatabaseMetaData.cxx b/connectivity/source/drivers/evoab2/NDatabaseMetaData.cxx new file mode 100644 index 000000000..9eb6f1d34 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NDatabaseMetaData.cxx @@ -0,0 +1,1218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "NDatabaseMetaData.hxx" +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <connectivity/dbexception.hxx> +#include <connectivity/FValue.hxx> +#include <com/sun/star/sdbc/ColumnSearch.hpp> + +#include <cstddef> +#include <string.h> +#include "EApi.h" + +using namespace connectivity::evoab; +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; + +namespace +{ + bool equal(const char *str1, const char *str2) + { + return str1 == nullptr || str2 == nullptr ? str1 == str2 : strcmp(str1, str2) == 0; + } +} + +namespace connectivity::evoab +{ + static sal_Int32 const s_nCOLUMN_SIZE = 256; + static sal_Int32 const s_nDECIMAL_DIGITS = 0; + static sal_Int32 const s_nNULLABLE = 1; + static sal_Int32 const s_nCHAR_OCTET_LENGTH = 65535; + + static ColumnProperty **pFields=nullptr; + static guint nFields = 0; + + static const char *pBlackList[] = + { + "id", + "list-show-addresses", + "address-label-home", + "address-label-work", + "address-label-other" + }; + + const SplitEvoColumns* get_evo_addr() + { + static const SplitEvoColumns evo_addr[] = { + {"addr-line1",DEFAULT_ADDR_LINE1},{"addr-line2",DEFAULT_ADDR_LINE2},{"city",DEFAULT_CITY},{"state",DEFAULT_STATE},{"country",DEFAULT_COUNTRY},{"zip",DEFAULT_ZIP}, + {"work-addr-line1",WORK_ADDR_LINE1},{"work-addr-line2",WORK_ADDR_LINE2},{"work-city",WORK_CITY},{"work-state",WORK_STATE},{"work-country",WORK_COUNTRY},{"work-zip",WORK_ZIP}, + {"home-addr-line1",HOME_ADDR_LINE1},{"home-addr-line2",HOME_ADDR_LINE2},{"home-addr-City",HOME_CITY},{"home-state",HOME_STATE},{"home-country",HOME_COUNTRY},{"home-zip",HOME_ZIP}, + {"other-addr-line1",OTHER_ADDR_LINE1},{"other-addr-line2",OTHER_ADDR_LINE2},{"other-addr-city",OTHER_CITY},{"other-addr-state",OTHER_STATE},{"other-addr-country",OTHER_COUNTRY},{"other-addr-zip",OTHER_ZIP} + }; + return evo_addr; + } + + static void + splitColumn (ColumnProperty **pToBeFields) + { + const SplitEvoColumns* evo_addr( get_evo_addr() ); + for (int i = 0; i < OTHER_ZIP; i++) + { + pToBeFields[nFields] = g_new0(ColumnProperty,1); + pToBeFields[nFields]->bIsSplittedValue = true; + pToBeFields[nFields]->pField = g_param_spec_ref(g_param_spec_string (evo_addr[i].pColumnName,evo_addr[i].pColumnName,"",nullptr,G_PARAM_WRITABLE)); + nFields++; + } + } + + static void + initFields() + { + if( pFields ) + return; + + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + if( pFields ) + return; + + guint nProps; + ColumnProperty **pToBeFields; + GParamSpec **pProps; + nFields = 0; + pProps = g_object_class_list_properties + ( static_cast<GObjectClass *>(g_type_class_ref( E_TYPE_CONTACT )), + &nProps ); + pToBeFields = g_new0(ColumnProperty *, (nProps + OTHER_ZIP)/* new column(s)*/ ); + for ( guint i = 0; i < nProps; i++ ) + { + switch (pProps[i]->value_type) + { + case G_TYPE_STRING: + case G_TYPE_BOOLEAN: + { + bool bAdd = true; + const char *pName = g_param_spec_get_name( pProps[i] ); + for (unsigned int j = 0; j < G_N_ELEMENTS( pBlackList ); j++ ) + { + if( !strcmp( pBlackList[j], pName ) ) + { + bAdd = false; + break; + } + } + if( bAdd ) + { + pToBeFields[nFields]= g_new0(ColumnProperty,1); + pToBeFields[nFields]->bIsSplittedValue=false; + pToBeFields[ nFields++ ]->pField = g_param_spec_ref( pProps[i] ); + } + break; + } + default: + break; + } + } + + splitColumn(pToBeFields); + pFields = pToBeFields; + } + + + const ColumnProperty * + getField(guint n) + { + initFields(); + if( n < nFields ) + return pFields[n]; + else + return nullptr; + } + + GType + getGFieldType( guint nCol ) + { + initFields(); + + if ( nCol < nFields ) + return pFields[nCol]->pField->value_type; + return G_TYPE_STRING; + } + + sal_Int32 + getFieldType( guint nCol ) + { + sal_Int32 nType = getGFieldType( nCol ); + return nType == G_TYPE_STRING ? DataType::VARCHAR : DataType::BIT; + } + + guint findEvoabField(const OUString& aColName) + { + guint nRet = guint(-1); + bool bFound = false; + initFields(); + for (guint i=0;(i < nFields) && !bFound;i++) + { + OUString aName = getFieldName(i); + if (aName == aColName) + { + nRet = i; + bFound = true; + } + } + return nRet; + } + + OUString + getFieldTypeName( guint nCol ) + { + switch( getFieldType( nCol ) ) + { + case DataType::BIT: + return "BIT"; + case DataType::VARCHAR: + return "VARCHAR"; + default: + break; + } + return OUString(); + } + + OUString + getFieldName( guint nCol ) + { + const GParamSpec *pSpec = getField( nCol )->pField; + OUString aName; + initFields(); + + if( pSpec ) + { + aName = OStringToOUString( g_param_spec_get_name( const_cast<GParamSpec *>(pSpec) ), + RTL_TEXTENCODING_UTF8 ); + aName = aName.replace( '-', '_' ); + } + return aName; + } + + void + free_column_resources() + { + for (int i=nFields-1;i > 0;i--) + { + if (pFields && pFields[i] ) + { + if (pFields[i]->pField) + g_param_spec_unref(pFields[i]->pField); + g_free(pFields[i]); + } + } + if(pFields) + { + g_free(pFields); + pFields=nullptr; + } + + } + + +} + + +OEvoabDatabaseMetaData::OEvoabDatabaseMetaData(OEvoabConnection* _pCon) + : ::connectivity::ODatabaseMetaDataBase(_pCon, _pCon->getConnectionInfo()) + ,m_pConnection(_pCon) +{ + OSL_ENSURE(m_pConnection,"OEvoabDatabaseMetaData::OEvoabDatabaseMetaData: No connection set!"); +} +OEvoabDatabaseMetaData::~OEvoabDatabaseMetaData() +{ +} + + +ODatabaseMetaDataResultSet::ORows OEvoabDatabaseMetaData::getColumnRows( const OUString& columnNamePattern ) +{ + ODatabaseMetaDataResultSet::ORows aRows; + ODatabaseMetaDataResultSet::ORow aRow(19); + + // **************************************************** + // Some entries in a row never change, so set them now + // **************************************************** + + // Catalog + aRow[1] = new ORowSetValueDecorator(OUString()); + // Schema + aRow[2] = new ORowSetValueDecorator(OUString()); + // COLUMN_SIZE + aRow[7] = new ORowSetValueDecorator(s_nCOLUMN_SIZE); + // BUFFER_LENGTH, not used + aRow[8] = ODatabaseMetaDataResultSet::getEmptyValue(); + // DECIMAL_DIGITS. + aRow[9] = new ORowSetValueDecorator(s_nDECIMAL_DIGITS); + // NUM_PREC_RADIX + aRow[10] = new ORowSetValueDecorator(sal_Int32(10)); + // NULLABLE + aRow[11] = new ORowSetValueDecorator(s_nNULLABLE); + // REMARKS + aRow[12] = ODatabaseMetaDataResultSet::getEmptyValue(); + // COULUMN_DEF, not used + aRow[13] = ODatabaseMetaDataResultSet::getEmptyValue(); + // SQL_DATA_TYPE, not used + aRow[14] = ODatabaseMetaDataResultSet::getEmptyValue(); + // SQL_DATETIME_SUB, not used + aRow[15] = ODatabaseMetaDataResultSet::getEmptyValue(); + // CHAR_OCTET_LENGTH, refer to [5] + aRow[16] = new ORowSetValueDecorator(s_nCHAR_OCTET_LENGTH); + // IS_NULLABLE + aRow[18] = new ORowSetValueDecorator(OUString("YES")); + + + aRow[3] = new ORowSetValueDecorator(OUString("TABLE")); + ::osl::MutexGuard aGuard( m_aMutex ); + + initFields(); + for (sal_Int32 i = 0; i < static_cast<sal_Int32>(nFields); i++) + { + if( match( columnNamePattern, getFieldName( i ), '\0' ) ) + { + aRow[5] = new ORowSetValueDecorator( static_cast<sal_Int16>( getFieldType( i ) ) ); + aRow[6] = new ORowSetValueDecorator( getFieldTypeName( i ) ); + + // COLUMN_NAME + aRow[4] = new ORowSetValueDecorator( getFieldName( i ) ); + // ORDINAL_POSITION + aRow[17] = new ORowSetValueDecorator( i ); + aRows.push_back( aRow ); + } + } + + return aRows ; +} + +OUString OEvoabDatabaseMetaData::impl_getCatalogSeparator_throw( ) +{ + return OUString(); +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxRowSize( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxCatalogNameLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxCharLiteralLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxColumnNameLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxColumnsInIndex( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxCursorNameLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxConnections( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxColumnsInTable( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 OEvoabDatabaseMetaData::impl_getMaxStatements_throw( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxTableNameLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 OEvoabDatabaseMetaData::impl_getMaxTablesInSelect_throw( ) +{ + // We only support a single table + return 1; +} + + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::storesLowerCaseQuotedIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::storesLowerCaseIdentifiers( ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_storesMixedCaseQuotedIdentifiers_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::storesMixedCaseIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::storesUpperCaseQuotedIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::storesUpperCaseIdentifiers( ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_supportsAlterTableWithAddColumn_throw( ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_supportsAlterTableWithDropColumn_throw( ) +{ + return false; +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxIndexLength( ) +{ + return 0;// 0 means no limit +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsNonNullableColumns( ) +{ + return false; +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getCatalogTerm( ) +{ + return OUString(); +} + +OUString OEvoabDatabaseMetaData::impl_getIdentifierQuoteString_throw( ) +{ + // normally this is " + return "\""; +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getExtraNameCharacters( ) +{ + return OUString(); +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsDifferentTableCorrelationNames( ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_isCatalogAtStart_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::dataDefinitionIgnoredInTransactions( ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::dataDefinitionCausesTransactionCommit( ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsDataManipulationTransactionsOnly( ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsDataDefinitionAndDataManipulationTransactions( ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsPositionedDelete( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsPositionedUpdate( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsOpenStatementsAcrossRollback( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsOpenStatementsAcrossCommit( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsOpenCursorsAcrossCommit( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsOpenCursorsAcrossRollback( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsTransactionIsolationLevel( sal_Int32 /*level*/ ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_supportsSchemasInDataManipulation_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsANSI92FullSQL( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsANSI92EntryLevelSQL( ) +{ + return true; // should be supported at least +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsIntegrityEnhancementFacility( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsSchemasInIndexDefinitions( ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_supportsSchemasInTableDefinitions_throw( ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_supportsCatalogsInTableDefinitions_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsCatalogsInIndexDefinitions( ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_supportsCatalogsInDataManipulation_throw( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsOuterJoins( ) +{ + return false; +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxStatementLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxProcedureNameLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxSchemaNameLength( ) +{ + return 0;// 0 means no limit +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsTransactions( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::allProceduresAreCallable( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsStoredProcedures( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsSelectForUpdate( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::allTablesAreSelectable( ) +{ + // We allow you to select from any table. + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::isReadOnly( ) +{ + // For now definitely read-only, no support for update/delete + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::usesLocalFiles( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::usesLocalFilePerTable( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsTypeConversion( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::nullPlusNonNullIsNull( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsColumnAliasing( ) +{ + // todo add Support for this. + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsTableCorrelationNames( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsConvert( sal_Int32 /*fromType*/, sal_Int32 /*toType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsExpressionsInOrderBy( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsGroupBy( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsGroupByBeyondSelect( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsGroupByUnrelated( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsMultipleTransactions( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsMultipleResultSets( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsLikeEscapeClause( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsOrderByUnrelated( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsUnion( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsUnionAll( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsMixedCaseIdentifiers( ) +{ + return false; +} + +bool OEvoabDatabaseMetaData::impl_supportsMixedCaseQuotedIdentifiers_throw( ) +{ + // Any case may be used + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::nullsAreSortedAtEnd( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::nullsAreSortedAtStart( ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::nullsAreSortedHigh( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::nullsAreSortedLow( ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsSchemasInProcedureCalls( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsSchemasInPrivilegeDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsCatalogsInProcedureCalls( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsCatalogsInPrivilegeDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsCorrelatedSubqueries( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsSubqueriesInComparisons( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsSubqueriesInExists( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsSubqueriesInIns( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsSubqueriesInQuantifieds( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsANSI92IntermediateSQL( ) +{ + return false; +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getURL( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return m_pConnection->getURL(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getUserName( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getDriverName( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getDriverVersion() +{ + return "1"; +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getDatabaseProductVersion( ) +{ + return "0"; +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getDatabaseProductName( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getProcedureTerm( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getSchemaTerm( ) +{ + return OUString(); +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getDriverMajorVersion( ) +{ + return 1; +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getDefaultTransactionIsolation( ) +{ + return TransactionIsolation::NONE; +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getDriverMinorVersion( ) +{ + return 0; +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getSQLKeywords( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getSearchStringEscape( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getStringFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getTimeDateFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getSystemFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabDatabaseMetaData::getNumericFunctions( ) +{ + return OUString(); +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsExtendedSQLGrammar( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsCoreSQLGrammar( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsMinimumSQLGrammar( ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsFullOuterJoins( ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsLimitedOuterJoins( ) +{ + return false; +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxColumnsInGroupBy( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxColumnsInSelect( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL OEvoabDatabaseMetaData::getMaxUserNameLength( ) +{ + return 0;// 0 means no limit +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsResultSetType( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsResultSetConcurrency( sal_Int32 /*setType*/, sal_Int32 /*concurrency*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::ownUpdatesAreVisible( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::ownDeletesAreVisible( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::ownInsertsAreVisible( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::othersUpdatesAreVisible( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::othersDeletesAreVisible( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::othersInsertsAreVisible( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::updatesAreDetected( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::deletesAreDetected( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::insertsAreDetected( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabDatabaseMetaData::supportsBatchUpdates( ) +{ + return false; +} + +// here follow all methods which return a resultset +// the first methods is an example implementation how to use this resultset +// of course you could implement it on your and you should do this because +// the general way is more memory expensive + +Reference< XResultSet > SAL_CALL OEvoabDatabaseMetaData::getTableTypes( ) +{ + /* Don't need to change as evoab driver supports only table */ + + // there exists no possibility to get table types so we have to check + static const OUStringLiteral sTableTypes[] = + { + "TABLE" // Currently we only support a 'TABLE' nothing more complex + }; + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTableTypes); + Reference< XResultSet > xRef = pResult; + + // here we fill the rows which should be visible when ask for data from the resultset returned here + ODatabaseMetaDataResultSet::ORows aRows; + for(std::size_t i=0;i < SAL_N_ELEMENTS(sTableTypes);++i) + { + ODatabaseMetaDataResultSet::ORow aRow; + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(OUString(sTableTypes[i]))); + + // bound row + aRows.push_back(aRow); + } + // here we set the rows at the resultset + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > OEvoabDatabaseMetaData::impl_getTypeInfo_throw( ) +{ + /* + * Return the proper type information required by evo driver + */ + + ODatabaseMetaDataResultSet* pResultSet = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTypeInfo); + + Reference< XResultSet > xResultSet = pResultSet; + static ODatabaseMetaDataResultSet::ORows aRows = []() + { + ODatabaseMetaDataResultSet::ORows tmp; + ODatabaseMetaDataResultSet::ORow aRow; + aRow.reserve(19); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(OUString("VARCHAR"))); + aRow.push_back(new ORowSetValueDecorator(DataType::VARCHAR)); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(s_nCHAR_OCTET_LENGTH))); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + // aRow.push_back(new ORowSetValueDecorator((sal_Int32)ColumnValue::NULLABLE)); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(ColumnSearch::FULL))); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(10))); + + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("VARCHAR")); + aRow[2] = new ORowSetValueDecorator(DataType::VARCHAR); + aRow[3] = new ORowSetValueDecorator(sal_Int32(65535)); + tmp.push_back(aRow); + return tmp; + }(); + pResultSet->setRows(aRows); + return xResultSet; +} + +Reference< XResultSet > SAL_CALL OEvoabDatabaseMetaData::getColumns( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& /*tableNamePattern*/, + const OUString& columnNamePattern ) +{ + // this returns an empty resultset where the column-names are already set + // in special the metadata of the resultset already returns the right columns + ODatabaseMetaDataResultSet* pResultSet = new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eColumns ); + Reference< XResultSet > xResultSet = pResultSet; + pResultSet->setRows( getColumnRows( columnNamePattern ) ); + return xResultSet; +} + + +bool isSourceBackend(ESource *pSource, const char *backendname) +{ + if (!pSource || !e_source_has_extension (pSource, E_SOURCE_EXTENSION_ADDRESS_BOOK)) + return false; + + gpointer extension = e_source_get_extension (pSource, E_SOURCE_EXTENSION_ADDRESS_BOOK); + return extension && equal(e_source_backend_get_backend_name (extension), backendname); +} + +Reference< XResultSet > SAL_CALL OEvoabDatabaseMetaData::getTables( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& /*tableNamePattern*/, const Sequence< OUString >& types ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTableTypes); + Reference< XResultSet > xRef = pResult; + + // check if any type is given + // when no types are given then we have to return all tables e.g. TABLE + + const OUString aTable("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 xRef; + + ODatabaseMetaDataResultSet::ORows aRows; + + if (eds_check_version(3, 6, 0) == nullptr) + { + GList *pSources = e_source_registry_list_sources(get_e_source_registry(), E_SOURCE_EXTENSION_ADDRESS_BOOK); + + for (GList* liter = pSources; liter; liter = liter->next) + { + ESource *pSource = E_SOURCE (liter->data); + bool can = false; + switch (m_pConnection->getSDBCAddressType()) + { + case SDBCAddress::EVO_GWISE: + can = isSourceBackend( pSource, "groupwise"); // not supported in evo/eds 3.6.x+, somehow + break; + case SDBCAddress::EVO_LOCAL: + can = isSourceBackend( pSource, "local"); + break; + case SDBCAddress::EVO_LDAP: + can = isSourceBackend( pSource, "ldap"); + break; + case SDBCAddress::Unknown: + can = true; + break; + } + if (!can) + continue; + + OUString aHumanName = OStringToOUString( e_source_get_display_name( pSource ), + RTL_TEXTENCODING_UTF8 ); + OUString aUID = OStringToOUString( e_source_get_uid( pSource ), + RTL_TEXTENCODING_UTF8 ); + ODatabaseMetaDataResultSet::ORow aRow{ + ORowSetValueDecoratorRef(), + ORowSetValueDecoratorRef(), + ORowSetValueDecoratorRef(), + new ORowSetValueDecorator(aHumanName), //tablename + new ORowSetValueDecorator(aTable), + new ORowSetValueDecorator(aUID)}; //comment + //I'd prefer to swap the comment and the human name and + //just use e_source_registry_ref_source(get_e_source_registry(), aUID); + //in open book rather than search for the name again + aRows.push_back(aRow); + } + + g_list_foreach (pSources, reinterpret_cast<GFunc>(g_object_unref), nullptr); + g_list_free (pSources); + } + else + { + ESourceList *pSourceList; + if( !e_book_get_addressbooks (&pSourceList, nullptr) ) + pSourceList = nullptr; + + GSList *g; + for( g = e_source_list_peek_groups( pSourceList ); g; g = g->next) + { + GSList *s; + const char *p = e_source_group_peek_base_uri(E_SOURCE_GROUP(g->data)); + + switch (m_pConnection->getSDBCAddressType()) { + case SDBCAddress::EVO_GWISE: + if ( !strncmp( "groupwise://", p, 11 )) + break; + else + continue; + case SDBCAddress::EVO_LOCAL: + if ( !strncmp( "file://", p, 6 ) || + !strncmp( "local://", p, 6 ) ) + break; + else + continue; + case SDBCAddress::EVO_LDAP: + if ( !strncmp( "ldap://", p, 6 )) + break; + else + continue; + case SDBCAddress::Unknown: + break; + } + for (s = e_source_group_peek_sources (E_SOURCE_GROUP (g->data)); s; s = s->next) + { + ESource *pSource = E_SOURCE (s->data); + + OUString aName = OStringToOUString( e_source_peek_name( pSource ), + RTL_TEXTENCODING_UTF8 ); + + ODatabaseMetaDataResultSet::ORow aRow{ + ORowSetValueDecoratorRef(), + ORowSetValueDecoratorRef(), + ORowSetValueDecoratorRef(), + new ORowSetValueDecorator(aName), + new ORowSetValueDecorator(aTable), + ODatabaseMetaDataResultSet::getEmptyValue()}; + aRows.push_back(aRow); + } + } + } + + pResult->setRows(aRows); + + return xRef; +} + +Reference< XResultSet > SAL_CALL OEvoabDatabaseMetaData::getUDTs( const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& /*typeNamePattern*/, const Sequence< sal_Int32 >& /*types*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XDatabaseMetaDaza::getUDTs", *this ); + return nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NDatabaseMetaData.hxx b/connectivity/source/drivers/evoab2/NDatabaseMetaData.hxx new file mode 100644 index 000000000..61a9449e6 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NDatabaseMetaData.hxx @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NDATABASEMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NDATABASEMETADATA_HXX + +#include "NConnection.hxx" +#include <TDatabaseMetaDataBase.hxx> +#include <FDatabaseMetaDataResultSet.hxx> + + +namespace connectivity +{ + namespace evoab + { + + //************ Class: OEvoabDatabaseMetaData + + typedef struct{ + gboolean bIsSplittedValue; + GParamSpec *pField; + }ColumnProperty; + + typedef enum { + DEFAULT_ADDR_LINE1=1,DEFAULT_ADDR_LINE2,DEFAULT_CITY,DEFAULT_STATE,DEFAULT_COUNTRY,DEFAULT_ZIP, + WORK_ADDR_LINE1,WORK_ADDR_LINE2,WORK_CITY,WORK_STATE,WORK_COUNTRY,WORK_ZIP, + HOME_ADDR_LINE1,HOME_ADDR_LINE2,HOME_CITY,HOME_STATE,HOME_COUNTRY,HOME_ZIP, + OTHER_ADDR_LINE1,OTHER_ADDR_LINE2,OTHER_CITY,OTHER_STATE,OTHER_COUNTRY,OTHER_ZIP + }ColumnNumber; + + typedef struct { + const gchar *pColumnName; + ColumnNumber value; + }SplitEvoColumns; + + const SplitEvoColumns* get_evo_addr(); + + const ColumnProperty *getField(guint n); + GType getGFieldType(guint nCol) ; + sal_Int32 getFieldType(guint nCol) ; + OUString getFieldTypeName(guint nCol) ; + OUString getFieldName(guint nCol) ; + guint findEvoabField(const OUString& aColName); + + void free_column_resources(); + + class OEvoabDatabaseMetaData : public ODatabaseMetaDataBase + { + OEvoabConnection* m_pConnection; + + ODatabaseMetaDataResultSet::ORows getColumnRows( const OUString& columnNamePattern ); + + protected: + virtual css::uno::Reference< css::sdbc::XResultSet > impl_getTypeInfo_throw() override; + // cached database information + virtual OUString impl_getIdentifierQuoteString_throw( ) override; + virtual bool impl_isCatalogAtStart_throw( ) override; + virtual OUString impl_getCatalogSeparator_throw( ) override; + virtual bool impl_supportsCatalogsInTableDefinitions_throw( ) override; + virtual bool impl_supportsSchemasInTableDefinitions_throw( ) override ; + virtual bool impl_supportsCatalogsInDataManipulation_throw( ) override; + virtual bool impl_supportsSchemasInDataManipulation_throw( ) override ; + virtual bool impl_supportsMixedCaseQuotedIdentifiers_throw( ) override ; + virtual bool impl_supportsAlterTableWithAddColumn_throw( ) override; + virtual bool impl_supportsAlterTableWithDropColumn_throw( ) override; + virtual sal_Int32 impl_getMaxStatements_throw( ) override; + virtual sal_Int32 impl_getMaxTablesInSelect_throw( ) override; + virtual bool impl_storesMixedCaseQuotedIdentifiers_throw( ) override; + + virtual ~OEvoabDatabaseMetaData() override; + public: + explicit OEvoabDatabaseMetaData(OEvoabConnection* _pCon); + + // as I mentioned before this interface is really BIG + // XDatabaseMetaData + virtual sal_Bool SAL_CALL allProceduresAreCallable( ) override; + virtual sal_Bool SAL_CALL allTablesAreSelectable( ) override; + virtual OUString SAL_CALL getURL( ) override; + virtual OUString SAL_CALL getUserName( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedHigh( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedLow( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtStart( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtEnd( ) override; + virtual OUString SAL_CALL getDatabaseProductName( ) override; + virtual OUString SAL_CALL getDatabaseProductVersion( ) override; + virtual OUString SAL_CALL getDriverName( ) override; + virtual OUString SAL_CALL getDriverVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMajorVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMinorVersion( ) override; + virtual sal_Bool SAL_CALL usesLocalFiles( ) override; + virtual sal_Bool SAL_CALL usesLocalFilePerTable( ) override; + virtual sal_Bool SAL_CALL supportsMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesMixedCaseIdentifiers( ) override; + + virtual sal_Bool SAL_CALL storesUpperCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseQuotedIdentifiers( ) override; + + virtual OUString SAL_CALL getSQLKeywords( ) override; + virtual OUString SAL_CALL getNumericFunctions( ) override; + virtual OUString SAL_CALL getStringFunctions( ) override; + virtual OUString SAL_CALL getSystemFunctions( ) override; + virtual OUString SAL_CALL getTimeDateFunctions( ) override; + virtual OUString SAL_CALL getSearchStringEscape( ) override; + virtual OUString SAL_CALL getExtraNameCharacters( ) override; + virtual sal_Bool SAL_CALL supportsColumnAliasing( ) override; + virtual sal_Bool SAL_CALL nullPlusNonNullIsNull( ) override; + virtual sal_Bool SAL_CALL supportsTypeConversion( ) override; + virtual sal_Bool SAL_CALL supportsConvert( sal_Int32 fromType, sal_Int32 toType ) override; + virtual sal_Bool SAL_CALL supportsTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsDifferentTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsExpressionsInOrderBy( ) override; + virtual sal_Bool SAL_CALL supportsOrderByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupBy( ) override; + virtual sal_Bool SAL_CALL supportsGroupByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupByBeyondSelect( ) override; + virtual sal_Bool SAL_CALL supportsLikeEscapeClause( ) override; + virtual sal_Bool SAL_CALL supportsMultipleResultSets( ) override; + virtual sal_Bool SAL_CALL supportsMultipleTransactions( ) override; + virtual sal_Bool SAL_CALL supportsNonNullableColumns( ) override; + virtual sal_Bool SAL_CALL supportsMinimumSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsCoreSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsExtendedSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsANSI92EntryLevelSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92IntermediateSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92FullSQL( ) override; + virtual sal_Bool SAL_CALL supportsIntegrityEnhancementFacility( ) override; + virtual sal_Bool SAL_CALL supportsOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsFullOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsLimitedOuterJoins( ) override; + virtual OUString SAL_CALL getSchemaTerm( ) override; + virtual OUString SAL_CALL getProcedureTerm( ) override; + virtual OUString SAL_CALL getCatalogTerm( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsPositionedDelete( ) override; + virtual sal_Bool SAL_CALL supportsPositionedUpdate( ) override; + virtual sal_Bool SAL_CALL supportsSelectForUpdate( ) override; + virtual sal_Bool SAL_CALL supportsStoredProcedures( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInComparisons( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInExists( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInIns( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInQuantifieds( ) override; + virtual sal_Bool SAL_CALL supportsCorrelatedSubqueries( ) override; + virtual sal_Bool SAL_CALL supportsUnion( ) override; + virtual sal_Bool SAL_CALL supportsUnionAll( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossRollback( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossRollback( ) override; + virtual sal_Int32 SAL_CALL getMaxBinaryLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCharLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInGroupBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInIndex( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInOrderBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInSelect( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInTable( ) override; + virtual sal_Int32 SAL_CALL getMaxConnections( ) override; + virtual sal_Int32 SAL_CALL getMaxCursorNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxIndexLength( ) override; + virtual sal_Int32 SAL_CALL getMaxSchemaNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxProcedureNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCatalogNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxRowSize( ) override; + virtual sal_Bool SAL_CALL doesMaxRowSizeIncludeBlobs( ) override; + virtual sal_Int32 SAL_CALL getMaxStatementLength( ) override; + virtual sal_Int32 SAL_CALL getMaxTableNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxUserNameLength( ) override; + virtual sal_Int32 SAL_CALL getDefaultTransactionIsolation( ) override; + virtual sal_Bool SAL_CALL supportsTransactions( ) override; + virtual sal_Bool SAL_CALL supportsTransactionIsolationLevel( sal_Int32 level ) override; + virtual sal_Bool SAL_CALL supportsDataDefinitionAndDataManipulationTransactions( ) override; + virtual sal_Bool SAL_CALL supportsDataManipulationTransactionsOnly( ) override; + virtual sal_Bool SAL_CALL dataDefinitionCausesTransactionCommit( ) override; + virtual sal_Bool SAL_CALL dataDefinitionIgnoredInTransactions( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTables( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const css::uno::Sequence< OUString >& types ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTableTypes( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const OUString& columnNamePattern ) override; + virtual sal_Bool SAL_CALL supportsResultSetType( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 concurrency ) override; + virtual sal_Bool SAL_CALL ownUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL updatesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL deletesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL insertsAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsBatchUpdates( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getUDTs( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& typeNamePattern, const css::uno::Sequence< sal_Int32 >& types ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NDATABASEMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NDriver.cxx b/connectivity/source/drivers/evoab2/NDriver.cxx new file mode 100644 index 000000000..d7f4e376a --- /dev/null +++ b/connectivity/source/drivers/evoab2/NDriver.cxx @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "NDriver.hxx" +#include "NConnection.hxx" +#include <com/sun/star/lang/DisposedException.hpp> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + +using namespace osl; +using namespace connectivity::evoab; +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::lang; +using namespace ::com::sun::star::ucb; + + +OEvoabDriver::OEvoabDriver(const Reference< XMultiServiceFactory >& _rxFactory) : + ODriver_BASE( m_aMutex ), m_xFactory( _rxFactory ) +{ +} + +OEvoabDriver::~OEvoabDriver() +{ +} + +void OEvoabDriver::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + // when driver will be destroyed so all our connections have to be destroyed as well + for (const auto& rxConnection : m_xConnections) + { + Reference< XComponent > xComp(rxConnection.get(), UNO_QUERY); + if (xComp.is()) + { + try + { + xComp->dispose(); + } + catch (const css::lang::DisposedException&) + { + xComp.clear(); + } + } + } + m_xConnections.clear(); + connectivity::OWeakRefArray().swap(m_xConnections); // this really clears + + ODriver_BASE::disposing(); +} + +// static ServiceInfo + +OUString OEvoabDriver::getImplementationName_Static( ) +{ + return EVOAB_DRIVER_IMPL_NAME; + // this name is referenced in the configuration and in the evoab.xml + // Please take care when changing it. +} + + +Sequence< OUString > OEvoabDriver::getSupportedServiceNames_Static( ) +{ + // which service is supported + // for more information @see com.sun.star.sdbc.Driver + Sequence<OUString> aSNS { "com.sun.star.sdbc.Driver" }; + return aSNS; +} + +OUString SAL_CALL OEvoabDriver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL OEvoabDriver::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > SAL_CALL OEvoabDriver::getSupportedServiceNames( ) +{ + return getSupportedServiceNames_Static(); +} + + +css::uno::Reference< css::uno::XInterface > connectivity::evoab::OEvoabDriver_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory) +{ + return *(new OEvoabDriver(_rxFactory)); +} + +Reference< XConnection > SAL_CALL OEvoabDriver::connect( const OUString& url, const Sequence< PropertyValue >& info ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if (ODriver_BASE::rBHelper.bDisposed) + throw DisposedException(); + + if ( ! acceptsURL(url) ) + return nullptr; + + OEvoabConnection* pCon = new OEvoabConnection( *this ); + pCon->construct(url,info); + Reference< XConnection > xCon = pCon; + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return xCon; +} + +sal_Bool SAL_CALL OEvoabDriver::acceptsURL( const OUString& url ) +{ + return acceptsURL_Stat(url); +} + + +Sequence< DriverPropertyInfo > SAL_CALL OEvoabDriver::getPropertyInfo( 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); + } // if ( ! acceptsURL(url) ) + + // if you have something special to say return it here :-) + return Sequence< DriverPropertyInfo >(); +} + + +sal_Int32 SAL_CALL OEvoabDriver::getMajorVersion( ) +{ + return 1; +} + +sal_Int32 SAL_CALL OEvoabDriver::getMinorVersion( ) +{ + return 0; +} + +bool OEvoabDriver::acceptsURL_Stat( const OUString& url ) +{ + return ( url == "sdbc:address:evolution:local" || url == "sdbc:address:evolution:groupwise" || url == "sdbc:address:evolution:ldap" ) && EApiInit(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NDriver.hxx b/connectivity/source/drivers/evoab2/NDriver.hxx new file mode 100644 index 000000000..a2c12a87f --- /dev/null +++ b/connectivity/source/drivers/evoab2/NDriver.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NDRIVER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NDRIVER_HXX + +#include <com/sun/star/sdbc/XDriver.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/compbase.hxx> +#include <connectivity/CommonTools.hxx> + +#define EVOAB_DRIVER_IMPL_NAME "com.sun.star.comp.sdbc.evoab.OEvoabDriver" + +namespace connectivity +{ + namespace evoab + { + /// @throws css::uno::Exception + css::uno::Reference< css::uno::XInterface > OEvoabDriver_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory); + + + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XDriver, + css::lang::XServiceInfo > ODriver_BASE; + + + class OEvoabDriver final : public ODriver_BASE + { + ::osl::Mutex m_aMutex; + connectivity::OWeakRefArray m_xConnections; + css::uno::Reference< css::lang::XMultiServiceFactory > m_xFactory; + + public: + explicit OEvoabDriver(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory); + virtual ~OEvoabDriver() override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XInterface + /// @throws css::uno::RuntimeException + static OUString getImplementationName_Static( ); + /// @throws css::uno::RuntimeException + static css::uno::Sequence< OUString > getSupportedServiceNames_Static( ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + + // XDriver + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL connect( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + virtual sal_Bool SAL_CALL acceptsURL( const OUString& url ) override; + virtual css::uno::Sequence< css::sdbc::DriverPropertyInfo > SAL_CALL getPropertyInfo( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + virtual sal_Int32 SAL_CALL getMajorVersion( ) override; + virtual sal_Int32 SAL_CALL getMinorVersion( ) override; + + public: + css::uno::Reference< css::uno::XComponentContext > + getComponentContext( ) const { return comphelper::getComponentContext( m_xFactory ); } + + // static methods + static bool acceptsURL_Stat( const OUString& url ); + }; + } + +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NDRIVER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NPreparedStatement.cxx b/connectivity/source/drivers/evoab2/NPreparedStatement.cxx new file mode 100644 index 000000000..57981e95e --- /dev/null +++ b/connectivity/source/drivers/evoab2/NPreparedStatement.cxx @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "NPreparedStatement.hxx" +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <tools/diagnose_ex.h> + +#include <strings.hrc> + +using namespace connectivity::evoab; +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::container; +using namespace com::sun::star::io; +using namespace com::sun::star::util; + +IMPLEMENT_SERVICE_INFO(OEvoabPreparedStatement,"com.sun.star.sdbcx.evoab.PreparedStatement","com.sun.star.sdbc.PreparedStatement"); + + +OEvoabPreparedStatement::OEvoabPreparedStatement( OEvoabConnection* _pConnection ) + :OCommonStatement(_pConnection) + ,m_sSqlStatement() + ,m_xMetaData() +{ +} + + +void OEvoabPreparedStatement::construct( const OUString& _sql ) +{ + m_sSqlStatement = _sql; + + m_aQueryData = impl_getEBookQuery_throw( m_sSqlStatement ); + ENSURE_OR_THROW( m_aQueryData.getQuery(), "no EBookQuery" ); + ENSURE_OR_THROW( m_aQueryData.xSelectColumns.is(), "no SelectColumn" ); + + // create our meta data + OEvoabResultSetMetaData* pMeta = new OEvoabResultSetMetaData( m_aQueryData.sTable ); + m_xMetaData = pMeta; + pMeta->setEvoabFields( m_aQueryData.xSelectColumns ); +} + + +OEvoabPreparedStatement::~OEvoabPreparedStatement() +{ +} + + +void SAL_CALL OEvoabPreparedStatement::acquire() throw() +{ + OCommonStatement::acquire(); +} + + +void SAL_CALL OEvoabPreparedStatement::release() throw() +{ + OCommonStatement::release(); +} + + +Any SAL_CALL OEvoabPreparedStatement::queryInterface( const Type & rType ) +{ + Any aRet = OCommonStatement::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OPreparedStatement_BASE::queryInterface(rType); + return aRet; +} + +Sequence< Type > SAL_CALL OEvoabPreparedStatement::getTypes( ) +{ + return ::comphelper::concatSequences(OPreparedStatement_BASE::getTypes(),OCommonStatement::getTypes()); +} + + +Reference< XResultSetMetaData > SAL_CALL OEvoabPreparedStatement::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + // the meta data should have been created at construction time + ENSURE_OR_THROW( m_xMetaData.is(), "internal error: no meta data" ); + return m_xMetaData; +} + + +void SAL_CALL OEvoabPreparedStatement::close( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + free_column_resources(); + // Reset last warning message + try { + clearWarnings (); + OCommonStatement::close(); + } + catch (SQLException &) { + // If we get an error, ignore + } + +} + + +sal_Bool SAL_CALL OEvoabPreparedStatement::execute( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + Reference< XResultSet> xRS = impl_executeQuery_throw( m_aQueryData ); + return xRS.is(); +} + + +sal_Int32 SAL_CALL OEvoabPreparedStatement::executeUpdate( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + ::dbtools::throwFeatureNotImplementedSQLException( "XStatement::executeUpdate", *this ); + return 0; +} + + +void SAL_CALL OEvoabPreparedStatement::setString( sal_Int32 /*parameterIndex*/, const OUString& /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setString", *this ); +} + + +Reference< XConnection > SAL_CALL OEvoabPreparedStatement::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + return impl_getConnection(); +} + + +Reference< XResultSet > SAL_CALL OEvoabPreparedStatement::executeQuery( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + return impl_executeQuery_throw( m_aQueryData ); +} + + +void SAL_CALL OEvoabPreparedStatement::setBoolean( sal_Int32 /*parameterIndex*/, sal_Bool /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setBoolean", *this ); + +} + +void SAL_CALL OEvoabPreparedStatement::setByte( sal_Int32 /*parameterIndex*/, sal_Int8 /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setByte", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setDate( sal_Int32 /*parameterIndex*/, const Date& /*aData*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setDate", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setTime( sal_Int32 /*parameterIndex*/, const css::util::Time& /*aVal*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setTime", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setTimestamp( sal_Int32 /*parameterIndex*/, const DateTime& /*aVal*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setTimestamp", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setDouble( sal_Int32 /*parameterIndex*/, double /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setDouble", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setFloat( sal_Int32 /*parameterIndex*/, float /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setFloat", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setInt( sal_Int32 /*parameterIndex*/, sal_Int32 /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setInt", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setLong( sal_Int32 /*parameterIndex*/, sal_Int64 /*aVal*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setLong", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setNull( sal_Int32 /*parameterIndex*/, sal_Int32 /*sqlType*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setNull", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setClob( sal_Int32 /*parameterIndex*/, const Reference< XClob >& /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setClob", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setBlob( sal_Int32 /*parameterIndex*/, const Reference< XBlob >& /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setBlob", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setArray( sal_Int32 /*parameterIndex*/, const Reference< XArray >& /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setArray", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setRef( sal_Int32 /*parameterIndex*/, const Reference< XRef >& /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setRef", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setObjectWithInfo( sal_Int32 /*parameterIndex*/, const Any& /*x*/, sal_Int32 /*sqlType*/, sal_Int32 /*scale*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setObjectWithInfo", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setObjectNull( sal_Int32 /*parameterIndex*/, sal_Int32 /*sqlType*/, const OUString& /*typeName*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setObjectNull", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setObject( sal_Int32 parameterIndex, const Any& x ) +{ + if(!::dbtools::implSetObject(this,parameterIndex,x)) + { + const OUString sError( getOwnConnection()->getResources().getResourceStringWithSubstitution( + STR_UNKNOWN_PARA_TYPE, + "$position$", OUString::number(parameterIndex) + ) ); + ::dbtools::throwGenericSQLException(sError,*this); + } +} + + +void SAL_CALL OEvoabPreparedStatement::setShort( sal_Int32 /*parameterIndex*/, sal_Int16 /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setShort", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setBytes( sal_Int32 /*parameterIndex*/, const Sequence< sal_Int8 >& /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setBytes", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setCharacterStream( sal_Int32 /*parameterIndex*/, const Reference< XInputStream >& /*x*/, sal_Int32 /*length*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setCharacterStream", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::setBinaryStream( sal_Int32 /*parameterIndex*/, const Reference< XInputStream >& /*x*/, sal_Int32 /*length*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setBinaryStream", *this ); +} + + +void SAL_CALL OEvoabPreparedStatement::clearParameters( ) +{ +} + +Reference< XResultSet > SAL_CALL OEvoabPreparedStatement::getResultSet( ) +{ + return nullptr; +} + +sal_Int32 SAL_CALL OEvoabPreparedStatement::getUpdateCount( ) +{ + return 0; +} + +sal_Bool SAL_CALL OEvoabPreparedStatement::getMoreResults( ) +{ + return false; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NPreparedStatement.hxx b/connectivity/source/drivers/evoab2/NPreparedStatement.hxx new file mode 100644 index 000000000..20d5790ca --- /dev/null +++ b/connectivity/source/drivers/evoab2/NPreparedStatement.hxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NPREPAREDSTATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NPREPAREDSTATEMENT_HXX + +#include "NStatement.hxx" +#include "NConnection.hxx" +#include "NDatabaseMetaData.hxx" +#include "NResultSet.hxx" +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XPreparedBatchExecution.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <cppuhelper/implbase5.hxx> + +namespace connectivity +{ + namespace evoab + { + + typedef ::cppu::ImplHelper5< css::sdbc::XPreparedStatement, + css::sdbc::XParameters, + css::sdbc::XResultSetMetaDataSupplier, + css::sdbc::XMultipleResults, + css::lang::XServiceInfo> OPreparedStatement_BASE; + + class OEvoabPreparedStatement final:public OCommonStatement + ,public OPreparedStatement_BASE + { + // our SQL statement + OUString m_sSqlStatement; + // the EBookQuery we're working with + QueryData m_aQueryData; + // our meta data + css::uno::Reference< css::sdbc::XResultSetMetaData > m_xMetaData; + + virtual ~OEvoabPreparedStatement() override; + + public: + explicit OEvoabPreparedStatement( OEvoabConnection* _pConnection ); + + void construct( const OUString& _sql ); + + private: + DECLARE_SERVICE_INFO(); + //XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XPreparedStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( ) override; + virtual sal_Int32 SAL_CALL executeUpdate( ) override; + virtual sal_Bool SAL_CALL execute( ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; + // XParameters + virtual void SAL_CALL setNull( sal_Int32 parameterIndex, sal_Int32 sqlType ) override; + virtual void SAL_CALL setObjectNull( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) override; + virtual void SAL_CALL setBoolean( sal_Int32 parameterIndex, sal_Bool x ) override; + virtual void SAL_CALL setByte( sal_Int32 parameterIndex, sal_Int8 x ) override; + virtual void SAL_CALL setShort( sal_Int32 parameterIndex, sal_Int16 x ) override; + virtual void SAL_CALL setInt( sal_Int32 parameterIndex, sal_Int32 x ) override; + virtual void SAL_CALL setLong( sal_Int32 parameterIndex, sal_Int64 x ) override; + virtual void SAL_CALL setFloat( sal_Int32 parameterIndex, float x ) override; + virtual void SAL_CALL setDouble( sal_Int32 parameterIndex, double x ) override; + virtual void SAL_CALL setString( sal_Int32 parameterIndex, const OUString& x ) override; + virtual void SAL_CALL setBytes( sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL setDate( sal_Int32 parameterIndex, const css::util::Date& x ) override; + virtual void SAL_CALL setTime( sal_Int32 parameterIndex, const css::util::Time& x ) override; + virtual void SAL_CALL setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL setBinaryStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setCharacterStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setObject( sal_Int32 parameterIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL setObjectWithInfo( sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) override; + virtual void SAL_CALL setRef( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef >& x ) override; + virtual void SAL_CALL setBlob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob >& x ) override; + virtual void SAL_CALL setClob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XClob >& x ) override; + virtual void SAL_CALL setArray( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray >& x ) override; + virtual void SAL_CALL clearParameters( ) override; + // XCloseable + virtual void SAL_CALL close( ) override; + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + // XMultipleResults + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getResultSet( ) override; + virtual sal_Int32 SAL_CALL getUpdateCount( ) override; + virtual sal_Bool SAL_CALL getMoreResults( ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NPREPAREDSTATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NResultSet.cxx b/connectivity/source/drivers/evoab2/NResultSet.cxx new file mode 100644 index 000000000..77d53939c --- /dev/null +++ b/connectivity/source/drivers/evoab2/NResultSet.cxx @@ -0,0 +1,1142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "NDatabaseMetaData.hxx" +#include "NConnection.hxx" +#include "NResultSet.hxx" +#include <propertyids.hxx> +#include <strings.hrc> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> + +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/sqlerror.hxx> +#include <rtl/string.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/syslocale.hxx> +#include <unotools/intlwrapper.hxx> +#include <unotools/collatorwrapper.hxx> + +#include <cstring> + +namespace connectivity::evoab { + +using namespace ::comphelper; +using namespace com::sun::star; +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::io; +namespace ErrorCondition = ::com::sun::star::sdb::ErrorCondition; + + +OUString SAL_CALL OEvoabResultSet::getImplementationName( ) +{ + return "com.sun.star.sdbcx.evoab.ResultSet"; +} + + Sequence< OUString > SAL_CALL OEvoabResultSet::getSupportedServiceNames( ) +{ + Sequence< OUString > aSupported { "com.sun.star.sdbc.ResultSet" }; + return aSupported; +} + +sal_Bool SAL_CALL OEvoabResultSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +struct ComparisonData +{ + const SortDescriptor& rSortOrder; + IntlWrapper aIntlWrapper; + + ComparisonData(const SortDescriptor& _rSortOrder) + : rSortOrder(_rSortOrder) + , aIntlWrapper(SvtSysLocale().GetUILanguageTag()) + { + } +}; + +static OUString +valueToOUString( GValue& _rValue ) +{ + const char *pStr = g_value_get_string( &_rValue ); + OString aStr( pStr ? pStr : "" ); + OUString sResult( OStringToOUString( aStr, RTL_TEXTENCODING_UTF8 ) ); + g_value_unset( &_rValue ); + return sResult; +} + +static bool +valueToBool( GValue& _rValue ) +{ + bool bResult = g_value_get_boolean( &_rValue ); + g_value_unset( &_rValue ); + return bResult; +} + +static int +whichAddress(int value) +{ + int fieldEnum; + switch (value) + { + case HOME_ADDR_LINE1: + case HOME_ADDR_LINE2: + case HOME_CITY: + case HOME_STATE: + case HOME_COUNTRY: + case HOME_ZIP: + fieldEnum = e_contact_field_id("address_home"); + break; + + case WORK_ADDR_LINE1: + case WORK_ADDR_LINE2: + case WORK_CITY: + case WORK_STATE: + case WORK_COUNTRY: + case WORK_ZIP: + fieldEnum = e_contact_field_id("address_work"); + break; + + case OTHER_ADDR_LINE1: + case OTHER_ADDR_LINE2: + case OTHER_CITY: + case OTHER_STATE: + case OTHER_COUNTRY: + case OTHER_ZIP: + fieldEnum = e_contact_field_id("address_other"); + break; + + default: fieldEnum = e_contact_field_id("address_home"); + } + return fieldEnum; +} + +/* +* This function decides the default column values based on the first field of EContactAddress. +* The search order is Work->Home->other(defaults). +*/ +static EContactAddress * +getDefaultContactAddress( EContact *pContact,int *value ) +{ + EContactAddress *ec = static_cast<EContactAddress *>(e_contact_get(pContact,whichAddress(WORK_ADDR_LINE1))); + if ( ec && (ec->street[0]!='\0') ) + { + *value= *value +WORK_ADDR_LINE1 -1; + return ec; + } + else + { + ec = static_cast<EContactAddress *>(e_contact_get(pContact,whichAddress(HOME_ADDR_LINE1))); + if ( ec && (ec->street[0]!='\0') ) + { + *value=*value+HOME_ADDR_LINE1-1; + return ec; + } + } + + *value=*value+OTHER_ADDR_LINE1-1; + return static_cast<EContactAddress *>(e_contact_get(pContact,whichAddress(OTHER_ADDR_LINE1))); +} + +static EContactAddress* +getContactAddress( EContact *pContact, int * address_enum ) +{ + EContactAddress *ec = nullptr; + switch (*address_enum) { + + case DEFAULT_ADDR_LINE1: + case DEFAULT_ADDR_LINE2: + case DEFAULT_CITY: + case DEFAULT_STATE: + case DEFAULT_COUNTRY: + case DEFAULT_ZIP: + ec = getDefaultContactAddress(pContact,address_enum);break; + default: + ec = static_cast<EContactAddress *>(e_contact_get(pContact,whichAddress(*address_enum))); + } + return ec; +} + +static bool +handleSplitAddress( EContact *pContact,GValue *pStackValue, int value ) +{ + EContactAddress *ec = getContactAddress(pContact,&value) ; + + if (ec==nullptr) + return true; + + switch (value) { + case WORK_ADDR_LINE1: + g_value_set_string(pStackValue,ec->street ); break; + case WORK_ADDR_LINE2: + g_value_set_string(pStackValue,ec->po ); break; + case WORK_CITY: + g_value_set_string(pStackValue,ec->locality ); break; + case WORK_STATE: + g_value_set_string(pStackValue,ec->region ); break; + case WORK_COUNTRY: + g_value_set_string(pStackValue,ec->country ); break; + case WORK_ZIP: + g_value_set_string(pStackValue,ec->code ); break; + + case HOME_ADDR_LINE1: + g_value_set_string(pStackValue,ec->street ); break; + case HOME_ADDR_LINE2: + g_value_set_string(pStackValue,ec->po ); break; + case HOME_CITY: + g_value_set_string(pStackValue,ec->locality ); break; + case HOME_STATE: + g_value_set_string(pStackValue,ec->region ); break; + case HOME_COUNTRY: + g_value_set_string(pStackValue,ec->country ); break; + case HOME_ZIP: + g_value_set_string(pStackValue,ec->code ); break; + + case OTHER_ADDR_LINE1: + g_value_set_string(pStackValue,ec->street ); break; + case OTHER_ADDR_LINE2: + g_value_set_string(pStackValue,ec->po ); break; + case OTHER_CITY: + g_value_set_string(pStackValue,ec->locality ); break; + case OTHER_STATE: + g_value_set_string(pStackValue,ec->region ); break; + case OTHER_COUNTRY: + g_value_set_string(pStackValue,ec->country ); break; + case OTHER_ZIP: + g_value_set_string(pStackValue,ec->code ); break; + + } + + return false; +} + +static bool +getValue( EContact* pContact, sal_Int32 nColumnNum, GType nType, GValue* pStackValue, bool& _out_rWasNull ) +{ + const ColumnProperty * pSpecs = evoab::getField( nColumnNum ); + if ( !pSpecs ) + return false; + + GParamSpec* pSpec = pSpecs->pField; + bool bIsSplittedColumn = pSpecs->bIsSplittedValue; + + _out_rWasNull = true; + if ( !pSpec || !pContact) + return false; + + if ( G_PARAM_SPEC_VALUE_TYPE (pSpec) != nType ) + { + SAL_WARN("connectivity.evoab2", "Wrong type (0x" << std::hex << static_cast<int>(G_PARAM_SPEC_VALUE_TYPE(pSpec)) << ") (0x" + << std::hex << static_cast<int>(nType) << ") " << (pSpec->name ? pSpec->name : "<noname>")); + return false; + } + + g_value_init( pStackValue, nType ); + if ( bIsSplittedColumn ) + { + const SplitEvoColumns* evo_addr( get_evo_addr() ); + for (int i=0;i<OTHER_ZIP;i++) + { + if (0 == strcmp (g_param_spec_get_name (pSpec), evo_addr[i].pColumnName)) + { + _out_rWasNull = handleSplitAddress( pContact, pStackValue, evo_addr[i].value ); + return true; + } + } + } + else + { + g_object_get_property( G_OBJECT (pContact), + g_param_spec_get_name (pSpec), + pStackValue ); + if ( G_VALUE_TYPE( pStackValue ) != nType ) + { + SAL_WARN("connectivity.evoab2", "Fetched type mismatch" ); + g_value_unset( pStackValue ); + return false; + } + } + _out_rWasNull = false; + return true; +} + +extern "C" { + +static int CompareContacts( gconstpointer _lhs, gconstpointer _rhs, gpointer _userData ) +{ + EContact* lhs = const_cast< gpointer >( _lhs ); + EContact* rhs = const_cast< gpointer >( _rhs ); + + GValue aLhsValue = { 0, { { 0 } } }; + GValue aRhsValue = { 0, { { 0 } } }; + bool bLhsNull = true; + bool bRhsNull = true; + + OUString sLhs, sRhs; + bool bLhs(false), bRhs(false); + + const ComparisonData& rCompData = *static_cast< const ComparisonData* >( _userData ); + for ( const auto& sortCol : rCompData.rSortOrder ) + { + sal_Int32 nField = sortCol.nField; + GType eFieldType = evoab::getGFieldType( nField ); + + bool success = getValue( lhs, nField, eFieldType, &aLhsValue, bLhsNull ) + && getValue( rhs, nField, eFieldType, &aRhsValue, bRhsNull ); + OSL_ENSURE( success, "CompareContacts: could not retrieve both values!" ); + if ( !success ) + return 0; + + if ( bLhsNull && !bRhsNull ) + return -1; + if ( !bLhsNull && bRhsNull ) + return 1; + if ( bLhsNull && bRhsNull ) + continue; + + if ( eFieldType == G_TYPE_STRING ) + { + sLhs = valueToOUString( aLhsValue ); + sRhs = valueToOUString( aRhsValue ); + sal_Int32 nCompResult = rCompData.aIntlWrapper.getCaseCollator()->compareString( sLhs, sRhs ); + if ( nCompResult != 0 ) + return nCompResult; + continue; + } + + bLhs = valueToBool( aLhsValue ); + bRhs = valueToBool( aRhsValue ); + if ( bLhs && !bRhs ) + return -1; + if ( !bLhs && bRhs ) + return 1; + continue; + } + + return 0; +} + +} + +OString OEvoabVersionHelper::getUserName( EBook *pBook ) +{ + OString aName; + if( isLDAP( pBook ) ) + aName = e_source_get_property( e_book_get_source( pBook ), "binddn" ); + else + aName = e_source_get_property( e_book_get_source( pBook ), "user" ); + return aName; +} + +namespace { + +bool isBookBackend( EBookClient *pBook, const char *backendname) +{ + if (!pBook) + return false; + ESource *pSource = e_client_get_source (reinterpret_cast<EClient *>(pBook)); + return isSourceBackend(pSource, backendname); +} + +class OEvoabVersion36Helper : public OEvoabVersionHelper +{ +private: + GSList *m_pContacts; +public: + OEvoabVersion36Helper() + : m_pContacts(nullptr) + { + } + + virtual ~OEvoabVersion36Helper() override + { + freeContacts(); + } + + virtual EBook* openBook(const char *abname) override + { + //It would be better if here we had id to begin with, see + //NDatabaseMetaData.cxx + const char *id = nullptr; + GList *pSources = e_source_registry_list_sources(get_e_source_registry(), E_SOURCE_EXTENSION_ADDRESS_BOOK); + for (GList* liter = pSources; liter; liter = liter->next) + { + ESource *pSource = E_SOURCE (liter->data); + + if (strcmp(abname, e_source_get_display_name( pSource )) == 0) + { + id = e_source_get_uid( pSource ); + break; + } + } + g_list_foreach (pSources, reinterpret_cast<GFunc>(g_object_unref), nullptr); + g_list_free (pSources); + if (!id) + return nullptr; + + ESource *pSource = e_source_registry_ref_source(get_e_source_registry(), id); + EBookClient *pBook = pSource ? createClient (pSource) : nullptr; + if (pBook && !e_client_open_sync (pBook, true, nullptr, nullptr)) + { + g_object_unref (G_OBJECT (pBook)); + pBook = nullptr; + } + if (pSource) + g_object_unref (pSource); + return pBook; + } + + virtual bool isLDAP( EBook *pBook ) override + { + return isBookBackend(pBook, "ldap"); + } + + virtual bool isLocal( EBook *pBook ) override + { + return isBookBackend(pBook, "local"); + } + + virtual void freeContacts() override final + { + e_client_util_free_object_slist(m_pContacts); + m_pContacts = nullptr; + } + + virtual void executeQuery (EBook* pBook, EBookQuery* pQuery, OString &/*rPassword*/) override + { + freeContacts(); + char *sexp = e_book_query_to_string( pQuery ); + e_book_client_get_contacts_sync( pBook, sexp, &m_pContacts, nullptr, nullptr ); + g_free (sexp); + } + + virtual EContact *getContact(sal_Int32 nIndex) override + { + gpointer pData = g_slist_nth_data (m_pContacts, nIndex); + return pData ? E_CONTACT (pData) : nullptr; + } + + virtual sal_Int32 getNumContacts() override + { + return g_slist_length( m_pContacts ); + } + + virtual bool hasContacts() override + { + return m_pContacts != nullptr; + } + + virtual void sortContacts( const ComparisonData& _rCompData ) override + { + OSL_ENSURE( !_rCompData.rSortOrder.empty(), "sortContacts: no need to call this without any sort order!" ); + ENSURE_OR_THROW( _rCompData.aIntlWrapper.getCaseCollator(), "no collator for comparing strings" ); + + m_pContacts = g_slist_sort_with_data( m_pContacts, &CompareContacts, + const_cast< gpointer >( static_cast< gconstpointer >( &_rCompData ) ) ); + } + +protected: + virtual EBookClient * createClient( ESource *pSource ) + { + return e_book_client_new (pSource, nullptr); + } +}; + +class OEvoabVersion38Helper : public OEvoabVersion36Helper +{ +protected: + virtual EBookClient * createClient( ESource *pSource ) override + { + return e_book_client_connect_direct_sync (get_e_source_registry (), pSource, nullptr, nullptr); + } +}; + +ESource * findSource( const char *id ) +{ + ESourceList *pSourceList = nullptr; + + g_return_val_if_fail (id != nullptr, nullptr); + + if (!e_book_get_addressbooks (&pSourceList, nullptr)) + pSourceList = nullptr; + + for ( GSList *g = e_source_list_peek_groups (pSourceList); g; g = g->next) + { + for (GSList *s = e_source_group_peek_sources (E_SOURCE_GROUP (g->data)); s; s = s->next) + { + ESource *pSource = E_SOURCE (s->data); + if (!strcmp (e_source_peek_name (pSource), id)) + return pSource; + } + } + return nullptr; +} + +bool isAuthRequired( EBook *pBook ) +{ + return e_source_get_property( e_book_get_source( pBook ), + "auth" ) != nullptr; +} + +class OEvoabVersion35Helper : public OEvoabVersionHelper +{ +private: + GList *m_pContacts; + +public: + OEvoabVersion35Helper() + : m_pContacts(nullptr) + { + } + + virtual ~OEvoabVersion35Helper() override + { + freeContacts(); + } + + virtual EBook* openBook(const char *abname) override + { + ESource *pSource = findSource (abname); + EBook *pBook = pSource ? e_book_new (pSource, nullptr) : nullptr; + if (pBook && !e_book_open (pBook, true, nullptr)) + { + g_object_unref (G_OBJECT (pBook)); + pBook = nullptr; + } + return pBook; + } + + virtual bool isLDAP( EBook *pBook ) override + { + return pBook && !strncmp( "ldap://", e_book_get_uri( pBook ), 6 ); + } + + virtual bool isLocal( EBook *pBook ) override + { + return pBook && ( !strncmp( "file://", e_book_get_uri( pBook ), 6 ) || + !strncmp( "local:", e_book_get_uri( pBook ), 6 ) ); + } + + virtual void freeContacts() override final + { + g_list_free(m_pContacts); + m_pContacts = nullptr; + } + + virtual void executeQuery (EBook* pBook, EBookQuery* pQuery, OString &rPassword) override + { + freeContacts(); + + ESource *pSource = e_book_get_source( pBook ); + bool bAuthSuccess = true; + + if( isAuthRequired( pBook ) ) + { + OString aUser( getUserName( pBook ) ); + const char *pAuth = e_source_get_property( pSource, "auth" ); + bAuthSuccess = e_book_authenticate_user( pBook, aUser.getStr(), rPassword.getStr(), pAuth, nullptr ); + } + + if (bAuthSuccess) + e_book_get_contacts( pBook, pQuery, &m_pContacts, nullptr ); + } + + virtual EContact *getContact(sal_Int32 nIndex) override + { + gpointer pData = g_list_nth_data (m_pContacts, nIndex); + return pData ? E_CONTACT (pData) : nullptr; + } + + virtual sal_Int32 getNumContacts() override + { + return g_list_length( m_pContacts ); + } + + virtual bool hasContacts() override + { + return m_pContacts != nullptr; + } + + virtual void sortContacts( const ComparisonData& _rCompData ) override + { + OSL_ENSURE( !_rCompData.rSortOrder.empty(), "sortContacts: no need to call this without any sort order!" ); + ENSURE_OR_THROW( _rCompData.aIntlWrapper.getCaseCollator(), "no collator for comparing strings" ); + + m_pContacts = g_list_sort_with_data( m_pContacts, &CompareContacts, + const_cast< gpointer >( static_cast< gconstpointer >( &_rCompData ) ) ); + } +}; + +} + +OEvoabResultSet::OEvoabResultSet( OCommonStatement* pStmt, OEvoabConnection *pConnection ) + :OResultSet_BASE(m_aMutex) + ,::comphelper::OPropertyContainer( OResultSet_BASE::rBHelper ) + ,m_pStatement(pStmt) + ,m_pConnection(pConnection) + ,m_bWasNull(true) + ,m_nFetchSize(0) + ,m_nResultSetType(ResultSetType::SCROLL_INSENSITIVE) + ,m_nFetchDirection(FetchDirection::FORWARD) + ,m_nResultSetConcurrency(ResultSetConcurrency::READ_ONLY) + ,m_nIndex(-1) + ,m_nLength(0) +{ + if (eds_check_version( 3, 7, 6 ) == nullptr) + m_pVersionHelper = std::make_unique<OEvoabVersion38Helper>(); + else if (eds_check_version( 3, 6, 0 ) == nullptr) + m_pVersionHelper = std::make_unique<OEvoabVersion36Helper>(); + else + m_pVersionHelper = std::make_unique<OEvoabVersion35Helper>(); + + #define REGISTER_PROP( id, member ) \ + registerProperty( \ + OMetaConnection::getPropMap().getNameByIndex( id ), \ + id, \ + PropertyAttribute::READONLY, \ + &member, \ + cppu::UnoType<decltype(member)>::get() \ + ); + + REGISTER_PROP( PROPERTY_ID_FETCHSIZE, m_nFetchSize ); + REGISTER_PROP( PROPERTY_ID_RESULTSETTYPE, m_nResultSetType ); + REGISTER_PROP( PROPERTY_ID_FETCHDIRECTION, m_nFetchDirection ); + REGISTER_PROP( PROPERTY_ID_RESULTSETCONCURRENCY, m_nResultSetConcurrency ); +} + +OEvoabResultSet::~OEvoabResultSet() +{} + +void OEvoabResultSet::construct( const QueryData& _rData ) +{ + ENSURE_OR_THROW( _rData.getQuery(), "internal error: no EBookQuery" ); + + EBook *pBook = m_pVersionHelper->openBook(OUStringToOString(_rData.sTable, RTL_TEXTENCODING_UTF8).getStr()); + if ( !pBook ) + m_pConnection->throwGenericSQLException( STR_CANNOT_OPEN_BOOK, *this ); + + m_pVersionHelper->freeContacts(); + bool bExecuteQuery = true; + switch ( _rData.eFilterType ) + { + case eFilterNone: + if ( !m_pVersionHelper->isLocal( pBook ) ) + { + SQLError aErrorFactory; + SQLException aAsException = aErrorFactory.getSQLException( ErrorCondition::DATA_CANNOT_SELECT_UNFILTERED, *this ); + m_aWarnings.appendWarning( SQLWarning( + aAsException.Message, + aAsException.Context, + aAsException.SQLState, + aAsException.ErrorCode, + aAsException.NextException + ) ); + bExecuteQuery = false; + } + break; + case eFilterAlwaysFalse: + bExecuteQuery = false; + break; + case eFilterOther: + bExecuteQuery = true; + break; + } + if ( bExecuteQuery ) + { + OString aPassword = m_pConnection->getPassword(); + m_pVersionHelper->executeQuery(pBook, _rData.getQuery(), aPassword); + m_pConnection->setPassword( aPassword ); + + if ( m_pVersionHelper->hasContacts() && !_rData.aSortOrder.empty() ) + { + ComparisonData aCompData(_rData.aSortOrder); + m_pVersionHelper->sortContacts(aCompData); + } + } + m_nLength = m_pVersionHelper->getNumContacts(); + SAL_INFO("connectivity.evoab2", "Query return " << m_nLength << " records"); + m_nIndex = -1; + + // create our meta data (need the EBookQuery for this) + m_xMetaData = new OEvoabResultSetMetaData( _rData.sTable ); + + m_xMetaData->setEvoabFields( _rData.xSelectColumns ); +} + + +void OEvoabResultSet::disposing() +{ + ::comphelper::OPropertyContainer::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + m_pVersionHelper.reset(); + m_pStatement = nullptr; + m_xMetaData.clear(); +} + +Any SAL_CALL OEvoabResultSet::queryInterface( const Type & rType ) +{ + Any aRet = ::comphelper::OPropertyContainer::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OResultSet_BASE::queryInterface(rType); + return aRet; +} + +Sequence< Type > SAL_CALL OEvoabResultSet::getTypes( ) +{ + return ::comphelper::concatSequences( + OResultSet_BASE::getTypes(), + getBaseTypes() + ); +} + + +// XRow Interface + +/** + * getString: + * @nColumnNum: The column index from the table. + * + * If the equivalent NResultSetMetaData.cxx marks the columntype of + * nColumnNum as DataType::VARCHAR this accessor is used. + */ +OUString SAL_CALL OEvoabResultSet::getString( sal_Int32 nColumnNum ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + OUString aResult; + if ( m_xMetaData.is()) + { + sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(nColumnNum); + GValue aValue = { 0, { { 0 } } }; + if ( getValue( getCur(), nFieldNumber, G_TYPE_STRING, &aValue, m_bWasNull ) ) + aResult = valueToOUString( aValue ); + } + return aResult; +} + +sal_Bool SAL_CALL OEvoabResultSet::getBoolean( sal_Int32 nColumnNum ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + bool bResult = false; + + if ( m_xMetaData.is()) + { + sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(nColumnNum); + GValue aValue = { 0, { { 0 } } }; + if ( getValue( getCur(), nFieldNumber, G_TYPE_BOOLEAN, &aValue, m_bWasNull ) ) + bResult = valueToBool( aValue ); + } + return bResult; +} + +sal_Int64 SAL_CALL OEvoabResultSet::getLong( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getLong", *this ); + return sal_Int64(); +} + +Reference< XArray > SAL_CALL OEvoabResultSet::getArray( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getArray", *this ); + return nullptr; +} + +Reference< XClob > SAL_CALL OEvoabResultSet::getClob( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getClob", *this ); + return nullptr; +} + +Reference< XBlob > SAL_CALL OEvoabResultSet::getBlob( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBlob", *this ); + return nullptr; +} + +Reference< XRef > SAL_CALL OEvoabResultSet::getRef( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getRef", *this ); + return nullptr; +} + +Any SAL_CALL OEvoabResultSet::getObject( sal_Int32 /*nColumnNum*/, const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getObject", *this ); + return Any(); +} + +sal_Int16 SAL_CALL OEvoabResultSet::getShort( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getShort", *this ); + return 0; +} + +css::util::Time SAL_CALL OEvoabResultSet::getTime( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getTime", *this ); + return css::util::Time(); +} + +util::DateTime SAL_CALL OEvoabResultSet::getTimestamp( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getTimestamp", *this ); + return css::util::DateTime(); +} + +Reference< XInputStream > SAL_CALL OEvoabResultSet::getBinaryStream( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBinaryStream", *this ); + return nullptr; +} + +Reference< XInputStream > SAL_CALL OEvoabResultSet::getCharacterStream( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getCharacterStream", *this ); + return nullptr; +} + +sal_Int8 SAL_CALL OEvoabResultSet::getByte( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getByte", *this ); + return 0; +} + +Sequence< sal_Int8 > SAL_CALL OEvoabResultSet::getBytes( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBytes", *this ); + return Sequence< sal_Int8 >(); +} + +css::util::Date SAL_CALL OEvoabResultSet::getDate( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getDate", *this ); + return css::util::Date(); +} + +double SAL_CALL OEvoabResultSet::getDouble( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getDouble", *this ); + return 0; +} + +float SAL_CALL OEvoabResultSet::getFloat( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getFloat", *this ); + return 0; +} + +sal_Int32 SAL_CALL OEvoabResultSet::getInt( sal_Int32 /*nColumnNum*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getInt", *this ); + return 0; +} +// XRow Interface Ends + + +// XResultSetMetaDataSupplier Interface +Reference< XResultSetMetaData > SAL_CALL OEvoabResultSet::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + // the meta data should have been created at construction time + ENSURE_OR_THROW( m_xMetaData.is(), "internal error: no meta data" ); + return m_xMetaData.get(); +} +// XResultSetMetaDataSupplier Interface Ends + + +// XResultSet Interface +sal_Bool SAL_CALL OEvoabResultSet::next( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + if (m_nIndex+1 < m_nLength) { + ++m_nIndex ; + return true; + } + else + return false; +} + +sal_Bool SAL_CALL OEvoabResultSet::wasNull( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_bWasNull; +} + +sal_Bool SAL_CALL OEvoabResultSet::isBeforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nIndex < 0; +} + +sal_Int32 SAL_CALL OEvoabResultSet::getRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nIndex; +} + +sal_Bool SAL_CALL OEvoabResultSet::isAfterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nIndex >= m_nLength; +} + +sal_Bool SAL_CALL OEvoabResultSet::isFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nIndex == 0; +} + +sal_Bool SAL_CALL OEvoabResultSet::isLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nIndex == m_nLength - 1; +} + +void SAL_CALL OEvoabResultSet::beforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + m_nIndex = -1; +} + +void SAL_CALL OEvoabResultSet::afterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + m_nIndex = m_nLength; +} + + +sal_Bool SAL_CALL OEvoabResultSet::first( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + m_nIndex = 0; + return true; +} + + +sal_Bool SAL_CALL OEvoabResultSet::last( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + m_nIndex = m_nLength - 1; + return true; +} + +sal_Bool SAL_CALL OEvoabResultSet::absolute( sal_Int32 row ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + if (row < m_nLength) { + m_nIndex = row; + return true; + } + else + return false; +} + +sal_Bool SAL_CALL OEvoabResultSet::relative( sal_Int32 row ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if ((m_nIndex+row) < m_nLength) { + m_nIndex += row; + return true; + } + else + return false; +} + +sal_Bool SAL_CALL OEvoabResultSet::previous( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if(m_nIndex > 0) { + m_nIndex--; + return true; + } + else + return false; +} + +Reference< XInterface > SAL_CALL OEvoabResultSet::getStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + css::uno::WeakReferenceHelper aStatement(static_cast<OWeakObject*>(m_pStatement)); + return aStatement.get(); +} + + +sal_Bool SAL_CALL OEvoabResultSet::rowDeleted( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OEvoabResultSet::rowInserted( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OEvoabResultSet::rowUpdated( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +void SAL_CALL OEvoabResultSet::refreshRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); +} +//XResult Interface ends + +// XCancellable + +void SAL_CALL OEvoabResultSet::cancel( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); +} + +//XCloseable +void SAL_CALL OEvoabResultSet::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + } + dispose(); +} + +// XWarningsSupplier + +void SAL_CALL OEvoabResultSet::clearWarnings( ) +{ + m_aWarnings.clearWarnings(); +} + +Any SAL_CALL OEvoabResultSet::getWarnings( ) +{ + return m_aWarnings.getWarnings(); +} + +//XColumnLocate Interface +sal_Int32 SAL_CALL OEvoabResultSet::findColumn( const OUString& columnName ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + // find the first column with the name columnName + 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 +} + +//XColumnLocate interface ends + + +::cppu::IPropertyArrayHelper* OEvoabResultSet::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +::cppu::IPropertyArrayHelper & OEvoabResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +void SAL_CALL OEvoabResultSet::acquire() throw() +{ + OResultSet_BASE::acquire(); +} + +void SAL_CALL OEvoabResultSet::release() throw() +{ + OResultSet_BASE::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL +OEvoabResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + + +} // connectivity::evoab + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NResultSet.hxx b/connectivity/source/drivers/evoab2/NResultSet.hxx new file mode 100644 index 000000000..40b0027cf --- /dev/null +++ b/connectivity/source/drivers/evoab2/NResultSet.hxx @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NRESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NRESULTSET_HXX + +#include <memory> + +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/util/XCancellable.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdbcx/XDeleteRows.hpp> +#include <cppuhelper/compbase.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <connectivity/CommonTools.hxx> +#include <connectivity/FValue.hxx> +#include <connectivity/warningscontainer.hxx> +#include "NStatement.hxx" +#include "NResultSetMetaData.hxx" + +namespace connectivity +{ + namespace evoab + { + struct ComparisonData; + + class OEvoabVersionHelper + { + public: + virtual EBook* openBook(const char *abname) = 0; + virtual void executeQuery (EBook* pBook, EBookQuery* pQuery, OString &rPassword) = 0; + virtual void freeContacts() = 0; + virtual bool isLDAP( EBook *pBook ) = 0; + virtual bool isLocal( EBook *pBook ) = 0; + virtual EContact *getContact(sal_Int32 nIndex) = 0; + virtual sal_Int32 getNumContacts() = 0; + virtual bool hasContacts() = 0; + virtual void sortContacts( const ComparisonData& _rCompData ) = 0; + OString getUserName( EBook *pBook ); + virtual ~OEvoabVersionHelper() {} + }; + + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XResultSet + , css::sdbc::XRow + , css::sdbc::XResultSetMetaDataSupplier + , css::util::XCancellable + , css::sdbc::XWarningsSupplier + , css::sdbc::XCloseable + , css::sdbc::XColumnLocate + , css::lang::XServiceInfo + > OResultSet_BASE; + + + class OEvoabResultSet final : public cppu::BaseMutex + ,public OResultSet_BASE + ,public ::comphelper::OPropertyContainer + ,public ::comphelper::OPropertyArrayUsageHelper<OEvoabResultSet> + { + private: + std::unique_ptr<OEvoabVersionHelper> m_pVersionHelper; + + OCommonStatement* m_pStatement; + OEvoabConnection* m_pConnection; + rtl::Reference<OEvoabResultSetMetaData> m_xMetaData; + ::dbtools::WarningsContainer m_aWarnings; + + bool m_bWasNull; + // <properties> + sal_Int32 m_nFetchSize; + sal_Int32 m_nResultSetType; + sal_Int32 m_nFetchDirection; + sal_Int32 m_nResultSetConcurrency; + // </properties> + + // Data & iteration + sal_Int32 m_nIndex; + sal_Int32 m_nLength; + EContact *getCur() + { + return m_pVersionHelper->getContact(m_nIndex); + } + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + // you can't delete objects of this type + virtual ~OEvoabResultSet() override; + public: + DECLARE_SERVICE_INFO(); + + OEvoabResultSet( OCommonStatement *pStmt, OEvoabConnection *pConnection ); + void construct( const QueryData& _rData ); + + // ::cppu::OComponentHelper + virtual void SAL_CALL disposing() override; + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XResultSet + virtual sal_Bool SAL_CALL next( ) override; + virtual sal_Bool SAL_CALL isBeforeFirst( ) override; + virtual sal_Bool SAL_CALL isAfterLast( ) override; + virtual sal_Bool SAL_CALL isFirst( ) override; + virtual sal_Bool SAL_CALL isLast( ) override; + virtual void SAL_CALL beforeFirst( ) override; + virtual void SAL_CALL afterLast( ) override; + virtual sal_Bool SAL_CALL first( ) override; + virtual sal_Bool SAL_CALL last( ) override; + virtual sal_Int32 SAL_CALL getRow( ) override; + virtual sal_Bool SAL_CALL absolute( sal_Int32 row ) override; + virtual sal_Bool SAL_CALL relative( sal_Int32 rows ) override; + virtual sal_Bool SAL_CALL previous( ) override; + virtual void SAL_CALL refreshRow( ) override; + virtual sal_Bool SAL_CALL rowUpdated( ) override; + virtual sal_Bool SAL_CALL rowInserted( ) override; + virtual sal_Bool SAL_CALL rowDeleted( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getStatement( ) override; + // XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + // XCancellable + virtual void SAL_CALL cancel( ) override; + // XCloseable + virtual void SAL_CALL close( ) override; + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + // XColumnLocate + virtual sal_Int32 SAL_CALL findColumn( const OUString& columnName ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NRESULTSET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NResultSetMetaData.cxx b/connectivity/source/drivers/evoab2/NResultSetMetaData.cxx new file mode 100644 index 000000000..4982cc455 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NResultSetMetaData.cxx @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "NResultSetMetaData.hxx" +#include "NDatabaseMetaData.hxx" +#include <connectivity/dbexception.hxx> +#include <strings.hrc> + +using namespace connectivity::evoab; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; + +OEvoabResultSetMetaData::OEvoabResultSetMetaData(const OUString& _aTableName) + : m_aTableName(_aTableName), + m_aEvoabFields() +{ + +} + +OEvoabResultSetMetaData::~OEvoabResultSetMetaData() +{ +} + +void OEvoabResultSetMetaData::setEvoabFields(const ::rtl::Reference<connectivity::OSQLColumns> &xColumns) +{ + static const char aName[] = "Name"; + + for (const auto& rxColumn : *xColumns) + { + OUString aFieldName; + + rxColumn->getPropertyValue(aName) >>= aFieldName; + guint nFieldNumber = findEvoabField(aFieldName); + if (nFieldNumber == guint(-1)) + { + connectivity::SharedResources aResource; + const OUString sError( aResource.getResourceStringWithSubstitution( + STR_INVALID_COLUMNNAME, + "$columnname$", aFieldName + ) ); + ::dbtools::throwGenericSQLException( sError, *this ); + } + m_aEvoabFields.push_back(nFieldNumber); + } +} + + +sal_Int32 SAL_CALL OEvoabResultSetMetaData::getColumnDisplaySize( sal_Int32 /*nColumnNum*/ ) +{ + return 50; +} + +sal_Int32 SAL_CALL OEvoabResultSetMetaData::getColumnType( sal_Int32 nColumnNum ) +{ + sal_uInt32 nField = m_aEvoabFields[nColumnNum - 1]; + return evoab::getFieldType (nField); +} + +sal_Int32 SAL_CALL OEvoabResultSetMetaData::getColumnCount( ) +{ + return m_aEvoabFields.size(); +} + +sal_Bool SAL_CALL OEvoabResultSetMetaData::isCaseSensitive( sal_Int32 /*nColumnNum*/ ) +{ + return true; +} + +OUString SAL_CALL OEvoabResultSetMetaData::getSchemaName( sal_Int32 /*nColumnNum*/ ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabResultSetMetaData::getColumnName( sal_Int32 nColumnNum ) +{ + sal_uInt32 nField = m_aEvoabFields[nColumnNum - 1]; + return evoab::getFieldName( nField ); +} + +OUString SAL_CALL OEvoabResultSetMetaData::getColumnTypeName( sal_Int32 nColumnNum ) +{ + sal_uInt32 nField = m_aEvoabFields[nColumnNum - 1]; + return evoab::getFieldTypeName( nField ); +} + +OUString SAL_CALL OEvoabResultSetMetaData::getColumnLabel( sal_Int32 nColumnNum ) +{ + sal_uInt32 nField = m_aEvoabFields[nColumnNum - 1]; + const ColumnProperty *pSpecs = getField(nField); + GParamSpec *pSpec = pSpecs->pField; + OUString aLabel; + + if( pSpec ) + aLabel = OStringToOUString( g_param_spec_get_nick( pSpec ), + RTL_TEXTENCODING_UTF8 ); + return aLabel; +} + +OUString SAL_CALL OEvoabResultSetMetaData::getColumnServiceName( sal_Int32 /*nColumnNum*/ ) +{ + return OUString(); +} + +OUString SAL_CALL OEvoabResultSetMetaData::getTableName( sal_Int32 /*nColumnNum*/ ) +{ + return m_aTableName;//OUString("TABLE"); +} + +OUString SAL_CALL OEvoabResultSetMetaData::getCatalogName( sal_Int32 /*nColumnNum*/ ) +{ + return OUString(); +} + + +sal_Bool SAL_CALL OEvoabResultSetMetaData::isCurrency( sal_Int32 /*nColumnNum*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabResultSetMetaData::isAutoIncrement( sal_Int32 /*nColumnNum*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabResultSetMetaData::isSigned( sal_Int32 /*nColumnNum*/ ) +{ + return false; +} + +sal_Int32 SAL_CALL OEvoabResultSetMetaData::getPrecision( sal_Int32 /*nColumnNum*/ ) +{ + return 0; +} + +sal_Int32 SAL_CALL OEvoabResultSetMetaData::getScale( sal_Int32 /*nColumnNum*/ ) +{ + return 0; +} + +sal_Int32 SAL_CALL OEvoabResultSetMetaData::isNullable( sal_Int32 /*nColumnNum*/ ) +{ + return 0; +} + +sal_Bool SAL_CALL OEvoabResultSetMetaData::isSearchable( sal_Int32 /*nColumnNum*/ ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabResultSetMetaData::isReadOnly( sal_Int32 /*nColumnNum*/ ) +{ + return true; +} + +sal_Bool SAL_CALL OEvoabResultSetMetaData::isDefinitelyWritable( sal_Int32 /*nColumnNum*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OEvoabResultSetMetaData::isWritable( sal_Int32 /*nColumnNum*/ ) +{ + return false; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NResultSetMetaData.hxx b/connectivity/source/drivers/evoab2/NResultSetMetaData.hxx new file mode 100644 index 000000000..9c309aadd --- /dev/null +++ b/connectivity/source/drivers/evoab2/NResultSetMetaData.hxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NRESULTSETMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NRESULTSETMETADATA_HXX + +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <cppuhelper/implbase.hxx> +#include "NConnection.hxx" +#include <rtl/ref.hxx> +#include <com/sun/star/connection/XConnection.hpp> +namespace connectivity +{ + namespace evoab + { + + //************ Class: ResultSetMetaData + + typedef ::cppu::WeakImplHelper< css::sdbc::XResultSetMetaData> OResultSetMetaData_BASE; + + class OEvoabResultSetMetaData : public OResultSetMetaData_BASE + { + OUString m_aTableName; + std::vector<sal_Int32> m_aEvoabFields; + + protected: + virtual ~OEvoabResultSetMetaData() override; + public: + explicit OEvoabResultSetMetaData(const OUString& _aTableName); + /// @throws css::sdbc::SQLException + void setEvoabFields(const ::rtl::Reference<connectivity::OSQLColumns> &xColumns); + sal_uInt32 fieldAtColumn(sal_Int32 columnIndex) const + { return m_aEvoabFields[columnIndex - 1]; } + /// Avoid ambiguous cast error from the compiler. + operator css::uno::Reference< css::sdbc::XResultSetMetaData > () throw() + { return this; } + + virtual sal_Int32 SAL_CALL getColumnCount( ) override; + virtual sal_Bool SAL_CALL isAutoIncrement( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCaseSensitive( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSearchable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCurrency( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL isNullable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSigned( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnDisplaySize( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnLabel( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnName( sal_Int32 column ) override; + virtual OUString SAL_CALL getSchemaName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getPrecision( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getScale( sal_Int32 column ) override; + virtual OUString SAL_CALL getTableName( sal_Int32 column ) override; + virtual OUString SAL_CALL getCatalogName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnType( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnTypeName( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isReadOnly( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isWritable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isDefinitelyWritable( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnServiceName( sal_Int32 column ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NRESULTSETMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NServices.cxx b/connectivity/source/drivers/evoab2/NServices.cxx new file mode 100644 index 000000000..0fc8f8d07 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NServices.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "NDriver.hxx" +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <osl/diagnose.h> + +using namespace connectivity::evoab; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(const css::uno::Exception&) + { + OSL_FAIL("Service Creation Exception"); + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* evoab2_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + OEvoabDriver::getImplementationName_Static(), + OEvoabDriver::getSupportedServiceNames_Static(), + OEvoabDriver_CreateInstance, ::cppu::createSingleFactory) + ; + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NStatement.cxx b/connectivity/source/drivers/evoab2/NStatement.cxx new file mode 100644 index 000000000..1616f9fb1 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NStatement.cxx @@ -0,0 +1,632 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <osl/diagnose.h> +#include <rtl/ustring.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 <cppuhelper/typeprovider.hxx> +#include <propertyids.hxx> +#include "NStatement.hxx" +#include "NConnection.hxx" +#include "NDatabaseMetaData.hxx" +#include "NResultSet.hxx" +#include <sqlbison.hxx> +#include <strings.hrc> +#include <connectivity/dbexception.hxx> +#include <tools/diagnose_ex.h> + +namespace connectivity::evoab { + + +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::io; +using namespace com::sun::star::util; + +namespace { + +EBookQuery * createTrue() +{ // Not the world's most efficient unconditional true but ... + return e_book_query_from_string("(exists \"full_name\")"); +} + +EBookQuery * createTest( const OUString &aColumnName, + EBookQueryTest eTest, + const OUString &aMatch ) +{ + OString sMatch = OUStringToOString( aMatch, RTL_TEXTENCODING_UTF8 ); + OString sColumnName = OUStringToOString( aColumnName, RTL_TEXTENCODING_UTF8 ); + + return e_book_query_field_test( e_contact_field_id( sColumnName.getStr() ), + eTest, sMatch.getStr() ); +} + +} + +OCommonStatement::OCommonStatement(OEvoabConnection* _pConnection) + : OCommonStatement_IBase(m_aMutex) + , ::comphelper::OPropertyContainer(OCommonStatement_IBase::rBHelper) + , m_xResultSet(nullptr) + , m_xConnection(_pConnection) + , m_aParser(_pConnection->getDriver().getComponentContext()) + , m_aSQLIterator( _pConnection, _pConnection->createCatalog()->getTables(), m_aParser ) + , 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) +{ +#define REGISTER_PROP( id, member ) \ + registerProperty( \ + OMetaConnection::getPropMap().getNameByIndex( id ), \ + id, \ + 0, \ + &member, \ + cppu::UnoType<decltype(member)>::get() \ + ); + + REGISTER_PROP( PROPERTY_ID_CURSORNAME, m_aCursorName ); + REGISTER_PROP( PROPERTY_ID_MAXFIELDSIZE, m_nMaxFieldSize ); + REGISTER_PROP( PROPERTY_ID_MAXROWS, m_nMaxRows ); + REGISTER_PROP( PROPERTY_ID_QUERYTIMEOUT, m_nQueryTimeOut ); + REGISTER_PROP( PROPERTY_ID_FETCHSIZE, m_nFetchSize ); + REGISTER_PROP( PROPERTY_ID_RESULTSETTYPE, m_nResultSetType ); + REGISTER_PROP( PROPERTY_ID_FETCHDIRECTION, m_nFetchDirection ); + REGISTER_PROP( PROPERTY_ID_ESCAPEPROCESSING, m_bEscapeProcessing ); + REGISTER_PROP( PROPERTY_ID_RESULTSETCONCURRENCY, m_nResultSetConcurrency ); +} + +OCommonStatement::~OCommonStatement() +{ +} + +void OCommonStatement::disposeResultSet() +{ + // free the cursor if alive + Reference< XComponent > xComp(m_xResultSet.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + m_xResultSet.clear(); +} + +void OCommonStatement::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + disposeResultSet(); + + m_xConnection.clear(); + + OCommonStatement_IBase::disposing(); +} + +Any SAL_CALL OCommonStatement::queryInterface( const Type & rType ) +{ + Any aRet = OCommonStatement_IBase::queryInterface(rType); + if(!aRet.hasValue()) + aRet = ::comphelper::OPropertyContainer::queryInterface(rType); + return aRet; +} + +Sequence< Type > SAL_CALL OCommonStatement::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),OCommonStatement_IBase::getTypes()); +} + + +//void SAL_CALL OCommonStatement::cancel( ) throw(RuntimeException) +//{ +//::osl::MutexGuard aGuard( m_aMutex ); +//checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); +//// cancel the current sql statement +//} + + +void SAL_CALL OCommonStatement::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + } + dispose(); +} + +OUString OCommonStatement::impl_getColumnRefColumnName_throw( const OSQLParseNode& _rColumnRef ) +{ + ENSURE_OR_THROW( SQL_ISRULE( &_rColumnRef, column_ref ), "internal error: only column_refs supported as LHS" ); + + OUString sColumnName; + switch ( _rColumnRef.count() ) + { + case 3: // SQL_TOKEN_NAME '.' column_val + { + const OSQLParseNode* pPunct = _rColumnRef.getChild( 1 ); + const OSQLParseNode* pColVal = _rColumnRef.getChild( 2 ); + if ( SQL_ISPUNCTUATION( pPunct, "." ) + && ( pColVal->count() == 1 ) + ) + { + sColumnName = pColVal->getChild( 0 )->getTokenValue(); + } + } + break; + + case 1: // column + { + sColumnName = _rColumnRef.getChild( 0 )->getTokenValue(); + } + break; + } + + if ( !sColumnName.getLength() ) + m_xConnection->throwGenericSQLException( STR_QUERY_TOO_COMPLEX, *this ); + + return sColumnName; +} + + +void OCommonStatement::orderByAnalysis( const OSQLParseNode* _pOrderByClause, SortDescriptor& _out_rSort ) +{ + ENSURE_OR_THROW( _pOrderByClause, "NULL node" ); + ENSURE_OR_THROW( SQL_ISRULE( _pOrderByClause, opt_order_by_clause ), "wrong node type" ); + + _out_rSort.clear(); + + const OSQLParseNode* pOrderList = _pOrderByClause->getByRule( OSQLParseNode::ordering_spec_commalist ); + ENSURE_OR_THROW( pOrderList, "unexpected parse tree structure" ); + + for ( size_t i=0; i<pOrderList->count(); ++i ) + { + const OSQLParseNode* pOrderBy = pOrderList->getChild(i); + if ( !pOrderBy || !SQL_ISRULE( pOrderBy, ordering_spec ) ) + continue; + const OSQLParseNode* pColumnRef = pOrderBy->count() == 2 ? pOrderBy->getChild(0) : nullptr; + const OSQLParseNode* pAscDesc = pOrderBy->count() == 2 ? pOrderBy->getChild(1) : nullptr; + ENSURE_OR_THROW( + ( pColumnRef != nullptr ) + && ( pAscDesc != nullptr ) + && SQL_ISRULE( pAscDesc, opt_asc_desc ) + && ( pAscDesc->count() < 2 ), + "ordering_spec structure error" ); + + // column name -> column field + if ( !SQL_ISRULE( pColumnRef, column_ref ) ) + m_xConnection->throwGenericSQLException( STR_SORT_BY_COL_ONLY, *this ); + const OUString sColumnName( impl_getColumnRefColumnName_throw( *pColumnRef ) ); + guint nField = evoab::findEvoabField( sColumnName ); + // ascending/descending? + bool bAscending = true; + if ( ( pAscDesc->count() == 1 ) + && SQL_ISTOKEN( pAscDesc->getChild( 0 ), DESC ) + ) + bAscending = false; + + _out_rSort.push_back( FieldSort( nField, bAscending ) ); + } +} + + +EBookQuery *OCommonStatement::whereAnalysis( const OSQLParseNode* parseTree ) +{ + EBookQuery *pResult = nullptr; + + ENSURE_OR_THROW( parseTree, "invalid parse tree" ); + + // Nested brackets + if( parseTree->count() == 3 && + SQL_ISPUNCTUATION( parseTree->getChild( 0 ), "(" ) && + SQL_ISPUNCTUATION( parseTree->getChild( 2 ), ")" ) ) + { + pResult = whereAnalysis( parseTree->getChild( 1 ) ); + } + + // SQL AND, OR + else if( ( SQL_ISRULE( parseTree, search_condition ) || + SQL_ISRULE( parseTree, boolean_term ) ) && + parseTree->count() == 3 ) + { + ENSURE_OR_THROW( SQL_ISTOKEN( parseTree->getChild( 1 ), OR ) + || SQL_ISTOKEN( parseTree->getChild( 1 ), AND ), + "unexpected search_condition structure" ); + + EBookQuery *pArgs[2]; + pArgs[0] = whereAnalysis( parseTree->getChild( 0 ) ); + pArgs[1] = whereAnalysis( parseTree->getChild( 2 ) ); + + if( SQL_ISTOKEN( parseTree->getChild( 1 ), OR ) ) + pResult = e_book_query_or( 2, pArgs, true ); + else + pResult = e_book_query_and( 2, pArgs, true ); + } + // SQL =, != + else if( SQL_ISRULE( parseTree, comparison_predicate ) ) + { + OSQLParseNode *pPrec = parseTree->getChild( 1 ); + + ENSURE_OR_THROW( parseTree->count() == 3, "unexpected comparison_predicate structure" ); + + OSQLParseNode* pLHS = parseTree->getChild( 0 ); + OSQLParseNode* pRHS = parseTree->getChild( 2 ); + + if ( ( ! SQL_ISRULE( pLHS, column_ref ) // on the LHS, we accept a column or a constant int value + && ( pLHS->getNodeType() != SQLNodeType::IntNum ) + ) + || ( ( pRHS->getNodeType() != SQLNodeType::String ) // on the RHS, certain literals are acceptable + && ( pRHS->getNodeType() != SQLNodeType::IntNum ) + && ( pRHS->getNodeType() != SQLNodeType::ApproxNum ) + && ! SQL_ISTOKEN( pRHS, TRUE ) + && ! SQL_ISTOKEN( pRHS, FALSE ) + ) + || ( ( pLHS->getNodeType() == SQLNodeType::IntNum ) // an int on LHS requires an int on RHS + && ( pRHS->getNodeType() != SQLNodeType::IntNum ) + ) + ) + { + m_xConnection->throwGenericSQLException( STR_QUERY_TOO_COMPLEX, *this ); + } + + if ( ( pPrec->getNodeType() != SQLNodeType::Equal ) + && ( pPrec->getNodeType() != SQLNodeType::NotEqual ) + ) + { + m_xConnection->throwGenericSQLException( STR_OPERATOR_TOO_COMPLEX, *this ); + } + + // recognize the special "0 = 1" condition + if ( ( pLHS->getNodeType() == SQLNodeType::IntNum ) + && ( pRHS->getNodeType() == SQLNodeType::IntNum ) + && ( pPrec->getNodeType() == SQLNodeType::Equal ) + ) + { + const sal_Int32 nLHS = pLHS->getTokenValue().toInt64(); + const sal_Int32 nRHS = pRHS->getTokenValue().toInt64(); + return ( nLHS == nRHS ) ? createTrue() : nullptr; + } + + OUString aColumnName( impl_getColumnRefColumnName_throw( *pLHS ) ); + + OUString aMatchString; + if ( pRHS->isToken() ) + aMatchString = pRHS->getTokenValue(); + else + aMatchString = pRHS->getChild( 0 )->getTokenValue(); + + pResult = createTest( aColumnName, E_BOOK_QUERY_IS, aMatchString ); + + if ( pResult && ( pPrec->getNodeType() == SQLNodeType::NotEqual ) ) + pResult = e_book_query_not( pResult, true ); + } + // SQL like + else if( SQL_ISRULE( parseTree, like_predicate ) ) + { + ENSURE_OR_THROW( parseTree->count() == 2, "unexpected like_predicate structure" ); + const OSQLParseNode* pPart2 = parseTree->getChild(1); + + if( ! SQL_ISRULE( parseTree->getChild( 0 ), column_ref) ) + m_xConnection->throwGenericSQLException(STR_QUERY_INVALID_LIKE_COLUMN,*this); + + OUString aColumnName( impl_getColumnRefColumnName_throw( *parseTree->getChild( 0 ) ) ); + + OSQLParseNode *pAtom = pPart2->getChild( pPart2->count() - 2 ); // Match String + bool bNotLike = pPart2->getChild(0)->isToken(); + + if( !( pAtom->getNodeType() == SQLNodeType::String || + pAtom->getNodeType() == SQLNodeType::Name || + SQL_ISRULE( pAtom,parameter ) || + ( pAtom->getChild( 0 ) && pAtom->getChild( 0 )->getNodeType() == SQLNodeType::Name ) || + ( pAtom->getChild( 0 ) && pAtom->getChild( 0 )->getNodeType() == SQLNodeType::String ) ) ) + { + SAL_INFO( + "connectivity.evoab2", + "analyseSQL : pAtom->count() = " << pAtom->count()); + m_xConnection->throwGenericSQLException(STR_QUERY_INVALID_LIKE_STRING,*this); + } + + const sal_Unicode WILDCARD = '%'; + + OUString aMatchString = pAtom->getTokenValue(); + + // Determine where '%' character is... + if( aMatchString == OUStringChar(WILDCARD) ) + { + // String containing only a '%' and nothing else matches everything + pResult = createTest( aColumnName, E_BOOK_QUERY_CONTAINS, + "" ); + } + else if( aMatchString.indexOf( WILDCARD ) == -1 ) + { // Simple string , eg. "to match" "contains in evo" + SAL_INFO( "connectivity.evoab2", "Plain contains '" << aMatchString << "'" ); + pResult = createTest( aColumnName, E_BOOK_QUERY_CONTAINS, aMatchString ); + if( pResult && bNotLike ) + pResult = e_book_query_not( pResult, true ); + } + else if( bNotLike ) + { + // We currently can't handle a 'NOT LIKE' when there are '%' + m_xConnection->throwGenericSQLException(STR_QUERY_NOT_LIKE_TOO_COMPLEX,*this); + } + else if( aMatchString.indexOf ( WILDCARD ) == aMatchString.lastIndexOf ( WILDCARD ) ) + { // One occurrence of '%' matches... + if ( aMatchString.startsWith(OUStringChar(WILDCARD)) ) + pResult = createTest( aColumnName, E_BOOK_QUERY_ENDS_WITH, aMatchString.copy( 1 ) ); + else if ( aMatchString.indexOf ( WILDCARD ) == aMatchString.getLength() - 1 ) + pResult = createTest( aColumnName, E_BOOK_QUERY_BEGINS_WITH, aMatchString.copy( 0, aMatchString.getLength() - 1 ) ); + else + m_xConnection->throwGenericSQLException(STR_QUERY_LIKE_WILDCARD,*this); + } + else if( aMatchString.getLength() >= 3 && + aMatchString.startsWith(OUStringChar(WILDCARD)) && + aMatchString.indexOf ( WILDCARD, 1) == aMatchString.getLength() - 1 ) { + // one '%' at the start and another at the end + pResult = createTest( aColumnName, E_BOOK_QUERY_CONTAINS, aMatchString.copy (1, aMatchString.getLength() - 2) ); + } + else + m_xConnection->throwGenericSQLException(STR_QUERY_LIKE_WILDCARD_MANY,*this); + } + + return pResult; +} + +OUString OCommonStatement::getTableName() const +{ + OUString aTableName; + + if( m_pParseTree && m_aSQLIterator.getStatementType() == OSQLStatementType::Select ) + { + Any aCatalog; + OUString aSchema; + const OSQLParseNode *pSelectStmnt = m_aSQLIterator.getParseTree(); + const OSQLParseNode *pAllTableNames = pSelectStmnt->getChild( 3 )->getChild( 0 )->getChild( 1 ); + + if( OSQLParseTreeIterator::isTableNode( pAllTableNames->getChild( 0 ) ) ) + OSQLParseNode::getTableComponents( pAllTableNames->getChild( 0 ), + aCatalog,aSchema, aTableName,nullptr ); + + else if( SQL_ISRULE( pAllTableNames->getChild( 0 ), table_ref ) ) + { + OSQLParseNode *pNodeForTableName = pAllTableNames->getChild( 0 )->getChild( 0 ); + if( OSQLParseTreeIterator::isTableNode( pNodeForTableName ) ) + { + aTableName = OSQLParseNode::getTableRange(pAllTableNames->getChild( 0 )); + if( !aTableName.getLength() ) + OSQLParseNode::getTableComponents( pNodeForTableName, aCatalog, aSchema, aTableName,nullptr); + } + else + OSL_FAIL( "odd table layout" ); + } + else + OSL_FAIL( "unusual table layout" ); + } + return aTableName; +} + +void OCommonStatement::parseSql( const OUString& sql, QueryData& _out_rQueryData ) +{ + SAL_INFO( "connectivity.evoab2", "parsing " << sql ); + + _out_rQueryData.eFilterType = eFilterOther; + + OUString aErr; + m_pParseTree = m_aParser.parseTree( aErr, sql ).release(); + m_aSQLIterator.setParseTree( m_pParseTree ); + m_aSQLIterator.traverseAll(); + + _out_rQueryData.sTable = getTableName(); + + // to be sorted? + const OSQLParseNode* pOrderByClause = m_aSQLIterator.getOrderTree(); + if ( pOrderByClause ) + { + #if OSL_DEBUG_LEVEL > 1 + OUString sTreeDebug; + pOrderByClause->showParseTree( sTreeDebug ); + SAL_INFO( "connectivity.evoab2", "found order-by tree:\n" << sTreeDebug ); + #endif + orderByAnalysis( pOrderByClause, _out_rQueryData.aSortOrder ); + } + + const OSQLParseNode* pWhereClause = m_aSQLIterator.getWhereTree(); + if ( pWhereClause && SQL_ISRULE( pWhereClause, where_clause ) ) + { + #if OSL_DEBUG_LEVEL > 1 + OUString sTreeDebug; + pWhereClause->showParseTree( sTreeDebug ); + SAL_INFO( "connectivity.evoab2", "found where tree:\n" << sTreeDebug ); + #endif + EBookQuery* pQuery = whereAnalysis( pWhereClause->getChild( 1 ) ); + if ( !pQuery ) + { + _out_rQueryData.eFilterType = eFilterAlwaysFalse; + pQuery = createTrue(); + } + _out_rQueryData.setQuery( pQuery ); + } + else + { + _out_rQueryData.eFilterType = eFilterNone; + _out_rQueryData.setQuery( createTrue() ); + } +} + + +Reference< XConnection > SAL_CALL OStatement::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + // just return our connection here + return impl_getConnection(); +} + + +Any SAL_CALL OCommonStatement::getWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + + return makeAny(SQLWarning()); +} + + +void SAL_CALL OCommonStatement::clearWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + +} + +::cppu::IPropertyArrayHelper* OCommonStatement::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +::cppu::IPropertyArrayHelper & OCommonStatement::getInfoHelper() +{ + return *getArrayHelper(); +} + + +void SAL_CALL OCommonStatement::acquire() throw() +{ + OCommonStatement_IBase::acquire(); +} + +void SAL_CALL OCommonStatement::release() throw() +{ + OCommonStatement_IBase::release(); +} + + +QueryData OCommonStatement::impl_getEBookQuery_throw( const OUString& _rSql ) +{ + QueryData aData; + parseSql( _rSql, aData ); + +#if OSL_DEBUG_LEVEL > 1 + char *pSexpr = aData.getQuery() ? e_book_query_to_string( aData.getQuery() ) : g_strdup( "<map failed>" ); + g_message( "Parsed SQL to sexpr '%s'\n", pSexpr ); + g_free( pSexpr ); +#endif + + if ( !aData.getQuery() ) + m_xConnection->throwGenericSQLException( STR_QUERY_TOO_COMPLEX, *this ); + + // a postcondition of this method is that we properly determined the SELECT columns + aData.xSelectColumns = m_aSQLIterator.getSelectColumns(); + if ( !aData.xSelectColumns.is() ) + m_xConnection->throwGenericSQLException( STR_QUERY_TOO_COMPLEX, *this ); + + return aData; +} + + +Reference< XResultSet > OCommonStatement::impl_executeQuery_throw( const QueryData& _rQueryData ) +{ + // create result set + OEvoabResultSet* pResult = new OEvoabResultSet( this, m_xConnection.get() ); + Reference< XResultSet > xRS = pResult; + pResult->construct( _rQueryData ); + + // done + m_xResultSet = xRS; + return xRS; +} + + +Reference< XResultSet > OCommonStatement::impl_executeQuery_throw( const OUString& _rSql ) +{ + SAL_INFO( "connectivity.evoab2", "OCommonStatement::impl_executeQuery_throw " << _rSql ); + +#if OSL_DEBUG_LEVEL > 1 + g_message( "Parse SQL '%s'\n", + OUStringToOString(_rSql, RTL_TEXTENCODING_UTF8).getStr() ); +#endif + + return impl_executeQuery_throw( impl_getEBookQuery_throw( _rSql ) ); +} + + +Reference< XPropertySetInfo > SAL_CALL OCommonStatement::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() ); +} + + +// = OStatement + + +IMPLEMENT_SERVICE_INFO( OStatement, "com.sun.star.comp.sdbcx.evoab.OStatement", "com.sun.star.sdbc.Statement" ); + + +IMPLEMENT_FORWARD_XINTERFACE2( OStatement, OCommonStatement, OStatement_IBase ) + + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( OStatement, OCommonStatement, OStatement_IBase ) + + +sal_Bool SAL_CALL OStatement::execute( const OUString& _sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + Reference< XResultSet > xRS = impl_executeQuery_throw( _sql ); + return xRS.is(); +} + + +Reference< XResultSet > SAL_CALL OStatement::executeQuery( const OUString& _sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + + return impl_executeQuery_throw( _sql ); +} + + +sal_Int32 SAL_CALL OStatement::executeUpdate( const OUString& /*sql*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed); + ::dbtools::throwFeatureNotImplementedSQLException( "XStatement::executeUpdate", *this ); + return 0; +} + +} // namespace ::connectivity::evoab + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NStatement.hxx b/connectivity/source/drivers/evoab2/NStatement.hxx new file mode 100644 index 000000000..14c12700c --- /dev/null +++ b/connectivity/source/drivers/evoab2/NStatement.hxx @@ -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 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NSTATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NSTATEMENT_HXX + +#include <com/sun/star/sdbc/XStatement.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbc/XMultipleResults.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <comphelper/proparrhlp.hxx> +#include <cppuhelper/implbase2.hxx> +#include <cppuhelper/basemutex.hxx> +#include <comphelper/uno3.hxx> +#include <connectivity/CommonTools.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <connectivity/sqliterator.hxx> +#include <connectivity/sqlparse.hxx> +#include <connectivity/FValue.hxx> +#include <com/sun/star/util/XCancellable.hpp> +#include <cppuhelper/compbase.hxx> +#include <comphelper/propertycontainer.hxx> + +#include "EApi.h" +#include "NConnection.hxx" + +#include <list> + +namespace connectivity +{ + namespace evoab + { + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XWarningsSupplier + , css::sdbc::XCloseable + > OCommonStatement_IBase; + + struct FieldSort + { + sal_Int32 nField; + bool bAscending; + + FieldSort( const sal_Int32 _nField, const bool _bAscending ) : nField( _nField ), bAscending( _bAscending ) { } + }; + typedef std::vector< FieldSort > SortDescriptor; + + enum QueryFilterType + { + eFilterAlwaysFalse, + eFilterNone, + eFilterOther + }; + + class EBookQueryWrapper + { + private: + EBookQuery* mpQuery; + public: + EBookQueryWrapper() + : mpQuery(nullptr) + { + } + EBookQueryWrapper(const EBookQueryWrapper& rhs) + : mpQuery(rhs.mpQuery) + { + if (mpQuery) + e_book_query_ref(mpQuery); + } + EBookQueryWrapper(EBookQueryWrapper&& rhs) noexcept + : mpQuery(rhs.mpQuery) + { + rhs.mpQuery = nullptr; + } + void reset(EBookQuery* pQuery) + { + if (mpQuery) + e_book_query_unref(mpQuery); + mpQuery = pQuery; + if (mpQuery) + e_book_query_ref(mpQuery); + } + EBookQueryWrapper& operator=(const EBookQueryWrapper& rhs) + { + if (this != &rhs) + reset(rhs.mpQuery); + return *this; + } + EBookQueryWrapper& operator=(EBookQueryWrapper&& rhs) + { + if (mpQuery) + e_book_query_unref(mpQuery); + mpQuery = rhs.mpQuery; + rhs.mpQuery = nullptr; + return *this; + } + ~EBookQueryWrapper() + { + if (mpQuery) + e_book_query_unref(mpQuery); + } + EBookQuery* getQuery() const + { + return mpQuery; + } + }; + + struct QueryData + { + private: + EBookQueryWrapper aQuery; + + public: + OUString sTable; + QueryFilterType eFilterType; + rtl::Reference<connectivity::OSQLColumns> xSelectColumns; + SortDescriptor aSortOrder; + + QueryData() + : sTable() + , eFilterType( eFilterOther ) + , xSelectColumns() + , aSortOrder() + { + } + + EBookQuery* getQuery() const { return aQuery.getQuery(); } + void setQuery(EBookQuery* pQuery) { aQuery.reset(pQuery); } + }; + + //************ Class: OCommonStatement + // is a base class for the normal statement and for the prepared statement + + class OCommonStatement :public cppu::BaseMutex + ,public OCommonStatement_IBase + ,public ::comphelper::OPropertyContainer + ,public ::comphelper::OPropertyArrayUsageHelper< OCommonStatement > + { + private: + css::uno::WeakReference< css::sdbc::XResultSet> m_xResultSet; // The last ResultSet created + rtl::Reference<OEvoabConnection> m_xConnection; + connectivity::OSQLParser m_aParser; + connectivity::OSQLParseTreeIterator m_aSQLIterator; + connectivity::OSQLParseNode *m_pParseTree; + + // <properties> + OUString m_aCursorName; + sal_Int32 m_nMaxFieldSize; + sal_Int32 m_nMaxRows; + sal_Int32 m_nQueryTimeOut; + sal_Int32 m_nFetchSize; + sal_Int32 m_nResultSetType; + sal_Int32 m_nFetchDirection; + sal_Int32 m_nResultSetConcurrency; + bool m_bEscapeProcessing; + // </properties> + + protected: + + void disposeResultSet(); + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + virtual ~OCommonStatement() override; + + protected: + void parseSql( const OUString& sql, QueryData& _out_rQueryData ); + EBookQuery *whereAnalysis( const OSQLParseNode* parseTree ); + void orderByAnalysis( const OSQLParseNode* _pOrderByClause, SortDescriptor& _out_rSort ); + OUString getTableName() const; + + public: + + // other methods + OEvoabConnection* getOwnConnection() const { return m_xConnection.get(); } + + using OCommonStatement_IBase::operator css::uno::Reference< css::uno::XInterface >; + + protected: + explicit OCommonStatement( OEvoabConnection* _pConnection ); + + // OComponentHelper + virtual void SAL_CALL disposing() override; + // XInterface + virtual void SAL_CALL release() throw() override; + virtual void SAL_CALL acquire() throw() override; + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + + // XCloseable + virtual void SAL_CALL close( ) override; + + protected: + /** will return the EBookQuery representing the statement WHERE condition, or throw + + Also, all statement dependent members (such as the parser/iterator) will be inited afterwards. + */ + QueryData + impl_getEBookQuery_throw( const OUString& _rSql ); + + css::uno::Reference< css::sdbc::XResultSet > + impl_executeQuery_throw( const OUString& _rSql ); + + css::uno::Reference< css::sdbc::XResultSet > + impl_executeQuery_throw( const QueryData& _rData ); + + css::uno::Reference< css::sdbc::XConnection > + impl_getConnection() { return css::uno::Reference< css::sdbc::XConnection >( m_xConnection.get() ); } + + OUString + impl_getColumnRefColumnName_throw( const ::connectivity::OSQLParseNode& _rColumnRef ); + }; + + typedef ::cppu::ImplHelper2 < css::lang::XServiceInfo + , css::sdbc::XStatement + > OStatement_IBase; + class OStatement :public OCommonStatement + ,public OStatement_IBase + { + protected: + virtual ~OStatement() override {} + + public: + explicit OStatement( OEvoabConnection* _pConnection) + :OCommonStatement( _pConnection) + { + } + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XServiceInfo + DECLARE_SERVICE_INFO(); + + // XStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( const OUString& sql ) override ; + virtual sal_Int32 SAL_CALL executeUpdate( const OUString& sql ) override ; + virtual sal_Bool SAL_CALL execute( const OUString& sql ) override ; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override ; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NSTATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NTable.cxx b/connectivity/source/drivers/evoab2/NTable.cxx new file mode 100644 index 000000000..b5c652a8f --- /dev/null +++ b/connectivity/source/drivers/evoab2/NTable.cxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "NTable.hxx" +#include "NColumns.hxx" + +#include <com/sun/star/sdbc/XRow.hpp> + +using namespace connectivity; +using namespace ::comphelper; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace connectivity::evoab; + +OEvoabTable::OEvoabTable( sdbcx::OCollection* _pTables, + OEvoabConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName + ) : OEvoabTable_TYPEDEF(_pTables,true, + Name, + Type, + Description, + SchemaName, + CatalogName), + m_pConnection(_pConnection) +{ + construct(); +} + +void OEvoabTable::refreshColumns() +{ + ::std::vector< OUString> aVector; + + if (!isNew()) + { + 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 = new OEvoabColumns(this,m_aMutex,aVector); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NTable.hxx b/connectivity/source/drivers/evoab2/NTable.hxx new file mode 100644 index 000000000..eb1159847 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NTable.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NTABLE_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NTABLE_HXX + +#include "NConnection.hxx" +#include <connectivity/sdbcx/VTable.hxx> + +namespace connectivity +{ + namespace evoab + { + typedef connectivity::sdbcx::OTable OEvoabTable_TYPEDEF; + + class OEvoabTable : public OEvoabTable_TYPEDEF + { + OEvoabConnection* m_pConnection; + + public: + OEvoabTable( sdbcx::OCollection* _pTables, + OEvoabConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description, + const OUString& SchemaName, + const OUString& CatalogName + ); + + OEvoabConnection* getConnection() { return m_pConnection;} + + virtual void refreshColumns() override; + + OUString const & getTableName() const { return m_Name; } + OUString const & getSchema() const { return m_SchemaName; } + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NTABLE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NTables.cxx b/connectivity/source/drivers/evoab2/NTables.cxx new file mode 100644 index 000000000..b7a844fac --- /dev/null +++ b/connectivity/source/drivers/evoab2/NTables.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 "NTables.hxx" +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include "NCatalog.hxx" +#include <comphelper/types.hxx> +#include "NTable.hxx" +using namespace ::comphelper; + +using namespace ::cppu; +using namespace connectivity::evoab; +using namespace connectivity::sdbcx; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace dbtools; + +ObjectType OEvoabTables::createObject(const OUString& aName) +{ + Sequence< OUString > aTypes { "TABLE" }; + + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(),"%",aName,aTypes); + + ObjectType xRet; + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + if(xResult->next()) // there can be only one table with this name + { + OEvoabTable* pRet = new OEvoabTable( + this, + static_cast<OEvoabCatalog&>(m_rParent).getConnection(), + aName, + xRow->getString(4), + xRow->getString(5), + "", + ""); + xRet = pRet; + } + } + + ::comphelper::disposeComponent(xResult); + + return xRet; +} + +void OEvoabTables::impl_refresh( ) +{ + static_cast<OEvoabCatalog&>(m_rParent).refreshTables(); +} + +void OEvoabTables::disposing() +{ + m_xMetaData.clear(); + OCollection::disposing(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/NTables.hxx b/connectivity/source/drivers/evoab2/NTables.hxx new file mode 100644 index 000000000..99542ba53 --- /dev/null +++ b/connectivity/source/drivers/evoab2/NTables.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NTABLES_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NTABLES_HXX + +#include <connectivity/sdbcx/VCollection.hxx> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +namespace connectivity +{ + namespace evoab + { + class OEvoabTables : public sdbcx::OCollection + { + css::uno::Reference< css::sdbc::XDatabaseMetaData > m_xMetaData; + protected: + virtual sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override; + public: + OEvoabTables(const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _rMetaData, + ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector) : + sdbcx::OCollection(_rParent,true,_rMutex,_rVector), + m_xMetaData(_rMetaData) + {} + virtual void disposing() override; + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_EVOAB2_NTABLES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/evoab2/evoab.component b/connectivity/source/drivers/evoab2/evoab.component new file mode 100644 index 000000000..6bd8cb5e3 --- /dev/null +++ b/connectivity/source/drivers/evoab2/evoab.component @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="evoab2" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.evoab.OEvoabDriver"> + <service name="com.sun.star.sdbc.Driver"/> + </implementation> +</component> 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..e703112f7 --- /dev/null +++ b/connectivity/source/drivers/file/FColumns.cxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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) + { + sdbcx::OColumn* pRet = 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); + xRet = pRet; + 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..5c56ce4d5 --- /dev/null +++ b/connectivity/source/drivers/file/FConnection.cxx @@ -0,0 +1,435 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <cppuhelper/typeprovider.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/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); + { + 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); + + + OPreparedStatement* pStmt = new OPreparedStatement(this); + Reference< XPreparedStatement > xHoldAlive = pStmt; + 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 (isUnoTunnelId<OConnection>(rId)) + ? reinterpret_cast< sal_Int64 >( this ) + : sal_Int64(0); +} + +Sequence< sal_Int8 > OConnection::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +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..3d01216a3 --- /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 <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 +{ + sal_Int16 isCaseSensitiveParentFolder( const OUString& _rFolderOrDoc, const OUString& _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; + } +} + + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTables( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& tableNamePattern, const Sequence< OUString >& types ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTables ); + Reference< XResultSet > xRef = pResult; + + // check if any type is given + // when no types are given then we have to return all tables e.g. TABLE + + static const char aTable[] = "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 xRef; + + 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 bKnowCaseSensivity = 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 ( !bKnowCaseSensivity ) + { + bKnowCaseSensivity = true; + sal_Int16 nCase = isCaseSensitiveParentFolder( m_pConnection->getURL(), aURL.getName() ); + switch( nCase ) + { + case 1: + bCaseSensitiveDir = true; + break; + case -1: + bKnowCaseSensivity = false; + [[fallthrough]]; + case 0: + bCaseSensitiveDir = false; + } + if ( bKnowCaseSensivity ) + { + 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(aRows); + + return xRef; +} + +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 ); + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTablePrivileges ); + Reference< XResultSet > xRef = pResult; + 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::getUnoTunnelImplementation<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(aRows); + return xRef; +} + +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 ); + + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTableTypes ); + Reference< XResultSet > xRef = pResult; + static ODatabaseMetaDataResultSet::ORows aRows; + if(aRows.empty()) + { + ODatabaseMetaDataResultSet::ORow aRow; + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(OUString("TABLE"))); + aRows.push_back(aRow); + } + pResult->setRows(aRows); + return xRef; +} + +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..5f9d14418 --- /dev/null +++ b/connectivity/source/drivers/file/FDateFunctions.cxx @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <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; + 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; + 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; + 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; + 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; + 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; + 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; + 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]; + Date aDate(aD.Day,aD.Month,aD.Year); + + sal_Int16 nStartDay = SUNDAY; + if ( nSize == 2 && !lhs[0].isNull() ) + nStartDay = lhs[0]; + + 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; + return aD.Year; +} + +ORowSetValue OOp_Hour::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + css::util::Time aT = lhs; + 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; + 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; + 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..c2956adc2 --- /dev/null +++ b/connectivity/source/drivers/file/FDriver.cxx @@ -0,0 +1,227 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/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(); +} + +// static ServiceInfo + +OUString OFileDriver::getImplementationName_Static( ) +{ + return "com.sun.star.sdbc.driver.file.Driver"; +} + +Sequence< OUString > OFileDriver::getSupportedServiceNames_Static( ) +{ + return { "com.sun.star.sdbc.Driver", "com.sun.star.sdbcx.Driver" }; +} + + +OUString SAL_CALL OFileDriver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL OFileDriver::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + + +Sequence< OUString > SAL_CALL OFileDriver::getSupportedServiceNames( ) +{ + return getSupportedServiceNames_Static(); +} + + +Reference< XConnection > SAL_CALL OFileDriver::connect( const OUString& url, const Sequence< PropertyValue >& info ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODriver_BASE::rBHelper.bDisposed); + + OConnection* pCon = new OConnection(this); + Reference< XConnection > xCon = pCon; + pCon->construct(url,info); + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return xCon; +} + +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) ) + { + std::vector< DriverPropertyInfo > aDriverInfo; + + Sequence< OUString > aBoolean(2); + aBoolean[0] = "0"; + aBoolean[1] = "1"; + + aDriverInfo.push_back(DriverPropertyInfo( + "CharSet" + ,"CharSet of the database." + ,false + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "Extension" + ,"Extension of the file format." + ,false + ,".*" + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "ShowDeleted" + ,"Display inactive records." + ,false + ,"0" + ,aBoolean) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "EnableSQL92Check" + ,"Use SQL92 naming constraints." + ,false + ,"0" + ,aBoolean) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "UseRelativePath" + ,"Handle the connection url as relative path." + ,false + ,"0" + ,aBoolean) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "URL" + ,"The URL of the database document which is used to create an absolute path." + ,false + ,OUString() + ,Sequence< OUString >()) + ); + return Sequence< DriverPropertyInfo >(aDriverInfo.data(),aDriverInfo.size()); + } // 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); + + Reference< XTablesSupplier > xTab; + Reference< css::lang::XUnoTunnel> xTunnel(connection,UNO_QUERY); + if(xTunnel.is()) + { + OConnection* pSearchConnection = reinterpret_cast< OConnection* >( xTunnel->getSomething(OConnection::getUnoTunnelId()) ); + OConnection* pConnection = nullptr; + for (auto const& elem : m_xConnections) + { + if (static_cast<OConnection*>( Reference< XConnection >::query(elem.get().get()).get() ) == pSearchConnection) + { + pConnection = pSearchConnection; + break; + } + } + + if(pConnection) + xTab = pConnection->createCatalog(); + } + return xTab; +} + + +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..7c26081da --- /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(static_cast<sal_Int32>((*_rRow)[0]->getValue())); + + 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..d2188cbbc --- /dev/null +++ b/connectivity/source/drivers/file/FNumericFunctions.cxx @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <file/FNumericFunctions.hxx> +#include <rtl/math.hxx> + +using namespace connectivity; +using namespace connectivity::file; + +static const double fPi = 3.14159265358979323846; + +ORowSetValue OOp_Abs::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nVal(lhs); + 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); + 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(static_cast<double>(lhs),static_cast<double>(rhs)); +} + +ORowSetValue OOp_Floor::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return floor(static_cast<double>(lhs)); +} + +ORowSetValue OOp_Ceiling::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nVal(lhs); + 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]; + + sal_Int32 nDec = 0; + if ( nSize == 2 && !lhs[0].isNull() ) + nDec = lhs[0]; + return ::rtl::math::round(nVal,nDec); +} + +ORowSetValue OOp_Exp::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nVal(lhs); + return exp(nVal); +} + +ORowSetValue OOp_Ln::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() || static_cast<double>(lhs) < 0.0 ) + return lhs; + + double nVal(lhs); + 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( static_cast<double>(lhs[nSize-1]) ); + + + if ( nSize == 2 && !lhs[0].isNull() ) + nVal /= log(static_cast<double>(lhs[0])); + + if ( std::isnan(nVal) ) + return ORowSetValue(); + return nVal; +} + +ORowSetValue OOp_Log10::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() || static_cast<double>(lhs) < 0.0 ) + return lhs; + + double nVal = log(static_cast<double>(lhs)); + 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(static_cast<double>(lhs),static_cast<double>(rhs)); +} + +ORowSetValue OOp_Sqrt::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nVal = sqrt(static_cast<double>(lhs)); + if ( std::isnan(nVal) ) + return ORowSetValue(); + return nVal; +} + +ORowSetValue OOp_Pi::operate(const std::vector<ORowSetValue>& /*lhs*/) const +{ + return fPi; +} + +ORowSetValue OOp_Cos::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return cos(static_cast<double>(lhs)); +} + +ORowSetValue OOp_Sin::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return sin(static_cast<double>(lhs)); +} + +ORowSetValue OOp_Tan::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return tan(static_cast<double>(lhs)); +} + +ORowSetValue OOp_ACos::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return acos(static_cast<double>(lhs)); +} + +ORowSetValue OOp_ASin::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return asin(static_cast<double>(lhs)); +} + +ORowSetValue OOp_ATan::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + return atan(static_cast<double>(lhs)); +} + +ORowSetValue OOp_ATan2::operate(const ORowSetValue& lhs,const ORowSetValue& rhs) const +{ + if ( lhs.isNull() || rhs.isNull() ) + return lhs; + + return atan2(static_cast<double>(lhs),static_cast<double>(rhs)); +} + +ORowSetValue OOp_Degrees::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nLhs = lhs; + return nLhs*180*(1.0/fPi); +} + +ORowSetValue OOp_Radians::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + double nLhs = lhs; + return nLhs*fPi*(1.0/180.0); +} + + +/* 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..fc5b81923 --- /dev/null +++ b/connectivity/source/drivers/file/FPreparedStatement.cxx @@ -0,0 +1,554 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <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 = xResultSet.get(); + 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.get(); +} + + +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 Reference< XConnection >(m_pConnection.get()); +} + + +Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + return makeResultSet().get(); +} + + +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)) ); +} + +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() throw() +{ + OStatement_BASE2::acquire(); +} + +void SAL_CALL OPreparedStatement::release() throw() +{ + OStatement_BASE2::release(); +} + +void OPreparedStatement::checkAndResizeParameters(sal_Int32 parameterIndex) +{ + ::connectivity::checkDisposed(OStatement_BASE::rBHelper.bDisposed); + if ( m_aAssignValues.is() && (parameterIndex < 1 || parameterIndex >= static_cast<sal_Int32>(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..d7f3d6bd2 --- /dev/null +++ b/connectivity/source/drivers/file/FResultSet.cxx @@ -0,0 +1,1595 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <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> + +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(const char* 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(); + + Reference<XComponent> xComp = m_pTable.get(); + if ( xComp.is() ) + xComp->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 bool(getValue(columnIndex)); +} + + +sal_Int8 SAL_CALL OResultSet::getByte( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + + +Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + + +css::util::Date SAL_CALL OResultSet::getDate( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + + +double SAL_CALL OResultSet::getDouble( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + + +float SAL_CALL OResultSet::getFloat( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + + +sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + + +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()); +} + + +sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + + +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); +} + +OUString SAL_CALL OResultSet::getString( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + +css::util::Time SAL_CALL OResultSet::getTime( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + +css::util::DateTime SAL_CALL OResultSet::getTimestamp( sal_Int32 columnIndex ) +{ + return getValue(columnIndex); +} + + +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(); + m_pFileSet->push_back(nPos); + *(*m_aInsertRow)[0] = sal_Int32(m_pFileSet->size()); + clearInsertRow(); + + m_aSkipDeletedSet.insertNewPosition((*m_aRow)[0]->getValue()); + } +} + +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] = static_cast<sal_Int32>((*m_aRow)[0]->getValue()); + + 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 = static_cast<sal_Int32>((*m_aRow)[0]->getValue()); + 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(static_cast<sal_Int32>((*m_aEvaluateRow)[0]->getValue())); + 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 && m_nRowPos >= static_cast<sal_Int32>(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(eKeyType,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(); + 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; +} + +Sequence< sal_Int8 > OResultSet::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OResultSet::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OResultSet>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : 0; +} + +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&) + { + SAL_WARN( "connectivity.drivers","OResultSet::setBoundedColumns: caught an Exception!"); + } + } + // 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() throw() +{ + OResultSet_BASE::acquire(); +} + +void SAL_CALL OResultSet::release() throw() +{ + 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 = reinterpret_cast< OFileTable* >(xTunnel->getSomething(OFileTable::getUnoTunnelId())); + 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(); +} + +bool OResultSet::isRowDeleted() const +{ + return m_aRow->isDeleted(); +} + +void SAL_CALL OResultSet::disposing( const EventObject& Source ) +{ + Reference<XPropertySet> xProp = m_pTable.get(); + 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..f68a06532 --- /dev/null +++ b/connectivity/source/drivers/file/FResultSetMetaData.cxx @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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> + + +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 || column > static_cast<sal_Int32>(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..08c4f7fdd --- /dev/null +++ b/connectivity/source/drivers/file/FStatement.cxx @@ -0,0 +1,710 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <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() throw() +{ + OStatement_BASE::acquire(); +} + +void SAL_CALL OStatement_BASE2::release() throw() +{ + 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 makeAny(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(); +} + +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() throw() +{ + OStatement_BASE2::acquire(); +} + +void SAL_CALL OStatement::release() throw() +{ + 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; + OResultSet* pResult = createResultSet(); + xRS = pResult; + initializeResultSet(pResult); + m_xResultSet = xRS; + + pResult->OpenImpl(); + + return xRS; +} + +Reference< XConnection > SAL_CALL OStatement::getConnection( ) +{ + return Reference< XConnection >(m_pConnection.get()); +} + +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::anylizeSQL() +{ + OSL_ENSURE(m_pSQLAnalyzer,"OResultSet::anylizeSQL: 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; + default: + break; + } + + // at this moment we support only one table per select statement + Reference< css::lang::XUnoTunnel> xTunnel(rTabs.begin()->second,UNO_QUERY); + if(xTunnel.is()) + { + m_pTable = reinterpret_cast<OFileTable*>(xTunnel->getSomething(OFileTable::getUnoTunnelId())); + } + 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()) ); + + anylizeSQL(); +} + +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(m_aOrderbyColumnNumber); + _pResult->setOrderByAscending(m_aOrderbyAscending); + _pResult->setBindingRow(m_aRow); + _pResult->setColumnMapping(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(); + const OUString* pBegin = aNames.getConstArray(); + const OUString* pEnd = pBegin + aNames.getLength(); + for (; pBegin != pEnd; ++pBegin) + aColumnNameList.push_back(*pBegin); + } + 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..32f590e31 --- /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 <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,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; + 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>(static_cast<sal_Int32>(*aIter)); + + 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->operator OUString()); + } + + 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]) + 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 && static_cast<sal_Int32>(lhs[0]) >= sal_Int32(0) ) + return lhs[1].getString().copy(static_cast<sal_Int32>(lhs[0])-1); + + else if ( lhs.size() != 3 || static_cast<sal_Int32>(lhs[1]) < sal_Int32(0)) + return ORowSetValue(); + + return lhs[2].getString().copy(static_cast<sal_Int32>(lhs[1])-1,lhs[0]); +} + +ORowSetValue OOp_LTrim::operate(const ORowSetValue& lhs) const +{ + if ( lhs.isNull() ) + return lhs; + + OUString sRet = lhs; + 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; + 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; + + const char c = ' '; + OUStringBuffer sRet; + sal_Int32 nCount = lhs; + for (sal_Int32 i=0; i < nCount; ++i) + { + sRet.appendAscii(&c,1); + } + return sRet.makeStringAndClear(); +} + +ORowSetValue OOp_Replace::operate(const std::vector<ORowSetValue>& lhs) const +{ + if ( lhs.size() != 3 ) + return ORowSetValue(); + + OUString sStr = lhs[2]; + OUString sFrom = lhs[1]; + OUString sTo = lhs[0]; + 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; + + OUStringBuffer sRet; + sal_Int32 nCount = rhs; + for (sal_Int32 i=0; i < nCount; ++i) + { + sRet.append(lhs.operator OUString()); + } + return sRet.makeStringAndClear(); +} + +ORowSetValue OOp_Insert::operate(const std::vector<ORowSetValue>& lhs) const +{ + if ( lhs.size() != 4 ) + return ORowSetValue(); + + OUString sStr = lhs[3]; + + sal_Int32 nStart = static_cast<sal_Int32>(lhs[2]); + if ( nStart < 1 ) + nStart = 1; + return sStr.replaceAt(nStart-1,static_cast<sal_Int32>(lhs[1]),lhs[0]); +} + +ORowSetValue OOp_Left::operate(const ORowSetValue& lhs,const ORowSetValue& rhs) const +{ + if ( lhs.isNull() || rhs.isNull() ) + return lhs; + + OUString sRet = lhs; + sal_Int32 nCount = rhs; + 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; + OUString sRet = lhs; + 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..b47bfb811 --- /dev/null +++ b/connectivity/source/drivers/file/FTable.cxx @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <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 <cppuhelper/typeprovider.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 = 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(); +} + +Sequence< sal_Int8 > OFileTable::getUnoTunnelId() +{ + static ::cppu::OImplementationId s_Id; + + return s_Id.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OFileTable::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return isUnoTunnelId<OFileTable>(rId) + ? reinterpret_cast< sal_Int64 >( this ) + : OTable_TYPEDEF::getSomething(rId); +} + +void OFileTable::FileClose() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + if (m_pFileStream && m_pFileStream->IsWritable()) + m_pFileStream->Flush(); + + 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..36cba4a73 --- /dev/null +++ b/connectivity/source/drivers/file/fcode.cxx @@ -0,0 +1,391 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/sqlparse.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(OSQLParseNode const * pNode, sal_Int32 _nPos) + : OOperandRow(static_cast<sal_uInt16>(_nPos), DataType::VARCHAR) // Standard-Type +{ + OSL_ENSURE(SQL_ISRULE(pNode,parameter),"Argument is not a parameter"); + OSL_ENSURE(pNode->count() > 0,"Error in Parse Tree"); + OSQLParseNode *pMark = pNode->getChild(0); + + OUString aParameterName; + if (SQL_ISPUNCTUATION(pMark, "?")) + aParameterName = "?"; + else if (SQL_ISPUNCTUATION(pMark, ":")) + aParameterName = pNode->getChild(1)->getTokenValue(); + else + { + SAL_WARN( "connectivity.drivers","Error in Parse Tree"); + } + + // set up Parameter-Column with default type, can be specified more precisely later using Describe-Parameter + + // save Identity (not especially necessary here, just for the sake of symmetry) + + // todo + // OColumn* pColumn = new OFILEColumn(aParameterName,eDBType,255,0,SQL_FLAGS_NULLALLOWED); + // rParamColumns->AddColumn(pColumn); + + // the value will be set just before the evaluation +} + + +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, sRH = aRH; + 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 ,m = aRH; + + 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(), pRight->getValue()))); + 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..134565e3e --- /dev/null +++ b/connectivity/source/drivers/file/fcomp.cxx @@ -0,0 +1,890 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.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 <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(static_cast<double>(pOb1->getValue())); + pOb2->setValue(static_cast<double>(pOb2->getValue())); + break; + case DataType::FLOAT: + pOb1->setValue(static_cast<float>(pOb1->getValue())); + pOb2->setValue(static_cast<float>(pOb2->getValue())); + break; + case DataType::DOUBLE: + case DataType::REAL: + pOb1->setValue(static_cast<double>(pOb1->getValue())); + pOb2->setValue(static_cast<double>(pOb2->getValue())); + break; + case DataType::DATE: + pOb1->setValue(static_cast<util::Date>(pOb1->getValue())); + pOb2->setValue(static_cast<util::Date>(pOb2->getValue())); + break; + case DataType::TIME: + pOb1->setValue(static_cast<util::Time>(pOb1->getValue())); + pOb2->setValue(static_cast<util::Time>(pOb2->getValue())); + break; + case DataType::TIMESTAMP: + pOb1->setValue(static_cast<util::DateTime>(pOb1->getValue())); + pOb2->setValue(static_cast<util::DateTime>(pOb2->getValue())); + 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 &) + { + OSL_FAIL("OPredicateCompiler::execute_Operand Exception"); + } + } + else if (SQL_ISRULE(pPredicateNode,parameter)) + { + pOperand = new OOperandParam(pPredicateNode, ++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) +{ + static bool bResult; + + 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"); + + 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: */ diff --git a/connectivity/source/drivers/firebird/Blob.cxx b/connectivity/source/drivers/firebird/Blob.cxx new file mode 100644 index 000000000..8ed9fc4a8 --- /dev/null +++ b/connectivity/source/drivers/firebird/Blob.cxx @@ -0,0 +1,388 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "Blob.hxx" +#include "Util.hxx" + +#include <com/sun/star/io/BufferSizeExceededException.hpp> +#include <com/sun/star/io/NotConnectedException.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <connectivity/CommonTools.hxx> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <tools/diagnose_ex.h> + +using namespace ::connectivity::firebird; + +using namespace ::cppu; +using namespace ::osl; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; + +Blob::Blob(isc_db_handle* pDatabaseHandle, + isc_tr_handle* pTransactionHandle, + ISC_QUAD const & aBlobID): + Blob_BASE(m_aMutex), + m_pDatabaseHandle(pDatabaseHandle), + m_pTransactionHandle(pTransactionHandle), + m_blobID(aBlobID), +#if SAL_TYPES_SIZEOFPOINTER == 8 + m_blobHandle(0), +#else + m_blobHandle(nullptr), +#endif + m_bBlobOpened(false), + m_nBlobLength(0), + m_nMaxSegmentSize(0), + m_nBlobPosition(0) +{ +} + +void Blob::ensureBlobIsOpened() +{ + MutexGuard aGuard(m_aMutex); + + if (m_bBlobOpened) + return; + + ISC_STATUS aErr; + aErr = isc_open_blob2(m_statusVector, + m_pDatabaseHandle, + m_pTransactionHandle, + &m_blobHandle, + &m_blobID, + 0, + nullptr); + + if (aErr) + evaluateStatusVector(m_statusVector, "isc_open_blob2", *this); + + m_bBlobOpened = true; + m_nBlobPosition = 0; + + char aBlobItems[] = { + isc_info_blob_total_length, + isc_info_blob_max_segment + }; + + // Assuming a data (e.g. length of blob) is maximum 64 bit. + // That means we need 8 bytes for data + 2 for length of data + 1 for item + // identifier for each item. + char aResultBuffer[11 + 11]; + + aErr = isc_blob_info(m_statusVector, + &m_blobHandle, + sizeof(aBlobItems), + aBlobItems, + sizeof(aResultBuffer), + aResultBuffer); + + if (aErr) + evaluateStatusVector(m_statusVector, "isc_blob_info", *this); + + char* pIt = aResultBuffer; + while( *pIt != isc_info_end ) // info is in clusters + { + char item = *pIt++; + short aResultLength = static_cast<short>(isc_vax_integer(pIt, 2)); + + pIt += 2; + switch(item) + { + case isc_info_blob_total_length: + m_nBlobLength = isc_vax_integer(pIt, aResultLength); + break; + case isc_info_blob_max_segment: + m_nMaxSegmentSize = isc_vax_integer(pIt, aResultLength); + break; + default: + assert(false); + break; + } + pIt += aResultLength; + } +} + +sal_uInt16 Blob::getMaximumSegmentSize() +{ + ensureBlobIsOpened(); + + return m_nMaxSegmentSize; +} + +bool Blob::readOneSegment(uno::Sequence< sal_Int8 >& rDataOut) +{ + checkDisposed(Blob_BASE::rBHelper.bDisposed); + ensureBlobIsOpened(); + + sal_uInt16 nMaxSize = getMaximumSegmentSize(); + + if(rDataOut.getLength() < nMaxSize) + rDataOut.realloc(nMaxSize); + + sal_uInt16 nActualSize = 0; + ISC_STATUS aRet = isc_get_segment(m_statusVector, + &m_blobHandle, + &nActualSize, + nMaxSize, + reinterpret_cast<char*>(rDataOut.getArray()) ); + + if (aRet && aRet != isc_segstr_eof && IndicatesError(m_statusVector)) + { + OUString sError(StatusVectorToString(m_statusVector, "isc_get_segment")); + throw IOException(sError, *this); + } + m_nBlobPosition += nActualSize; + return aRet == isc_segstr_eof; // last segment read +} + + +void Blob::closeBlob() +{ + MutexGuard aGuard(m_aMutex); + + if (!m_bBlobOpened) + return; + + ISC_STATUS aErr; + aErr = isc_close_blob(m_statusVector, + &m_blobHandle); + if (aErr) + evaluateStatusVector(m_statusVector, "isc_close_blob", *this); + + m_bBlobOpened = false; +#if SAL_TYPES_SIZEOFPOINTER == 8 + m_blobHandle = 0; +#else + m_blobHandle = nullptr; +#endif +} + +void SAL_CALL Blob::disposing() +{ + try + { + closeBlob(); + } + catch (const SQLException &) + { + // we cannot throw any exceptions here... + TOOLS_WARN_EXCEPTION("connectivity.firebird", "isc_close_blob failed"); + assert(false); + } + Blob_BASE::disposing(); +} + +sal_Int64 SAL_CALL Blob::length() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Blob_BASE::rBHelper.bDisposed); + ensureBlobIsOpened(); + + return m_nBlobLength; +} + +uno::Sequence< sal_Int8 > SAL_CALL Blob::getBytes(sal_Int64 nPosition, + sal_Int32 nBytes) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Blob_BASE::rBHelper.bDisposed); + ensureBlobIsOpened(); + + if (nPosition > m_nBlobLength || nPosition < 1) + throw lang::IllegalArgumentException("nPosition out of range", *this, 0); + // We only have to read as many bytes as are available, i.e. nPosition+nBytes + // can legally be greater than the total length, hence we don't bother to check. + + if (nPosition -1 < m_nBlobPosition) + { + // Resets to the beginning (we can't seek these blobs) + closeBlob(); + ensureBlobIsOpened(); + } + + // nPosition is indexed from 1. + skipBytes(nPosition - m_nBlobPosition -1 ); + + // Don't bother preallocating: readBytes does the appropriate calculations + // and reallocates for us. + uno::Sequence< sal_Int8 > aBytes; + readBytes(aBytes, nBytes); + return aBytes; +} + +uno::Reference< XInputStream > SAL_CALL Blob::getBinaryStream() +{ + return this; +} + +sal_Int64 SAL_CALL Blob::position(const uno::Sequence< sal_Int8 >& /*rPattern*/, + sal_Int64 /*nStart*/) +{ + ::dbtools::throwFeatureNotImplementedSQLException("Blob::position", *this); + return 0; +} + +sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& /*rPattern*/, + sal_Int64 /*aStart*/) +{ + ::dbtools::throwFeatureNotImplementedSQLException("Blob::positionOfBlob", *this); + return 0; +} + +// ---- XInputStream ---------------------------------------------------------- + +sal_Int32 SAL_CALL Blob::readBytes(uno::Sequence< sal_Int8 >& rDataOut, + sal_Int32 nBytes) +{ + MutexGuard aGuard(m_aMutex); + + try + { + checkDisposed(Blob_BASE::rBHelper.bDisposed); + ensureBlobIsOpened(); + } + catch (const NotConnectedException&) + { + throw; + } + catch (const BufferSizeExceededException&) + { + throw; + } + catch (const IOException&) + { + throw; + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception& e) + { + css::uno::Any a(cppu::getCaughtException()); + throw css::lang::WrappedTargetRuntimeException( + "wrapped Exception " + e.Message, + css::uno::Reference<css::uno::XInterface>(), a); + } + + // Ensure we have enough space for the amount of data we can actually read. + const sal_Int64 nBytesAvailable = m_nBlobLength - m_nBlobPosition; + const sal_Int32 nBytesToRead = std::min<sal_Int64>(nBytes, nBytesAvailable); + + if (rDataOut.getLength() < nBytesToRead) + rDataOut.realloc(nBytesToRead); + + sal_Int32 nTotalBytesRead = 0; + ISC_STATUS aErr; + while (nTotalBytesRead < nBytesToRead) + { + sal_uInt16 nBytesRead = 0; + sal_uInt64 nDataRemaining = nBytesToRead - nTotalBytesRead; + sal_uInt16 nReadSize = std::min<sal_uInt64>(nDataRemaining, SAL_MAX_UINT16); + aErr = isc_get_segment(m_statusVector, + &m_blobHandle, + &nBytesRead, + nReadSize, + reinterpret_cast<char*>(rDataOut.getArray()) + nTotalBytesRead); + if (aErr && IndicatesError(m_statusVector)) + { + OUString sError(StatusVectorToString(m_statusVector, "isc_get_segment")); + throw IOException(sError, *this); + } + nTotalBytesRead += nBytesRead; + m_nBlobPosition += nBytesRead; + } + + return nTotalBytesRead; +} + +sal_Int32 SAL_CALL Blob::readSomeBytes(uno::Sequence< sal_Int8 >& rDataOut, + sal_Int32 nMaximumBytes) +{ + // We don't have any way of verifying how many bytes are immediately available, + // hence we just pass through direct to readBytes + // (Spec: "reads the available number of bytes, at maximum nMaxBytesToRead.") + return readBytes(rDataOut, nMaximumBytes); +} + +void SAL_CALL Blob::skipBytes(sal_Int32 nBytesToSkip) +{ + // There is no way of directly skipping, hence we have to pretend to skip + // by reading & discarding the data. + uno::Sequence< sal_Int8 > aBytes; + readBytes(aBytes, nBytesToSkip); +} + +sal_Int32 SAL_CALL Blob::available() +{ + MutexGuard aGuard(m_aMutex); + + try + { + checkDisposed(Blob_BASE::rBHelper.bDisposed); + ensureBlobIsOpened(); + } + catch (const NotConnectedException&) + { + throw; + } + catch (const IOException&) + { + throw; + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception& e) + { + css::uno::Any a(cppu::getCaughtException()); + throw css::lang::WrappedTargetRuntimeException( + "wrapped Exception " + e.Message, + css::uno::Reference<css::uno::XInterface>(), a); + } + + return m_nBlobLength - m_nBlobPosition; +} + +void SAL_CALL Blob::closeInput() +{ + try + { + closeBlob(); + } + catch (const NotConnectedException&) + { + throw; + } + catch (const IOException&) + { + throw; + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception& e) + { + css::uno::Any a(cppu::getCaughtException()); + throw css::lang::WrappedTargetRuntimeException( + "wrapped Exception " + e.Message, + css::uno::Reference<css::uno::XInterface>(), a); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Blob.hxx b/connectivity/source/drivers/firebird/Blob.hxx new file mode 100644 index 000000000..0a3627de4 --- /dev/null +++ b/connectivity/source/drivers/firebird/Blob.hxx @@ -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/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_BLOB_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_BLOB_HXX + +#include <ibase.h> + +#include <cppuhelper/compbase.hxx> + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/sdbc/XBlob.hpp> + +namespace connectivity +{ + namespace firebird + { + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XBlob, + css::io::XInputStream > + Blob_BASE; + + class Blob : + public Blob_BASE + { + protected: + ::osl::Mutex m_aMutex; + + isc_db_handle* m_pDatabaseHandle; + isc_tr_handle* m_pTransactionHandle; + // We store our own copy of the blob id as typically the statement + // manages its own blob id, and blobs are independent of a statement + // in firebird. + ISC_QUAD m_blobID; + isc_blob_handle m_blobHandle; + + bool m_bBlobOpened; + sal_Int64 m_nBlobLength; + sal_uInt16 m_nMaxSegmentSize; + sal_Int64 m_nBlobPosition; + + ISC_STATUS_ARRAY m_statusVector; + + /// @throws css::sdbc::SQLException + void ensureBlobIsOpened(); + /** + * Closes the blob and cleans up resources -- can be used to reset + * the blob if we e.g. want to read from the beginning again. + * + * @throws css::sdbc::SQLException + */ + void closeBlob(); + sal_uInt16 getMaximumSegmentSize(); + + public: + Blob(isc_db_handle* pDatabaseHandle, + isc_tr_handle* pTransactionHandle, + ISC_QUAD const & aBlobID); + + bool readOneSegment(css::uno::Sequence< sal_Int8 >& rDataOut); + + // ---- XBlob ---------------------------------------------------- + virtual sal_Int64 SAL_CALL + length() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL + getBytes(sal_Int64 aPosition, sal_Int32 aLength) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL + getBinaryStream() override; + virtual sal_Int64 SAL_CALL + position(const css::uno::Sequence< sal_Int8 >& rPattern, + sal_Int64 aStart) override; + virtual sal_Int64 SAL_CALL + positionOfBlob(const css::uno::Reference< css::sdbc::XBlob >& rPattern, + sal_Int64 aStart) override; + + // ---- XInputStream ---------------------------------------------- + virtual sal_Int32 SAL_CALL + readBytes(css::uno::Sequence< sal_Int8 >& rDataOut, + sal_Int32 nBytes) override; + virtual sal_Int32 SAL_CALL + readSomeBytes(css::uno::Sequence< sal_Int8 >& rDataOut, + sal_Int32 nMaximumBytes) override; + virtual void SAL_CALL + skipBytes(sal_Int32 nBytes) override; + virtual sal_Int32 SAL_CALL + available() override; + virtual void SAL_CALL + closeInput() override; + + // ---- OComponentHelper ------------------------------------------ + virtual void SAL_CALL disposing() override; + }; + } + +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_BLOB_HXX +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Catalog.cxx b/connectivity/source/drivers/firebird/Catalog.cxx new file mode 100644 index 000000000..f5969f31c --- /dev/null +++ b/connectivity/source/drivers/firebird/Catalog.cxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "Catalog.hxx" +#include "Tables.hxx" +#include "Users.hxx" + +#include <com/sun/star/sdbc/XRow.hpp> + +using namespace ::connectivity::firebird; +using namespace ::com::sun::star; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; + +Catalog::Catalog(const uno::Reference< XConnection >& rConnection): + OCatalog(rConnection), + m_xConnection(rConnection) +{ +} + +//----- OCatalog ------------------------------------------------------------- +void Catalog::refreshTables() +{ + Sequence< OUString > aTypes(2); + aTypes[0] = "TABLE"; + aTypes[1] = "VIEW"; + + uno::Reference< XResultSet > xTables = m_xMetaData->getTables(Any(), + "%", + "%", + aTypes); + + if (!xTables.is()) + return; + + ::std::vector< OUString> aTableNames; + + fillNames(xTables, aTableNames); + + if (!m_pTables) + m_pTables.reset( new Tables(m_xConnection->getMetaData(), + *this, + m_aMutex, + aTableNames) ); + else + m_pTables->reFill(aTableNames); + +} + +void Catalog::refreshViews() +{ + // TODO: implement me. + // Sets m_pViews (OCatalog) +} + +//----- IRefreshableGroups --------------------------------------------------- +void Catalog::refreshGroups() +{ + // TODO: implement me +} + +//----- IRefreshableUsers ---------------------------------------------------- +void Catalog::refreshUsers() +{ + OUString const sSql("SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES"); + + Reference<XStatement> xStmt= m_xMetaData->getConnection()->createStatement(); + uno::Reference< XResultSet > xUsers = xStmt->executeQuery(sSql); + + if (!xUsers.is()) + return; + + ::std::vector< OUString> aUserNames; + + uno::Reference< XRow > xRow(xUsers,UNO_QUERY); + while (xUsers->next()) + { + aUserNames.push_back(xRow->getString(1)); + } + + if (!m_pUsers) + m_pUsers.reset( new Users(m_xConnection->getMetaData(), + *this, + m_aMutex, + aUserNames) ); + else + m_pUsers->reFill(aUserNames); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Catalog.hxx b/connectivity/source/drivers/firebird/Catalog.hxx new file mode 100644 index 000000000..b3c9ae4fe --- /dev/null +++ b/connectivity/source/drivers/firebird/Catalog.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CATALOG_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CATALOG_HXX + +#include <sdbcx/VCatalog.hxx> + +namespace connectivity +{ + namespace firebird + { + class Catalog: public ::connectivity::sdbcx::OCatalog + { + css::uno::Reference< css::sdbc::XConnection > + m_xConnection; + + public: + explicit Catalog(const css::uno::Reference< css::sdbc::XConnection >& rConnection); + + // OCatalog + virtual void refreshTables() override; + virtual void refreshViews() override; + + // IRefreshableGroups + virtual void refreshGroups() override; + + // IRefreshableUsers + virtual void refreshUsers() override; + }; + } // namespace firebird +} // namespace connectivity + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CATALOG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Clob.cxx b/connectivity/source/drivers/firebird/Clob.cxx new file mode 100644 index 000000000..2038dfc11 --- /dev/null +++ b/connectivity/source/drivers/firebird/Clob.cxx @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <string_view> + +#include "Clob.hxx" +#include "Blob.hxx" + +#include <connectivity/CommonTools.hxx> +#include <connectivity/dbexception.hxx> + +using namespace ::connectivity::firebird; + +using namespace ::osl; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; + +Clob::Clob(isc_db_handle* pDatabaseHandle, + isc_tr_handle* pTransactionHandle, + ISC_QUAD const & aBlobID): + Clob_BASE(m_aMutex), + m_aBlob(new connectivity::firebird::Blob(pDatabaseHandle, pTransactionHandle, aBlobID)), + m_nCharCount(-1) +{ +} + +void SAL_CALL Clob::disposing() +{ + m_aBlob->dispose(); + m_aBlob.clear(); + Clob_BASE::disposing(); +} + +sal_Int64 SAL_CALL Clob::length() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Clob_BASE::rBHelper.bDisposed); + + if( m_nCharCount >= 0 ) + return m_nCharCount; + m_nCharCount = 0; + + // Read each segment, and calculate it's size by interpreting it as a + // character stream. Assume that no characters are split by the segments. + bool bLastSegmRead = false; + do + { + uno::Sequence < sal_Int8 > aSegmentBytes; + bLastSegmRead = m_aBlob->readOneSegment( aSegmentBytes ); + OUString sSegment ( reinterpret_cast< char *>( aSegmentBytes.getArray() ), + aSegmentBytes.getLength(), + RTL_TEXTENCODING_UTF8 ); + + if( !bLastSegmRead) + m_nCharCount += sSegment.getLength(); + }while( !bLastSegmRead ); + + m_aBlob->closeInput(); // reset position + return m_nCharCount; +} + +OUString SAL_CALL Clob::getSubString(sal_Int64 nPosition, + sal_Int32 nLength) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Clob_BASE::rBHelper.bDisposed); + // TODO do not reset position if it is not necessary + m_aBlob->closeInput(); // reset position + + OUStringBuffer sSegmentBuffer; + sal_Int64 nActPos = 1; + sal_Int32 nActLen = 0; + + // skip irrelevant parts + while( nActPos < nPosition ) + { + uno::Sequence < sal_Int8 > aSegmentBytes; + bool bLastRead = m_aBlob->readOneSegment( aSegmentBytes ); + if( bLastRead ) + throw lang::IllegalArgumentException("nPosition out of range", *this, 0); + + OUString sSegment ( reinterpret_cast< char *>( aSegmentBytes.getArray() ), + aSegmentBytes.getLength(), + RTL_TEXTENCODING_UTF8 ); + sal_Int32 nStrLen = sSegment.getLength(); + nActPos += nStrLen; + if( nActPos > nPosition ) + { + sal_Int32 nCharsToCopy = static_cast<sal_Int32>(nActPos - nPosition); + if( nCharsToCopy > nLength ) + nCharsToCopy = nLength; + // append relevant part of first segment + sSegmentBuffer.append( std::u16string_view(sSegment).substr(0, nCharsToCopy) ); + nActLen += sSegmentBuffer.getLength(); + } + } + + // read nLength characters + while( nActLen < nLength ) + { + uno::Sequence < sal_Int8 > aSegmentBytes; + bool bLastRead = m_aBlob->readOneSegment( aSegmentBytes ); + + OUString sSegment ( reinterpret_cast< char *>( aSegmentBytes.getArray() ), + aSegmentBytes.getLength(), + RTL_TEXTENCODING_UTF8 ); + sal_Int32 nStrLen = sSegment.getLength(); + if( nActLen + nStrLen > nLength ) + sSegmentBuffer.append(std::u16string_view(sSegment).substr(0, nLength - nActLen)); + else + sSegmentBuffer.append(sSegment); + nActLen += nStrLen; + + if( bLastRead && nActLen < nLength ) + throw lang::IllegalArgumentException("out of range", *this, 0); + } + + return sSegmentBuffer.makeStringAndClear(); +} + +uno::Reference< XInputStream > SAL_CALL Clob::getCharacterStream() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Clob_BASE::rBHelper.bDisposed); + + return m_aBlob->getBinaryStream(); +} + +sal_Int64 SAL_CALL Clob::position(const OUString& /*rPattern*/, + sal_Int32 /*nStart*/) +{ + ::dbtools::throwFeatureNotImplementedSQLException("Clob::position", *this); + return 0; +} + +sal_Int64 SAL_CALL Clob::positionOfClob(const Reference <XClob >& /*rPattern*/, + sal_Int64 /*aStart*/) +{ + ::dbtools::throwFeatureNotImplementedSQLException("Blob::positionOfBlob", *this); + return 0; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Clob.hxx b/connectivity/source/drivers/firebird/Clob.hxx new file mode 100644 index 000000000..626b284d3 --- /dev/null +++ b/connectivity/source/drivers/firebird/Clob.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CLOB_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CLOB_HXX + +#include "Blob.hxx" + +#include <cppuhelper/compbase.hxx> + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/sdbc/XClob.hpp> +#include <rtl/ref.hxx> + +namespace connectivity +{ + namespace firebird + { + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XClob > + Clob_BASE; + + class Clob : + public Clob_BASE + { + protected: + ::osl::Mutex m_aMutex; + + /* + * In Firebird Clob (textual Blob) is a subtype of blob, + * hence we store the data in a Blob, and the Clob class is + * a wrapper around that. + */ + rtl::Reference<connectivity::firebird::Blob> m_aBlob; + + sal_Int64 m_nCharCount; + + public: + Clob(isc_db_handle* pDatabaseHandle, + isc_tr_handle* pTransactionHandle, + ISC_QUAD const & aBlobID); + + // ---- XClob ---------------------------------------------------- + virtual sal_Int64 SAL_CALL + length() override; + virtual OUString SAL_CALL + getSubString(sal_Int64 aPosition, sal_Int32 aLength) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL + getCharacterStream() override; + virtual sal_Int64 SAL_CALL + position(const OUString& rPattern, + sal_Int32 aStart) override; + virtual sal_Int64 SAL_CALL + positionOfClob(const ::css::uno::Reference< ::css::sdbc::XClob >& rPattern, + sal_Int64 aStart) override; + // ---- OComponentHelper ------------------------------------------ + virtual void SAL_CALL disposing() override; + }; + } + +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CLOB_HXX +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Column.cxx b/connectivity/source/drivers/firebird/Column.cxx new file mode 100644 index 000000000..aa8abf9bb --- /dev/null +++ b/connectivity/source/drivers/firebird/Column.cxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "Column.hxx" + +#include <TConnection.hxx> + +using namespace connectivity; +using namespace connectivity::firebird; +using namespace connectivity::sdbcx; + +Column::Column() + : OColumn( true ) // case sensitive +{ + construct(); +} + +void Column::construct() +{ + m_sAutoIncrement = "GENERATED BY DEFAULT AS IDENTITY"; + registerProperty(OMetaConnection::getPropMap().getNameByIndex( + PROPERTY_ID_AUTOINCREMENTCREATION), + PROPERTY_ID_AUTOINCREMENTCREATION, + 0, + &m_sAutoIncrement, + cppu::UnoType<decltype(m_sAutoIncrement)>::get() + ); +} + +::cppu::IPropertyArrayHelper* Column::createArrayHelper( sal_Int32 /*_nId*/ ) const +{ + return doCreateArrayHelper(); +} + +::cppu::IPropertyArrayHelper & SAL_CALL Column::getInfoHelper() +{ + return *Column_PROP::getArrayHelper(isNew() ? 1 : 0); +} + +css::uno::Sequence< OUString > SAL_CALL Column::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdbc.Firebird" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Column.hxx b/connectivity/source/drivers/firebird/Column.hxx new file mode 100644 index 000000000..b72203da8 --- /dev/null +++ b/connectivity/source/drivers/firebird/Column.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_HCOLUMN_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_HCOLUMN_HXX + +#include <connectivity/sdbcx/VColumn.hxx> + +namespace connectivity +{ + namespace firebird + { + class Column; + typedef ::comphelper::OIdPropertyArrayUsageHelper<Column> Column_PROP; + class Column : public sdbcx::OColumn, + public Column_PROP + { + OUString m_sAutoIncrement; + protected: + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( sal_Int32 _nId) const override; + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + public: + Column(); + virtual void construct() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_INC_HSQLDB_HCOLUMNS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Columns.cxx b/connectivity/source/drivers/firebird/Columns.cxx new file mode 100644 index 000000000..293dac97d --- /dev/null +++ b/connectivity/source/drivers/firebird/Columns.cxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "Columns.hxx" +#include "Column.hxx" + +using namespace ::connectivity; +using namespace ::connectivity::firebird; +using namespace ::connectivity::sdbcx; + +using namespace ::cppu; +using namespace ::osl; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; + +Columns::Columns(Table& rTable, + Mutex& rMutex, + const ::std::vector< OUString>& rVector): + OColumnsHelper(rTable, + true, // TODO: is this case sensitivity? + rMutex, + rVector, + /*bUseHardRef*/true) +{ + OColumnsHelper::setParent(&rTable); +} + +Reference< css::beans::XPropertySet > Columns::createDescriptor() +{ + return new Column; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Columns.hxx b/connectivity/source/drivers/firebird/Columns.hxx new file mode 100644 index 000000000..844d99ee0 --- /dev/null +++ b/connectivity/source/drivers/firebird/Columns.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_COLUMNS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_COLUMNS_HXX + +#include "Table.hxx" + +#include <connectivity/TColumnsHelper.hxx> + +namespace connectivity +{ + namespace firebird + { + class Columns: public ::connectivity::OColumnsHelper + { + protected: + virtual css::uno::Reference< css::beans::XPropertySet > createDescriptor() override; + public: + Columns(Table& rTable, + ::osl::Mutex& rMutex, + const ::std::vector< OUString> &_rVector); + }; + + } // namespace firebird +} // namespace connectivity + + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_COLUMNS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Connection.cxx b/connectivity/source/drivers/firebird/Connection.cxx new file mode 100644 index 000000000..b5a3c4ce7 --- /dev/null +++ b/connectivity/source/drivers/firebird/Connection.cxx @@ -0,0 +1,959 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "Blob.hxx" +#include "Catalog.hxx" +#include "Clob.hxx" +#include "Connection.hxx" +#include "DatabaseMetaData.hxx" +#include "Driver.hxx" +#include "PreparedStatement.hxx" +#include "Statement.hxx" +#include "Util.hxx" + +#include <stdexcept> + +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess2.hpp> + +#include <connectivity/dbexception.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/storagehelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/localfilehelper.hxx> + +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> + +using namespace connectivity::firebird; +using namespace connectivity; + +using namespace ::osl; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::uno; + +/** + * Location within the .odb that an embedded .fdb will be stored. + * Only relevant for embedded dbs. + */ +static const OUStringLiteral our_sFDBLocation( "firebird.fdb" ); +/** + * Older version of LO may store the database in a .fdb file + */ +static const OUStringLiteral our_sFBKLocation( "firebird.fbk" ); + +Connection::Connection() + : Connection_BASE(m_aMutex) + , m_sConnectionURL() + , m_sFirebirdURL() + , m_bIsEmbedded(false) + , m_bIsFile(false) + , m_bIsAutoCommit(true) + , m_bIsReadOnly(false) + , m_aTransactionIsolation(TransactionIsolation::REPEATABLE_READ) +#if SAL_TYPES_SIZEOFPOINTER == 8 + , m_aDBHandle(0) + , m_aTransactionHandle(0) +#else + , m_aDBHandle(nullptr) + , m_aTransactionHandle(nullptr) +#endif + , m_xCatalog(nullptr) + , m_xMetaData(nullptr) + , m_aStatements() +{ +} + +Connection::~Connection() +{ + if(!isClosed()) + close(); +} + +namespace { + +struct ConnectionGuard +{ + oslInterlockedCount& m_refCount; + explicit ConnectionGuard(oslInterlockedCount& refCount) + : m_refCount(refCount) + { + osl_atomic_increment(&m_refCount); + } + ~ConnectionGuard() + { + osl_atomic_decrement(&m_refCount); + } +}; + +} + +void Connection::construct(const OUString& url, const Sequence< PropertyValue >& info) +{ + ConnectionGuard aGuard(m_refCount); + + try + { + m_sConnectionURL = url; + + bool bIsNewDatabase = false; + // the database may be stored as an + // fdb file in older versions + bool bIsFdbStored = false; + if (url == "sdbc:embedded:firebird") + { + m_bIsEmbedded = true; + + const PropertyValue* pIter = info.getConstArray(); + const PropertyValue* pEnd = pIter + info.getLength(); + + for (;pIter != pEnd; ++pIter) + { + if ( pIter->Name == "Storage" ) + { + m_xEmbeddedStorage.set(pIter->Value,UNO_QUERY); + } + else if ( pIter->Name == "Document" ) + { + pIter->Value >>= m_xParentDocument; + } + } + + if ( !m_xEmbeddedStorage.is() ) + { + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_NO_STORAGE); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + + bIsNewDatabase = !m_xEmbeddedStorage->hasElements(); + + m_pDatabaseFileDir.reset(new ::utl::TempFile(nullptr, true)); + m_pDatabaseFileDir->EnableKillingFile(); + m_sFirebirdURL = m_pDatabaseFileDir->GetFileName() + "/firebird.fdb"; + m_sFBKPath = m_pDatabaseFileDir->GetFileName() + "/firebird.fbk"; + + SAL_INFO("connectivity.firebird", "Temporary .fdb location: " << m_sFirebirdURL); + + if (!bIsNewDatabase) + { + if (m_xEmbeddedStorage->hasByName(our_sFBKLocation) && + m_xEmbeddedStorage->isStreamElement(our_sFBKLocation)) + { + SAL_INFO("connectivity.firebird", "Extracting* .fbk from .odb" ); + loadDatabaseFile(our_sFBKLocation, m_sFBKPath); + } + else if(m_xEmbeddedStorage->hasByName(our_sFDBLocation) && + m_xEmbeddedStorage->isStreamElement(our_sFDBLocation)) + { + SAL_INFO("connectivity.firebird", "Found .fdb instead of .fbk"); + bIsFdbStored = true; + loadDatabaseFile(our_sFDBLocation, m_sFirebirdURL); + } + else + { + // There might be files which are not firebird databases. + // This is not a problem. + bIsNewDatabase = true; + } + } + // TODO: Get DB properties from XML + + } + // External file AND/OR remote connection + else if (url.startsWith("sdbc:firebird:")) + { + m_sFirebirdURL = url.copy(OUString("sdbc:firebird:").getLength()); + if (m_sFirebirdURL.startsWith("file://")) + { + m_bIsFile = true; + uno::Reference< ucb::XSimpleFileAccess > xFileAccess = + ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext()); + if (!xFileAccess->exists(m_sFirebirdURL)) + bIsNewDatabase = true; + + m_sFirebirdURL = m_sFirebirdURL.copy(OUString("file://").getLength()); + } + } + + std::string dpbBuffer; + { + char userName[256] = ""; + char userPassword[256] = ""; + + dpbBuffer.push_back(isc_dpb_version1); + dpbBuffer.push_back(isc_dpb_sql_dialect); + dpbBuffer.push_back(1); // 1 byte long + dpbBuffer.push_back(FIREBIRD_SQL_DIALECT); + + // set UTF8 as default character set of the database + const char sCharset[] = "UTF8"; + dpbBuffer.push_back(isc_dpb_set_db_charset); + dpbBuffer.push_back(sizeof(sCharset) - 1); + dpbBuffer.append(sCharset); + // set UTF8 as default character set of the connection + dpbBuffer.push_back(isc_dpb_lc_ctype); + dpbBuffer.push_back(sizeof(sCharset) - 1); + dpbBuffer.append(sCharset); + + // Do any more dpbBuffer additions here + + if (m_bIsEmbedded || m_bIsFile) + { + strcpy(userName,"sysdba"); + strcpy(userPassword,"masterkey"); + } + else + { + // TODO: parse password from connection string as needed? + } + + if (strlen(userName)) + { + int nUsernameLength = strlen(userName); + dpbBuffer.push_back(isc_dpb_user_name); + dpbBuffer.push_back(nUsernameLength); + dpbBuffer.append(userName); + } + + if (strlen(userPassword)) + { + int nPasswordLength = strlen(userPassword); + dpbBuffer.push_back(isc_dpb_password); + dpbBuffer.push_back(nPasswordLength); + dpbBuffer.append(userPassword); + } + } + + ISC_STATUS_ARRAY status; /* status vector */ + ISC_STATUS aErr; + if (bIsNewDatabase) + { + aErr = isc_create_database(status, + m_sFirebirdURL.getLength(), + OUStringToOString(m_sFirebirdURL,RTL_TEXTENCODING_UTF8).getStr(), + &m_aDBHandle, + dpbBuffer.size(), + dpbBuffer.c_str(), + 0); + if (aErr) + { + evaluateStatusVector(status, "isc_create_database", *this); + } + } + else + { + if (m_bIsEmbedded && !bIsFdbStored) // We need to restore the .fbk first + { + runBackupService(isc_action_svc_restore); + } + + aErr = isc_attach_database(status, + m_sFirebirdURL.getLength(), + OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8).getStr(), + &m_aDBHandle, + dpbBuffer.size(), + dpbBuffer.c_str()); + if (aErr) + { + evaluateStatusVector(status, "isc_attach_database", *this); + } + } + + if (m_bIsEmbedded) // Add DocumentEventListener to save the .fdb as needed + { + // We need to attach as a document listener in order to be able to store + // the temporary db back into the .odb when saving + uno::Reference<XDocumentEventBroadcaster> xBroadcaster(m_xParentDocument, UNO_QUERY); + + if (xBroadcaster.is()) + xBroadcaster->addDocumentEventListener(this); + else + assert(false); + } + } + catch (const Exception&) + { + throw; + } + catch (const std::exception&) + { + throw; + } + catch (...) // const Firebird::Exception& firebird throws this, but doesn't install the fb_exception.h that declares it + + { + throw std::runtime_error("Generic Firebird::Exception"); + } +} + +void Connection::notifyDatabaseModified() +{ + if (m_xParentDocument.is()) // Only true in embedded mode + m_xParentDocument->setModified(true); +} + +//----- XServiceInfo --------------------------------------------------------- +IMPLEMENT_SERVICE_INFO(Connection, "com.sun.star.sdbc.drivers.firebird.Connection", + "com.sun.star.sdbc.Connection") + +Reference< XBlob> Connection::createBlob(ISC_QUAD const * pBlobId) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + Reference< XBlob > xReturn = new Blob(&m_aDBHandle, + &m_aTransactionHandle, + *pBlobId); + + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XClob> Connection::createClob(ISC_QUAD const * pBlobId) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + Reference< XClob > xReturn = new Clob(&m_aDBHandle, + &m_aTransactionHandle, + *pBlobId); + + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + + +//----- XConnection ---------------------------------------------------------- +Reference< XStatement > SAL_CALL Connection::createStatement( ) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + // the pre + if(m_aTypeInfo.empty()) + buildTypeInfo(); + + // create a statement + // the statement can only be executed once + Reference< XStatement > xReturn = new OStatement(this); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL Connection::prepareStatement( + const OUString& _sSql) +{ + SAL_INFO("connectivity.firebird", "prepareStatement() " + "called with sql: " << _sSql); + MutexGuard aGuard(m_aMutex); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + if(m_aTypeInfo.empty()) + buildTypeInfo(); + + Reference< XPreparedStatement > xReturn = new OPreparedStatement(this, _sSql); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL Connection::prepareCall( + const OUString& _sSql ) +{ + SAL_INFO("connectivity.firebird", "prepareCall(). " + "_sSql: " << _sSql); + + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + // OUString sSqlStatement (transformPreparedStatement( _sSql )); + + // not implemented yet :-) a task to do + return nullptr; +} + +OUString SAL_CALL Connection::nativeSQL( const OUString& _sSql ) +{ + MutexGuard aGuard( m_aMutex ); + // We do not need to adapt the SQL for Firebird atm. + return _sSql; +} + +void SAL_CALL Connection::setAutoCommit( sal_Bool autoCommit ) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + m_bIsAutoCommit = autoCommit; + + if (m_aTransactionHandle) + { + setupTransaction(); + } +} + +sal_Bool SAL_CALL Connection::getAutoCommit() +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + return m_bIsAutoCommit; +} + +void Connection::setupTransaction() +{ + MutexGuard aGuard( m_aMutex ); + ISC_STATUS status_vector[20]; + + // TODO: is this sensible? If we have changed parameters then transaction + // is lost... + if (m_aTransactionHandle) + { + disposeStatements(); + isc_rollback_transaction(status_vector, &m_aTransactionHandle); + } + + char aTransactionIsolation = 0; + switch (m_aTransactionIsolation) + { + // TODO: confirm that these are correct. + case TransactionIsolation::READ_UNCOMMITTED: + aTransactionIsolation = isc_tpb_concurrency; + break; + case TransactionIsolation::READ_COMMITTED: + aTransactionIsolation = isc_tpb_read_committed; + break; + case TransactionIsolation::REPEATABLE_READ: + aTransactionIsolation = isc_tpb_consistency; + break; + case TransactionIsolation::SERIALIZABLE: + aTransactionIsolation = isc_tpb_consistency; + break; + default: + assert( false ); // We must have a valid TransactionIsolation. + } + + // You cannot pass an empty tpb parameter so we have to do some pointer + // arithmetic to avoid problems. (i.e. aTPB[x] = 0 is invalid) + char aTPB[5]; + char* pTPB = aTPB; + + *pTPB++ = isc_tpb_version3; + if (m_bIsAutoCommit) + *pTPB++ = isc_tpb_autocommit; + *pTPB++ = (!m_bIsReadOnly ? isc_tpb_write : isc_tpb_read); + *pTPB++ = aTransactionIsolation; + *pTPB++ = isc_tpb_wait; + + isc_start_transaction(status_vector, + &m_aTransactionHandle, + 1, + &m_aDBHandle, + pTPB - aTPB, // bytes used in TPB + aTPB); + + evaluateStatusVector(status_vector, + "isc_start_transaction", + *this); +} + +isc_tr_handle& Connection::getTransaction() +{ + MutexGuard aGuard( m_aMutex ); + if (!m_aTransactionHandle) + { + setupTransaction(); + } + return m_aTransactionHandle; +} + +void SAL_CALL Connection::commit() +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + ISC_STATUS status_vector[20]; + + if (!m_bIsAutoCommit && m_aTransactionHandle) + { + disposeStatements(); + isc_commit_transaction(status_vector, &m_aTransactionHandle); + evaluateStatusVector(status_vector, + "isc_commit_transaction", + *this); + } +} + +void Connection::loadDatabaseFile(const OUString& srcLocation, const OUString& tmpLocation) +{ + Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(srcLocation, + ElementModes::READ)); + + uno::Reference< ucb::XSimpleFileAccess2 > xFileAccess = + ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ); + if ( !xFileAccess.is() ) + { + ::connectivity::SharedResources aResources; + // TODO FIXME: this does _not_ look like the right error message + const OUString sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + xFileAccess->writeFile(tmpLocation,xDBStream->getInputStream()); +} + +isc_svc_handle Connection::attachServiceManager() +{ + ISC_STATUS_ARRAY aStatusVector; +#if SAL_TYPES_SIZEOFPOINTER == 8 + isc_svc_handle aServiceHandle = 0; +#else + isc_svc_handle aServiceHandle = nullptr; +#endif + + char aSPBBuffer[256]; + char* pSPB = aSPBBuffer; + *pSPB++ = isc_spb_version; + *pSPB++ = isc_spb_current_version; + *pSPB++ = isc_spb_user_name; + OUString sUserName("SYSDBA"); + char aLength = static_cast<char>(sUserName.getLength()); + *pSPB++ = aLength; + strncpy(pSPB, + OUStringToOString(sUserName, + RTL_TEXTENCODING_UTF8).getStr(), + aLength); + pSPB += aLength; + // TODO: do we need ", isc_dpb_trusted_auth, 1, 1" -- probably not but ... + if (isc_service_attach(aStatusVector, + 0, // Denotes null-terminated string next + "service_mgr", + &aServiceHandle, + pSPB - aSPBBuffer, + aSPBBuffer)) + { + evaluateStatusVector(aStatusVector, + "isc_service_attach", + *this); + } + + return aServiceHandle; +} + +void Connection::detachServiceManager(isc_svc_handle aServiceHandle) +{ + ISC_STATUS_ARRAY aStatusVector; + if (isc_service_detach(aStatusVector, + &aServiceHandle)) + { + evaluateStatusVector(aStatusVector, + "isc_service_detach", + *this); + } +} + +void Connection::runBackupService(const short nAction) +{ + assert(nAction == isc_action_svc_backup + || nAction == isc_action_svc_restore); + + ISC_STATUS_ARRAY aStatusVector; + + // convert paths to 8-Bit strings + OString sFDBPath = OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8); + OString sFBKPath = OUStringToOString(m_sFBKPath, RTL_TEXTENCODING_UTF8); + + + OStringBuffer aRequest; // byte array + + + aRequest.append(static_cast<char>(nAction)); + + aRequest.append(char(isc_spb_dbname)); // .fdb + sal_uInt16 nFDBLength = sFDBPath.getLength(); + aRequest.append(static_cast<char>(nFDBLength & 0xFF)); // least significant byte first + aRequest.append(static_cast<char>((nFDBLength >> 8) & 0xFF)); + aRequest.append(sFDBPath); + + aRequest.append(char(isc_spb_bkp_file)); // .fbk + sal_uInt16 nFBKLength = sFBKPath.getLength(); + aRequest.append(static_cast<char>(nFBKLength & 0xFF)); + aRequest.append(static_cast<char>((nFBKLength >> 8) & 0xFF)); + aRequest.append(sFBKPath); + + if (nAction == isc_action_svc_restore) + { + aRequest.append(char(isc_spb_options)); // 4-Byte bitmask + char sOptions[4]; + char * pOptions = sOptions; +#ifdef _WIN32 +#pragma warning(push) +#pragma warning(disable: 4310) // cast truncates data +#endif + ADD_SPB_NUMERIC(pOptions, isc_spb_res_create); +#ifdef _WIN32 +#pragma warning(pop) +#endif + aRequest.append(sOptions, 4); + } + + isc_svc_handle aServiceHandle; + aServiceHandle = attachServiceManager(); + + if (isc_service_start(aStatusVector, + &aServiceHandle, + nullptr, + aRequest.getLength(), + aRequest.getStr())) + { + evaluateStatusVector(aStatusVector, "isc_service_start", *this); + } + + char aInfoSPB = isc_info_svc_line; + char aResults[256]; + + // query blocks until success or error + if(isc_service_query(aStatusVector, + &aServiceHandle, + nullptr, // Reserved null + 0,nullptr, // "send" spb -- size and spb -- not needed? + 1, + &aInfoSPB, + sizeof(aResults), + aResults)) + { + evaluateStatusVector(aStatusVector, "isc_service_query", *this); + } + + detachServiceManager(aServiceHandle); +} + + +void SAL_CALL Connection::rollback() +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + ISC_STATUS status_vector[20]; + + if (!m_bIsAutoCommit && m_aTransactionHandle) + { + isc_rollback_transaction(status_vector, &m_aTransactionHandle); + } +} + +sal_Bool SAL_CALL Connection::isClosed( ) +{ + MutexGuard aGuard( m_aMutex ); + + // just simple -> we are close when we are disposed that means someone called dispose(); (XComponent) + return Connection_BASE::rBHelper.bDisposed; +} + +Reference< XDatabaseMetaData > SAL_CALL Connection::getMetaData( ) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + // here we have to create the class with biggest interface + // The answer is 42 :-) + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + xMetaData = new ODatabaseMetaData(this); // need the connection because it can return it + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +void SAL_CALL Connection::setReadOnly(sal_Bool readOnly) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + m_bIsReadOnly = readOnly; + setupTransaction(); +} + +sal_Bool SAL_CALL Connection::isReadOnly() +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + return m_bIsReadOnly; +} + +void SAL_CALL Connection::setCatalog(const OUString& /*catalog*/) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setCatalog", *this); +} + +OUString SAL_CALL Connection::getCatalog() +{ + ::dbtools::throwFunctionNotSupportedSQLException("getCatalog", *this); + return OUString(); +} + +void SAL_CALL Connection::setTransactionIsolation( sal_Int32 level ) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + m_aTransactionIsolation = level; + setupTransaction(); +} + +sal_Int32 SAL_CALL Connection::getTransactionIsolation( ) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + return m_aTransactionIsolation; +} + +Reference< XNameAccess > SAL_CALL Connection::getTypeMap() +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::getTypeMap", *this ); + return nullptr; +} + +void SAL_CALL Connection::setTypeMap(const Reference< XNameAccess >&) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this ); +} + +//----- XCloseable ----------------------------------------------------------- +void SAL_CALL Connection::close( ) +{ + // we just dispose us + { + MutexGuard aGuard( m_aMutex ); + checkDisposed(Connection_BASE::rBHelper.bDisposed); + + } + dispose(); +} + +// XWarningsSupplier +Any SAL_CALL Connection::getWarnings( ) +{ + // when you collected some warnings -> return it + return Any(); +} + +void SAL_CALL Connection::clearWarnings( ) +{ + // you should clear your collected warnings here +} + +// XDocumentEventListener +void SAL_CALL Connection::documentEventOccured( const DocumentEvent& Event ) +{ + MutexGuard aGuard(m_aMutex); + + if (!m_bIsEmbedded) + return; + + if (!(Event.EventName == "OnSave" || Event.EventName == "OnSaveAs")) + return; + + commit(); // Commit and close transaction + if ( !(m_bIsEmbedded && m_xEmbeddedStorage.is()) ) + return; + + SAL_INFO("connectivity.firebird", "Writing .fbk from running db"); + try + { + runBackupService(isc_action_svc_backup); + } + catch (const SQLException& e) + { + auto a = cppu::getCaughtException(); + throw WrappedTargetRuntimeException(e.Message, e.Context, a); + } + + + Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(our_sFBKLocation, + ElementModes::WRITE)); + + // TODO: verify the backup actually exists -- the backup service + // can fail without giving any sane error messages / telling us + // that it failed. + using namespace ::comphelper; + Reference< XComponentContext > xContext = comphelper::getProcessComponentContext(); + Reference< XInputStream > xInputStream; + if (!xContext.is()) + return; + + xInputStream = + OStorageHelper::GetInputStreamFromURL(m_sFBKPath, xContext); + if (xInputStream.is()) + OStorageHelper::CopyInputToOutput( xInputStream, + xDBStream->getOutputStream()); + + // remove old fdb file if exists + uno::Reference< ucb::XSimpleFileAccess > xFileAccess = + ucb::SimpleFileAccess::create(xContext); + if (xFileAccess->exists(m_sFirebirdURL)) + xFileAccess->kill(m_sFirebirdURL); +} +// XEventListener +void SAL_CALL Connection::disposing(const EventObject& /*rSource*/) +{ + MutexGuard aGuard( m_aMutex ); + + m_xEmbeddedStorage.clear(); +} + +void Connection::buildTypeInfo() +{ + MutexGuard aGuard( m_aMutex ); + + Reference< XResultSet> xRs = getMetaData ()->getTypeInfo (); + Reference< XRow> xRow(xRs,UNO_QUERY); + // Information for a single SQL type + + // Loop on the result set until we reach end of file + + while (xRs->next ()) + { + OTypeInfo aInfo; + aInfo.aTypeName = xRow->getString (1); + aInfo.nType = xRow->getShort (2); + aInfo.nPrecision = xRow->getInt (3); + // aLiteralPrefix = xRow->getString (4); + // aLiteralSuffix = xRow->getString (5); + // aCreateParams = xRow->getString (6); + // bNullable = xRow->getBoolean (7); + // bCaseSensitive = xRow->getBoolean (8); + // nSearchType = xRow->getShort (9); + // bUnsigned = xRow->getBoolean (10); + // bCurrency = xRow->getBoolean (11); + // bAutoIncrement = xRow->getBoolean (12); + aInfo.aLocalTypeName = xRow->getString (13); + // nMinimumScale = xRow->getShort (14); + aInfo.nMaximumScale = xRow->getShort (15); + // nNumPrecRadix = (sal_Int16)xRow->getInt(18); + + + // Now that we have the type info, save it + // in the Hashtable if we don't already have an + // entry for this SQL type. + + m_aTypeInfo.push_back(aInfo); + } + + SAL_INFO("connectivity.firebird", "buildTypeInfo(). " + "Type info built."); + + // Close the result set/statement. + + Reference< XCloseable> xClose(xRs,UNO_QUERY); + xClose->close(); + + SAL_INFO("connectivity.firebird", "buildTypeInfo(). " + "Closed."); +} + +void Connection::disposing() +{ + MutexGuard aGuard(m_aMutex); + + disposeStatements(); + + m_xMetaData = css::uno::WeakReference< css::sdbc::XDatabaseMetaData>(); + + ISC_STATUS_ARRAY status; /* status vector */ + if (m_aTransactionHandle) + { + // TODO: confirm whether we need to ask the user here. + isc_rollback_transaction(status, &m_aTransactionHandle); + } + + if (m_aDBHandle) + { + if (isc_detach_database(status, &m_aDBHandle)) + { + evaluateStatusVector(status, "isc_detach_database", *this); + } + } + // TODO: write to storage again? + + cppu::WeakComponentImplHelperBase::disposing(); + + if (m_pDatabaseFileDir) + { + ::utl::removeTree(m_pDatabaseFileDir->GetURL()); + m_pDatabaseFileDir.reset(); + } +} + +void Connection::disposeStatements() +{ + MutexGuard aGuard(m_aMutex); + for (auto const& statement : m_aStatements) + { + Reference< XComponent > xComp(statement.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + } + m_aStatements.clear(); +} + +uno::Reference< XTablesSupplier > Connection::createCatalog() +{ + MutexGuard aGuard(m_aMutex); + + // m_xCatalog is a weak reference. Reuse it if it still exists. + Reference< XTablesSupplier > xCatalog = m_xCatalog; + if (xCatalog.is()) + { + return xCatalog; + } + else + { + xCatalog = new Catalog(this); + m_xCatalog = xCatalog; + return m_xCatalog; + } + +} + + diff --git a/connectivity/source/drivers/firebird/Connection.hxx b/connectivity/source/drivers/firebird/Connection.hxx new file mode 100644 index 000000000..7365aa06a --- /dev/null +++ b/connectivity/source/drivers/firebird/Connection.hxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CONNECTION_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CONNECTION_HXX + +#include <ibase.h> + +#include <connectivity/CommonTools.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/weakref.hxx> +#include <memory> +#include <OTypeInfo.hxx> +#include <unotools/tempfile.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/document/DocumentEvent.hpp> +#include <com/sun/star/document/XDocumentEventListener.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/XBlob.hpp> +#include <com/sun/star/sdbc/XClob.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +namespace connectivity +{ + namespace firebird + { + + typedef ::cppu::WeakComponentImplHelper< css::document::XDocumentEventListener, + css::lang::XServiceInfo, + css::sdbc::XConnection, + css::sdbc::XWarningsSupplier + > Connection_BASE; + + class OStatementCommonBase; + class FirebirdDriver; + class ODatabaseMetaData; + + + typedef std::vector< ::connectivity::OTypeInfo> TTypeInfoVector; + typedef std::vector< css::uno::WeakReferenceHelper > OWeakRefArray; + + class Connection final : public Connection_BASE + { + ::osl::Mutex m_aMutex; + + TTypeInfoVector m_aTypeInfo; // vector containing an entry + // for each row returned by + // DatabaseMetaData.getTypeInfo. + + /** The URL passed to us when opening, i.e. of the form sdbc:* */ + OUString m_sConnectionURL; + /** + * The URL passed to firebird, i.e. either a local file (for a + * temporary .fdb extracted from a .odb or a normal local file) or + * a remote url. + */ + OUString m_sFirebirdURL; + + /* EMBEDDED MODE DATA */ + /** Denotes that we have a database stored within a .odb file. */ + bool m_bIsEmbedded; + + /** + * Handle for the parent DatabaseDocument. We need to notify this + * whenever any data is written to our temporary database so that + * the user is able to save this back to the .odb file. + * + * Note that this is ONLY set in embedded mode. + */ + css::uno::Reference< css::util::XModifiable > + m_xParentDocument; + + /** + * Handle for the folder within the .odb where we store our .fbk + * (Only used if m_bIsEmbedded is true). + */ + css::uno::Reference< css::embed::XStorage > + m_xEmbeddedStorage; + /** + * The temporary folder where we extract the .fbk from a .odb, + * and also store the temporary .fdb + * It is only valid if m_bIsEmbedded is true. + * + * The extracted .fbk is written in firebird.fbk, the temporary + * .fdb is stored as firebird.fdb. + */ + std::unique_ptr< ::utl::TempFile > m_pDatabaseFileDir; + /** + * Path for our extracted .fbk file. + * + * (The temporary .fdb is our m_sFirebirdURL.) + */ + OUString m_sFBKPath; + + void loadDatabaseFile(const OUString& pSrcLocation, const OUString& pTmpLocation); + + /** + * Run the backup service, use nAction = + * isc_action_svc_backup to backup, nAction = isc_action_svc_restore + * to restore. + */ + void runBackupService(const short nAction); + + isc_svc_handle attachServiceManager(); + + void detachServiceManager(isc_svc_handle pServiceHandle); + + /** We are using an external (local) file */ + bool m_bIsFile; + + /* CONNECTION PROPERTIES */ + bool m_bIsAutoCommit; + bool m_bIsReadOnly; + sal_Int32 m_aTransactionIsolation; + + isc_db_handle m_aDBHandle; + isc_tr_handle m_aTransactionHandle; + + css::uno::WeakReference< css::sdbcx::XTablesSupplier> + m_xCatalog; + css::uno::WeakReference< css::sdbc::XDatabaseMetaData > + m_xMetaData; + /** Statements owned by this connection. */ + OWeakRefArray m_aStatements; + + /// @throws css::sdbc::SQLException + void buildTypeInfo(); + + /** + * Creates a new transaction with the desired parameters, if + * necessary discarding an existing transaction. This has to be done + * anytime we change the transaction isolation, or autocommitting. + * + * @throws css::sdbc::SQLException + */ + void setupTransaction(); + void disposeStatements(); + + public: + explicit Connection(); + virtual ~Connection() override; + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void construct( const OUString& url, + const css::uno::Sequence< css::beans::PropertyValue >& info); + + const OUString& getConnectionURL() const {return m_sConnectionURL;} + bool isEmbedded() const {return m_bIsEmbedded;} + isc_db_handle& getDBHandle() {return m_aDBHandle;} + /// @throws css::sdbc::SQLException + isc_tr_handle& getTransaction(); + + /** + * Must be called anytime the underlying database is likely to have + * changed. + * + * This is used to notify the database document of any changes, so + * that the user is informed of any pending changes needing to be + * saved. + */ + void notifyDatabaseModified(); + + /** + * Create a new Blob tied to this connection. Blobs are tied to a + * transaction and not to a statement, hence the connection should + * deal with their management. + * + * @throws css::sdbc::SQLException + * @throws css::uno::RuntimeException + */ + css::uno::Reference< css::sdbc::XBlob> + createBlob(ISC_QUAD const * pBlobID); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::sdbc::XClob> + createClob(ISC_QUAD const * pBlobID); + + /** + * Create and/or connect to the sdbcx Catalog. This is completely + * unrelated to the SQL "Catalog". + */ + css::uno::Reference< css::sdbcx::XTablesSupplier > + createCatalog(); + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XServiceInfo + DECLARE_SERVICE_INFO(); + // XConnection + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual void SAL_CALL setAutoCommit( sal_Bool autoCommit ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual void SAL_CALL setReadOnly( sal_Bool readOnly ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL setCatalog( const OUString& catalog ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual void SAL_CALL setTransactionIsolation( sal_Int32 level ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + // XCloseable + virtual void SAL_CALL close( ) override; + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + // XDocumentEventListener + virtual void SAL_CALL documentEventOccured( const css::document::DocumentEvent& Event ) override; + // css.lang.XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_CONNECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/DatabaseMetaData.cxx b/connectivity/source/drivers/firebird/DatabaseMetaData.cxx new file mode 100644 index 000000000..fdcfd1d64 --- /dev/null +++ b/connectivity/source/drivers/firebird/DatabaseMetaData.cxx @@ -0,0 +1,1810 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "DatabaseMetaData.hxx" +#include "Util.hxx" + +#include <ibase.h> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <FDatabaseMetaDataResultSet.hxx> + +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/IndexType.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbc/Deferrability.hpp> + +using namespace connectivity::firebird; +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + +ODatabaseMetaData::ODatabaseMetaData(Connection* _pCon) +: m_pConnection(_pCon) +{ + SAL_WARN_IF(!m_pConnection.is(), "connectivity.firebird", + "ODatabaseMetaData::ODatabaseMetaData: No connection set!"); +} + +ODatabaseMetaData::~ODatabaseMetaData() +{ +} + +//----- Catalog Info -- UNSUPPORTED ------------------------------------------- +OUString SAL_CALL ODatabaseMetaData::getCatalogSeparator() +{ + return OUString(); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCatalogNameLength() +{ + return -1; +} + +OUString SAL_CALL ODatabaseMetaData::getCatalogTerm() +{ + return OUString(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::isCatalogAtStart() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInTableDefinitions() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInIndexDefinitions() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInDataManipulation( ) +{ + return false; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCatalogs() +{ + OSL_FAIL("Not implemented yet!"); + // TODO implement + return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eCatalogs); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInProcedureCalls() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInPrivilegeDefinitions() +{ + return false; +} + +//----- Schema Info -- UNSUPPORTED -------------------------------------------- +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInProcedureCalls() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInPrivilegeDefinitions() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInDataManipulation() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInIndexDefinitions() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInTableDefinitions() +{ + return false; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxSchemaNameLength() +{ + return -1; +} + +OUString SAL_CALL ODatabaseMetaData::getSchemaTerm() +{ + return OUString(); +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getSchemas() +{ + OSL_FAIL("Not implemented yet!"); + // TODO implement + return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eSchemas); +} + +//----- Max Sizes/Lengths ----------------------------------------------------- +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxBinaryLiteralLength() +{ + return 32767; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxRowSize() +{ + return 32767; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCharLiteralLength() +{ + return 32767; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnNameLength() +{ + return 31; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInIndex() +{ + // TODO: No idea. + // See: http://www.firebirdsql.org/en/firebird-technical-specifications/ + return 16; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCursorNameLength() +{ + return 32; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxConnections() +{ + return 100; // Arbitrary +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInTable() +{ + // May however be smaller. + // See: http://www.firebirdsql.org/en/firebird-technical-specifications/ + return 32767; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatementLength() +{ + return 32767; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTableNameLength() +{ + return 31; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTablesInSelect( ) +{ + return 0; // 0 means no limit +} + + +sal_Bool SAL_CALL ODatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + return false; +} + +// ---- Identifiers ----------------------------------------------------------- +// Only quoted identifiers are case sensitive, unquoted are case insensitive +OUString SAL_CALL ODatabaseMetaData::getIdentifierQuoteString() +{ + OUString aVal('"'); + return aVal; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseQuotedIdentifiers( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseQuotedIdentifiers() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseQuotedIdentifiers() +{ + // TODO: confirm this -- the documentation is highly ambiguous + // However it seems this should be true as quoted identifiers ARE + // stored mixed case. + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseQuotedIdentifiers() +{ + return false; +} + +// ---- Unquoted Identifiers ------------------------------------------------- +// All unquoted identifiers are stored upper case. +sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseIdentifiers() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseIdentifiers() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseIdentifiers() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseIdentifiers() +{ + return true; +} + +// ---- SQL Feature Support --------------------------------------------------- +sal_Bool SAL_CALL ODatabaseMetaData::supportsCoreSQLGrammar() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMinimumSQLGrammar() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsAlterTableWithAddColumn() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsAlterTableWithDropColumn() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedDelete() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedUpdate() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOuterJoins() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSelectForUpdate() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::allTablesAreSelectable() +{ + // TODO: true if embedded, but unsure about remote server + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsConvert(sal_Int32, + sal_Int32) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTypeConversion() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsColumnAliasing() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTableCorrelationNames() +{ + return true; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxIndexLength( ) +{ + return 0; // 0 means no limit +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsNonNullableColumns( ) +{ + return true; +} + +OUString SAL_CALL ODatabaseMetaData::getExtraNameCharacters( ) +{ + return OUString(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDifferentTableCorrelationNames( ) +{ + return false; +} +// ---- Data definition stuff ------------------------------------------------- +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionIgnoredInTransactions() +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionCausesTransactionCommit() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDataManipulationTransactionsOnly() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData:: + supportsDataDefinitionAndDataManipulationTransactions() +{ + return false; +} +//----- Transaction Support -------------------------------------------------- +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactions() +{ + return true; +} + +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::supportsMultipleTransactions() +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactionIsolationLevel( + sal_Int32 aLevel) +{ + return aLevel == TransactionIsolation::READ_UNCOMMITTED + || aLevel == TransactionIsolation::READ_COMMITTED + || aLevel == TransactionIsolation::REPEATABLE_READ + || aLevel == TransactionIsolation::SERIALIZABLE; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDefaultTransactionIsolation() +{ + return TransactionIsolation::REPEATABLE_READ; +} + + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92FullSQL( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92EntryLevelSQL( ) +{ + return true; // should be supported at least +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsIntegrityEnhancementFacility( ) +{ + return true; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatements( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxProcedureNameLength( ) +{ + return 31; // TODO: confirm +} + +sal_Bool SAL_CALL ODatabaseMetaData::allProceduresAreCallable( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsStoredProcedures( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::isReadOnly( ) +{ + return m_pConnection->isReadOnly(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFiles( ) +{ + return m_pConnection->isEmbedded(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFilePerTable( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullPlusNonNullIsNull( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExpressionsInOrderBy( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupBy( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByBeyondSelect( ) +{ + // Unsure + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByUnrelated( ) +{ + // Unsure + 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 true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnionAll( ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtEnd( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtStart( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedHigh( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedLow( ) +{ + 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 m_pConnection->getConnectionURL(); +} + +OUString SAL_CALL ODatabaseMetaData::getUserName( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getDriverName( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getDriverVersion() +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductVersion( ) +{ + uno::Reference< XStatement > xSelect = m_pConnection->createStatement(); + + uno::Reference< XResultSet > xRs = xSelect->executeQuery("SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version from rdb$database"); + (void)xRs->next(); // first and only row + uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW ); + return xRow->getString(1); +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductName( ) +{ + return "Firebird (engine12)"; +} + +OUString SAL_CALL ODatabaseMetaData::getProcedureTerm( ) +{ + return OUString(); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMajorVersion( ) +{ + return 1; +} + +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 "ASCII_CHAR,ASCII_VAL,BIT_LENGTH,CHAR_LENGTH,CHAR_TO_UUID,CHARACTER_LENGTH," + "GEN_UUID,HASH,LEFT,LOWER,LPAD,OCTET_LENGTH,OVERLAY,POSITION,REPLACE,REVERSE," + "RIGHT,RPAD,SUBSTRING,TRIM,UPPER,UUID_TO_CHAR"; +} + +OUString SAL_CALL ODatabaseMetaData::getTimeDateFunctions( ) +{ + return "CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,DATEADD, DATEDIFF," + "EXTRACT,'NOW','TODAY','TOMORROW','YESTERDAY'"; +} + +OUString SAL_CALL ODatabaseMetaData::getSystemFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getNumericFunctions( ) +{ + return "ABS,ACOS,ASIN,ATAN,ATAN2,BIN_AND,BIN_NOT,BIN_OR,BIN_SHL," + "BIN_SHR,BIN_XOR,CEIL,CEILING,COS,COSH,COT,EXP,FLOOR,LN," + "LOG,LOG10,MOD,PI,POWER,RAND,ROUND,SIGN,SIN,SINH,SQRT,TAN,TANH,TRUNC"; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExtendedSQLGrammar( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsFullOuterJoins( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLimitedOuterJoins( ) +{ + return false; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInGroupBy( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInSelect( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxUserNameLength( ) +{ + return 31; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetType(sal_Int32 setType) +{ + switch (setType) + { + case ResultSetType::FORWARD_ONLY: + return true; + default: + return false; + } +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetConcurrency( + sal_Int32 aResultSetType, + sal_Int32 aConcurrency) +{ + if (aResultSetType == ResultSetType::FORWARD_ONLY + && aConcurrency == ResultSetConcurrency::READ_ONLY) + return true; + else + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownUpdatesAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownDeletesAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownInsertsAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersUpdatesAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersDeletesAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersInsertsAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::updatesAreDetected( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::deletesAreDetected( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::insertsAreDetected( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsBatchUpdates() +{ + // No batch support in firebird + return false; +} + +uno::Reference< XConnection > SAL_CALL ODatabaseMetaData::getConnection() +{ + return uno::Reference<XConnection>(m_pConnection.get()); +} + +// here follow all methods which return a resultset +// the first methods is an example implementation how to use this resultset +// of course you could implement it on your and you should do this because +// the general way is more memory expensive + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTableTypes( ) +{ + ODatabaseMetaDataResultSet* pResultSet = new + ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTableTypes); + uno::Reference< XResultSet > xResultSet = pResultSet; + + ODatabaseMetaDataResultSet::ORows aResults; + ODatabaseMetaDataResultSet::ORow aRow(2); + + aRow[0] = new ORowSetValueDecorator(); // unused + + // TODO Put these statics to one place + // like postgreSQL's Statics class. + + aRow[1] = new ORowSetValueDecorator(OUString("TABLE")); + aResults.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("VIEW")); + aResults.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("SYSTEM TABLE")); + aResults.push_back(aRow); + + pResultSet->setRows(aResults); + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTypeInfo() +{ + SAL_INFO("connectivity.firebird", "getTypeInfo()"); + + // this returns an empty resultset where the column-names are already set + // in special the metadata of the resultset already returns the right columns + ODatabaseMetaDataResultSet* pResultSet = + new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTypeInfo); + uno::Reference< XResultSet > xResultSet = pResultSet; + static ODatabaseMetaDataResultSet::ORows aResults = []() + { + ODatabaseMetaDataResultSet::ORows tmp; + ODatabaseMetaDataResultSet::ORow aRow(19); + + // Common data + aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); // Literal quote marks + aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); // Literal quote marks + aRow[7] = new ORowSetValueDecorator(true); // Nullable + aRow[8] = new ORowSetValueDecorator(true); // Case sensitive + aRow[10] = new ORowSetValueDecorator(false); // Is unsigned + // FIXED_PREC_SCALE: docs state "can it be a money value? " however + // in reality this causes Base to treat all numbers as money formatted + // by default which is wrong (and formatting as money value is still + // possible for all values). + aRow[11] = new ORowSetValueDecorator(false); + // Localised Type Name -- TODO: implement (but can be null): + aRow[13] = new ORowSetValueDecorator(); + aRow[16] = new ORowSetValueDecorator(); // Unused + aRow[17] = new ORowSetValueDecorator(); // Unused + aRow[18] = new ORowSetValueDecorator(sal_Int16(10));// Radix + + // Char + aRow[1] = new ORowSetValueDecorator(OUString("CHAR")); + aRow[2] = new ORowSetValueDecorator(DataType::CHAR); + aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::FULL)); // Searchable + aRow[12] = new ORowSetValueDecorator(false); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + + // Varchar + aRow[1] = new ORowSetValueDecorator(OUString("VARCHAR")); + aRow[2] = new ORowSetValueDecorator(DataType::VARCHAR); + aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::FULL)); // Searchable + aRow[12] = new ORowSetValueDecorator(false); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + + // Binary (CHAR); we use the Firebird synonym CHARACTER + // to fool LO into seeing it as different types. + // It is distinguished from Text type by its character set OCTETS; + // that will be added by Tables::createStandardColumnPart + aRow[1] = new ORowSetValueDecorator(OUString("CHARACTER")); + aRow[2] = new ORowSetValueDecorator(DataType::BINARY); + aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::NONE)); // Searchable + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + + // Varbinary (VARCHAR) + aRow[1] = new ORowSetValueDecorator(OUString("CHARACTER VARYING")); + aRow[2] = new ORowSetValueDecorator(DataType::VARBINARY); + aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::NONE)); // Searchable + + // Clob (SQL_BLOB) + aRow[1] = new ORowSetValueDecorator(OUString("BLOB SUB_TYPE TEXT")); // BLOB, with subtype 1 + aRow[2] = new ORowSetValueDecorator(DataType::CLOB); + aRow[3] = new ORowSetValueDecorator(sal_Int32(2147483647)); // Precision = max length + aRow[6] = new ORowSetValueDecorator(); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::FULL)); // Searchable + aRow[12] = new ORowSetValueDecorator(false); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + + // Longvarbinary (SQL_BLOB) + // Distinguished from simple blob with a user-defined subtype. + aRow[1] = new ORowSetValueDecorator(OUString("BLOB SUB_TYPE " + OUString::number(static_cast<short>(BlobSubtype::Image))) ); // BLOB, with subtype 0 + aRow[2] = new ORowSetValueDecorator(DataType::LONGVARBINARY); + tmp.push_back(aRow); + + // Integer Types common + { + aRow[6] = new ORowSetValueDecorator(); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::FULL)); // Searchable + aRow[12] = new ORowSetValueDecorator(true); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + } + // Smallint (SQL_SHORT) + aRow[1] = new ORowSetValueDecorator(OUString("SMALLINT")); + aRow[2] = new ORowSetValueDecorator(DataType::SMALLINT); + aRow[3] = new ORowSetValueDecorator(sal_Int16(5)); // Prevision + tmp.push_back(aRow); + // Integer (SQL_LONG) + aRow[1] = new ORowSetValueDecorator(OUString("INTEGER")); + aRow[2] = new ORowSetValueDecorator(DataType::INTEGER); + aRow[3] = new ORowSetValueDecorator(sal_Int16(10)); // Precision + tmp.push_back(aRow); + // Bigint (SQL_INT64) + aRow[1] = new ORowSetValueDecorator(OUString("BIGINT")); + aRow[2] = new ORowSetValueDecorator(DataType::BIGINT); + aRow[3] = new ORowSetValueDecorator(sal_Int16(20)); // Precision + tmp.push_back(aRow); + + // Decimal Types common + { + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::FULL)); // Searchable + aRow[12] = new ORowSetValueDecorator(true); // Autoincrement + } + + aRow[6] = new ORowSetValueDecorator(OUString("PRECISION,SCALE")); // Create params + // Numeric + aRow[1] = new ORowSetValueDecorator(OUString("NUMERIC")); + aRow[2] = new ORowSetValueDecorator(DataType::NUMERIC); + aRow[3] = new ORowSetValueDecorator(sal_Int16(18)); // Precision + aRow[14] = new ORowSetValueDecorator(sal_Int16(0)); // Minimum scale + aRow[15] = new ORowSetValueDecorator(sal_Int16(18)); // Max scale + tmp.push_back(aRow); + // Decimal + aRow[1] = new ORowSetValueDecorator(OUString("DECIMAL")); + aRow[2] = new ORowSetValueDecorator(DataType::DECIMAL); + aRow[3] = new ORowSetValueDecorator(sal_Int16(18)); // Precision + aRow[14] = new ORowSetValueDecorator(sal_Int16(0)); // Minimum scale + aRow[15] = new ORowSetValueDecorator(sal_Int16(18)); // Max scale + tmp.push_back(aRow); + + aRow[6] = new ORowSetValueDecorator(); // Create Params + // Float (SQL_FLOAT) + aRow[1] = new ORowSetValueDecorator(OUString("FLOAT")); + aRow[2] = new ORowSetValueDecorator(DataType::FLOAT); + aRow[3] = new ORowSetValueDecorator(sal_Int16(7)); // Precision + aRow[14] = new ORowSetValueDecorator(sal_Int16(1)); // Minimum scale + aRow[15] = new ORowSetValueDecorator(sal_Int16(7)); // Max scale + tmp.push_back(aRow); + // Double (SQL_DOUBLE) + aRow[1] = new ORowSetValueDecorator(OUString("DOUBLE PRECISION")); + aRow[2] = new ORowSetValueDecorator(DataType::DOUBLE); + aRow[3] = new ORowSetValueDecorator(sal_Int16(15)); // Precision + aRow[14] = new ORowSetValueDecorator(sal_Int16(1)); // Minimum scale + aRow[15] = new ORowSetValueDecorator(sal_Int16(15)); // Max scale + tmp.push_back(aRow); + + // TODO: no idea whether D_FLOAT corresponds to an sql type + + // SQL_TIMESTAMP + aRow[1] = new ORowSetValueDecorator(OUString("TIMESTAMP")); + aRow[2] = new ORowSetValueDecorator(DataType::TIMESTAMP); + aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::FULL)); // Searchable + aRow[12] = new ORowSetValueDecorator(false); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + + // SQL_TYPE_TIME + aRow[1] = new ORowSetValueDecorator(OUString("TIME")); + aRow[2] = new ORowSetValueDecorator(DataType::TIME); + aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::FULL)); // Searchable + aRow[12] = new ORowSetValueDecorator(false); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + + // SQL_TYPE_DATE + aRow[1] = new ORowSetValueDecorator(OUString("DATE")); + aRow[2] = new ORowSetValueDecorator(DataType::DATE); + aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::FULL)); // Searchable + aRow[12] = new ORowSetValueDecorator(false); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + + // SQL_BLOB + aRow[1] = new ORowSetValueDecorator(OUString("BLOB SUB_TYPE BINARY")); + aRow[2] = new ORowSetValueDecorator(DataType::BLOB); + aRow[3] = new ORowSetValueDecorator(sal_Int32(0)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::NONE)); // Searchable + aRow[12] = new ORowSetValueDecorator(false); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + + // SQL_BOOLEAN + aRow[1] = new ORowSetValueDecorator(OUString("BOOLEAN")); + aRow[2] = new ORowSetValueDecorator(DataType::BOOLEAN); + aRow[3] = new ORowSetValueDecorator(sal_Int32(1)); // Prevision = max length + aRow[6] = new ORowSetValueDecorator(); // Create Params + aRow[9] = new ORowSetValueDecorator( + sal_Int16(ColumnSearch::BASIC)); // Searchable + aRow[12] = new ORowSetValueDecorator(false); // Autoincrement + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale + tmp.push_back(aRow); + return tmp; + }(); + pResultSet->setRows(aResults); + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumnPrivileges( + const Any& /*aCatalog*/, + const OUString& /*sSchema*/, + const OUString& sTable, + const OUString& sColumnNamePattern) +{ + SAL_INFO("connectivity.firebird", "getColumnPrivileges() with " + "Table: " << sTable + << " & ColumnNamePattern: " << sColumnNamePattern); + + ODatabaseMetaDataResultSet* pResultSet = new + ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eColumnPrivileges); + uno::Reference< XResultSet > xResultSet = pResultSet; + uno::Reference< XStatement > statement = m_pConnection->createStatement(); + + static const char wld[] = "%"; + OUStringBuffer queryBuf( + "SELECT " + "priv.RDB$RELATION_NAME, " // 1 Table name + "priv.RDB$GRANTOR," // 2 + "priv.RDB$USER, " // 3 Grantee + "priv.RDB$PRIVILEGE, " // 4 + "priv.RDB$GRANT_OPTION, " // 5 is Grantable + "priv.RDB$FIELD_NAME " // 6 Column name + "FROM RDB$USER_PRIVILEGES priv "); + + { + OUString sAppend = "WHERE priv.RDB$RELATION_NAME = '%' "; + queryBuf.append(sAppend.replaceAll("%", sTable)); + } + if (!sColumnNamePattern.isEmpty()) + { + OUString sAppend; + if (sColumnNamePattern.match(wld)) + sAppend = "AND priv.RDB$FIELD_NAME LIKE '%' "; + else + sAppend = "AND priv.RDB$FIELD_NAME = '%' "; + + queryBuf.append(sAppend.replaceAll(wld, sColumnNamePattern)); + } + + queryBuf.append(" ORDER BY priv.RDB$FIELD, " + "priv.RDB$PRIVILEGE"); + + OUString query = queryBuf.makeStringAndClear(); + + uno::Reference< XResultSet > rs = statement->executeQuery(query.getStr()); + uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + ODatabaseMetaDataResultSet::ORows aResults; + + ODatabaseMetaDataResultSet::ORow aCurrentRow(9); + aCurrentRow[0] = new ORowSetValueDecorator(); // Unused + aCurrentRow[1] = new ORowSetValueDecorator(); // 1. TABLE_CAT Unsupported + aCurrentRow[2] = new ORowSetValueDecorator(); // 1. TABLE_SCHEM Unsupported + + while( rs->next() ) + { + // 3. TABLE_NAME + aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1))); + // 4. COLUMN_NAME + aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(6))); + aCurrentRow[5] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); // 5. GRANTOR + aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // 6. GRANTEE + aCurrentRow[7] = new ORowSetValueDecorator(xRow->getString(4)); // 7. Privilege + aCurrentRow[8] = new ORowSetValueDecorator( ( xRow->getShort(5) == 1 ) ? + OUString("YES") : OUString("NO")); // 8. Grantable + + aResults.push_back(aCurrentRow); + } + + pResultSet->setRows( aResults ); + + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumns( + const Any& /*catalog*/, + const OUString& /*schemaPattern*/, + const OUString& tableNamePattern, + const OUString& columnNamePattern) +{ + SAL_INFO("connectivity.firebird", "getColumns() with " + "TableNamePattern: " << tableNamePattern << + " & ColumnNamePattern: " << columnNamePattern); + + OUStringBuffer queryBuf("SELECT " + "relfields.RDB$RELATION_NAME, " // 1 + "relfields.RDB$FIELD_NAME, " // 2 + "relfields.RDB$DESCRIPTION," // 3 + "relfields.RDB$DEFAULT_VALUE, " // 4 + "relfields.RDB$FIELD_POSITION, "// 5 + "fields.RDB$FIELD_TYPE, " // 6 + "fields.RDB$FIELD_SUB_TYPE, " // 7 + "fields.RDB$FIELD_LENGTH, " // 8 + "fields.RDB$FIELD_PRECISION, " // 9 + "fields.RDB$FIELD_SCALE, " // 10 + // Specifically use relfields null flag -- the one in fields is used + // for domains, whether a specific field is nullable is set in relfields, + // this is also the one we manually fiddle when changing NULL/NOT NULL + // (see Table.cxx) + "relfields.RDB$NULL_FLAG, " // 11 + "fields.RDB$CHARACTER_LENGTH, " // 12 + "charset.RDB$CHARACTER_SET_NAME " // 13 + "FROM RDB$RELATION_FIELDS relfields " + "JOIN RDB$FIELDS fields " + "on (fields.RDB$FIELD_NAME = relfields.RDB$FIELD_SOURCE) " + "LEFT JOIN RDB$CHARACTER_SETS charset " + "on (fields.RDB$CHARACTER_SET_ID = charset.RDB$CHARACTER_SET_ID) " + "WHERE (1 = 1) "); + + if (!tableNamePattern.isEmpty()) + { + OUString sAppend; + if (tableNamePattern.match("%")) + sAppend = "AND relfields.RDB$RELATION_NAME LIKE '%' "; + else + sAppend = "AND relfields.RDB$RELATION_NAME = '%' "; + + queryBuf.append(sAppend.replaceAll("%", tableNamePattern)); + } + + if (!columnNamePattern.isEmpty()) + { + OUString sAppend; + if (columnNamePattern.match("%")) + sAppend = "AND relfields.RDB$FIELD_NAME LIKE '%' "; + else + sAppend = "AND relfields.RDB$FIELD_NAME = '%' "; + + queryBuf.append(sAppend.replaceAll("%", columnNamePattern)); + } + + OUString query = queryBuf.makeStringAndClear(); + + uno::Reference< XStatement > statement = m_pConnection->createStatement(); + uno::Reference< XResultSet > rs = statement->executeQuery(query.getStr()); + uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + + ODatabaseMetaDataResultSet::ORows aResults; + ODatabaseMetaDataResultSet::ORow aCurrentRow(19); + + aCurrentRow[0] = new ORowSetValueDecorator(); // Unused -- numbering starts from 0 + aCurrentRow[1] = new ORowSetValueDecorator(); // Catalog - can be null + aCurrentRow[2] = new ORowSetValueDecorator(); // Schema - can be null + aCurrentRow[8] = new ORowSetValueDecorator(); // Unused + aCurrentRow[10] = new ORowSetValueDecorator(sal_Int32(10)); // Radix: fixed in FB + aCurrentRow[14] = new ORowSetValueDecorator(); // Unused + aCurrentRow[15] = new ORowSetValueDecorator(); // Unused + + while( rs->next() ) + { + // 3. TABLE_NAME + aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1))); + // 4. Column Name + aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); + // 5. Datatype + short aType = getFBTypeFromBlrType(xRow->getShort(6)); + short aScale = xRow->getShort(10); + OUString sCharsetName = xRow->getString(13); + // result field may be filled with spaces + sCharsetName = sCharsetName.trim(); + ColumnTypeInfo aInfo(aType, xRow->getShort(7), aScale, + xRow->getString(13)); + + aCurrentRow[5] = new ORowSetValueDecorator(aInfo.getSdbcType()); + // 6. Typename (SQL_*) + aCurrentRow[6] = new ORowSetValueDecorator(aInfo.getColumnTypeName()); + + // 7. Column Sizes + { + sal_Int32 aColumnSize = 0; + switch (aType) + { + case SQL_TEXT: + case SQL_VARYING: + aColumnSize = xRow->getShort(12); + break; + case SQL_SHORT: + case SQL_LONG: + case SQL_FLOAT: + case SQL_DOUBLE: + case SQL_D_FLOAT: + case SQL_INT64: + case SQL_QUAD: + aColumnSize = xRow->getShort(9); + break; + case SQL_TIMESTAMP: + case SQL_BLOB: + case SQL_ARRAY: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + case SQL_NULL: + // TODO: implement. + break; + } + aCurrentRow[7] = new ORowSetValueDecorator(aColumnSize); + } + + // 9. Decimal digits (scale) + // fb stores a negative number + aCurrentRow[9] = new ORowSetValueDecorator( static_cast<sal_Int16>(-aScale) ); + + // 11. Nullable + if (xRow->getShort(11)) + { + aCurrentRow[11] = new ORowSetValueDecorator(ColumnValue::NO_NULLS); + } + else + { + aCurrentRow[11] = new ORowSetValueDecorator(ColumnValue::NULLABLE); + } + // 12. Comments -- may be omitted + { + OUString aDescription; + uno::Reference< XBlob > xBlob = xRow->getBlob(3); + if (xBlob.is()) + { + const sal_Int64 aBlobLength = xBlob->length(); + if (aBlobLength > SAL_MAX_INT32) + { + SAL_WARN("connectivity.firebird", "getBytes can't return " << aBlobLength << " bytes but only max " << SAL_MAX_INT32); + aDescription = OUString(reinterpret_cast<char*>(xBlob->getBytes(1, SAL_MAX_INT32).getArray()), + SAL_MAX_INT32, + RTL_TEXTENCODING_UTF8); + } + else + { + aDescription = OUString(reinterpret_cast<char*>(xBlob->getBytes(1, static_cast<sal_Int32>(aBlobLength)).getArray()), + aBlobLength, + RTL_TEXTENCODING_UTF8); + } + } + aCurrentRow[12] = new ORowSetValueDecorator(aDescription); + } + // 13. Default -- may be omitted. + { + uno::Reference< XBlob > xDefaultValueBlob = xRow->getBlob(4); + if (xDefaultValueBlob.is()) + { + // TODO: Implement + } + aCurrentRow[13] = new ORowSetValueDecorator(); + } + + // 16. Bytes in Column for char + if (aType == SQL_TEXT) + { + aCurrentRow[16] = new ORowSetValueDecorator(xRow->getShort(8)); + } + else if (aType == SQL_VARYING) + { + aCurrentRow[16] = new ORowSetValueDecorator(sal_Int32(32767)); + } + else + { + aCurrentRow[16] = new ORowSetValueDecorator(sal_Int32(0)); + } + // 17. Index of column + { + short nColumnNumber = xRow->getShort(5); + // Firebird stores column numbers beginning with 0 internally + // SDBC expects column numbering to begin with 1. + aCurrentRow[17] = new ORowSetValueDecorator(sal_Int32(nColumnNumber + 1)); + } + // 18. Is nullable + if (xRow->getShort(9)) + { + aCurrentRow[18] = new ORowSetValueDecorator(OUString("NO")); + } + else + { + aCurrentRow[18] = new ORowSetValueDecorator(OUString("YES")); + } + + aResults.push_back(aCurrentRow); + } + ODatabaseMetaDataResultSet* pResultSet = new + ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eColumns); + uno::Reference< XResultSet > xResultSet = pResultSet; + pResultSet->setRows( aResults ); + + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTables( + const Any& /*catalog*/, + const OUString& /*schemaPattern*/, + const OUString& tableNamePattern, + const Sequence< OUString >& types) +{ + SAL_INFO("connectivity.firebird", "getTables() with " + "TableNamePattern: " << tableNamePattern); + + ODatabaseMetaDataResultSet* pResultSet = new + ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTables); + uno::Reference< XResultSet > xResultSet = pResultSet; + uno::Reference< XStatement > statement = m_pConnection->createStatement(); + + static const char wld[] = "%"; + OUStringBuffer queryBuf( + "SELECT " + "RDB$RELATION_NAME, " + "RDB$SYSTEM_FLAG, " + "RDB$RELATION_TYPE, " + "RDB$DESCRIPTION, " + "RDB$VIEW_BLR " + "FROM RDB$RELATIONS " + "WHERE "); + + // TODO: GLOBAL TEMPORARY, LOCAL TEMPORARY, ALIAS, SYNONYM + if (!types.hasElements() || (types.getLength() == 1 && types[0].match(wld))) + { + // All table types? I.e. includes system tables. + queryBuf.append("(RDB$RELATION_TYPE = 0 OR RDB$RELATION_TYPE = 1) "); + } + else + { + queryBuf.append("( (0 = 1) "); + for (OUString const & t : types) + { + if (t == "SYSTEM TABLE") + queryBuf.append("OR (RDB$SYSTEM_FLAG = 1 AND RDB$VIEW_BLR IS NULL) "); + else if (t == "TABLE") + queryBuf.append("OR (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0 AND RDB$VIEW_BLR IS NULL) "); + else if (t == "VIEW") + queryBuf.append("OR (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0 AND RDB$VIEW_BLR IS NOT NULL) "); + else + throw SQLException(); // TODO: implement other types, see above. + } + queryBuf.append(") "); + } + + if (!tableNamePattern.isEmpty()) + { + OUString sAppend; + if (tableNamePattern.match(wld)) + sAppend = "AND RDB$RELATION_NAME LIKE '%' "; + else + sAppend = "AND RDB$RELATION_NAME = '%' "; + + queryBuf.append(sAppend.replaceAll(wld, tableNamePattern)); + } + + queryBuf.append(" ORDER BY RDB$RELATION_TYPE, RDB$RELATION_NAME"); + + OUString query = queryBuf.makeStringAndClear(); + + uno::Reference< XResultSet > rs = statement->executeQuery(query.getStr()); + uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + ODatabaseMetaDataResultSet::ORows aResults; + + ODatabaseMetaDataResultSet::ORow aCurrentRow(6); + aCurrentRow[0] = new ORowSetValueDecorator(); // 0. Unused + aCurrentRow[1] = new ORowSetValueDecorator(); // 1. Table_Cat Unsupported + aCurrentRow[2] = new ORowSetValueDecorator(); // 2. Table_Schem Unsupported + + while( rs->next() ) + { + // 3. TABLE_NAME + aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1))); + // 4. TABLE_TYPE + { + // TODO: check this as the docs are a bit unclear. + sal_Int16 nSystemFlag = xRow->getShort(2); + sal_Int16 nTableType = xRow->getShort(3); + xRow->getBlob(5); // We have to retrieve a column to verify it is null. + bool aIsView = !xRow->wasNull(); + OUString sTableType; + + if (nSystemFlag == 1) + { + sTableType = "SYSTEM TABLE"; + } + else if (aIsView) + { + sTableType = "VIEW"; + } + else + { + if (nTableType == 0) + sTableType = "TABLE"; + } + + aCurrentRow[4] = new ORowSetValueDecorator(sTableType); + } + // 5. REMARKS + { + uno::Reference< XClob > xClob = xRow->getClob(4); + if (xClob.is()) + { + aCurrentRow[5] = new ORowSetValueDecorator(xClob->getSubString(0, xClob->length())); + } + } + + aResults.push_back(aCurrentRow); + } + + pResultSet->setRows( aResults ); + + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedureColumns( + const Any&, const OUString&, + const OUString&, const OUString& ) +{ + SAL_WARN("connectivity.firebird", "Not yet implemented"); + OSL_FAIL("Not implemented yet!"); + // TODO implement + return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eProcedureColumns); +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedures( + const Any&, const OUString&, + const OUString& ) +{ + SAL_WARN("connectivity.firebird", "Not yet implemented"); + OSL_FAIL("Not implemented yet!"); + // TODO implement + return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eProcedures); +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getVersionColumns( + const Any&, const OUString&, const OUString& ) +{ + SAL_WARN("connectivity.firebird", "Not yet implemented"); + OSL_FAIL("Not implemented yet!"); + // TODO implement + return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eVersionColumns); +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getExportedKeys( + const Any&, const OUString&, const OUString& table ) +{ + return ODatabaseMetaData::lcl_getKeys(false, table); +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getImportedKeys( + const Any&, const OUString&, const OUString& table ) +{ + return ODatabaseMetaData::lcl_getKeys(true, table); +} + +uno::Reference< XResultSet > ODatabaseMetaData::lcl_getKeys(const bool& bIsImport, const OUString& table ) +{ + ODatabaseMetaDataResultSet* pResultSet = new + ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eImportedKeys); + uno::Reference< XResultSet > xResultSet = pResultSet; + + uno::Reference< XStatement > statement = m_pConnection->createStatement(); + + OUString sSQL = "SELECT " + "RDB$REF_CONSTRAINTS.RDB$UPDATE_RULE, " // 1 update rule + "RDB$REF_CONSTRAINTS.RDB$DELETE_RULE, " // 2 delete rule + "RDB$REF_CONSTRAINTS.RDB$CONST_NAME_UQ, " // 3 primary or unique key name + "RDB$REF_CONSTRAINTS.RDB$CONSTRAINT_NAME, " // 4 foreign key name + "PRIM.RDB$DEFERRABLE, " // 5 deferrability + "PRIM.RDB$INITIALLY_DEFERRED, " // 6 deferrability + "PRIM.RDB$RELATION_NAME, " // 7 PK table name + "PRIMARY_INDEX.RDB$FIELD_NAME, " // 8 PK column name + "PRIMARY_INDEX.RDB$FIELD_POSITION, " // 9 PK sequence number + "FOREI.RDB$RELATION_NAME, " // 10 FK table name + "FOREIGN_INDEX.RDB$FIELD_NAME " // 11 FK column name + "FROM RDB$REF_CONSTRAINTS " + "INNER JOIN RDB$RELATION_CONSTRAINTS AS PRIM " + "ON RDB$REF_CONSTRAINTS.RDB$CONST_NAME_UQ = PRIM.RDB$CONSTRAINT_NAME " + "INNER JOIN RDB$RELATION_CONSTRAINTS AS FOREI " + "ON RDB$REF_CONSTRAINTS.RDB$CONSTRAINT_NAME = FOREI.RDB$CONSTRAINT_NAME " + "INNER JOIN RDB$INDEX_SEGMENTS AS PRIMARY_INDEX " + "ON PRIM.RDB$INDEX_NAME = PRIMARY_INDEX.RDB$INDEX_NAME " + "INNER JOIN RDB$INDEX_SEGMENTS AS FOREIGN_INDEX " + "ON FOREI.RDB$INDEX_NAME = FOREIGN_INDEX.RDB$INDEX_NAME " + "WHERE FOREI.RDB$CONSTRAINT_TYPE = 'FOREIGN KEY' "; + if (bIsImport) + sSQL += "AND FOREI.RDB$RELATION_NAME = '"+ table +"'"; + else + sSQL += "AND PRIM.RDB$RELATION_NAME = '"+ table +"'"; + + uno::Reference< XResultSet > rs = statement->executeQuery(sSQL); + uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + + ODatabaseMetaDataResultSet::ORows aResults; + ODatabaseMetaDataResultSet::ORow aCurrentRow(15); + + // TODO is it necessary to initialize these? + aCurrentRow[0] = new ORowSetValueDecorator(); // Unused + aCurrentRow[1] = new ORowSetValueDecorator(); // PKTABLE_CAT unsupported + aCurrentRow[2] = new ORowSetValueDecorator(); // PKTABLE_SCHEM unsupported + aCurrentRow[5] = new ORowSetValueDecorator(); // FKTABLE_CAT unsupported + aCurrentRow[6] = new ORowSetValueDecorator(); // FKTABLE_SCHEM unsupported + + std::map< OUString,sal_Int32> aRuleMap; + aRuleMap[ OUString("CASCADE")] = KeyRule::CASCADE; + aRuleMap[ OUString("RESTRICT")] = KeyRule::RESTRICT; + aRuleMap[ OUString("SET NULL")] = KeyRule::SET_NULL; + aRuleMap[ OUString("SET DEFAULT")] = KeyRule::SET_DEFAULT; + aRuleMap[ OUString("NO ACTION")] = KeyRule::NO_ACTION; + + while(rs->next()) + { + aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(7))); // PK table + aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(8))); // PK column + aCurrentRow[7] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(10))); // FK table + aCurrentRow[8] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(11))); // FK column + + aCurrentRow[9] = new ORowSetValueDecorator(xRow->getShort(9)); // PK sequence number + aCurrentRow[10] = new ORowSetValueDecorator(aRuleMap[sanitizeIdentifier(xRow->getString(1))]); // update role + aCurrentRow[11] = new ORowSetValueDecorator(aRuleMap[sanitizeIdentifier(xRow->getString(2))]); // delete role + + aCurrentRow[12] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4))); // FK name + aCurrentRow[13] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // PK name + + aCurrentRow[14] = new ORowSetValueDecorator(Deferrability::NONE); // deferrability + + // deferrability is currently not supported, but may be supported in the future. + /* + aCurrentRow[14] = (xRow->getString(5) == "NO" ? + new ORowSetValueDecorator(Deferrability::NONE) + : (xRow->getString(6) == "NO" ? + new ORowSetValueDecorator(Deferrability::INITIALLY_IMMEDIATE) + : new ORowSetValueDecorator(Deferrability::INITIALLY_DEFERRED)); + */ + + aResults.push_back(aCurrentRow); + } + + pResultSet->setRows( aResults ); + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getPrimaryKeys( + const Any& /*aCatalog*/, + const OUString& /*sSchema*/, + const OUString& sTable) +{ + SAL_INFO("connectivity.firebird", "getPrimaryKeys() with " + "Table: " << sTable); + + OUString sAppend = "WHERE constr.RDB$RELATION_NAME = '%' "; + OUString sQuery = "SELECT " + "constr.RDB$RELATION_NAME, " // 1. Table Name + "inds.RDB$FIELD_NAME, " // 2. Column Name + "inds.RDB$FIELD_POSITION, " // 3. Sequence Number + "constr.RDB$CONSTRAINT_NAME " // 4 Constraint name + "FROM RDB$RELATION_CONSTRAINTS constr " + "JOIN RDB$INDEX_SEGMENTS inds " + "on (constr.RDB$INDEX_NAME = inds.RDB$INDEX_NAME) " + + sAppend.replaceAll("%", sTable) + + "AND constr.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' " + "ORDER BY inds.RDB$FIELD_NAME"; + + uno::Reference< XStatement > xStatement = m_pConnection->createStatement(); + uno::Reference< XResultSet > xRs = xStatement->executeQuery(sQuery); + uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW ); + + ODatabaseMetaDataResultSet::ORows aResults; + ODatabaseMetaDataResultSet::ORow aCurrentRow(7); + + aCurrentRow[0] = new ORowSetValueDecorator(); // Unused -- numbering starts from 0 + aCurrentRow[1] = new ORowSetValueDecorator(); // Catalog - can be null + aCurrentRow[2] = new ORowSetValueDecorator(); // Schema - can be null + + while(xRs->next()) + { + // 3. Table Name + if (xRs->getRow() == 1) // Table name doesn't change, so only retrieve once + { + aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1))); + } + // 4. Column Name + aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); + // 5. KEY_SEQ (which key in the sequence) + aCurrentRow[5] = new ORowSetValueDecorator(xRow->getShort(3)); + // 6. Primary Key Name + aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4))); + + aResults.push_back(aCurrentRow); + } + ODatabaseMetaDataResultSet* pResultSet = new + ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::ePrimaryKeys); + uno::Reference< XResultSet > xResultSet = pResultSet; + pResultSet->setRows( aResults ); + + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getIndexInfo( + const Any& /*aCatalog*/, + const OUString& /*sSchema*/, + const OUString& sTable, + sal_Bool bIsUnique, + sal_Bool) // TODO: what is bIsApproximate? + +{ + // Apparently this method can also return a "tableIndexStatistic" + // However this is only mentioned in XDatabaseMetaData.idl (whose comments + // are duplicated in the postgresql driver), and is otherwise undocumented. + SAL_INFO("connectivity.firebird", "getPrimaryKeys() with " + "Table: " << sTable); + + OUStringBuffer aQueryBuf("SELECT " + "indices.RDB$RELATION_NAME, " // 1. Table Name + "index_segments.RDB$FIELD_NAME, " // 2. Column Name + "index_segments.RDB$FIELD_POSITION, " // 3. Sequence Number + "indices.RDB$INDEX_NAME, " // 4. Index name + "indices.RDB$UNIQUE_FLAG, " // 5. Unique Flag + "indices.RDB$INDEX_TYPE " // 6. Index Type + "FROM RDB$INDICES indices " + "JOIN RDB$INDEX_SEGMENTS index_segments " + "on (indices.RDB$INDEX_NAME = index_segments.RDB$INDEX_NAME) " + "WHERE indices.RDB$RELATION_NAME = '" + sTable + "' " + "AND (indices.RDB$SYSTEM_FLAG = 0) "); + // Not sure whether we should exclude system indices, but otoh. we never + // actually deal with system tables (system indices only apply to system + // tables) within the GUI. + + // Only filter if true (according to the docs), i.e.: + // If false we return all indices, if true we return only unique indices + if (bIsUnique) + aQueryBuf.append("AND (indices.RDB$UNIQUE_FLAG = 1) "); + + OUString sQuery = aQueryBuf.makeStringAndClear(); + + uno::Reference< XStatement > xStatement = m_pConnection->createStatement(); + uno::Reference< XResultSet > xRs = xStatement->executeQuery(sQuery); + uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW ); + + ODatabaseMetaDataResultSet::ORows aResults; + ODatabaseMetaDataResultSet::ORow aCurrentRow(14); + + aCurrentRow[0] = new ORowSetValueDecorator(); // Unused -- numbering starts from 0 + aCurrentRow[1] = new ORowSetValueDecorator(); // Catalog - can be null + aCurrentRow[2] = new ORowSetValueDecorator(); // Schema - can be null + aCurrentRow[5] = new ORowSetValueDecorator(); // Index Catalog -- can be null + // Wikipedia indicates: + // 'Firebird makes all indices of the database behave like well-tuned "clustered indexes" used by other architectures.' + // but it's not "CLUSTERED", neither "STATISTIC" nor "HASHED" (the other specific types from offapi/com/sun/star/sdbc/IndexType.idl) + // According to https://www.ibphoenix.com/resources/documents/design/doc_18, + // it seems another type => OTHER + aCurrentRow[7] = new ORowSetValueDecorator(IndexType::OTHER); // 7. INDEX TYPE + aCurrentRow[13] = new ORowSetValueDecorator(); // Filter Condition -- can be null + + while(xRs->next()) + { + // 3. Table Name + if (xRs->getRow() == 1) // Table name doesn't change, so only retrieve once + { + aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1))); + } + + // 4. NON_UNIQUE -- i.e. specifically negate here. + aCurrentRow[4] = new ORowSetValueDecorator(xRow->getShort(5) == 0); + // 6. INDEX NAME + aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4))); + + // 8. ORDINAL POSITION + aCurrentRow[8] = new ORowSetValueDecorator(xRow->getShort(3)); + // 9. COLUMN NAME + aCurrentRow[9] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); + // 10. ASC(ending)/DESC(ending) + if (xRow->getShort(6) == 1) + aCurrentRow[10] = new ORowSetValueDecorator(OUString("D")); + else + aCurrentRow[10] = new ORowSetValueDecorator(OUString("A")); + // TODO: double check this^^^, doesn't seem to be officially documented anywhere. + // 11. CARDINALITY + aCurrentRow[11] = new ORowSetValueDecorator(sal_Int32(0)); // TODO: determine how to do this + // 12. PAGES + aCurrentRow[12] = new ORowSetValueDecorator(sal_Int32(0)); // TODO: determine how to do this + + aResults.push_back(aCurrentRow); + } + ODatabaseMetaDataResultSet* pResultSet = new + ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::ePrimaryKeys); + uno::Reference< XResultSet > xResultSet = pResultSet; + pResultSet->setRows( aResults ); + + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getBestRowIdentifier( + const Any&, const OUString&, const OUString&, sal_Int32, + sal_Bool ) +{ + OSL_FAIL("Not implemented yet!"); + // TODO implement + return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eBestRowIdentifier); +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTablePrivileges( + const Any& /*aCatalog*/, + const OUString& /*sSchemaPattern*/, + const OUString& sTableNamePattern) +{ + SAL_INFO("connectivity.firebird", "getTablePrivileges() with " + "TableNamePattern: " << sTableNamePattern); + + ODatabaseMetaDataResultSet* pResultSet = new + ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTablePrivileges); + uno::Reference< XResultSet > xResultSet = pResultSet; + uno::Reference< XStatement > statement = m_pConnection->createStatement(); + + // TODO: column specific privileges are included, we may need + // to have WHERE RDB$FIELD_NAME = NULL or similar. + static const char wld[] = "%"; + OUStringBuffer queryBuf( + "SELECT " + "priv.RDB$RELATION_NAME, " // 1 + "priv.RDB$GRANTOR," // 2 + "priv.RDB$USER, " // 3 Grantee + "priv.RDB$PRIVILEGE, " // 4 + "priv.RDB$GRANT_OPTION " // 5 is Grantable + "FROM RDB$USER_PRIVILEGES priv "); + + if (!sTableNamePattern.isEmpty()) + { + OUString sAppend; + if (sTableNamePattern.match(wld)) + sAppend = "WHERE priv.RDB$RELATION_NAME LIKE '%' "; + else + sAppend = "WHERE priv.RDB$RELATION_NAME = '%' "; + + queryBuf.append(sAppend.replaceAll(wld, sTableNamePattern)); + } + queryBuf.append(" ORDER BY priv.RDB$RELATION_TYPE, " + "priv.RDB$RELATION_NAME, " + "priv.RDB$PRIVILEGE"); + + OUString query = queryBuf.makeStringAndClear(); + + uno::Reference< XResultSet > rs = statement->executeQuery(query.getStr()); + uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + ODatabaseMetaDataResultSet::ORows aResults; + + ODatabaseMetaDataResultSet::ORow aRow(8); + aRow[0] = new ORowSetValueDecorator(); // Unused + aRow[1] = new ORowSetValueDecorator(); // TABLE_CAT unsupported + aRow[2] = new ORowSetValueDecorator(); // TABLE_SCHEM unsupported. + + while( rs->next() ) + { + // 3. TABLE_NAME + aRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1))); + aRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); // 4. GRANTOR + aRow[5] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // 5. GRANTEE + aRow[6] = new ORowSetValueDecorator(xRow->getString(4)); // 6. Privilege + aRow[7] = new ORowSetValueDecorator(bool(xRow->getBoolean(5))); // 7. Is Grantable + + aResults.push_back(aRow); + } + + pResultSet->setRows( aResults ); + + return xResultSet; +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCrossReference( + const Any&, const OUString&, + const OUString&, const Any&, + const OUString&, const OUString& ) +{ + OSL_FAIL("Not implemented yet!"); + // TODO implement + return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eCrossReference); +} + +uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getUDTs( const Any&, const OUString&, const OUString&, const Sequence< sal_Int32 >& ) +{ + OSL_FAIL("Not implemented yet!"); + // TODO implement + return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eUDTs); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/DatabaseMetaData.hxx b/connectivity/source/drivers/firebird/DatabaseMetaData.hxx new file mode 100644 index 000000000..95e744bcf --- /dev/null +++ b/connectivity/source/drivers/firebird/DatabaseMetaData.hxx @@ -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 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_DATABASEMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_DATABASEMETADATA_HXX + +#include "Connection.hxx" + +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <cppuhelper/implbase.hxx> + +namespace connectivity +{ + namespace firebird + { + + //************ Class: ODatabaseMetaData + + + typedef ::cppu::WeakImplHelper< css::sdbc::XDatabaseMetaData> ODatabaseMetaData_BASE; + + class ODatabaseMetaData : public ODatabaseMetaData_BASE + { + ::rtl::Reference<Connection> m_pConnection; + private: + css::uno::Reference< css::sdbc::XResultSet > lcl_getKeys( const bool& bIsImport, const OUString& table ); + public: + + explicit ODatabaseMetaData(Connection* _pCon); + virtual ~ODatabaseMetaData() override; + + // as I mentioned before this interface is really BIG + // XDatabaseMetaData + virtual sal_Bool SAL_CALL allProceduresAreCallable( ) override; + virtual sal_Bool SAL_CALL allTablesAreSelectable( ) override; + virtual OUString SAL_CALL getURL( ) override; + virtual OUString SAL_CALL getUserName( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedHigh( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedLow( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtStart( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtEnd( ) override; + virtual OUString SAL_CALL getDatabaseProductName( ) override; + virtual OUString SAL_CALL getDatabaseProductVersion( ) override; + virtual OUString SAL_CALL getDriverName( ) override; + virtual OUString SAL_CALL getDriverVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMajorVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMinorVersion( ) override; + virtual sal_Bool SAL_CALL usesLocalFiles( ) override; + virtual sal_Bool SAL_CALL usesLocalFilePerTable( ) override; + virtual sal_Bool SAL_CALL supportsMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL supportsMixedCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesMixedCaseQuotedIdentifiers( ) override; + virtual OUString SAL_CALL getIdentifierQuoteString( ) override; + virtual OUString SAL_CALL getSQLKeywords( ) override; + virtual OUString SAL_CALL getNumericFunctions( ) override; + virtual OUString SAL_CALL getStringFunctions( ) override; + virtual OUString SAL_CALL getSystemFunctions( ) override; + virtual OUString SAL_CALL getTimeDateFunctions( ) override; + virtual OUString SAL_CALL getSearchStringEscape( ) override; + virtual OUString SAL_CALL getExtraNameCharacters( ) override; + virtual sal_Bool SAL_CALL supportsAlterTableWithAddColumn( ) override; + virtual sal_Bool SAL_CALL supportsAlterTableWithDropColumn( ) override; + virtual sal_Bool SAL_CALL supportsColumnAliasing( ) override; + virtual sal_Bool SAL_CALL nullPlusNonNullIsNull( ) override; + virtual sal_Bool SAL_CALL supportsTypeConversion( ) override; + virtual sal_Bool SAL_CALL supportsConvert( sal_Int32 fromType, sal_Int32 toType ) override; + virtual sal_Bool SAL_CALL supportsTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsDifferentTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsExpressionsInOrderBy( ) override; + virtual sal_Bool SAL_CALL supportsOrderByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupBy( ) override; + virtual sal_Bool SAL_CALL supportsGroupByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupByBeyondSelect( ) override; + virtual sal_Bool SAL_CALL supportsLikeEscapeClause( ) override; + virtual sal_Bool SAL_CALL supportsMultipleResultSets( ) override; + virtual sal_Bool SAL_CALL supportsMultipleTransactions( ) override; + virtual sal_Bool SAL_CALL supportsNonNullableColumns( ) override; + virtual sal_Bool SAL_CALL supportsMinimumSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsCoreSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsExtendedSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsANSI92EntryLevelSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92IntermediateSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92FullSQL( ) override; + virtual sal_Bool SAL_CALL supportsIntegrityEnhancementFacility( ) override; + virtual sal_Bool SAL_CALL supportsOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsFullOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsLimitedOuterJoins( ) override; + virtual OUString SAL_CALL getSchemaTerm( ) override; + virtual OUString SAL_CALL getProcedureTerm( ) override; + virtual OUString SAL_CALL getCatalogTerm( ) override; + virtual sal_Bool SAL_CALL isCatalogAtStart( ) override; + virtual OUString SAL_CALL getCatalogSeparator( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInDataManipulation( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInTableDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInDataManipulation( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInTableDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsPositionedDelete( ) override; + virtual sal_Bool SAL_CALL supportsPositionedUpdate( ) override; + virtual sal_Bool SAL_CALL supportsSelectForUpdate( ) override; + virtual sal_Bool SAL_CALL supportsStoredProcedures( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInComparisons( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInExists( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInIns( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInQuantifieds( ) override; + virtual sal_Bool SAL_CALL supportsCorrelatedSubqueries( ) override; + virtual sal_Bool SAL_CALL supportsUnion( ) override; + virtual sal_Bool SAL_CALL supportsUnionAll( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossRollback( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossRollback( ) override; + virtual sal_Int32 SAL_CALL getMaxBinaryLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCharLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInGroupBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInIndex( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInOrderBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInSelect( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInTable( ) override; + virtual sal_Int32 SAL_CALL getMaxConnections( ) override; + virtual sal_Int32 SAL_CALL getMaxCursorNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxIndexLength( ) override; + virtual sal_Int32 SAL_CALL getMaxSchemaNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxProcedureNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCatalogNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxRowSize( ) override; + virtual sal_Bool SAL_CALL doesMaxRowSizeIncludeBlobs( ) override; + virtual sal_Int32 SAL_CALL getMaxStatementLength( ) override; + virtual sal_Int32 SAL_CALL getMaxStatements( ) override; + virtual sal_Int32 SAL_CALL getMaxTableNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxTablesInSelect( ) override; + virtual sal_Int32 SAL_CALL getMaxUserNameLength( ) override; + virtual sal_Int32 SAL_CALL getDefaultTransactionIsolation( ) override; + virtual sal_Bool SAL_CALL supportsTransactions( ) override; + virtual sal_Bool SAL_CALL supportsTransactionIsolationLevel( sal_Int32 level ) override; + virtual sal_Bool SAL_CALL supportsDataDefinitionAndDataManipulationTransactions( ) override; + virtual sal_Bool SAL_CALL supportsDataManipulationTransactionsOnly( ) override; + virtual sal_Bool SAL_CALL dataDefinitionCausesTransactionCommit( ) override; + virtual sal_Bool SAL_CALL dataDefinitionIgnoredInTransactions( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getProcedures( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getProcedureColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTables( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const css::uno::Sequence< OUString >& types ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getSchemas( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getCatalogs( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTableTypes( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumnPrivileges( const css::uno::Any& catalog, const OUString& schema, const OUString& table, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTablePrivileges( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getBestRowIdentifier( const css::uno::Any& catalog, const OUString& schema, const OUString& table, sal_Int32 scope, sal_Bool nullable ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getVersionColumns( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getPrimaryKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getImportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getExportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getCrossReference( const css::uno::Any& primaryCatalog, const OUString& primarySchema, const OUString& primaryTable, const css::uno::Any& foreignCatalog, const OUString& foreignSchema, const OUString& foreignTable ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTypeInfo( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getIndexInfo( const css::uno::Any& catalog, const OUString& schema, const OUString& table, sal_Bool unique, sal_Bool approximate ) override; + virtual sal_Bool SAL_CALL supportsResultSetType( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 concurrency ) override; + virtual sal_Bool SAL_CALL ownUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL updatesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL deletesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL insertsAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsBatchUpdates( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getUDTs( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& typeNamePattern, const css::uno::Sequence< sal_Int32 >& types ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_DATABASEMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Driver.cxx b/connectivity/source/drivers/firebird/Driver.cxx new file mode 100644 index 000000000..aa6f0d9d5 --- /dev/null +++ b/connectivity/source/drivers/firebird/Driver.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 "Connection.hxx" +#include "Driver.hxx" +#include "SubComponent.hxx" + +#include <connectivity/dbexception.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + +#include <comphelper/processfactory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/file.hxx> +#include <osl/process.h> +#include <rtl/bootstrap.hxx> +#include <sal/log.hxx> +#include <unotools/localfilehelper.hxx> + +using namespace com::sun::star; +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 ::osl; + +using namespace connectivity::firebird; + +namespace connectivity::firebird +{ + Reference< XInterface > FirebirdDriver_CreateInstance( + const Reference< XMultiServiceFactory >& _rxFactory) + { + SAL_INFO("connectivity.firebird", "FirebirdDriver_CreateInstance()" ); + return *(new FirebirdDriver(comphelper::getComponentContext(_rxFactory))); + } +} + +// Static const variables +namespace { +const char our_sFirebirdTmpVar[] = "FIREBIRD_TMP"; +const char our_sFirebirdLockVar[] = "FIREBIRD_LOCK"; +const char our_sFirebirdMsgVar[] = "FIREBIRD_MSG"; +#ifdef MACOSX +const char our_sFirebirdLibVar[] = "LIBREOFFICE_FIREBIRD_LIB"; +#endif +}; + +FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentContext >& _rxContext) + : ODriver_BASE(m_aMutex) + , m_aContext(_rxContext) + , m_firebirdTMPDirectory(nullptr, true) + , m_firebirdLockDirectory(nullptr, true) +{ + // ::utl::TempFile uses a unique temporary directory (subdirectory of + // /tmp or other user specific tmp directory) per instance in which + // we can create directories for firebird at will. + + // Overrides firebird's default of /tmp or c:\temp + osl_setEnvironment(OUString(our_sFirebirdTmpVar).pData, m_firebirdTMPDirectory.GetFileName().pData); + + // Overrides firebird's default of /tmp/firebird or c:\temp\firebird + osl_setEnvironment(OUString(our_sFirebirdLockVar).pData, m_firebirdLockDirectory.GetFileName().pData); + +#ifndef SYSTEM_FIREBIRD + // Overrides firebird's hardcoded default of /usr/local/firebird on *nix, + // however on Windows it seems to use the current directory as a default. + OUString sMsgURL("$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/firebird"); + ::rtl::Bootstrap::expandMacros(sMsgURL); + OUString sMsgPath; + ::osl::FileBase::getSystemPathFromFileURL(sMsgURL, sMsgPath); + osl_setEnvironment(OUString(our_sFirebirdMsgVar).pData, sMsgPath.pData); +#ifdef MACOSX + // Set an env. variable to specify library location + // for dlopen used in fbclient. + OUString sLibURL("$LO_LIB_DIR"); + ::rtl::Bootstrap::expandMacros(sLibURL); + OUString sLibPath; + ::osl::FileBase::getSystemPathFromFileURL(sLibURL, sLibPath); + osl_setEnvironment(OUString(our_sFirebirdLibVar).pData, sLibPath.pData); +#endif /*MACOSX*/ +#endif /*!SYSTEM_FIREBIRD*/ +} + +FirebirdDriver::~FirebirdDriver() +{ + utl::removeTree(m_firebirdTMPDirectory.GetURL()); + utl::removeTree(m_firebirdLockDirectory.GetURL()); +} + +void FirebirdDriver::disposing() +{ + MutexGuard aGuard(m_aMutex); + + for (auto const& elem : m_xConnections) + { + Reference< XComponent > xComp(elem.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + } + m_xConnections.clear(); + + osl_clearEnvironment(OUString(our_sFirebirdTmpVar).pData); + osl_clearEnvironment(OUString(our_sFirebirdLockVar).pData); + +#ifndef SYSTEM_FIREBIRD + osl_clearEnvironment(OUString(our_sFirebirdMsgVar).pData); +#ifdef MACOSX + osl_clearEnvironment(OUString(our_sFirebirdLibVar).pData); +#endif /*MACOSX*/ +#endif /*!SYSTEM_FIREBIRD*/ + + OSL_VERIFY(fb_shutdown(0, 1)); + + ODriver_BASE::disposing(); +} + +//----- static ServiceInfo --------------------------------------------------- +OUString FirebirdDriver::getImplementationName_Static() +{ + return "com.sun.star.comp.sdbc.firebird.Driver"; +} + +Sequence< OUString > FirebirdDriver::getSupportedServiceNames_Static() +{ + return { "com.sun.star.sdbc.Driver", "com.sun.star.sdbcx.Driver" }; +} + +OUString SAL_CALL FirebirdDriver::getImplementationName() +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL FirebirdDriver::supportsService(const OUString& _rServiceName) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > SAL_CALL FirebirdDriver::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +// ---- XDriver ------------------------------------------------------------- +Reference< XConnection > SAL_CALL FirebirdDriver::connect( + const OUString& url, const Sequence< PropertyValue >& info ) +{ + SAL_INFO("connectivity.firebird", "connect(), URL: " << url ); + + MutexGuard aGuard( m_aMutex ); + if (ODriver_BASE::rBHelper.bDisposed) + throw DisposedException(); + + if ( ! acceptsURL(url) ) + return nullptr; + + Connection* pCon = new Connection(); + Reference< XConnection > xCon = pCon; + pCon->construct(url, info); + + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return xCon; +} + +sal_Bool SAL_CALL FirebirdDriver::acceptsURL( const OUString& url ) +{ + return (url == "sdbc:embedded:firebird" || url.startsWith("sdbc:firebird:")); +} + +Sequence< DriverPropertyInfo > SAL_CALL FirebirdDriver::getPropertyInfo( + const OUString& url, const Sequence< PropertyValue >& ) +{ + if ( ! acceptsURL(url) ) + { + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + + return Sequence< DriverPropertyInfo >(); +} + +sal_Int32 SAL_CALL FirebirdDriver::getMajorVersion( ) +{ + // The major and minor version are sdbc driver specific. Must begin with 1.0 + // as per https://api.libreoffice.org/docs/common/ref/com/sun/star/sdbc/XDriver.html + return 1; +} + +sal_Int32 SAL_CALL FirebirdDriver::getMinorVersion( ) +{ + return 0; +} + +//----- XDataDefinitionSupplier +uno::Reference< XTablesSupplier > SAL_CALL FirebirdDriver::getDataDefinitionByConnection( + const uno::Reference< XConnection >& rConnection) +{ + Connection* pConnection = static_cast< Connection* >(rConnection.get()); + return pConnection->createCatalog(); +} + +uno::Reference< XTablesSupplier > SAL_CALL FirebirdDriver::getDataDefinitionByURL( + const OUString& rURL, + const uno::Sequence< PropertyValue >& rInfo) +{ + uno::Reference< XConnection > xConnection = connect(rURL, rInfo); + return getDataDefinitionByConnection(xConnection); +} + +namespace connectivity::firebird +{ + void checkDisposed(bool _bThrow) + { + if (_bThrow) + throw DisposedException(); + + } + +} // namespace + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Driver.hxx b/connectivity/source/drivers/firebird/Driver.hxx new file mode 100644 index 000000000..8fcf17fa2 --- /dev/null +++ b/connectivity/source/drivers/firebird/Driver.hxx @@ -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 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_DRIVER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_DRIVER_HXX + +#include "Connection.hxx" + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdbc/XDriver.hpp> +#include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp> +#include <cppuhelper/compbase.hxx> +#include <unotools/tempfile.hxx> + +namespace connectivity +{ + namespace firebird + { + // The SQL dialect in use + // Has to be used in various isc_* calls. + // 3: Is IB6 -- minimum required for delimited identifiers. + static const int FIREBIRD_SQL_DIALECT = 3; + + /// @throws css::uno::Exception + css::uno::Reference< css::uno::XInterface > FirebirdDriver_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory); + + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XDriver, + css::sdbcx::XDataDefinitionSupplier, + css::lang::XServiceInfo > ODriver_BASE; + + class FirebirdDriver : public ODriver_BASE + { + private: + css::uno::Reference<css::uno::XComponentContext> m_aContext; + ::utl::TempFile m_firebirdTMPDirectory; + ::utl::TempFile m_firebirdLockDirectory; + + protected: + ::osl::Mutex m_aMutex; // mutex is need to control member access + OWeakRefArray m_xConnections; // vector containing a list + // of all the Connection objects + // for this Driver + + public: + + explicit FirebirdDriver(const css::uno::Reference< css::uno::XComponentContext >& _rxContext); + virtual ~FirebirdDriver() override; + const css::uno::Reference<css::uno::XComponentContext>& getContext() const { return m_aContext; } + + // OComponentHelper + virtual void SAL_CALL disposing() override; + // XInterface + /// @throws css::uno::RuntimeException + static OUString getImplementationName_Static( ); + /// @throws css::uno::RuntimeException + static css::uno::Sequence< OUString > getSupportedServiceNames_Static( ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XDriver + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL connect( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + virtual sal_Bool SAL_CALL acceptsURL( const OUString& url ) override; + virtual css::uno::Sequence< css::sdbc::DriverPropertyInfo > SAL_CALL getPropertyInfo( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + virtual sal_Int32 SAL_CALL getMajorVersion( ) override; + virtual sal_Int32 SAL_CALL getMinorVersion( ) override; + + // XDataDefinitionSupplier + virtual css::uno::Reference< css::sdbcx::XTablesSupplier > + SAL_CALL getDataDefinitionByConnection( + const css::uno::Reference< css::sdbc::XConnection >& rxConnection) override; + virtual css::uno::Reference< css::sdbcx::XTablesSupplier > + SAL_CALL getDataDefinitionByURL( + const OUString& rsURL, + const css::uno::Sequence< css::beans::PropertyValue >& rInfo) override; + }; + } + +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_DRIVER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Indexes.cxx b/connectivity/source/drivers/firebird/Indexes.cxx new file mode 100644 index 000000000..86f4f9f6f --- /dev/null +++ b/connectivity/source/drivers/firebird/Indexes.cxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "Indexes.hxx" + +using namespace ::connectivity; +using namespace ::connectivity::firebird; + +using namespace ::osl; +using namespace ::std; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::sdbc; + +Indexes::Indexes(Table* pTable, Mutex& rMutex, const vector< OUString>& rVector) + : OIndexesHelper(pTable, rMutex, rVector) + , m_pTable(pTable) +{ +} + +// XDrop +void Indexes::dropObject(sal_Int32 /*nPosition*/, const OUString& sIndexName) +{ + OUString sSql("DROP INDEX \"" + sIndexName +"\""); + m_pTable->getConnection()->createStatement()->execute(sSql); +} diff --git a/connectivity/source/drivers/firebird/Indexes.hxx b/connectivity/source/drivers/firebird/Indexes.hxx new file mode 100644 index 000000000..364b36b04 --- /dev/null +++ b/connectivity/source/drivers/firebird/Indexes.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_INDEXES_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_INDEXES_HXX + +#include "Table.hxx" + +#include <connectivity/TIndexes.hxx> + +namespace connectivity +{ + namespace firebird + { + + /** + * Firebird has a non-standard DROP INDEX statement, hence we need + * to override OIndexesHelper::dropObject + */ + class Indexes: public ::connectivity::OIndexesHelper + { + private: + Table* m_pTable; + protected: + // XDrop + virtual void dropObject(sal_Int32 nPosition, + const OUString& sIndexName) override; + public: + Indexes(Table* pTable, + ::osl::Mutex& rMutex, + const std::vector< OUString>& rVector); + }; + + } // namespace firebird +} // namespace connectivity + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_INDEXES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Keys.cxx b/connectivity/source/drivers/firebird/Keys.cxx new file mode 100644 index 000000000..dd4cca47f --- /dev/null +++ b/connectivity/source/drivers/firebird/Keys.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/. + */ + +#include "Keys.hxx" +#include "Table.hxx" + +#include <connectivity/dbtools.hxx> + +using namespace ::connectivity; +using namespace ::connectivity::firebird; + +using namespace ::dbtools; +using namespace ::osl; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; + +Keys::Keys(Table* pTable, Mutex& rMutex, const ::std::vector< OUString>& rNames): + OKeysHelper(pTable, + rMutex, + rNames), + m_pTable(pTable) +{ +} + +//----- XDrop ---------------------------------------------------------------- +void Keys::dropObject(sal_Int32 nPosition, const OUString& sName) +{ + if (m_pTable->isNew()) + return; + + uno::Reference<XPropertySet> xKey(getObject(nPosition), UNO_QUERY); + + if (xKey.is()) + { + const OUString sQuote = m_pTable->getConnection()->getMetaData() + ->getIdentifierQuoteString(); + + OUString sSql("ALTER TABLE " + quoteName(sQuote, m_pTable->getName()) + + " DROP CONSTRAINT " + quoteName(sQuote, sName)); + + m_pTable->getConnection()->createStatement()->execute(sSql); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Keys.hxx b/connectivity/source/drivers/firebird/Keys.hxx new file mode 100644 index 000000000..143dbbf54 --- /dev/null +++ b/connectivity/source/drivers/firebird/Keys.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_KEYS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_KEYS_HXX + +#include <connectivity/TKeys.hxx> + +namespace connectivity +{ + + namespace firebird + { + + class Table; + + class Keys: public ::connectivity::OKeysHelper + { + private: + Table* m_pTable; + + public: + Keys(Table* pTable, + ::osl::Mutex& rMutex, + const ::std::vector< OUString>& rNames); + + // OKeysHelper / XDrop + void dropObject(sal_Int32 nPosition, const OUString& sName) override; + + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_KEYS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/PreparedStatement.cxx b/connectivity/source/drivers/firebird/PreparedStatement.cxx new file mode 100644 index 000000000..e4510b758 --- /dev/null +++ b/connectivity/source/drivers/firebird/PreparedStatement.cxx @@ -0,0 +1,1018 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <cmath> + +#include <string_view> + +#include "Connection.hxx" +#include "PreparedStatement.hxx" +#include "ResultSet.hxx" +#include "ResultSetMetaData.hxx" +#include "Util.hxx" + +#include <comphelper/sequence.hxx> +#include <connectivity/dbexception.hxx> +#include <propertyids.hxx> +#include <connectivity/dbtools.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/sdbc/DataType.hpp> + +using namespace connectivity::firebird; + +using namespace ::comphelper; +using namespace ::osl; + +using namespace com::sun::star; +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::container; +using namespace com::sun::star::io; +using namespace com::sun::star::util; + +IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement"); + + +OPreparedStatement::OPreparedStatement( Connection* _pConnection, + const OUString& sql) + :OStatementCommonBase(_pConnection) + ,m_sSqlStatement(sql) + ,m_pOutSqlda(nullptr) + ,m_pInSqlda(nullptr) +{ + SAL_INFO("connectivity.firebird", "OPreparedStatement(). " + "sql: " << sql); +} + +void OPreparedStatement::ensurePrepared() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + + if (m_aStatementHandle) + return; + + ISC_STATUS aErr = 0; + + if (!m_pInSqlda) + { + m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(10))); + m_pInSqlda->version = SQLDA_VERSION1; + m_pInSqlda->sqln = 10; + } + + prepareAndDescribeStatement(m_sSqlStatement, + m_pOutSqlda, + m_pInSqlda); + + aErr = isc_dsql_describe_bind(m_statusVector, + &m_aStatementHandle, + 1, + m_pInSqlda); + + if (aErr) + { + SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed"); + } + else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough + { + short nItems = m_pInSqlda->sqld; + free(m_pInSqlda); + m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(nItems))); + m_pInSqlda->version = SQLDA_VERSION1; + m_pInSqlda->sqln = nItems; + aErr = isc_dsql_describe_bind(m_statusVector, + &m_aStatementHandle, + 1, + m_pInSqlda); + SAL_WARN_IF(aErr, "connectivity.firebird", "isc_dsql_describe_bind failed"); + } + + if (!aErr) + mallocSQLVAR(m_pInSqlda); + else + evaluateStatusVector(m_statusVector, m_sSqlStatement, *this); +} + +OPreparedStatement::~OPreparedStatement() +{ +} + +void SAL_CALL OPreparedStatement::acquire() throw() +{ + OStatementCommonBase::acquire(); +} + +void SAL_CALL OPreparedStatement::release() throw() +{ + OStatementCommonBase::release(); +} + +Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType) +{ + Any aRet = OStatementCommonBase::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OPreparedStatement_Base::queryInterface(rType); + return aRet; +} + +uno::Sequence< Type > SAL_CALL OPreparedStatement::getTypes() +{ + return concatSequences(OPreparedStatement_Base::getTypes(), + OStatementCommonBase::getTypes()); +} + +Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + ensurePrepared(); + + if(!m_xMetaData.is()) + m_xMetaData = new OResultSetMetaData(m_pConnection.get() + , m_pOutSqlda); + + return m_xMetaData; +} + +void SAL_CALL OPreparedStatement::close() +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + + OStatementCommonBase::close(); + if (m_pInSqlda) + { + freeSQLVAR(m_pInSqlda); + free(m_pInSqlda); + m_pInSqlda = nullptr; + } + if (m_pOutSqlda) + { + freeSQLVAR(m_pOutSqlda); + free(m_pOutSqlda); + m_pOutSqlda = nullptr; + } +} + +void SAL_CALL OPreparedStatement::disposing() +{ + close(); +} + +void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex, + const OUString& sInput) +{ + SAL_INFO("connectivity.firebird", + "setString(" << nParameterIndex << " , " << sInput << ")"); + + MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + ensurePrepared(); + + checkParameterIndex(nParameterIndex); + setParameterNull(nParameterIndex, false); + + OString str = OUStringToOString(sInput , RTL_TEXTENCODING_UTF8 ); + + XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1); + + int dtype = (pVar->sqltype & ~1); // drop flag bit for now + + if (str.getLength() > pVar->sqllen) + str = str.copy(0, pVar->sqllen); + + switch (dtype) { + case SQL_VARYING: + { + const sal_Int32 max_varchar_len = 0xFFFF; + // First 2 bytes indicate string size + if (str.getLength() > max_varchar_len) + { + str = str.copy(0, max_varchar_len); + } + const auto nLength = str.getLength(); + memcpy(pVar->sqldata, &nLength, 2); + // Actual data + memcpy(pVar->sqldata + 2, str.getStr(), str.getLength()); + break; + } + case SQL_TEXT: + memcpy(pVar->sqldata, str.getStr(), str.getLength()); + // Fill remainder with spaces + memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength()); + break; + case SQL_BLOB: // Clob + assert( pVar->sqlsubtype == static_cast<short>(BlobSubtype::Clob) ); + setClob(nParameterIndex, sInput ); + break; + case SQL_SHORT: + { + sal_Int32 int32Value = sInput.toInt32(); + if ( (int32Value < std::numeric_limits<sal_Int16>::min()) || + (int32Value > std::numeric_limits<sal_Int16>::max()) ) + { + ::dbtools::throwSQLException( + "Value out of range for SQL_SHORT type", + ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, + *this); + } + setShort(nParameterIndex, int32Value); + break; + } + case SQL_LONG: + { + sal_Int32 int32Value = sInput.toInt32(); + setInt(nParameterIndex, int32Value); + break; + } + case SQL_INT64: + { + sal_Int64 int64Value = sInput.toInt64(); + setLong(nParameterIndex, int64Value); + break; + } + case SQL_FLOAT: + { + float floatValue = sInput.toFloat(); + setFloat(nParameterIndex, floatValue); + break; + } + case SQL_BOOLEAN: + { + bool boolValue = sInput.toBoolean(); + setBoolean(nParameterIndex, boolValue); + break; + } + default: + ::dbtools::throwSQLException( + "Incorrect type for setString", + ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, + *this); + } +} + +Reference< XConnection > SAL_CALL OPreparedStatement::getConnection() +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + + return Reference<XConnection>(m_pConnection.get()); +} + +sal_Bool SAL_CALL OPreparedStatement::execute() +{ + SAL_INFO("connectivity.firebird", "executeQuery(). " + "Got called with sql: " << m_sSqlStatement); + + MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + + ensurePrepared(); + + ISC_STATUS aErr; + + if (m_xResultSet.is()) // Checks whether we have already run the statement. + { + disposeResultSet(); + // Closes the cursor from the last run. + // This doesn't actually free the statement -- using DSQL_close closes + // the cursor and keeps the statement, using DSQL_drop frees the statement + // (and associated cursors). + aErr = isc_dsql_free_statement(m_statusVector, + &m_aStatementHandle, + DSQL_close); + if (aErr) + { + // Do not throw error. Trying to close a closed cursor is not a + // critical mistake. + OUString sErrMsg = StatusVectorToString(m_statusVector, + "isc_dsql_free_statement: close cursor"); + SAL_WARN("connectivity.firebird", sErrMsg); + } + } + + aErr = isc_dsql_execute(m_statusVector, + &m_pConnection->getTransaction(), + &m_aStatementHandle, + 1, + m_pInSqlda); + if (aErr) + { + SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" ); + evaluateStatusVector(m_statusVector, "isc_dsql_execute", *this); + } + + m_xResultSet = new OResultSet(m_pConnection.get(), + m_aMutex, + uno::Reference< XInterface >(*this), + m_aStatementHandle, + m_pOutSqlda); + + if (getStatementChangeCount() > 0) + m_pConnection->notifyDatabaseModified(); + + return m_xResultSet.is(); + // TODO: implement handling of multiple ResultSets. +} + +sal_Int32 SAL_CALL OPreparedStatement::executeUpdate() +{ + execute(); + return getStatementChangeCount(); +} + +Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery() +{ + execute(); + return m_xResultSet; +} + +namespace { + +/** + * Take out the number part of a fix point decimal without + * the information of where is the fractional part from a + * string representation of a number. (e.g. 54.654 -> 54654) + */ +sal_Int64 toNumericWithoutDecimalPlace(const OUString& sSource) +{ + OUString sNumber(sSource); + + // cut off leading 0 eventually ( eg. 0.567 -> .567) + (void)sSource.startsWith("0", &sNumber); + + sal_Int32 nDotIndex = sNumber.indexOf('.'); + + if( nDotIndex < 0) + { + return sNumber.toInt64(); // no dot -> it's an integer + } + else + { + // remove dot + OUStringBuffer sBuffer(15); + if(nDotIndex > 0) + { + sBuffer.append(std::u16string_view(sNumber).substr(0, nDotIndex)); + } + sBuffer.append(std::u16string_view(sNumber).substr(nDotIndex + 1)); + return sBuffer.makeStringAndClear().toInt64(); + } +} + +} + +//----- XParameters ----------------------------------------------------------- +void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + ensurePrepared(); + + checkParameterIndex(nIndex); + setParameterNull(nIndex); +} + +void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue) +{ + setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN); +} + +template <typename T> +void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + ensurePrepared(); + + checkParameterIndex(nIndex); + setParameterNull(nIndex, false); + + XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1); + + if ((pVar->sqltype & ~1) != nType) + { + ::dbtools::throwSQLException( + "Incorrect type for setValue", + ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, + *this); + } + + memcpy(pVar->sqldata, &nValue, sizeof(nValue)); +} + +void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue) +{ + // there's no TINYINT or equivalent on Firebird, + // so do the same as setShort + setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT); +} + +void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue) +{ + setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT); +} + +void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue) +{ + setValue< sal_Int32 >(nIndex, nValue, SQL_LONG); +} + +void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue) +{ + setValue< sal_Int64 >(nIndex, nValue, SQL_INT64); +} + +void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue) +{ + setValue< float >(nIndex, nValue, SQL_FLOAT); +} + +void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue) +{ + MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + ensurePrepared(); + + XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1); + short dType = (pVar->sqltype & ~1); // drop flag bit for now + short dSubType = pVar->sqlsubtype; + // Assume it is a sub type of a number. + if(dSubType < 0 || dSubType > 2) + { + ::dbtools::throwSQLException( + "Incorrect number sub type", + ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, + *this); + } + // firebird stores scale as a negative number + ColumnTypeInfo columnType{ dType, dSubType, + static_cast<short>(-pVar->sqlscale) }; + + // Caller might try to set an integer type here. It makes sense to convert + // it instead of throwing an error. + switch(columnType.getSdbcType()) + { + case DataType::SMALLINT: + setValue< sal_Int16 >(nIndex, + static_cast<sal_Int16>(nValue), + dType); + break; + case DataType::INTEGER: + setValue< sal_Int32 >(nIndex, + static_cast<sal_Int32>(nValue), + dType); + break; + case DataType::BIGINT: + setValue< sal_Int64 >(nIndex, + static_cast<sal_Int64>(nValue), + dType); + break; + case DataType::NUMERIC: + case DataType::DECIMAL: + // take decimal places into account, later on they are removed in makeNumericString + setObjectWithInfo(nIndex,Any{nValue}, columnType.getSdbcType(), columnType.getScale()); + break; + default: + setValue< double >(nIndex, nValue, SQL_DOUBLE); // TODO: SQL_D_FLOAT? + } +} + +void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate) +{ + struct tm aCTime; + aCTime.tm_mday = rDate.Day; + aCTime.tm_mon = rDate.Month -1; + aCTime.tm_year = rDate.Year -1900; + + ISC_DATE aISCDate; + isc_encode_sql_date(&aCTime, &aISCDate); + + setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE); +} + +void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime) +{ + struct tm aCTime; + aCTime.tm_sec = rTime.Seconds; + aCTime.tm_min = rTime.Minutes; + aCTime.tm_hour = rTime.Hours; + + ISC_TIME aISCTime; + isc_encode_sql_time(&aCTime, &aISCTime); + + // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no + // other funkiness, so we can simply add the fraction of a second. + aISCTime += rTime.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION); + + setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME); +} + +void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp) +{ + struct tm aCTime; + aCTime.tm_sec = rTimestamp.Seconds; + aCTime.tm_min = rTimestamp.Minutes; + aCTime.tm_hour = rTimestamp.Hours; + aCTime.tm_mday = rTimestamp.Day; + aCTime.tm_mon = rTimestamp.Month - 1; + aCTime.tm_year = rTimestamp.Year - 1900; + + ISC_TIMESTAMP aISCTimestamp; + isc_encode_timestamp(&aCTime, &aISCTimestamp); + + // As in previous function + aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION); + + setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP); +} + + +// void OPreparedStatement::set +void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId) +{ + ISC_STATUS aErr; + + aErr = isc_create_blob2(m_statusVector, + &m_pConnection->getDBHandle(), + &m_pConnection->getTransaction(), + &rBlobHandle, + &rBlobId, + 0, // Blob parameter buffer length + nullptr); // Blob parameter buffer handle + + if (aErr) + { + evaluateStatusVector(m_statusVector, + "setBlob failed on " + m_sSqlStatement, + *this); + assert(false); + } +} + +void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle) +{ + ISC_STATUS aErr; + + aErr = isc_close_blob(m_statusVector, + &rBlobHandle); + if (aErr) + { + evaluateStatusVector(m_statusVector, + "isc_close_blob failed", + *this); + assert(false); + } +} + +void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + +#if SAL_TYPES_SIZEOFPOINTER == 8 + isc_blob_handle aBlobHandle = 0; +#else + isc_blob_handle aBlobHandle = nullptr; +#endif + ISC_QUAD aBlobId; + + openBlobForWriting(aBlobHandle, aBlobId); + + + // Max segment size is 2^16 == SAL_MAX_UINT16 + // SAL_MAX_UINT16 / 4 is surely enough for UTF-8 + // TODO apply max segment size to character encoding + sal_Int64 nCharWritten = 1; // XClob is indexed from 1 + ISC_STATUS aErr = 0; + sal_Int64 nLen = xClob->length(); + while ( nLen > nCharWritten ) + { + sal_Int64 nCharRemain = nLen - nCharWritten; + constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4; + sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE); + OString sData = OUStringToOString( + xClob->getSubString(nCharWritten, nWriteSize), + RTL_TEXTENCODING_UTF8); + aErr = isc_put_segment( m_statusVector, + &aBlobHandle, + sData.getLength(), + sData.getStr() ); + nCharWritten += nWriteSize; + + if (aErr) + break; + } + + // We need to make sure we close the Blob even if there are errors, hence evaluate + // errors after closing. + closeBlobAfterWriting(aBlobHandle); + + if (aErr) + { + evaluateStatusVector(m_statusVector, + "isc_put_segment failed", + *this); + assert(false); + } + + setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB); +} + +void OPreparedStatement::setClob( sal_Int32 nParameterIndex, const OUString& rStr ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nParameterIndex); + +#if SAL_TYPES_SIZEOFPOINTER == 8 + isc_blob_handle aBlobHandle = 0; +#else + isc_blob_handle aBlobHandle = nullptr; +#endif + ISC_QUAD aBlobId; + + openBlobForWriting(aBlobHandle, aBlobId); + + OString sData = OUStringToOString( + rStr, + RTL_TEXTENCODING_UTF8); + ISC_STATUS aErr = isc_put_segment( m_statusVector, + &aBlobHandle, + sData.getLength(), + sData.getStr() ); + + // We need to make sure we close the Blob even if there are errors, hence evaluate + // errors after closing. + closeBlobAfterWriting(aBlobHandle); + + if (aErr) + { + evaluateStatusVector(m_statusVector, + "isc_put_segment failed", + *this); + assert(false); + } + + setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB); +} + +void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex, + const Reference< XBlob >& xBlob) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nParameterIndex); + +#if SAL_TYPES_SIZEOFPOINTER == 8 + isc_blob_handle aBlobHandle = 0; +#else + isc_blob_handle aBlobHandle = nullptr; +#endif + ISC_QUAD aBlobId; + + openBlobForWriting(aBlobHandle, aBlobId); + + ISC_STATUS aErr = 0; + const sal_Int64 nBlobLen = xBlob->length(); + if (nBlobLen > 0) + { + // Max write size is 0xFFFF == SAL_MAX_UINT16 + sal_uInt64 nDataWritten = 0; + while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten) + { + sal_uInt64 nDataRemaining = nBlobLen - nDataWritten; + sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16)); + aErr = isc_put_segment(m_statusVector, + &aBlobHandle, + nWriteSize, + reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray())); + nDataWritten += nWriteSize; + + if (aErr) + break; + } + } + + // We need to make sure we close the Blob even if there are errors, hence evaluate + // errors after closing. + closeBlobAfterWriting(aBlobHandle); + + if (aErr) + { + evaluateStatusVector(m_statusVector, + "isc_put_segment failed", + *this); + assert(false); + } + + setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB); +} + + +void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nIndex); +} + + +void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nIndex); +} + + +void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale ) +{ + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + ensurePrepared(); + + checkParameterIndex(parameterIndex); + setParameterNull(parameterIndex, false); + + XSQLVAR* pVar = m_pInSqlda->sqlvar + (parameterIndex - 1); + int dType = (pVar->sqltype & ~1); // drop null flag + + if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC) + { + double dbValue =0.0; + OUString sValue; + if( x >>= dbValue ) + { + // truncate and round to 'scale' number of decimal places + sValue = OUString::number( std::floor((dbValue * pow10Integer(scale)) + .5) / pow10Integer(scale) ); + } + else + { + x >>= sValue; + } + + // fill in the number with nulls in fractional part. + // We need this because e.g. 0.450 != 0.045 despite + // their scale is equal + OUStringBuffer sBuffer(15); + sBuffer.append(sValue); + if(sValue.indexOf('.') != -1) // there is a dot + { + for(sal_Int32 i=sValue.copy(sValue.indexOf('.')+1).getLength(); i<scale;i++) + { + sBuffer.append('0'); + } + } + else + { + for (sal_Int32 i=0; i<scale; i++) + { + sBuffer.append('0'); + } + } + + sValue = sBuffer.makeStringAndClear(); + switch(dType) + { + case SQL_SHORT: + setValue< sal_Int16 >(parameterIndex, + static_cast<sal_Int16>( toNumericWithoutDecimalPlace(sValue) ), + dType); + break; + case SQL_LONG: + case SQL_DOUBLE: + setValue< sal_Int32 >(parameterIndex, + static_cast<sal_Int32>( toNumericWithoutDecimalPlace(sValue) ), + dType); + break; + case SQL_INT64: + setValue< sal_Int64 >(parameterIndex, + toNumericWithoutDecimalPlace(sValue), + dType); + break; + default: + SAL_WARN("connectivity.firebird", + "No Firebird sql type found for numeric or decimal types"); + ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale); + } + } + else + { + ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale); + } + +} + + +void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nIndex); +} + + +void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nIndex); +} + +void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex, + const Sequence< sal_Int8 >& xBytes) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nParameterIndex); + + XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1); + int dType = (pVar->sqltype & ~1); // drop flag bit for now + + if( dType == SQL_BLOB ) + { +#if SAL_TYPES_SIZEOFPOINTER == 8 + isc_blob_handle aBlobHandle = 0; +#else + isc_blob_handle aBlobHandle = nullptr; +#endif + ISC_QUAD aBlobId; + + openBlobForWriting(aBlobHandle, aBlobId); + + ISC_STATUS aErr = 0; + const sal_Int32 nBytesLen = xBytes.getLength(); + if (nBytesLen > 0) + { + // Max write size is 0xFFFF == SAL_MAX_UINT16 + sal_uInt32 nDataWritten = 0; + while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten) + { + sal_uInt32 nDataRemaining = nBytesLen - nDataWritten; + sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16)); + aErr = isc_put_segment(m_statusVector, + &aBlobHandle, + nWriteSize, + reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten); + nDataWritten += nWriteSize; + + if (aErr) + break; + } + } + + // We need to make sure we close the Blob even if there are errors, hence evaluate + // errors after closing. + closeBlobAfterWriting(aBlobHandle); + + if (aErr) + { + evaluateStatusVector(m_statusVector, + "isc_put_segment failed", + *this); + assert(false); + } + + setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB); + } + else if( dType == SQL_VARYING ) + { + setParameterNull(nParameterIndex, false); + const sal_Int32 nMaxSize = 0xFFFF; + Sequence<sal_Int8> xBytesCopy(xBytes); + if (xBytesCopy.getLength() > nMaxSize) + { + xBytesCopy.realloc( nMaxSize ); + } + const auto nSize = xBytesCopy.getLength(); + // 8000 corresponds to value from lcl_addDefaultParameters + // in dbaccess/source/filter/hsqldb/createparser.cxx + if (nSize > 8000) + { + free(pVar->sqldata); + pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2)); + } + // First 2 bytes indicate string size + memcpy(pVar->sqldata, &nSize, 2); + // Actual data + memcpy(pVar->sqldata + 2, xBytesCopy.getConstArray(), nSize); + } + else if( dType == SQL_TEXT ) + { + setParameterNull(nParameterIndex, false); + memcpy(pVar->sqldata, xBytes.getConstArray(), xBytes.getLength() ); + // Fill remainder with spaces + memset(pVar->sqldata + xBytes.getLength(), 0, pVar->sqllen - xBytes.getLength()); + } + else + { + ::dbtools::throwSQLException( + "Incorrect type for setBytes", + ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, + *this); + } +} + + +void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nIndex); +} + + +void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + checkParameterIndex(nIndex); +} + + +void SAL_CALL OPreparedStatement::clearParameters( ) +{ +} + +// ---- Batch methods -- unsupported ----------------------------------------- +void SAL_CALL OPreparedStatement::clearBatch() +{ + // Unsupported +} + +void SAL_CALL OPreparedStatement::addBatch() +{ + // Unsupported by firebird +} + +Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch() +{ + // Unsupported by firebird + return Sequence< sal_Int32 >(); +} + +void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + switch(nHandle) + { + case PROPERTY_ID_RESULTSETCONCURRENCY: + break; + case PROPERTY_ID_RESULTSETTYPE: + break; + case PROPERTY_ID_FETCHDIRECTION: + break; + case PROPERTY_ID_USEBOOKMARKS: + break; + default: + OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle,rValue); + } +} + +void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex) +{ + ensurePrepared(); + if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld)) + { + ::dbtools::throwSQLException( + "No column " + OUString::number(nParameterIndex), + ::dbtools::StandardSQLState::COLUMN_NOT_FOUND, + *this); + } +} + +void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex, + bool bSetNull) +{ + XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1); + if (bSetNull) + { + pVar->sqltype |= 1; + *pVar->sqlind = -1; + } + else + *pVar->sqlind = 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/PreparedStatement.hxx b/connectivity/source/drivers/firebird/PreparedStatement.hxx new file mode 100644 index 000000000..e77201791 --- /dev/null +++ b/connectivity/source/drivers/firebird/PreparedStatement.hxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_PREPAREDSTATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_PREPAREDSTATEMENT_HXX + +#include "StatementCommonBase.hxx" + +#include <cppuhelper/implbase5.hxx> + +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XPreparedBatchExecution.hpp> +#include <com/sun/star/io/XInputStream.hpp> + +#include <ibase.h> + +namespace connectivity +{ + namespace firebird + { + + class OBoundParam; + typedef ::cppu::ImplHelper5< css::sdbc::XPreparedStatement, + css::sdbc::XParameters, + css::sdbc::XPreparedBatchExecution, + css::sdbc::XResultSetMetaDataSupplier, + css::lang::XServiceInfo> OPreparedStatement_Base; + + class OPreparedStatement : public OStatementCommonBase, + public OPreparedStatement_Base + { + protected: + OUString m_sSqlStatement; + css::uno::Reference< css::sdbc::XResultSetMetaData > m_xMetaData; + + XSQLDA* m_pOutSqlda; + XSQLDA* m_pInSqlda; + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkParameterIndex(sal_Int32 nParameterIndex); + + /** + * Set a numeric value in the input SQLDA. If the destination + * parameter is not of nType then an Exception will be thrown. + * + * @throws css::sdbc::SQLException + * @throws css::uno::RuntimeException + */ + template <typename T> void setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType); + void setParameterNull(sal_Int32 nParameterIndex, bool bSetNull = true); + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void ensurePrepared(); + /** + * Assumes that all necessary mutexes have been taken. + */ + void openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId); + /** + * Assumes that all necessary mutexes have been taken. + */ + void closeBlobAfterWriting(isc_blob_handle& rBlobHandle); + void setClob(sal_Int32 nParamIndex, const OUString& rStr); + + protected: + virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, + const css::uno::Any& rValue) override; + virtual ~OPreparedStatement() override; + public: + DECLARE_SERVICE_INFO(); + // a constructor, which is required for returning objects: + OPreparedStatement( Connection* _pConnection, + const OUString& sql); + + //XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XPreparedStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL + executeQuery() override; + virtual sal_Int32 SAL_CALL + executeUpdate() override; + virtual sal_Bool SAL_CALL + execute() override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL + getConnection() override; + + // XParameters + virtual void SAL_CALL setNull(sal_Int32 nIndex, sal_Int32 nValue) override; + virtual void SAL_CALL setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) override; + virtual void SAL_CALL setBoolean( sal_Int32 nIndex, sal_Bool nValue) override; + virtual void SAL_CALL setByte(sal_Int32 nIndex, sal_Int8 nValue) override; + virtual void SAL_CALL setShort(sal_Int32 nIndex, sal_Int16 nValue) override; + virtual void SAL_CALL setInt(sal_Int32 nIndex, sal_Int32 nValue) override; + virtual void SAL_CALL setLong(sal_Int32 nIndex, sal_Int64 nValue) override; + virtual void SAL_CALL setFloat( sal_Int32 parameterIndex, float x ) override; + virtual void SAL_CALL setDouble( sal_Int32 parameterIndex, double x ) override; + virtual void SAL_CALL setString( sal_Int32 parameterIndex, const OUString& x ) override; + virtual void SAL_CALL setBytes( sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL setDate( sal_Int32 parameterIndex, const css::util::Date& x ) override; + virtual void SAL_CALL setTime( sal_Int32 parameterIndex, const css::util::Time& x ) override; + virtual void SAL_CALL setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL setBinaryStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setCharacterStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setObject( sal_Int32 parameterIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL setObjectWithInfo( sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) override; + virtual void SAL_CALL setRef( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef >& x ) override; + virtual void SAL_CALL setBlob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob >& x ) override; + virtual void SAL_CALL setClob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XClob >& x ) override; + virtual void SAL_CALL setArray( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray >& x ) override; + virtual void SAL_CALL clearParameters( ) override; + + // XPreparedBatchExecution -- UNSUPPORTED by firebird + virtual void SAL_CALL + addBatch() override; + virtual void SAL_CALL + clearBatch() override; + virtual css::uno::Sequence< sal_Int32 > SAL_CALL + executeBatch() override; + + // XCloseable + virtual void SAL_CALL close() override; + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_PREPAREDSTATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/ResultSet.cxx b/connectivity/source/drivers/firebird/ResultSet.cxx new file mode 100644 index 000000000..7ae77c607 --- /dev/null +++ b/connectivity/source/drivers/firebird/ResultSet.cxx @@ -0,0 +1,909 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ResultSet.hxx" +#include "ResultSetMetaData.hxx" +#include "Util.hxx" + +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <connectivity/dbexception.hxx> +#include <propertyids.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <TConnection.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +using namespace ::comphelper; +using namespace ::connectivity; +using namespace ::connectivity::firebird; +using namespace ::cppu; +using namespace ::dbtools; +using namespace ::osl; + +using namespace ::com::sun::star; +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::io; +using namespace ::com::sun::star::util; + +OResultSet::OResultSet(Connection* pConnection, + ::osl::Mutex& rMutex, + const uno::Reference< XInterface >& xStatement, + isc_stmt_handle aStatementHandle, + XSQLDA* pSqlda ) + : OResultSet_BASE(rMutex) + , OPropertyContainer(OResultSet_BASE::rBHelper) + , m_bIsBookmarkable(false) + , m_nFetchSize(1) + , m_nResultSetType(css::sdbc::ResultSetType::FORWARD_ONLY) + , m_nFetchDirection(css::sdbc::FetchDirection::FORWARD) + , m_nResultSetConcurrency(css::sdbc::ResultSetConcurrency::READ_ONLY) + , m_pConnection(pConnection) + , m_rMutex(rMutex) + , m_xStatement(xStatement) + , m_pSqlda(pSqlda) + , m_statementHandle(aStatementHandle) + , m_bWasNull(false) + , m_currentRow(0) + , m_bIsAfterLastRow(false) + , m_fieldCount(pSqlda? pSqlda->sqld : 0) +{ + SAL_INFO("connectivity.firebird", "OResultSet()."); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), + PROPERTY_ID_ISBOOKMARKABLE, + PropertyAttribute::READONLY, + &m_bIsBookmarkable, + cppu::UnoType<decltype(m_bIsBookmarkable)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, + PropertyAttribute::READONLY, + &m_nFetchSize, + cppu::UnoType<decltype(m_nFetchSize)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, + PropertyAttribute::READONLY, + &m_nResultSetType, + cppu::UnoType<decltype(m_nResultSetType)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, + PropertyAttribute::READONLY, + &m_nFetchDirection, + cppu::UnoType<decltype(m_nFetchDirection)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, + PropertyAttribute::READONLY, + &m_nResultSetConcurrency, + cppu::UnoType<decltype(m_nResultSetConcurrency)>::get()); + + if (!pSqlda) + return; // TODO: what? + +} + +OResultSet::~OResultSet() +{ +} + +// ---- XResultSet -- Row retrieval methods ------------------------------------ +sal_Int32 SAL_CALL OResultSet::getRow() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_currentRow; +} + +sal_Bool SAL_CALL OResultSet::next() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + m_currentRow++; + + ISC_STATUS fetchStat = isc_dsql_fetch(m_statusVector, + &m_statementHandle, + 1, + m_pSqlda); + if (fetchStat == 0) // SUCCESSFUL + { + return true; + } + else if (fetchStat == 100) // END OF DATASET + { + m_bIsAfterLastRow = true; + return false; + } + else + { + SAL_WARN("connectivity.firebird", "Error when fetching data"); + // Throws sql exception as appropriate + evaluateStatusVector(m_statusVector, "isc_dsql_fetch", *this); + return false; + } +} + +sal_Bool SAL_CALL OResultSet::previous() +{ + ::dbtools::throwFunctionNotSupportedSQLException("previous not supported in firebird", + *this); + return false; +} + +sal_Bool SAL_CALL OResultSet::isLast() +{ + ::dbtools::throwFunctionNotSupportedSQLException("isLast not supported in firebird", + *this); + return false; +} + +sal_Bool SAL_CALL OResultSet::isBeforeFirst() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_currentRow == 0; +} + +sal_Bool SAL_CALL OResultSet::isAfterLast() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_bIsAfterLastRow; +} + +sal_Bool SAL_CALL OResultSet::isFirst() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_currentRow == 1 && !m_bIsAfterLastRow; +} + +void SAL_CALL OResultSet::beforeFirst() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if (m_currentRow != 0) + ::dbtools::throwFunctionNotSupportedSQLException("beforeFirst not supported in firebird", + *this); +} + +void SAL_CALL OResultSet::afterLast() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if (!m_bIsAfterLastRow) + ::dbtools::throwFunctionNotSupportedSQLException("afterLast not supported in firebird", + *this); +} + +sal_Bool SAL_CALL OResultSet::first() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if (m_currentRow == 0) + { + return next(); + } + else if (m_currentRow == 1 && !m_bIsAfterLastRow) + { + return true; + } + else + { + ::dbtools::throwFunctionNotSupportedSQLException("first not supported in firebird", + *this); + return false; + } +} + +sal_Bool SAL_CALL OResultSet::last() +{ + // We need to iterate past the last row to know when we've passed the last + // row, hence we can't actually move to last. + ::dbtools::throwFunctionNotSupportedSQLException("last not supported in firebird", + *this); + return false; +} + +sal_Bool SAL_CALL OResultSet::absolute(sal_Int32 aRow) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if (aRow > m_currentRow) + { + sal_Int32 aIterations = aRow - m_currentRow; + return relative(aIterations); + } + else + { + ::dbtools::throwFunctionNotSupportedSQLException("absolute not supported in firebird", + *this); + return false; + } +} + +sal_Bool SAL_CALL OResultSet::relative(sal_Int32 row) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if (row > 0) + { + while (row--) + { + if (!next()) + return false; + } + return true; + } + else + { + ::dbtools::throwFunctionNotSupportedSQLException("relative not supported in firebird", + *this); + return false; + } +} + +void OResultSet::checkColumnIndex(sal_Int32 nIndex) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if( nIndex < 1 || nIndex > m_fieldCount ) + { + ::dbtools::throwSQLException( + "No column " + OUString::number(nIndex), + ::dbtools::StandardSQLState::COLUMN_NOT_FOUND, + *this); + } +} + +void OResultSet::checkRowIndex() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if((m_currentRow < 1) || m_bIsAfterLastRow) + { + ::dbtools::throwSQLException( + "Invalid Row", + ::dbtools::StandardSQLState::INVALID_CURSOR_POSITION, + *this); + } +} + +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() +{ + return concatSequences(OPropertySetHelper::getTypes(), OResultSet_BASE::getTypes()); +} +// ---- XColumnLocate --------------------------------------------------------- +sal_Int32 SAL_CALL OResultSet::findColumn(const OUString& rColumnName) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + uno::Reference< XResultSetMetaData > xMeta = getMetaData(); + sal_Int32 nLen = xMeta->getColumnCount(); + sal_Int32 i; + + for(i = 1; i<=nLen; ++i) + { + // We assume case sensitive, otherwise you'd have to test + // xMeta->isCaseSensitive and use qualsIgnoreAsciiCase as needed. + if (rColumnName == xMeta->getColumnName(i)) + return i; + } + + ::dbtools::throwInvalidColumnException(rColumnName, *this); + assert(false); + return 0; // Never reached +} + +uno::Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 ) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return nullptr; +} + +uno::Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 ) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return nullptr; +} + +// ---- Internal Utilities --------------------------------------------------- +bool OResultSet::isNull(const sal_Int32 nColumnIndex) +{ + assert(nColumnIndex <= m_fieldCount); + XSQLVAR* pVar = m_pSqlda->sqlvar; + + if (pVar[nColumnIndex-1].sqltype & 1) // Indicates column may contain null + { + if (*pVar[nColumnIndex-1].sqlind == -1) + return true; + } + return false; +} + +template <typename T> +OUString OResultSet::makeNumericString(const sal_Int32 nColumnIndex) +{ + // minus because firebird stores scale as a negative number + int nDecimalCount = -(m_pSqlda->sqlvar[nColumnIndex-1].sqlscale); + if(nDecimalCount < 0) + { + // scale should be always positive + assert(false); + return OUString(); + } + + OUStringBuffer sRetBuffer; + T nAllDigits = *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); + sal_Int64 nDecimalCountExp = pow10Integer(nDecimalCount); + + if(nAllDigits < 0) + { + sRetBuffer.append('-'); + nAllDigits = -nAllDigits; // abs + } + + sRetBuffer.append(static_cast<sal_Int64>(nAllDigits / nDecimalCountExp) ); + if( nDecimalCount > 0) + { + sRetBuffer.append('.'); + + sal_Int64 nFractionalPart = nAllDigits % nDecimalCountExp; + + int iCount = 0; // digit count + sal_Int64 nFracTemp = nFractionalPart; + while(nFracTemp>0) + { + nFracTemp /= 10; + iCount++; + } + + int nMissingNulls = nDecimalCount - iCount; + + // append nulls after dot and before nFractionalPart + for(int i=0; i<nMissingNulls; i++) + { + sRetBuffer.append('0'); + } + + // the rest + sRetBuffer.append(nFractionalPart); + } + + return sRetBuffer.makeStringAndClear(); +} + +template <typename T> +T OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType) +{ + m_bWasNull = isNull(nColumnIndex); + if (m_bWasNull) + return T(); + + if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == nType) + return *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); + else + return retrieveValue< ORowSetValue >(nColumnIndex, 0); +} + +template <> +ORowSetValue OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) +{ + // See http://wiki.openoffice.org/wiki/Documentation/DevGuide/Database/Using_the_getXXX_Methods + // (bottom of page) for a chart of possible conversions, we should allow all + // of these -- Blob/Clob will probably need some specialist handling especially + // w.r.t. to generating Strings for them. + // + // Basically we just have to map to the correct direct request and + // ORowSetValue does the rest for us here. + int nSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype; + + // TODO Firebird 3.0 does not set subtype (i.e. set to 0) for computed numeric/decimal value. + // It may change in the future. + // Imply numeric data type when subtype is 0 and scale is negative + if( nSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0 ) + nSqlSubType = 1; + + switch (m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) + { + case SQL_TEXT: + case SQL_VARYING: + return getString(nColumnIndex); + case SQL_SHORT: + if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal + return getString(nColumnIndex); + return getShort(nColumnIndex); + case SQL_LONG: + if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal + return getString(nColumnIndex); + return getInt(nColumnIndex); + case SQL_FLOAT: + return getFloat(nColumnIndex); + case SQL_DOUBLE: + if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal + return getString(nColumnIndex); + return getDouble(nColumnIndex); + case SQL_D_FLOAT: + return getFloat(nColumnIndex); + case SQL_TIMESTAMP: + return getTimestamp(nColumnIndex); + case SQL_TYPE_TIME: + return getTime(nColumnIndex); + case SQL_TYPE_DATE: + return getDate(nColumnIndex); + case SQL_INT64: + if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal + return getString(nColumnIndex); + return getLong(nColumnIndex); + case SQL_BOOLEAN: + return ORowSetValue(bool(getBoolean(nColumnIndex))); + case SQL_BLOB: + case SQL_NULL: + case SQL_QUAD: + case SQL_ARRAY: + // TODO: these are all invalid conversions, so maybe we should + // throw an exception? + return ORowSetValue(); + default: + assert(false); + return ORowSetValue(); + } +} + +template <> +Date OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) +{ + if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_DATE) + { + ISC_DATE aISCDate = *reinterpret_cast<ISC_DATE*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); + + struct tm aCTime; + isc_decode_sql_date(&aISCDate, &aCTime); + + return Date(aCTime.tm_mday, aCTime.tm_mon + 1, aCTime.tm_year + 1900); + } + else + { + return retrieveValue< ORowSetValue >(nColumnIndex, 0); + } +} + +template <> +Time OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) +{ + if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_TIME) + { + ISC_TIME aISCTime = *reinterpret_cast<ISC_TIME*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); + + struct tm aCTime; + isc_decode_sql_time(&aISCTime, &aCTime); + + // First field is nanoseconds. + // last field denotes UTC (true) or unknown (false) + // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION + // with no other funkiness, so we can get the fractional seconds easily. + return Time((aISCTime % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION), + aCTime.tm_sec, aCTime.tm_min, aCTime.tm_hour, false); + } + else + { + return retrieveValue< ORowSetValue >(nColumnIndex, 0); + } +} + +template <> +DateTime OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) +{ + if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TIMESTAMP) + { + ISC_TIMESTAMP aISCTimestamp = *reinterpret_cast<ISC_TIMESTAMP*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); + + struct tm aCTime; + isc_decode_timestamp(&aISCTimestamp, &aCTime); + + // Ditto here, see comment in previous function about ISC_TIME and ISC_TIME_SECONDS_PRECISION. + return DateTime((aISCTimestamp.timestamp_time % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION), //nanoseconds + aCTime.tm_sec, + aCTime.tm_min, + aCTime.tm_hour, + aCTime.tm_mday, + aCTime.tm_mon + 1, // tm is from 0 to 11 + aCTime.tm_year + 1900, //tm_year is the years since 1900 + false); // denotes UTC (true), or unknown (false) + } + else + { + return retrieveValue< ORowSetValue >(nColumnIndex, 0); + } +} + +template <> +OUString OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/) +{ + // &~1 to remove the "can contain NULL" indicator + int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1; + int aSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype; + if (aSqlType == SQL_TEXT ) + { + return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata, + m_pSqlda->sqlvar[nColumnIndex-1].sqllen, + RTL_TEXTENCODING_UTF8); + } + else if (aSqlType == SQL_VARYING) + { + // First 2 bytes are a short containing the length of the string + // No idea if sqllen is still valid here? + sal_uInt16 aLength = *reinterpret_cast<sal_uInt16*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); + return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata + 2, + aLength, + RTL_TEXTENCODING_UTF8); + } + else if ((aSqlType == SQL_SHORT || aSqlType == SQL_LONG || + aSqlType == SQL_DOUBLE || aSqlType == SQL_INT64) + && (aSqlSubType == 1 || + aSqlSubType == 2 || + (aSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0) ) ) + { + // decimal and numeric types + switch(aSqlType) + { + case SQL_SHORT: + return makeNumericString<sal_Int16>(nColumnIndex); + case SQL_LONG: + return makeNumericString<sal_Int32>(nColumnIndex); + case SQL_DOUBLE: + // TODO FIXME 64 bits? + case SQL_INT64: + return makeNumericString<sal_Int64>(nColumnIndex); + default: + assert(false); + return OUString(); // never reached + } + } + else if(aSqlType == SQL_BLOB && aSqlSubType == static_cast<short>(BlobSubtype::Clob) ) + { + uno::Reference<XClob> xClob = getClob(nColumnIndex); + return xClob->getSubString( 0, xClob->length() ); + } + else + { + return retrieveValue< ORowSetValue >(nColumnIndex, 0); + } +} + +template <> +ISC_QUAD* OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType) +{ + // TODO: this is probably wrong + if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) != nType) + throw SQLException(); // TODO: better exception (can't convert Blob) + + return reinterpret_cast<ISC_QUAD*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata); +} + +template <typename T> +T OResultSet::safelyRetrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + checkColumnIndex(nColumnIndex); + checkRowIndex(); + + m_bWasNull = isNull(nColumnIndex); + if (m_bWasNull) + return T(); + + return retrieveValue< T >(nColumnIndex, nType); +} + +// ---- XRow ----------------------------------------------------------------- +sal_Bool SAL_CALL OResultSet::wasNull() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_bWasNull; +} + +// ---- XRow: Simple Numerical types ------------------------------------------ +sal_Bool SAL_CALL OResultSet::getBoolean(sal_Int32 nColumnIndex) +{ + return safelyRetrieveValue< bool >(nColumnIndex, SQL_BOOLEAN); +} + +sal_Int8 SAL_CALL OResultSet::getByte(sal_Int32 nColumnIndex) +{ + // Not a native firebird type hence we always have to convert. + return safelyRetrieveValue< ORowSetValue >(nColumnIndex); +} + +Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes(sal_Int32 nColumnIndex) +{ + // &~1 to remove the "can contain NULL" indicator + int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1; + if ( aSqlType == SQL_BLOB ) + { + Reference< XBlob> xBlob = getBlob(nColumnIndex); + if (xBlob.is()) + { + const sal_Int64 aBlobLength = xBlob->length(); + if (aBlobLength > SAL_MAX_INT32) + { + SAL_WARN("connectivity.firebird", "getBytes can't return " << aBlobLength << " bytes but only max " << SAL_MAX_INT32); + return xBlob->getBytes(1, SAL_MAX_INT32); + } + return xBlob->getBytes(1, static_cast<sal_Int32>(aBlobLength)); + } + else + return Sequence< sal_Int8 >(); + } + // TODO implement SQL_VARYING and SQL_TEXT + // as it's the counterpart as OPreparedStatement::setBytes + else + { + return Sequence< sal_Int8 >(); // TODO: implement + } +} + +sal_Int16 SAL_CALL OResultSet::getShort(sal_Int32 columnIndex) +{ + return safelyRetrieveValue< sal_Int16 >(columnIndex, SQL_SHORT); +} + +sal_Int32 SAL_CALL OResultSet::getInt(sal_Int32 columnIndex) +{ + return safelyRetrieveValue< sal_Int32 >(columnIndex, SQL_LONG); +} + +sal_Int64 SAL_CALL OResultSet::getLong(sal_Int32 columnIndex) +{ + return safelyRetrieveValue< sal_Int64 >(columnIndex, SQL_INT64); +} + +float SAL_CALL OResultSet::getFloat(sal_Int32 columnIndex) +{ + return safelyRetrieveValue< float >(columnIndex, SQL_FLOAT); +} + +double SAL_CALL OResultSet::getDouble(sal_Int32 columnIndex) +{ + return safelyRetrieveValue< double >(columnIndex, SQL_DOUBLE); +} + +// ---- XRow: More complex types ---------------------------------------------- +OUString SAL_CALL OResultSet::getString(sal_Int32 nIndex) +{ + return safelyRetrieveValue< OUString >(nIndex); +} + +Date SAL_CALL OResultSet::getDate(sal_Int32 nIndex) +{ + return safelyRetrieveValue< Date >(nIndex, SQL_TYPE_DATE); +} + +Time SAL_CALL OResultSet::getTime(sal_Int32 nIndex) +{ + return safelyRetrieveValue< css::util::Time >(nIndex, SQL_TYPE_TIME); +} + +DateTime SAL_CALL OResultSet::getTimestamp(sal_Int32 nIndex) +{ + return safelyRetrieveValue< DateTime >(nIndex, SQL_TIMESTAMP); +} + + +uno::Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData( ) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if(!m_xMetaData.is()) + m_xMetaData = new OResultSetMetaData(m_pConnection + , m_pSqlda); + return m_xMetaData; +} + +uno::Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 ) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return nullptr; +} + + +uno::Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + int aSqlSubType = m_pSqlda->sqlvar[columnIndex-1].sqlsubtype; + + SAL_WARN_IF(aSqlSubType != 1, + "connectivity.firebird", "wrong subtype, not a textual blob"); + + ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB); + if (!pBlobID) + return nullptr; + return m_pConnection->createClob(pBlobID); +} + +uno::Reference< XBlob > SAL_CALL OResultSet::getBlob(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + // TODO: CLOB etc. should be valid here too, but we probably want some more + // cleverness around this. + ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB); + if (!pBlobID) + return nullptr; + return m_pConnection->createBlob(pBlobID); +} + + +uno::Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 ) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return nullptr; +} + + +Any SAL_CALL OResultSet::getObject( sal_Int32, const uno::Reference< css::container::XNameAccess >& ) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return Any(); +} + + +void SAL_CALL OResultSet::close() +{ + SAL_INFO("connectivity.firebird", "close()."); + + { + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + } + dispose(); +} + + +uno::Reference< XInterface > SAL_CALL OResultSet::getStatement() +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_xStatement; +} +//----- XResultSet: unsupported change detection methods --------------------- +sal_Bool SAL_CALL OResultSet::rowDeleted() +{ + ::dbtools::throwFunctionNotSupportedSQLException("rowDeleted not supported in firebird", + *this); + return false; +} +sal_Bool SAL_CALL OResultSet::rowInserted() +{ + ::dbtools::throwFunctionNotSupportedSQLException("rowInserted not supported in firebird", + *this); + return false; +} + +sal_Bool SAL_CALL OResultSet::rowUpdated() +{ + ::dbtools::throwFunctionNotSupportedSQLException("rowUpdated not supported in firebird", + *this); + return false; +} + +void SAL_CALL OResultSet::refreshRow() +{ + ::dbtools::throwFunctionNotSupportedSQLException("refreshRow not supported in firebird", + *this); +} + + +void SAL_CALL OResultSet::cancel( ) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + +} + +//----- OIdPropertyArrayUsageHelper ------------------------------------------ +IPropertyArrayHelper* OResultSet::createArrayHelper() const +{ + Sequence< Property > aProperties; + describeProperties(aProperties); + return new ::cppu::OPropertyArrayHelper(aProperties); +} + +IPropertyArrayHelper & OResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +void SAL_CALL OResultSet::acquire() throw() +{ + OResultSet_BASE::acquire(); +} + +void SAL_CALL OResultSet::release() throw() +{ + OResultSet_BASE::release(); +} + +uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +// ---- XServiceInfo ----------------------------------------------------------- +OUString SAL_CALL OResultSet::getImplementationName() +{ + return "com.sun.star.sdbcx.firebird.ResultSet"; +} + +Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames() +{ + return {"com.sun.star.sdbc.ResultSet","com.sun.star.sdbcx.ResultSet"}; +} + +sal_Bool SAL_CALL OResultSet::supportsService(const OUString& _rServiceName) +{ + return cppu::supportsService(this, _rServiceName); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/ResultSet.hxx b/connectivity/source/drivers/firebird/ResultSet.hxx new file mode 100644 index 000000000..dc1a611fe --- /dev/null +++ b/connectivity/source/drivers/firebird/ResultSet.hxx @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_RESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_RESULTSET_HXX + +#include "Connection.hxx" + +#include <ibase.h> + +#include <connectivity/FValue.hxx> +#include <cppuhelper/compbase.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> + +#include <com/sun/star/util/XCancellable.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> + +namespace connectivity +{ + namespace firebird + { + /* + ** OResultSet + */ + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XResultSet, + css::sdbc::XRow, + css::sdbc::XResultSetMetaDataSupplier, + css::util::XCancellable, + css::sdbc::XCloseable, + css::sdbc::XColumnLocate, + css::lang::XServiceInfo> OResultSet_BASE; + + /** + * This ResultSet does not deal with the management of the SQLDA + * it is supplied with. The owner must mange its SQLDA appropriately + * and ensure that the ResultSet is destroyed before disposing of the + * SQLDA. + */ + class OResultSet: public OResultSet_BASE, + public ::comphelper::OPropertyContainer, + public ::comphelper::OPropertyArrayUsageHelper<OResultSet> + { + private: + bool m_bIsBookmarkable; + sal_Int32 m_nFetchSize; + sal_Int32 m_nResultSetType; + sal_Int32 m_nFetchDirection; + sal_Int32 m_nResultSetConcurrency; + + protected: + // Connection kept alive by m_xStatement + Connection* m_pConnection; + ::osl::Mutex& m_rMutex; + const css::uno::Reference< css::uno::XInterface >& m_xStatement; + + css::uno::Reference< css::sdbc::XResultSetMetaData> m_xMetaData; + + XSQLDA* m_pSqlda; + isc_stmt_handle m_statementHandle; + + bool m_bWasNull; + // Row numbering starts with 0 for "in front of first row" + sal_Int32 m_currentRow; + bool m_bIsAfterLastRow; + + const sal_Int32 m_fieldCount; + ISC_STATUS_ARRAY m_statusVector; + + bool isNull(const sal_Int32 nColumnIndex); + + template <typename T> OUString makeNumericString( + const sal_Int32 nColumnIndex); + + template <typename T> T retrieveValue(const sal_Int32 nColumnIndex, + const ISC_SHORT nType); + + template <typename T> T safelyRetrieveValue( + const sal_Int32 nColumnIndex, + const ISC_SHORT nType = 0); + + // OIdPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkColumnIndex( sal_Int32 index ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkRowIndex(); + + // you can't delete objects of this type + virtual ~OResultSet() override; + public: + DECLARE_SERVICE_INFO(); + + OResultSet(Connection* pConnection, + ::osl::Mutex& rMutex, + const css::uno::Reference< css::uno::XInterface >& xStatement, + isc_stmt_handle aStatementHandle, + XSQLDA* aSqlda + ); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type& rType) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XResultSet + virtual sal_Bool SAL_CALL next( ) override; + virtual sal_Bool SAL_CALL isBeforeFirst( ) override; + virtual sal_Bool SAL_CALL isAfterLast( ) override; + virtual sal_Bool SAL_CALL isFirst( ) override; + virtual sal_Bool SAL_CALL isLast( ) override; + virtual void SAL_CALL beforeFirst( ) override; + virtual void SAL_CALL afterLast( ) override; + virtual sal_Bool SAL_CALL first( ) override; + virtual sal_Bool SAL_CALL last( ) override; + virtual sal_Int32 SAL_CALL getRow( ) override; + virtual sal_Bool SAL_CALL absolute( sal_Int32 row ) override; + virtual sal_Bool SAL_CALL relative( sal_Int32 rows ) override; + virtual sal_Bool SAL_CALL previous( ) override; + virtual void SAL_CALL refreshRow( ) override; + virtual sal_Bool SAL_CALL rowUpdated( ) override; + virtual sal_Bool SAL_CALL rowInserted( ) override; + virtual sal_Bool SAL_CALL rowDeleted( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getStatement( ) override; + // XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + // XCancellable + virtual void SAL_CALL cancel( ) override; + // XCloseable + virtual void SAL_CALL close( ) override; + // XWarningsSupplier + + // XColumnLocate + virtual sal_Int32 SAL_CALL findColumn(const OUString& columnName) override; + + }; + + // Specialisations have to be in the namespace and can't be within the class. + template <> css::util::Date + OResultSet::retrieveValue( + const sal_Int32 nColumnIndex, + const ISC_SHORT nType); + template <> ::connectivity::ORowSetValue + OResultSet::retrieveValue( + const sal_Int32 nColumnIndex, + const ISC_SHORT nType); + template <> css::util::Time + OResultSet::retrieveValue( + const sal_Int32 nColumnIndex, + const ISC_SHORT nType); + template <> css::util::DateTime + OResultSet::retrieveValue( + const sal_Int32 nColumnIndex, + const ISC_SHORT nType); + template <> ISC_QUAD* + OResultSet::retrieveValue( + const sal_Int32 nColumnIndex, + const ISC_SHORT nType); + template <> OUString + OResultSet::retrieveValue( + const sal_Int32 nColumnIndex, + const ISC_SHORT nType); + template <> ISC_QUAD* + OResultSet::retrieveValue( + const sal_Int32 nColumnIndex, + const ISC_SHORT nType); + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_RESULTSET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/ResultSetMetaData.cxx b/connectivity/source/drivers/firebird/ResultSetMetaData.cxx new file mode 100644 index 000000000..d21115029 --- /dev/null +++ b/connectivity/source/drivers/firebird/ResultSetMetaData.cxx @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ResultSetMetaData.hxx" +#include "Util.hxx" + +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/DataType.hpp> + +#include <sal/log.hxx> + +using namespace connectivity::firebird; + +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::sdbcx; +using namespace com::sun::star::uno; + +OResultSetMetaData::~OResultSetMetaData() +{ +} + +OUString OResultSetMetaData::getCharacterSet( sal_Int32 nIndex ) +{ + OUString sTable = getTableName( nIndex ); + if( !sTable.isEmpty() ) + { + OUString sColumnName = getColumnName( nIndex ); + + OUString sSql = "SELECT charset.RDB$CHARACTER_SET_NAME " + "FROM RDB$CHARACTER_SETS charset " + "JOIN RDB$FIELDS fields " + "ON (fields.RDB$CHARACTER_SET_ID = charset.RDB$CHARACTER_SET_ID) " + "JOIN RDB$RELATION_FIELDS relfields " + "ON (fields.RDB$FIELD_NAME = relfields.RDB$FIELD_SOURCE) " + "WHERE relfields.RDB$RELATION_NAME = '" + + escapeWith(sTable, '\'', '\'') + "' AND " + "relfields.RDB$FIELD_NAME = '"+ escapeWith(sColumnName, '\'', '\'') +"'"; + + Reference<XStatement> xStmt= m_pConnection->createStatement(); + + Reference<XResultSet> xRes = + xStmt->executeQuery(sSql); + Reference<XRow> xRow ( xRes, UNO_QUERY); + if(xRes->next()) + { + OUString sCharset = xRow->getString(1).trim(); + return sCharset; + } + } + return OUString(); + + +} + +void OResultSetMetaData::verifyValidColumn(sal_Int32 column) +{ + if (column>getColumnCount() || column < 1) + throw SQLException("Invalid column specified", *this, OUString(), 0, Any()); +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount() +{ + return m_pSqlda->sqld; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnDisplaySize( sal_Int32 column ) +{ + verifyValidColumn(column); + return 32; // Hard limit for firebird +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnType(sal_Int32 column) +{ + verifyValidColumn(column); + + short aType = m_pSqlda->sqlvar[column-1].sqltype & ~1; + OUString sCharset; + + // do not query the character set unnecessarily + if(aType == SQL_TEXT || aType == SQL_VARYING) + { + sCharset = getCharacterSet(column); + } + + ColumnTypeInfo aInfo( m_pSqlda->sqlvar[column-1].sqltype, + m_pSqlda->sqlvar[column-1].sqlsubtype, + -(m_pSqlda->sqlvar[column-1].sqlscale), + sCharset ); + + return aInfo.getSdbcType(); +} + +sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive(sal_Int32) +{ + // Firebird is generally case sensitive when using quoted identifiers. + // IF THIS CHANGES make ResultSet::findColumn to be case-insensitive as needed. + // Generally names that are entirely UPPERCASE are case insensitive, however + // there remains some ambiguity if there is another mixed-case-named column + // of the same name. For safety always assume case insensitive. + return true; +} + +OUString SAL_CALL OResultSetMetaData::getSchemaName(sal_Int32) +{ + return OUString(); // Schemas supported by firebird +} + +OUString SAL_CALL OResultSetMetaData::getColumnName(sal_Int32 column) +{ + verifyValidColumn(column); + OUString sRet(m_pSqlda->sqlvar[column-1].sqlname, + m_pSqlda->sqlvar[column-1].sqlname_length, + RTL_TEXTENCODING_UTF8); + sanitizeIdentifier(sRet); + return sRet; +} + +OUString SAL_CALL OResultSetMetaData::getTableName(sal_Int32 column) +{ + verifyValidColumn(column); + return OUString(m_pSqlda->sqlvar[column-1].relname, + m_pSqlda->sqlvar[column-1].relname_length, + RTL_TEXTENCODING_UTF8); +} + +OUString SAL_CALL OResultSetMetaData::getCatalogName(sal_Int32) +{ + return OUString(); // Catalogs not supported by firebird +} + +OUString SAL_CALL OResultSetMetaData::getColumnTypeName(sal_Int32 column) +{ + verifyValidColumn(column); + + ColumnTypeInfo aInfo( m_pSqlda->sqlvar[column-1].sqltype, + m_pSqlda->sqlvar[column-1].sqlsubtype, + -(m_pSqlda->sqlvar[column-1].sqlscale) ); + + return aInfo.getColumnTypeName(); +} + +OUString SAL_CALL OResultSetMetaData::getColumnLabel(sal_Int32 column) +{ + // aliasname + verifyValidColumn(column); + OUString sRet(m_pSqlda->sqlvar[column-1].aliasname, + m_pSqlda->sqlvar[column-1].aliasname_length, + RTL_TEXTENCODING_UTF8); + sanitizeIdentifier(sRet); + return sRet; +} + +OUString SAL_CALL OResultSetMetaData::getColumnServiceName(sal_Int32) +{ + // TODO: implement + return OUString(); +} + +sal_Bool SAL_CALL OResultSetMetaData::isCurrency(sal_Int32) +{ + return false; +} + +sal_Bool SAL_CALL OResultSetMetaData::isAutoIncrement(sal_Int32 column) +{ + OUString sTable = getTableName(column); + if( !sTable.isEmpty() ) + { + OUString sColumnName = getColumnName( column ); + + OUString sSql = "SELECT RDB$IDENTITY_TYPE FROM RDB$RELATION_FIELDS " + "WHERE RDB$RELATION_NAME = '" + + escapeWith(sTable, '\'', '\'') + "' AND " + "RDB$FIELD_NAME = '"+ escapeWith(sColumnName, '\'', '\'') +"'"; + + Reference<XStatement> xStmt =m_pConnection ->createStatement(); + + Reference<XResultSet> xRes = + xStmt->executeQuery(sSql); + Reference<XRow> xRow ( xRes, UNO_QUERY); + if(xRes->next()) + { + int iType = xRow->getShort(1); + if(iType == 1) // IDENTITY + return true; + } + else + { + SAL_WARN("connectivity.firebird","Column '" + << sColumnName + << "' not found in database"); + + return false; + } + } + return false; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isSigned(sal_Int32) +{ + // Unsigned values aren't supported in firebird. + return true; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getPrecision(sal_Int32 column) +{ + sal_Int32 nType = getColumnType(column); + if( nType == DataType::NUMERIC || nType == DataType::DECIMAL ) + { + OUString sColumnName = getColumnName( column ); + + // RDB$FIELD_SOURCE is a unique name of column per database + OUString sSql = "SELECT RDB$FIELD_PRECISION FROM RDB$FIELDS " + " INNER JOIN RDB$RELATION_FIELDS " + " ON RDB$RELATION_FIELDS.RDB$FIELD_SOURCE = RDB$FIELDS.RDB$FIELD_NAME " + "WHERE RDB$RELATION_FIELDS.RDB$RELATION_NAME = '" + + escapeWith(getTableName(column), '\'', '\'') + "' AND " + "RDB$RELATION_FIELDS.RDB$FIELD_NAME = '" + + escapeWith(sColumnName, '\'', '\'') +"'"; + Reference<XStatement> xStmt= m_pConnection->createStatement(); + + Reference<XResultSet> xRes = + xStmt->executeQuery(sSql); + Reference<XRow> xRow ( xRes, UNO_QUERY); + if(xRes->next()) + { + return static_cast<sal_Int32>(xRow->getShort(1)); + } + else + { + SAL_WARN("connectivity.firebird","Column '" + << sColumnName + << "' not found in database"); + return 0; + } + } + return 0; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getScale(sal_Int32 column) +{ + return -(m_pSqlda->sqlvar[column-1].sqlscale); // fb stores negative number +} + +sal_Int32 SAL_CALL OResultSetMetaData::isNullable(sal_Int32 column) +{ + if (m_pSqlda->sqlvar[column-1].sqltype & 1) + return ColumnValue::NULLABLE; + else + return ColumnValue::NO_NULLS; +} + +sal_Bool SAL_CALL OResultSetMetaData::isSearchable(sal_Int32) +{ + // TODO: Can the column be used as part of a where clause? Assume yes + return true; +} + +sal_Bool SAL_CALL OResultSetMetaData::isReadOnly(sal_Int32) +{ + return m_pConnection->isReadOnly(); // Readonly only available on db level +} + +sal_Bool SAL_CALL OResultSetMetaData::isDefinitelyWritable(sal_Int32) +{ + return !m_pConnection->isReadOnly(); +} + +sal_Bool SAL_CALL OResultSetMetaData::isWritable( sal_Int32 ) +{ + return !m_pConnection->isReadOnly(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/ResultSetMetaData.hxx b/connectivity/source/drivers/firebird/ResultSetMetaData.hxx new file mode 100644 index 000000000..1c6d1ca22 --- /dev/null +++ b/connectivity/source/drivers/firebird/ResultSetMetaData.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_RESULTSETMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_RESULTSETMETADATA_HXX + +#include "Connection.hxx" + +#include <ibase.h> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> + +namespace connectivity +{ + namespace firebird + { + typedef ::cppu::WeakImplHelper< css::sdbc::XResultSetMetaData> + OResultSetMetaData_BASE; + + class OResultSetMetaData : public OResultSetMetaData_BASE + { + protected: + ::rtl::Reference<Connection> m_pConnection; + XSQLDA* m_pSqlda; + + virtual ~OResultSetMetaData() override; + + /// @throws css::sdbc::SQLException + void verifyValidColumn(sal_Int32 column); + OUString getCharacterSet(sal_Int32 nIndex); + public: + // a constructor, which is required for returning objects: + OResultSetMetaData(Connection* pConnection, + XSQLDA* pSqlda) + : m_pConnection(pConnection) + , m_pSqlda(pSqlda) + {} + + virtual sal_Int32 SAL_CALL getColumnCount() override; + virtual sal_Bool SAL_CALL isAutoIncrement(sal_Int32 column) override; + virtual sal_Bool SAL_CALL isCaseSensitive(sal_Int32 column) override; + virtual sal_Bool SAL_CALL isSearchable(sal_Int32 column) override; + virtual sal_Bool SAL_CALL isCurrency(sal_Int32 column) override; + virtual sal_Int32 SAL_CALL isNullable(sal_Int32 column) override; + virtual sal_Bool SAL_CALL isSigned(sal_Int32 column) override; + virtual sal_Int32 SAL_CALL getColumnDisplaySize(sal_Int32 column) override; + virtual OUString SAL_CALL getColumnLabel(sal_Int32 column) override; + virtual OUString SAL_CALL getColumnName(sal_Int32 column) override; + virtual OUString SAL_CALL getSchemaName(sal_Int32 column) override; + virtual sal_Int32 SAL_CALL getPrecision(sal_Int32 column) override; + virtual sal_Int32 SAL_CALL getScale(sal_Int32 column) override; + virtual OUString SAL_CALL getTableName(sal_Int32 column) override; + virtual OUString SAL_CALL getCatalogName(sal_Int32 column) override; + virtual sal_Int32 SAL_CALL getColumnType(sal_Int32 column) override; + virtual OUString SAL_CALL getColumnTypeName(sal_Int32 column) override; + virtual sal_Bool SAL_CALL isReadOnly(sal_Int32 column) override; + virtual sal_Bool SAL_CALL isWritable(sal_Int32 column) override; + virtual sal_Bool SAL_CALL isDefinitelyWritable(sal_Int32 column) override; + virtual OUString SAL_CALL getColumnServiceName(sal_Int32 column) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_RESULTSETMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Services.cxx b/connectivity/source/drivers/firebird/Services.cxx new file mode 100644 index 000000000..e72133692 --- /dev/null +++ b/connectivity/source/drivers/firebird/Services.cxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "Driver.hxx" + +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <sal/types.h> + +using namespace connectivity::firebird; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* _pTemp + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* firebird_sdbc_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void*) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + FirebirdDriver::getImplementationName_Static(), + FirebirdDriver::getSupportedServiceNames_Static(), + FirebirdDriver_CreateInstance, ::cppu::createSingleFactory) + ; + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Statement.cxx b/connectivity/source/drivers/firebird/Statement.cxx new file mode 100644 index 000000000..418c66a7d --- /dev/null +++ b/connectivity/source/drivers/firebird/Statement.cxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "Connection.hxx" +#include "ResultSet.hxx" +#include "Statement.hxx" +#include "Util.hxx" + +#include <comphelper/sequence.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <sal/log.hxx> + +using namespace connectivity::firebird; + +using namespace com::sun::star; +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::io; +using namespace com::sun::star::util; + +using namespace ::comphelper; +using namespace ::osl; +using namespace ::std; + +// ---- XBatchExecution - UNSUPPORTED ---------------------------------------- +void SAL_CALL OStatement::addBatch(const OUString&) +{ +} + +void SAL_CALL OStatement::clearBatch() +{ +} + +Sequence< sal_Int32 > SAL_CALL OStatement::executeBatch() +{ + return Sequence< sal_Int32 >(); +} + +IMPLEMENT_SERVICE_INFO(OStatement,"com.sun.star.sdbcx.OStatement","com.sun.star.sdbc.Statement"); + +void SAL_CALL OStatement::acquire() throw() +{ + OStatementCommonBase::acquire(); +} + +void SAL_CALL OStatement::release() throw() +{ + OStatementCommonBase::release(); +} + +void OStatement::disposeResultSet() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + + OStatementCommonBase::disposeResultSet(); + + if (m_pSqlda) + { + freeSQLVAR(m_pSqlda); + free(m_pSqlda); + m_pSqlda = nullptr; + } +} + +// ---- XStatement ----------------------------------------------------------- +sal_Int32 SAL_CALL OStatement::executeUpdate(const OUString& sql) +{ + execute(sql); + return getStatementChangeCount(); +} + + +uno::Reference< XResultSet > SAL_CALL OStatement::executeQuery(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + + SAL_INFO("connectivity.firebird", "executeQuery(" << sql << ")"); + + ISC_STATUS aErr = 0; + + disposeResultSet(); + + prepareAndDescribeStatement(sql, + m_pSqlda); + + aErr = isc_dsql_execute(m_statusVector, + &m_pConnection->getTransaction(), + &m_aStatementHandle, + 1, + nullptr); + if (aErr) + SAL_WARN("connectivity.firebird", "isc_dsql_execute failed"); + + m_xResultSet = new OResultSet(m_pConnection.get(), + m_aMutex, + uno::Reference< XInterface >(*this), + m_aStatementHandle, + m_pSqlda ); + + // TODO: deal with cleanup + + evaluateStatusVector(m_statusVector, sql, *this); + + if (isDDLStatement()) + { + m_pConnection->commit(); + m_pConnection->notifyDatabaseModified(); + } + else if (getStatementChangeCount() > 0) + { + m_pConnection->notifyDatabaseModified(); + } + + return m_xResultSet; +} + +sal_Bool SAL_CALL OStatement::execute(const OUString& sql) +{ + uno::Reference< XResultSet > xResults = executeQuery(sql); + return xResults.is(); + // TODO: what if we have multiple results? +} + +uno::Reference< XConnection > SAL_CALL OStatement::getConnection() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + + return uno::Reference<XConnection>(m_pConnection.get()); +} + +Any SAL_CALL OStatement::queryInterface( const Type & rType ) +{ + Any aRet = OStatement_Base::queryInterface(rType); + if(!aRet.hasValue()) + aRet = ::cppu::queryInterface(rType,static_cast< XBatchExecution*> (this)); + if(!aRet.hasValue()) + aRet = OStatementCommonBase::queryInterface(rType); + return aRet; +} + +uno::Sequence< Type > SAL_CALL OStatement::getTypes() +{ + return concatSequences(OStatement_Base::getTypes(), + OStatementCommonBase::getTypes()); +} + +void SAL_CALL OStatement::disposing() +{ + disposeResultSet(); + close(); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Statement.hxx b/connectivity/source/drivers/firebird/Statement.hxx new file mode 100644 index 000000000..4c9168fd4 --- /dev/null +++ b/connectivity/source/drivers/firebird/Statement.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_STATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_STATEMENT_HXX + +#include "StatementCommonBase.hxx" + +#include <cppuhelper/implbase1.hxx> +#include <com/sun/star/sdbc/XBatchExecution.hpp> + +namespace connectivity +{ + namespace firebird + { + + typedef ::cppu::ImplHelper1< css::sdbc::XStatement > + OStatement_Base; + + class OStatement : public OStatementCommonBase, + public OStatement_Base, + public css::sdbc::XBatchExecution, + public css::lang::XServiceInfo + { + XSQLDA* m_pSqlda; + protected: + virtual ~OStatement() override {} + + public: + // a constructor, which is required for returning objects: + explicit OStatement( Connection* _pConnection) + : OStatementCommonBase( _pConnection), + m_pSqlda(nullptr) + {} + + virtual void disposeResultSet() override; + + DECLARE_SERVICE_INFO(); + + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + + // XStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL + executeQuery(const OUString& sql) override; + virtual sal_Int32 SAL_CALL executeUpdate(const OUString& sqlIn) override; + virtual sal_Bool SAL_CALL + execute(const OUString& sql) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL + getConnection() override; + + // XBatchExecution - UNSUPPORTED + virtual void SAL_CALL addBatch( const OUString& sql ) override; + virtual void SAL_CALL clearBatch( ) override; + virtual css::uno::Sequence< sal_Int32 > SAL_CALL executeBatch( ) override; + + // XInterface + virtual css::uno::Any SAL_CALL + queryInterface(const css::uno::Type & rType) override; + + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL + getTypes() override; + // OComponentHelper + virtual void SAL_CALL disposing() override; + + + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_STATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/StatementCommonBase.cxx b/connectivity/source/drivers/firebird/StatementCommonBase.cxx new file mode 100644 index 000000000..c1219d74b --- /dev/null +++ b/connectivity/source/drivers/firebird/StatementCommonBase.cxx @@ -0,0 +1,463 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "Driver.hxx" +#include "StatementCommonBase.hxx" +#include "Util.hxx" + +#include <sal/log.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <propertyids.hxx> +#include <vcl/svapp.hxx> +#include <TConnection.hxx> + +#include <com/sun/star/sdbc/SQLException.hpp> + +using namespace ::connectivity::firebird; + +using namespace ::com::sun::star; +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::io; +using namespace ::com::sun::star::util; + +using namespace ::comphelper; +using namespace ::osl; +using namespace ::std; + +OStatementCommonBase::OStatementCommonBase(Connection* _pConnection) + : OStatementCommonBase_Base(m_aMutex), + OPropertySetHelper(OStatementCommonBase_Base::rBHelper), + m_pConnection(_pConnection), +#if SAL_TYPES_SIZEOFPOINTER == 8 + m_aStatementHandle(0) +#else + m_aStatementHandle(nullptr) +#endif +{ +} + +OStatementCommonBase::~OStatementCommonBase() +{ +} + +void OStatementCommonBase::disposeResultSet() +{ + uno::Reference< XComponent > xComp(m_xResultSet, UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + m_xResultSet.clear(); +} + +void OStatementCommonBase::freeStatementHandle() +{ + if (m_aStatementHandle) + { + isc_dsql_free_statement(m_statusVector, + &m_aStatementHandle, + DSQL_drop); + evaluateStatusVector(m_statusVector, + "isc_dsql_free_statement", + *this); + } +} + + +Any SAL_CALL OStatementCommonBase::queryInterface( const Type & rType ) +{ + Any aRet = OStatementCommonBase_Base::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OPropertySetHelper::queryInterface(rType); + return aRet; +} + +Sequence< Type > SAL_CALL OStatementCommonBase::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( + ::cppu::UnoType<XMultiPropertySet>::get(), + ::cppu::UnoType<XFastPropertySet>::get(), + ::cppu::UnoType<XPropertySet>::get()); + + return concatSequences(aTypes.getTypes(),OStatementCommonBase_Base::getTypes()); +} + + +void SAL_CALL OStatementCommonBase::cancel( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + // cancel the current sql statement +} + +void SAL_CALL OStatementCommonBase::close() +{ + SAL_INFO("connectivity.firebird", "close"); + + { + MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + disposeResultSet(); + freeStatementHandle(); + } + + dispose(); +} + +void OStatementCommonBase::prepareAndDescribeStatement(const OUString& sql, + XSQLDA*& pOutSqlda, + XSQLDA* pInSqlda) +{ + SolarMutexGuard g; // tdf#122129 + + freeStatementHandle(); + + if (!pOutSqlda) + { + pOutSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(10))); + pOutSqlda->version = SQLDA_VERSION1; + pOutSqlda->sqln = 10; + } + + ISC_STATUS aErr = isc_dsql_allocate_statement(m_statusVector, + &m_pConnection->getDBHandle(), + &m_aStatementHandle); + + if (aErr) + { + evaluateStatusVector(m_statusVector, + "isc_dsql_allocate_statement", + *this); + } + else + { + aErr = isc_dsql_prepare(m_statusVector, + &m_pConnection->getTransaction(), + &m_aStatementHandle, + 0, + OUStringToOString(sql, RTL_TEXTENCODING_UTF8).getStr(), + FIREBIRD_SQL_DIALECT, + pInSqlda); + + if (aErr) + { + evaluateStatusVector(m_statusVector, + "isc_dsql_prepare", + *this); + } + else + { + aErr = isc_dsql_describe(m_statusVector, + &m_aStatementHandle, + 1, + pOutSqlda); + + if (aErr) + { + // TODO: free statement handle, etc.? + evaluateStatusVector(m_statusVector, + "isc_dsql_describe", + *this); + } + else + { + // Ensure we have enough space in pOutSqlda + if (pOutSqlda->sqld > pOutSqlda->sqln) + { + int n = pOutSqlda->sqld; + free(pOutSqlda); + pOutSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(n))); + pOutSqlda->version = SQLDA_VERSION1; + pOutSqlda->sqln = n; + aErr = isc_dsql_describe(m_statusVector, + &m_aStatementHandle, + 1, + pOutSqlda); + } + + // Process each XSQLVAR parameter structure in the output XSQLDA + if (aErr) + { + evaluateStatusVector(m_statusVector, + "isc_dsql_describe", + *this); + } + else + { + mallocSQLVAR(pOutSqlda); + } + } + } + if(aErr) + { + freeStatementHandle(); + } + } + if(aErr) + { + free(pOutSqlda); + pOutSqlda = nullptr; + } +} + +// ---- XMultipleResults - UNSUPPORTED ---------------------------------------- +uno::Reference< XResultSet > SAL_CALL OStatementCommonBase::getResultSet() +{ + // TODO: verify we really can't support this +// return uno::Reference< XResultSet >(); + MutexGuard aGuard(m_aMutex); + checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); + + return m_xResultSet; +} + +sal_Bool SAL_CALL OStatementCommonBase::getMoreResults() +{ + // TODO: verify we really can't support this + return false; +// MutexGuard aGuard( m_aMutex ); +// checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed); +} + +sal_Int32 SAL_CALL OStatementCommonBase::getUpdateCount() +{ + // TODO: verify we really can't support this + return -1; +} + + +// ---- XWarningsSupplier - UNSUPPORTED ---------------------------------------- +Any SAL_CALL OStatementCommonBase::getWarnings() +{ + return Any(); +} + +void SAL_CALL OStatementCommonBase::clearWarnings() +{ +} + +::cppu::IPropertyArrayHelper* OStatementCommonBase::createArrayHelper( ) const +{ + // this properties are define by the service statement + // they must in alphabetic order + Sequence< Property > aProps(10); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING), + PROPERTY_ID_ESCAPEPROCESSING, cppu::UnoType<bool>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE), + PROPERTY_ID_MAXFIELDSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS), + PROPERTY_ID_MAXROWS, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT), + PROPERTY_ID_QUERYTIMEOUT, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_USEBOOKMARKS), + PROPERTY_ID_USEBOOKMARKS, cppu::UnoType<bool>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +::cppu::IPropertyArrayHelper & OStatementCommonBase::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OStatementCommonBase::convertFastPropertyValue( + Any &, + Any &, + sal_Int32, + const Any& ) +{ + // here we have to try to convert + return false; +} + +void OStatementCommonBase::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any&) +{ + // set the value to whatever is necessary + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + case PROPERTY_ID_MAXFIELDSIZE: + case PROPERTY_ID_MAXROWS: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + case PROPERTY_ID_ESCAPEPROCESSING: + case PROPERTY_ID_USEBOOKMARKS: + default: + ; + } +} + +void OStatementCommonBase::getFastPropertyValue(Any&,sal_Int32 nHandle) const +{ + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + case PROPERTY_ID_MAXFIELDSIZE: + case PROPERTY_ID_MAXROWS: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + case PROPERTY_ID_ESCAPEPROCESSING: + case PROPERTY_ID_USEBOOKMARKS: + default: + ; + } +} + +void SAL_CALL OStatementCommonBase::acquire() throw() +{ + OStatementCommonBase_Base::acquire(); +} + +void SAL_CALL OStatementCommonBase::release() throw() +{ + OStatementCommonBase_Base::release(); +} + +uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OStatementCommonBase::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +short OStatementCommonBase::getSqlInfoItem(char aInfoItem) +{ + ISC_STATUS_ARRAY aStatusVector; + ISC_STATUS aErr; + + char aInfoItems[] = {aInfoItem}; + char aResultsBuffer[8]; + + aErr = isc_dsql_sql_info(aStatusVector, + &m_aStatementHandle, + sizeof(aInfoItems), + aInfoItems, + sizeof(aResultsBuffer), + aResultsBuffer); + + if (!aErr && aResultsBuffer[0] == aInfoItem) + { + const short aBytes = static_cast<short>(isc_vax_integer(aResultsBuffer+1, 2)); + return static_cast<short>(isc_vax_integer(aResultsBuffer+3, aBytes)); + } + + evaluateStatusVector(aStatusVector, + "isc_dsq_sql_info", + *this); + return 0; +} + +bool OStatementCommonBase::isDDLStatement() +{ + return getSqlInfoItem(isc_info_sql_stmt_type) == isc_info_sql_stmt_ddl; +} + +sal_Int32 OStatementCommonBase::getStatementChangeCount() +{ + const short aStatementType = getSqlInfoItem(isc_info_sql_stmt_type); + + + ISC_STATUS_ARRAY aStatusVector; + ISC_STATUS aErr; + + // This is somewhat undocumented so I'm just guessing and hoping for the best. + char aInfoItems[] = {isc_info_sql_records}; + char aResultsBuffer[1024]; + + aErr = isc_dsql_sql_info(aStatusVector, + &m_aStatementHandle, + sizeof(aInfoItems), + aInfoItems, + sizeof(aResultsBuffer), + aResultsBuffer); + + if (aErr) + { + evaluateStatusVector(aStatusVector, + "isc_dsq_sql_info", + *this); + return 0; + } + + short aDesiredInfoType = 0; + switch (aStatementType) + { + case isc_info_sql_stmt_select: + aDesiredInfoType = isc_info_req_select_count; + break; + case isc_info_sql_stmt_insert: + aDesiredInfoType = isc_info_req_insert_count; + break; + case isc_info_sql_stmt_update: + aDesiredInfoType = isc_info_req_update_count; + break; + case isc_info_sql_stmt_delete: + aDesiredInfoType = isc_info_req_delete_count; + break; + case isc_info_sql_stmt_exec_procedure: + return 0; // cannot determine + default: + throw SQLException(); // TODO: better error message? + } + + char* pResults = aResultsBuffer; + if (static_cast<short>(*pResults++) == isc_info_sql_records) + { +// const short aTotalLength = (short) isc_vax_integer(pResults, 2); + pResults += 2; + + // Seems to be of form TOKEN (1 byte), LENGTH (2 bytes), DATA (LENGTH bytes) + while (*pResults != isc_info_rsb_end) + { + const char aToken = *pResults; + const short aLength = static_cast<short>(isc_vax_integer(pResults+1, 2)); + + if (aToken == aDesiredInfoType) + { + return isc_vax_integer(pResults + 3, aLength); + } + + pResults += (3 + aLength); + } + } + + return 0; +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/StatementCommonBase.hxx b/connectivity/source/drivers/firebird/StatementCommonBase.hxx new file mode 100644 index 000000000..bce9f7374 --- /dev/null +++ b/connectivity/source/drivers/firebird/StatementCommonBase.hxx @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_STATEMENTCOMMONBASE_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_STATEMENTCOMMONBASE_HXX + +#include "Connection.hxx" +#include "SubComponent.hxx" + +#include <ibase.h> + +#include <cppuhelper/compbase.hxx> + +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XMultipleResults.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/util/XCancellable.hpp> + +namespace connectivity +{ + namespace firebird + { + + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XWarningsSupplier, + css::util::XCancellable, + css::sdbc::XCloseable, + css::sdbc::XMultipleResults> OStatementCommonBase_Base; + + class OStatementCommonBase : public OStatementCommonBase_Base, + public ::cppu::OPropertySetHelper, + public OPropertyArrayUsageHelper<OStatementCommonBase> + + { + protected: + ::osl::Mutex m_aMutex; + + css::uno::Reference< css::sdbc::XResultSet> m_xResultSet; // The last ResultSet created + // for this Statement + + ::rtl::Reference<Connection> m_pConnection; + + ISC_STATUS_ARRAY m_statusVector; + isc_stmt_handle m_aStatementHandle; + + protected: + virtual void disposeResultSet(); + /// @throws css::sdbc::SQLException + void freeStatementHandle(); + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + // OPropertySetHelper + using OPropertySetHelper::getFastPropertyValue; + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle) const override; + virtual ~OStatementCommonBase() override; + + /// @throws css::sdbc::SQLException + void prepareAndDescribeStatement(const OUString& sqlIn, + XSQLDA*& pOutSqlda, + XSQLDA* pInSqlda=nullptr); + + /// @throws css::sdbc::SQLException + short getSqlInfoItem(char aInfoItem); + /// @throws css::sdbc::SQLException + bool isDDLStatement(); + /// @throws css::sdbc::SQLException + sal_Int32 getStatementChangeCount(); + + public: + + explicit OStatementCommonBase(Connection* _pConnection); + using OStatementCommonBase_Base::operator css::uno::Reference< css::uno::XInterface >; + + // OComponentHelper + virtual void SAL_CALL disposing() override { + disposeResultSet(); + OStatementCommonBase_Base::disposing(); + } + // XInterface + virtual void SAL_CALL release() throw() override; + virtual void SAL_CALL acquire() throw() override; + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XWarningsSupplier - UNSUPPORTED + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + // XMultipleResults - UNSUPPORTED + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getResultSet( ) override; + virtual sal_Int32 SAL_CALL getUpdateCount( ) override; + virtual sal_Bool SAL_CALL getMoreResults( ) override; + + // XCancellable + virtual void SAL_CALL cancel( ) override; + // XCloseable + virtual void SAL_CALL close( ) override; + + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_STATEMENTCOMMONBASE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/SubComponent.hxx b/connectivity/source/drivers/firebird/SubComponent.hxx new file mode 100644 index 000000000..d37100670 --- /dev/null +++ b/connectivity/source/drivers/firebird/SubComponent.hxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_SUBCOMPONENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_SUBCOMPONENT_HXX + +#include <cppuhelper/propshlp.hxx> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> + +namespace cppu { + class IPropertyArrayHelper; +} + +namespace com +{ + namespace sun + { + namespace star + { + namespace lang + { + class XComponent; + } + } + } +} +namespace connectivity +{ + + namespace firebird + { + /// @throws css::lang::DisposedException + void checkDisposed(bool _bThrow); + + + template <class TYPE> + class OPropertyArrayUsageHelper + { + protected: + static sal_Int32 s_nRefCount; + static ::cppu::IPropertyArrayHelper* s_pProps; + static ::osl::Mutex s_aMutex; + + public: + OPropertyArrayUsageHelper(); + virtual ~OPropertyArrayUsageHelper(); + + /** call this in the getInfoHelper method of your derived class. The method returns the array helper of the + class, which is created if necessary. + */ + ::cppu::IPropertyArrayHelper* getArrayHelper(); + + protected: + /** used to implement the creation of the array helper which is shared amongst all instances of the class. + This method needs to be implemented in derived classes. + <BR> + The method gets called with s_aMutex acquired. + @return a pointer to the newly created array helper. Must not be NULL. + */ + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const = 0; + }; + + template<class TYPE> + sal_Int32 OPropertyArrayUsageHelper< TYPE >::s_nRefCount = 0; + + template<class TYPE> + ::cppu::IPropertyArrayHelper* OPropertyArrayUsageHelper< TYPE >::s_pProps = nullptr; + + template<class TYPE> + ::osl::Mutex OPropertyArrayUsageHelper< TYPE >::s_aMutex; + + + template <class TYPE> + OPropertyArrayUsageHelper<TYPE>::OPropertyArrayUsageHelper() + { + ::osl::MutexGuard aGuard(s_aMutex); + ++s_nRefCount; + } + + + template <class TYPE> + OPropertyArrayUsageHelper<TYPE>::~OPropertyArrayUsageHelper() + { + ::osl::MutexGuard aGuard(s_aMutex); + OSL_ENSURE(s_nRefCount > 0, "OPropertyArrayUsageHelper::~OPropertyArrayUsageHelper : suspicious call : have a refcount of 0 !"); + if (!--s_nRefCount) + { + delete s_pProps; + s_pProps = nullptr; + } + } + + + template <class TYPE> + ::cppu::IPropertyArrayHelper* OPropertyArrayUsageHelper<TYPE>::getArrayHelper() + { + OSL_ENSURE(s_nRefCount, "OPropertyArrayUsageHelper::getArrayHelper : suspicious call : have a refcount of 0 !"); + if (!s_pProps) + { + ::osl::MutexGuard aGuard(s_aMutex); + if (!s_pProps) + { + s_pProps = createArrayHelper(); + OSL_ENSURE(s_pProps, "OPropertyArrayUsageHelper::getArrayHelper : createArrayHelper returned nonsense !"); + } + } + return s_pProps; + } + + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_SUBCOMPONENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Table.cxx b/connectivity/source/drivers/firebird/Table.cxx new file mode 100644 index 000000000..e0eba9d7e --- /dev/null +++ b/connectivity/source/drivers/firebird/Table.cxx @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "Columns.hxx" +#include "Indexes.hxx" +#include "Keys.hxx" +#include "Table.hxx" + +#include <TConnection.hxx> + +#include <sal/log.hxx> +#include <comphelper/sequence.hxx> +#include <connectivity/dbtools.hxx> + +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +using namespace ::connectivity; +using namespace ::connectivity::firebird; +using namespace ::connectivity::sdbcx; + +using namespace ::osl; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::uno; + +Table::Table(Tables* pTables, + Mutex& rMutex, + const uno::Reference< XConnection >& rConnection): + OTableHelper(pTables, + rConnection, + true), + m_rMutex(rMutex), + m_nPrivileges(0) +{ + construct(); +} + +Table::Table(Tables* pTables, + Mutex& rMutex, + const uno::Reference< XConnection >& rConnection, + const OUString& rName, + const OUString& rType, + const OUString& rDescription): + OTableHelper(pTables, + rConnection, + true, + rName, + rType, + rDescription, + "", + ""), + m_rMutex(rMutex), + m_nPrivileges(0) +{ + construct(); +} + +void Table::construct() +{ + OTableHelper::construct(); + if (isNew()) + return; + + // TODO: get privileges when in non-embedded mode. + m_nPrivileges = Privilege::DROP | + Privilege::REFERENCE | + Privilege::ALTER | + Privilege::CREATE | + Privilege::READ | + Privilege::DELETE | + Privilege::UPDATE | + Privilege::INSERT | + Privilege::SELECT; + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRIVILEGES), + PROPERTY_ID_PRIVILEGES, + PropertyAttribute::READONLY, + &m_nPrivileges, + cppu::UnoType<decltype(m_nPrivileges)>::get()); +} +//----- OTableHelper --------------------------------------------------------- +OCollection* Table::createColumns(const ::std::vector< OUString>& rNames) +{ + return new Columns(*this, + m_rMutex, + rNames); +} + +OCollection* Table::createKeys(const ::std::vector< OUString>& rNames) +{ + return new Keys(this, + m_rMutex, + rNames); +} + +OCollection* Table::createIndexes(const ::std::vector< OUString>& rNames) +{ + return new Indexes(this, + m_rMutex, + rNames); +} + +//----- XAlterTable ----------------------------------------------------------- +void SAL_CALL Table::alterColumnByName(const OUString& rColName, + const uno::Reference< XPropertySet >& rDescriptor) +{ + MutexGuard aGuard(m_rMutex); + checkDisposed(WeakComponentImplHelperBase::rBHelper.bDisposed); + + uno::Reference< XPropertySet > xColumn(m_xColumns->getByName(rColName), UNO_QUERY); + + // sdbcx::Descriptor + const bool bNameChanged = xColumn->getPropertyValue("Name") != rDescriptor->getPropertyValue("Name"); + // sdbcx::ColumnDescriptor + const bool bTypeChanged = xColumn->getPropertyValue("Type") != rDescriptor->getPropertyValue("Type"); + const bool bTypeNameChanged = xColumn->getPropertyValue("TypeName") != rDescriptor->getPropertyValue("TypeName"); + const bool bPrecisionChanged = xColumn->getPropertyValue("Precision") != rDescriptor->getPropertyValue("Precision"); + const bool bScaleChanged = xColumn->getPropertyValue("Scale") != rDescriptor->getPropertyValue("Scale"); + const bool bIsNullableChanged = xColumn->getPropertyValue("IsNullable") != rDescriptor->getPropertyValue("IsNullable"); + const bool bIsAutoIncrementChanged = xColumn->getPropertyValue("IsAutoIncrement") != rDescriptor->getPropertyValue("IsAutoIncrement"); + + // TODO: remainder -- these are all "optional" so have to detect presence and change. + + bool bDefaultChanged = xColumn->getPropertyValue("DefaultValue") + != rDescriptor->getPropertyValue("DefaultValue"); + + if (bTypeChanged || bTypeNameChanged || bPrecisionChanged || bScaleChanged) + { + // If bPrecisionChanged this will only succeed if we have increased the + // precision, otherwise an exception is thrown -- however the base + // gui then offers to delete and recreate the column. + OUString sSql(getAlterTableColumn(rColName) + "TYPE " + + ::dbtools::createStandardTypePart(rDescriptor, getConnection())); + getConnection()->createStatement()->execute(sSql); + // TODO: could cause errors e.g. if incompatible types, deal with them here as appropriate. + // possibly we have to wrap things in Util::evaluateStatusVector. + } + + if (bIsNullableChanged) + { + sal_Int32 nNullable = 0; + rDescriptor->getPropertyValue("IsNullable") >>= nNullable; + + if (nNullable != ColumnValue::NULLABLE_UNKNOWN) + { + + OUString sSql; + // Dirty hack: can't change null directly in sql, we have to fiddle + // the system tables manually. + if (nNullable == ColumnValue::NULLABLE) + { + sSql = "UPDATE RDB$RELATION_FIELDS SET RDB$NULL_FLAG = NULL " + "WHERE RDB$FIELD_NAME = '" + rColName + "' " + "AND RDB$RELATION_NAME = '" + getName() + "'"; + } + else if (nNullable == ColumnValue::NO_NULLS) + { + // And if we are making NOT NULL then we have to make sure we have + // no nulls left in the column. + OUString sFillNulls("UPDATE \"" + getName() + "\" SET \"" + + rColName + "\" = 0 " + "WHERE \"" + rColName + "\" IS NULL"); + getConnection()->createStatement()->execute(sFillNulls); + + sSql = "UPDATE RDB$RELATION_FIELDS SET RDB$NULL_FLAG = 1 " + "WHERE RDB$FIELD_NAME = '" + rColName + "' " + "AND RDB$RELATION_NAME = '" + getName() + "'"; + } + getConnection()->createStatement()->execute(sSql); + } + else + { + SAL_WARN("connectivity.firebird", "Attempting to set Nullable to NULLABLE_UNKNOWN"); + } + } + + if (bIsAutoIncrementChanged) + { + ::dbtools::throwSQLException( + "Changing autoincrement property of existing column is not supported", + ::dbtools::StandardSQLState::FUNCTION_NOT_SUPPORTED, + *this); + + } + + if (bDefaultChanged) + { + OUString sNewDefault; + rDescriptor->getPropertyValue("DefaultValue") >>= sNewDefault; + + OUString sSql; + if (sNewDefault.isEmpty()) + sSql = getAlterTableColumn(rColName) + "DROP DEFAULT"; + else + sSql = getAlterTableColumn(rColName) + "SET DEFAULT " + sNewDefault; + + getConnection()->createStatement()->execute(sSql); + } + // TODO: quote identifiers as needed. + if (bNameChanged) + { + OUString sNewColName; + rDescriptor->getPropertyValue("Name") >>= sNewColName; + OUString sSql(getAlterTableColumn(rColName) + + " TO \"" + sNewColName + "\""); + + getConnection()->createStatement()->execute(sSql); + } + + + m_xColumns->refresh(); +} + +// ----- XRename -------------------------------------------------------------- +void SAL_CALL Table::rename(const OUString&) +{ + throw RuntimeException("Table renaming not supported by Firebird."); +} + +// ----- XInterface ----------------------------------------------------------- +Any SAL_CALL Table::queryInterface(const Type& rType) +{ + if (rType.getTypeName() == "com.sun.star.sdbcx.XRename") + return Any(); + + return OTableHelper::queryInterface(rType); +} + +// ----- XTypeProvider -------------------------------------------------------- +uno::Sequence< Type > SAL_CALL Table::getTypes() +{ + uno::Sequence< Type > aTypes = OTableHelper::getTypes(); + + for (int i = 0; i < aTypes.getLength(); i++) + { + if (aTypes[i].getTypeName() == "com.sun.star.sdbcx.XRename") + { + ::comphelper::removeElementAt(aTypes, i); + break; + } + } + + return OTableHelper::getTypes(); +} + +OUString Table::getAlterTableColumn(const OUString& rColumn) +{ + return ("ALTER TABLE \"" + getName() + "\" ALTER COLUMN \"" + rColumn + "\" "); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Table.hxx b/connectivity/source/drivers/firebird/Table.hxx new file mode 100644 index 000000000..586152ecc --- /dev/null +++ b/connectivity/source/drivers/firebird/Table.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_TABLE_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_TABLE_HXX + +#include "Tables.hxx" + +#include <connectivity/TTableHelper.hxx> + +namespace connectivity +{ + namespace firebird + { + + /** + * Implements sdbcx.Table. We don't support table renaming (XRename) + * hence the appropriate methods are overridden. + */ + class Table: public OTableHelper + { + private: + ::osl::Mutex& m_rMutex; + sal_Int32 m_nPrivileges; + + /** + * Get the ALTER TABLE [TABLE] ALTER [COLUMN] String. + * Includes a trailing space. + */ + OUString getAlterTableColumn(const OUString& rColumn); + + protected: + void construct() override; + + public: + Table(Tables* pTables, + ::osl::Mutex& rMutex, + const css::uno::Reference< css::sdbc::XConnection >& _xConnection); + Table(Tables* pTables, + ::osl::Mutex& rMutex, + const css::uno::Reference< css::sdbc::XConnection >& _xConnection, + const OUString& rName, + const OUString& rType, + const OUString& rDescription); + + // OTableHelper + virtual ::connectivity::sdbcx::OCollection* createColumns( + const ::std::vector< OUString>& rNames) override; + virtual ::connectivity::sdbcx::OCollection* createKeys( + const ::std::vector< OUString>& rNames) override; + virtual ::connectivity::sdbcx::OCollection* createIndexes( + const ::std::vector< OUString>& rNames) override; + + // XAlterTable + /** + * See css::sdbcx::ColumnDescriptor for details of + * rDescriptor. + */ + virtual void SAL_CALL alterColumnByName( + const OUString& rColName, + const css::uno::Reference< css::beans::XPropertySet >& rDescriptor) override; + + // XRename -- UNSUPPORTED + virtual void SAL_CALL rename(const OUString& sName) override; + + //XInterface + virtual css::uno::Any + SAL_CALL queryInterface(const css::uno::Type & rType) override; + + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > + SAL_CALL getTypes() override; + + }; + + } // namespace firebird +} // namespace connectivity + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_TABLE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Tables.cxx b/connectivity/source/drivers/firebird/Tables.cxx new file mode 100644 index 000000000..5acb391ca --- /dev/null +++ b/connectivity/source/drivers/firebird/Tables.cxx @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "Table.hxx" +#include "Tables.hxx" +#include "Catalog.hxx" +#include "Util.hxx" + +#include <TConnection.hxx> + +#include <connectivity/dbtools.hxx> + +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <comphelper/types.hxx> + +using namespace ::connectivity; +using namespace ::connectivity::firebird; +using namespace ::connectivity::sdbcx; +using namespace ::cppu; +using namespace ::osl; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::uno; + + +//----- OCollection ----------------------------------------------------------- +void Tables::impl_refresh() +{ + static_cast<Catalog&>(m_rParent).refreshTables(); +} + +ObjectType Tables::createObject(const OUString& rName) +{ + // Only retrieving a single table, so table type is irrelevant (param 4) + uno::Reference< XResultSet > xTables = m_xMetaData->getTables(Any(), + OUString(), + rName, + uno::Sequence< OUString >()); + + if (!xTables.is()) + throw RuntimeException("Could not acquire table."); + + uno::Reference< XRow > xRow(xTables,UNO_QUERY_THROW); + + if (!xTables->next()) + throw RuntimeException(); + + ObjectType xRet(new Table(this, + m_rMutex, + m_xMetaData->getConnection(), + xRow->getString(3), // Name + xRow->getString(4), // Type + xRow->getString(5))); // Description / Remarks / Comments + + if (xTables->next()) + throw RuntimeException("Found more tables than expected."); + + return xRet; +} + +OUString Tables::createStandardColumnPart(const Reference< XPropertySet >& xColProp,const Reference< XConnection>& _xConnection) +{ + Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData(); + + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + + bool bIsAutoIncrement = false; + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT)) >>= bIsAutoIncrement; + + const OUString sQuoteString = xMetaData->getIdentifierQuoteString(); + OUStringBuffer aSql = ::dbtools::quoteName(sQuoteString,::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)))); + + // check if the user enter a specific string to create autoincrement values + OUString sAutoIncrementValue; + Reference<XPropertySetInfo> xPropInfo = xColProp->getPropertySetInfo(); + + if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) ) + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) >>= sAutoIncrementValue; + + aSql.append(" "); + + aSql.append(dbtools::createStandardTypePart(xColProp, _xConnection)); + // Add character set for (VAR)BINARY (fix) types: + // (VAR) BINARY is distinguished from other CHAR types by its character set. + // Octets is a special character set for binary data. + if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex( + PROPERTY_ID_TYPE)) ) + { + sal_Int32 aType = 0; + xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE)) + >>= aType; + if(aType == DataType::BINARY || aType == DataType::VARBINARY) + { + aSql.append(" "); + aSql.append("CHARACTER SET OCTETS"); + } + } + + if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty()) + { + aSql.append(" "); + aSql.append(sAutoIncrementValue); + } + // AutoIncrement "IDENTITY" is implicitly "NOT NULL" + else if(::comphelper::getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISNULLABLE))) == ColumnValue::NO_NULLS) + aSql.append(" NOT NULL"); + + return aSql.makeStringAndClear(); +} + +uno::Reference< XPropertySet > Tables::createDescriptor() +{ + // There is some internal magic so that the same class can be used as either + // a descriptor or as a normal table. See VTable.cxx for the details. In our + // case we just need to ensure we use the correct constructor. + return new Table(this, m_rMutex, m_xMetaData->getConnection()); +} + +//----- XAppend --------------------------------------------------------------- +ObjectType Tables::appendObject(const OUString& rName, + const uno::Reference< XPropertySet >& rDescriptor) +{ + /* OUString sSql(::dbtools::createSqlCreateTableStatement(rDescriptor, + m_xMetaData->getConnection())); */ + OUStringBuffer aSqlBuffer("CREATE TABLE "); + OUString sCatalog, sSchema, sComposedName, sTable; + const Reference< XConnection>& xConnection = m_xMetaData->getConnection(); + + ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap(); + + rDescriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) >>= sCatalog; + rDescriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= sSchema; + rDescriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= sTable; + + sComposedName = ::dbtools::composeTableName(m_xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions ); + if ( sComposedName.isEmpty() ) + ::dbtools::throwFunctionSequenceException(xConnection); + + aSqlBuffer.append(sComposedName); + aSqlBuffer.append(" ("); + + // columns + Reference<XColumnsSupplier> xColumnSup(rDescriptor,UNO_QUERY); + Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY); + // check if there are columns + if(!xColumns.is() || !xColumns->getCount()) + ::dbtools::throwFunctionSequenceException(xConnection); + + Reference< XPropertySet > xColProp; + + sal_Int32 nCount = xColumns->getCount(); + for(sal_Int32 i=0;i<nCount;++i) + { + if ( (xColumns->getByIndex(i) >>= xColProp) && xColProp.is() ) + { + aSqlBuffer.append(createStandardColumnPart(xColProp,xConnection)); + aSqlBuffer.append(","); + } + } + OUString sSql = aSqlBuffer.makeStringAndClear(); + + const OUString sKeyStmt = ::dbtools::createStandardKeyStatement(rDescriptor,xConnection); + if ( !sKeyStmt.isEmpty() ) + sSql += sKeyStmt; + else + { + if ( sSql.endsWith(",") ) + sSql = sSql.replaceAt(sSql.getLength()-1, 1, ")"); + else + sSql += ")"; + } + + m_xMetaData->getConnection()->createStatement()->execute(sSql); + + return createObject(rName); +} + +//----- XDrop ----------------------------------------------------------------- +void Tables::dropObject(sal_Int32 nPosition, const OUString& sName) +{ + uno::Reference< XPropertySet > xTable(getObject(nPosition)); + + if (ODescriptor::isNew(xTable)) + return; + + OUStringBuffer sSql("DROP "); + + OUString sType; + xTable->getPropertyValue("Type") >>= sType; + sSql.append(sType); + + const OUString sQuoteString = m_xMetaData->getIdentifierQuoteString(); + sSql.append(::dbtools::quoteName(sQuoteString,sName)); + + m_xMetaData->getConnection()->createStatement()->execute(sSql.makeStringAndClear()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Tables.hxx b/connectivity/source/drivers/firebird/Tables.hxx new file mode 100644 index 000000000..635774727 --- /dev/null +++ b/connectivity/source/drivers/firebird/Tables.hxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_TABLES_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_TABLES_HXX + +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> + +#include <connectivity/sdbcx/VCollection.hxx> + +namespace connectivity +{ + namespace firebird + { + + /** + * This implements com.sun.star.sdbcx.Container, which seems to be + * also known by the name of Tables and Collection. + */ + class Tables: public ::connectivity::sdbcx::OCollection + { + protected: + css::uno::Reference< css::sdbc::XDatabaseMetaData > + m_xMetaData; + + static OUString createStandardColumnPart(const css::uno::Reference< css::beans::XPropertySet >& xColProp,const css::uno::Reference< com::sun::star::sdbc::XConnection>& _xConnection); + + // OCollection + virtual void impl_refresh() override; + virtual ::connectivity::sdbcx::ObjectType createObject( + const OUString& rName) override; + virtual css::uno::Reference< css::beans::XPropertySet > + createDescriptor() override; + virtual ::connectivity::sdbcx::ObjectType appendObject( + const OUString& rName, + const css::uno::Reference< css::beans::XPropertySet >& rDescriptor) override; + + public: + Tables(const css::uno::Reference< css::sdbc::XDatabaseMetaData >& rMetaData, + ::cppu::OWeakObject& rParent, + ::osl::Mutex& rMutex, + ::std::vector< OUString> const & rNames) : sdbcx::OCollection(rParent, true, rMutex, rNames), m_xMetaData(rMetaData) {} + + // TODO: we should also implement XDataDescriptorFactory, XRefreshable, + // XAppend, etc., but all are optional. + + // XDrop + virtual void dropObject(sal_Int32 nPosition, const OUString& rName) override; + + }; + + } // namespace firebird +} // namespace connectivity + + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_TABLES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/User.cxx b/connectivity/source/drivers/firebird/User.cxx new file mode 100644 index 000000000..3a9682fb8 --- /dev/null +++ b/connectivity/source/drivers/firebird/User.cxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "User.hxx" + +using namespace ::connectivity; +using namespace ::connectivity::firebird; +using namespace ::connectivity::sdbcx; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::sdbc; + +User::User(const css::uno::Reference< css::sdbc::XConnection >& rConnection): + OUser(true) // Case Sensitive + , m_xConnection(rConnection) +{} + +User::User(const css::uno::Reference< css::sdbc::XConnection >& rConnection, const OUString& rName): + OUser(rName, + true) // Case Sensitive + , m_xConnection(rConnection) +{} + +void User::changePassword(const OUString&, const OUString& newPassword) +{ + m_xConnection->createStatement()->execute("ALTER USER " + m_Name + " PASSWORD '" + newPassword + "'"); +} + +sal_Int32 User::getPrivileges(const OUString& , sal_Int32 ) +{ + // TODO: implement. + return 0; +} + +sal_Int32 User::getGrantablePrivileges(const OUString& , sal_Int32 ) +{ + // TODO: implement. + return 0; +} + +//----- IRefreshableGroups ---------------------------------------------------- +void User::refreshGroups() +{ + // TODO: implement. +} diff --git a/connectivity/source/drivers/firebird/User.hxx b/connectivity/source/drivers/firebird/User.hxx new file mode 100644 index 000000000..5668c3223 --- /dev/null +++ b/connectivity/source/drivers/firebird/User.hxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_USER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_USER_HXX + +#include <sdbcx/VUser.hxx> +#include <com/sun/star/sdbc/XConnection.hpp> + +namespace connectivity +{ + namespace firebird + { + + /** + * This implements com.sun.star.sdbcx.Container. + */ + class User: public ::connectivity::sdbcx::OUser + { + css::uno::Reference< css::sdbc::XConnection > m_xConnection; + + public: + /** + * Create a "new" descriptor, which isn't yet in the database. + */ + User(const css::uno::Reference< css::sdbc::XConnection >& rConnection); + /** + * For a user that already exists in the db. + */ + User(const css::uno::Reference< css::sdbc::XConnection >& rConnection, const OUString& rName); + + // XAuthorizable + virtual void SAL_CALL changePassword(const OUString&, const OUString& newPassword) override; + virtual sal_Int32 SAL_CALL getPrivileges(const OUString&, sal_Int32) override; + virtual sal_Int32 SAL_CALL getGrantablePrivileges(const OUString&, sal_Int32) override; + + // IRefreshableGroups:: + virtual void refreshGroups() override; + }; + + } // namespace firebird +} // namespace connectivity + + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_USER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Users.cxx b/connectivity/source/drivers/firebird/Users.cxx new file mode 100644 index 000000000..fc8914250 --- /dev/null +++ b/connectivity/source/drivers/firebird/Users.cxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "User.hxx" +#include "Users.hxx" + +using namespace ::connectivity; +using namespace ::connectivity::firebird; +using namespace ::connectivity::sdbcx; +using namespace ::cppu; +using namespace ::osl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; + + +Users::Users(const uno::Reference< XDatabaseMetaData >& rMetaData, + OWeakObject& rParent, + Mutex& rMutex, + ::std::vector< OUString> const & rNames) : + OCollection(rParent, + true, + rMutex, + rNames), + m_xMetaData(rMetaData) +{ +} + +//----- OCollection ----------------------------------------------------------- +void Users::impl_refresh() +{ + // TODO: IMPLEMENT ME +} + +ObjectType Users::createObject(const OUString& rName) +{ + return new User(m_xMetaData->getConnection(), rName); +} + +uno::Reference< XPropertySet > Users::createDescriptor() +{ + // There is some internal magic so that the same class can be used as either + // a descriptor or as a normal user. See VUser.cxx for the details. In our + // case we just need to ensure we use the correct constructor. + return new User(m_xMetaData->getConnection()); +} + +//----- XAppend --------------------------------------------------------------- +ObjectType Users::appendObject(const OUString& rName, + const uno::Reference< XPropertySet >&) +{ + // TODO: set sSql as appropriate + m_xMetaData->getConnection()->createStatement()->execute(OUString()); + + return createObject(rName); +} + +//----- XDrop ----------------------------------------------------------------- +void Users::dropObject(sal_Int32 nPosition, const OUString&) +{ + uno::Reference< XPropertySet > xUser(getObject(nPosition)); + + if (!ODescriptor::isNew(xUser)) + { + // TODO: drop me + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Users.hxx b/connectivity/source/drivers/firebird/Users.hxx new file mode 100644 index 000000000..39e019e4d --- /dev/null +++ b/connectivity/source/drivers/firebird/Users.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_USERS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_USERS_HXX + +#include <connectivity/sdbcx/VCollection.hxx> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> + +namespace connectivity +{ + namespace firebird + { + + /** + * This implements com.sun.star.sdbcx.Container. + */ + class Users: public ::connectivity::sdbcx::OCollection + { + css::uno::Reference< css::sdbc::XDatabaseMetaData > + m_xMetaData; + protected: + + // OCollection + virtual void impl_refresh() override; + virtual ::connectivity::sdbcx::ObjectType createObject( + const OUString& rName) override; + virtual css::uno::Reference< css::beans::XPropertySet > + createDescriptor() override; + virtual ::connectivity::sdbcx::ObjectType appendObject( + const OUString& rName, + const css::uno::Reference< css::beans::XPropertySet >& rDescriptor) override; + + public: + Users(const css::uno::Reference< css::sdbc::XDatabaseMetaData >& rMetaData, + ::cppu::OWeakObject& rParent, + ::osl::Mutex& rMutex, + ::std::vector< OUString> const & rNames); + + // TODO: we should also implement XDataDescriptorFactory, XRefreshable, + // XAppend, etc., but all are optional. + + // XDrop + virtual void dropObject(sal_Int32 nPosition, const OUString& rName) override; + + }; + + } // namespace firebird +} // namespace connectivity + + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_USERS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Util.cxx b/connectivity/source/drivers/firebird/Util.cxx new file mode 100644 index 000000000..64e329723 --- /dev/null +++ b/connectivity/source/drivers/firebird/Util.cxx @@ -0,0 +1,436 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "Util.hxx" +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +using namespace ::connectivity; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; + +using namespace firebird; + +OUString firebird::sanitizeIdentifier(const OUString& rIdentifier) +{ + OUString sRet = rIdentifier.trim(); + assert(sRet.getLength() <= 31); // Firebird identifiers cannot be longer than this. + + return sRet; +} + +OUString firebird::StatusVectorToString(const ISC_STATUS_ARRAY& rStatusVector, + const OUString& rCause) +{ + OUStringBuffer buf; + const ISC_STATUS* pStatus = reinterpret_cast<const ISC_STATUS*>(&rStatusVector); + + buf.append("firebird_sdbc error:"); + try + { + char msg[512]; // Size is based on suggestion in docs. + while(fb_interpret(msg, sizeof(msg), &pStatus)) + { + // TODO: verify encoding + buf.append("\n*"); + buf.append(OUString(msg, strlen(msg), RTL_TEXTENCODING_UTF8)); + } + } + catch (...) + { + SAL_WARN("connectivity.firebird", "ignore fb_interpret exception"); + } + buf.append("\ncaused by\n'").append(rCause).append("'\n"); + + OUString error = buf.makeStringAndClear(); + SAL_WARN("connectivity.firebird", error); + return error; +} + +void firebird::evaluateStatusVector(const ISC_STATUS_ARRAY& rStatusVector, + const OUString& rCause, + const uno::Reference< XInterface >& _rxContext) +{ + if (IndicatesError(rStatusVector)) + { + OUString error = StatusVectorToString(rStatusVector, rCause); + throw SQLException(error, _rxContext, OUString(), 1, Any()); + } +} + +static sal_Int32 lcl_getNumberType( short aType, NumberSubType aSubType ) +{ + switch(aSubType) + { + case NumberSubType::Numeric: + return DataType::NUMERIC; + case NumberSubType::Decimal: + return DataType::DECIMAL; + default: + switch(aType) + { + case SQL_SHORT: + return DataType::SMALLINT; + case SQL_LONG: + return DataType::INTEGER; + case SQL_DOUBLE: + return DataType::DOUBLE; + case SQL_INT64: + return DataType::BIGINT; + default: + assert(false); // not a number + return 0; + } + } +} +static sal_Int32 lcl_getCharColumnType( short aType, const OUString& sCharset ) +{ + switch(aType) + { + case SQL_TEXT: + if( sCharset == "OCTETS") + return DataType::BINARY; + else + return DataType::CHAR; + case SQL_VARYING: + if( sCharset == "OCTETS") + return DataType::VARBINARY; + else + return DataType::VARCHAR; + default: + assert(false); + return 0; + } +} + +sal_Int32 firebird::ColumnTypeInfo::getSdbcType() const +{ + short aType = m_aType & ~1; // Remove last bit -- it is used to denote whether column + // can store Null, not needed for type determination + short aSubType = m_aSubType; + if( m_nScale > 0 ) + { + // numeric / decimal + if(aType == SQL_SHORT || aType == SQL_LONG || aType == SQL_DOUBLE + || aType == SQL_INT64) + { + // if scale is set without subtype then imply numeric + if( static_cast<NumberSubType>(aSubType) == NumberSubType::Other ) + aSubType = static_cast<short>(NumberSubType::Numeric); + } + } + + switch (aType) + { + case SQL_TEXT: + case SQL_VARYING: + return lcl_getCharColumnType(aType, m_sCharsetName); + case SQL_SHORT: + case SQL_LONG: + case SQL_DOUBLE: + case SQL_INT64: + return lcl_getNumberType(aType, static_cast<NumberSubType>(aSubType) ); + case SQL_FLOAT: + return DataType::FLOAT; + case SQL_D_FLOAT: + return DataType::DOUBLE; + case SQL_TIMESTAMP: + return DataType::TIMESTAMP; + case SQL_BLOB: + switch (static_cast<BlobSubtype>(aSubType)) + { + case BlobSubtype::Blob: + return DataType::BLOB; + case BlobSubtype::Clob: + return DataType::CLOB; + case BlobSubtype::Image: + return DataType::LONGVARBINARY; + default: + SAL_WARN("connectivity.firebird", "Unknown subtype for Blob type: " << aSubType); + assert(!"Unknown subtype for Blob type"); // Should never happen + return 0; + } + case SQL_ARRAY: + return DataType::ARRAY; + case SQL_TYPE_TIME: + return DataType::TIME; + case SQL_TYPE_DATE: + return DataType::DATE; + case SQL_NULL: + return DataType::SQLNULL; + case SQL_QUAD: // Is a "Blob ID" according to the docs + return 0; // TODO: verify + case SQL_BOOLEAN: + return DataType::BOOLEAN; + default: + assert(false); // Should never happen + return 0; + } +} + +OUString firebird::ColumnTypeInfo::getColumnTypeName() const +{ + sal_Int32 nDataType = this->getSdbcType(); + switch (nDataType) + { + case DataType::BIT: + return "BIT"; + case DataType::TINYINT: + return "TINYINT"; + case DataType::SMALLINT: + return "SMALLINT"; + case DataType::INTEGER: + return "INTEGER"; + case DataType::BIGINT: + return "BIGINT"; + case DataType::FLOAT: + return "FLOAT"; + case DataType::REAL: + return "REAL"; + case DataType::DOUBLE: + return "DOUBLE"; + case DataType::NUMERIC: + return "NUMERIC"; + case DataType::DECIMAL: + return "DECIMAL"; + case DataType::CHAR: + return "CHAR"; + case DataType::VARCHAR: + return "VARCHAR"; + case DataType::LONGVARCHAR: + return "LONGVARCHAR"; + case DataType::DATE: + return "DATE"; + case DataType::TIME: + return "TIME"; + case DataType::TIMESTAMP: + return "TIMESTAMP"; + case DataType::BINARY: + // in Firebird, that is the same datatype "CHAR" as DataType::CHAR, + // only with CHARACTER SET OCTETS; we use the synonym CHARACTER + // to fool LO into seeing it as different types. + return "CHARACTER"; + case DataType::VARBINARY: + // see above comment about DataType::BINARY. + return "CHARACTER VARYING"; + case DataType::LONGVARBINARY: + return "BLOB SUB_TYPE " + OUString::number(static_cast<short>(BlobSubtype::Image)); + case DataType::ARRAY: + return "ARRAY"; + case DataType::BLOB: + return "BLOB SUB_TYPE BINARY"; + case DataType::CLOB: + return "BLOB SUB_TYPE TEXT"; + case DataType::BOOLEAN: + return "BOOLEAN"; + case DataType::SQLNULL: + return "NULL"; + default: + assert(false); // Should never happen + return OUString(); + } +} + +short firebird::getFBTypeFromBlrType(short blrType) +{ + switch (blrType) + { + case blr_text: + return SQL_TEXT; + case blr_text2: + assert(false); + return 0; // No idea if this should be supported + case blr_varying: + return SQL_VARYING; + case blr_varying2: + assert(false); + return 0; // No idea if this should be supported + case blr_short: + return SQL_SHORT; + case blr_long: + return SQL_LONG; + case blr_float: + return SQL_FLOAT; + case blr_double: + return SQL_DOUBLE; + case blr_d_float: + return SQL_D_FLOAT; + case blr_timestamp: + return SQL_TIMESTAMP; + case blr_blob: + return SQL_BLOB; +// case blr_SQL_ARRAY: +// return OUString("SQL_ARRAY"); + case blr_sql_time: + return SQL_TYPE_TIME; + case blr_sql_date: + return SQL_TYPE_DATE; + case blr_int64: + return SQL_INT64; +// case SQL_NULL: +// return OUString("SQL_NULL"); + case blr_quad: + return SQL_QUAD; + case blr_bool: + return SQL_BOOLEAN; + default: + // If this happens we have hit one of the extra types in ibase.h + // look up blr_* for a list, e.g. blr_domain_name, blr_not_nullable etc. + assert(false); + return 0; + } +} + +void firebird::mallocSQLVAR(XSQLDA* pSqlda) +{ + // TODO: confirm the sizings below. + XSQLVAR* pVar = pSqlda->sqlvar; + for (int i=0; i < pSqlda->sqld; i++, pVar++) + { + int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */ + switch(dtype) { + case SQL_TEXT: + pVar->sqldata = static_cast<char *>(malloc(sizeof(char)*pVar->sqllen)); + break; + case SQL_VARYING: + pVar->sqldata = static_cast<char *>(malloc(sizeof(char)*pVar->sqllen + 2)); + break; + case SQL_SHORT: + pVar->sqldata = static_cast<char*>(malloc(sizeof(sal_Int16))); + break; + case SQL_LONG: + pVar->sqldata = static_cast<char*>(malloc(sizeof(sal_Int32))); + break; + case SQL_FLOAT: + pVar->sqldata = static_cast<char *>(malloc(sizeof(float))); + break; + case SQL_DOUBLE: + pVar->sqldata = static_cast<char *>(malloc(sizeof(double))); + break; + case SQL_D_FLOAT: + pVar->sqldata = static_cast<char *>(malloc(sizeof(double))); + break; + case SQL_TIMESTAMP: + pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_TIMESTAMP))); + break; + // an ARRAY is in fact a BLOB of a specialized type + // See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array + case SQL_ARRAY: + case SQL_BLOB: + pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_QUAD))); + break; + case SQL_TYPE_TIME: + pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_TIME))); + break; + case SQL_TYPE_DATE: + pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_DATE))); + break; + case SQL_INT64: + pVar->sqldata = static_cast<char *>(malloc(sizeof(sal_Int64))); + break; + case SQL_BOOLEAN: + pVar->sqldata = static_cast<char *>(malloc(sizeof(sal_Bool))); + break; + case SQL_NULL: + assert(false); // TODO: implement + break; + case SQL_QUAD: + assert(false); // TODO: implement + break; + default: + SAL_WARN("connectivity.firebird", "Unknown type: " << dtype); + assert(false); + break; + } + /* allocate variable to hold NULL status */ + pVar->sqlind = static_cast<short *>(malloc(sizeof(short))); + } +} + +void firebird::freeSQLVAR(XSQLDA* pSqlda) +{ + XSQLVAR* pVar = pSqlda->sqlvar; + for (int i=0; i < pSqlda->sqld; i++, pVar++) + { + int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */ + switch(dtype) { + case SQL_TEXT: + case SQL_VARYING: + case SQL_SHORT: + case SQL_LONG: + case SQL_FLOAT: + case SQL_DOUBLE: + case SQL_D_FLOAT: + case SQL_TIMESTAMP: + // an ARRAY is in fact a BLOB of a specialized type + // See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array + case SQL_ARRAY: + case SQL_BLOB: + case SQL_INT64: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + case SQL_BOOLEAN: + if(pVar->sqldata) + { + free(pVar->sqldata); + pVar->sqldata = nullptr; + } + break; + case SQL_NULL: + assert(false); // TODO: implement + break; + case SQL_QUAD: + assert(false); // TODO: implement + break; + default: + SAL_WARN("connectivity.firebird", "Unknown type: " << dtype); +// assert(false); + break; + } + + if(pVar->sqlind) + { + free(pVar->sqlind); + pVar->sqlind = nullptr; + } + } +} + + +OUString firebird::escapeWith( const OUString& sText, const char aKey, const char aEscapeChar) +{ + OUString sRet(sText); + sal_Int32 aIndex = 0; + for (;;) + { + aIndex = sRet.indexOf(aKey, aIndex); + if ( aIndex <= 0 || aIndex >= sRet.getLength()) + break; + sRet = sRet.replaceAt(aIndex, 1, OUStringChar(aEscapeChar) + OUStringChar(aKey) ); + aIndex += 2; + } + + return sRet; +} + +sal_Int64 firebird::pow10Integer(int nDecimalCount) +{ + sal_Int64 nRet = 1; + for(int i=0; i< nDecimalCount; i++) + { + nRet *= 10; + } + return nRet; +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Util.hxx b/connectivity/source/drivers/firebird/Util.hxx new file mode 100644 index 000000000..107840876 --- /dev/null +++ b/connectivity/source/drivers/firebird/Util.hxx @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_UTIL_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_UTIL_HXX + +#include <ibase.h> + +#include <rtl/ustring.hxx> + +#include <com/sun/star/uno/XInterface.hpp> + +namespace connectivity +{ + namespace firebird + { + // Type Blob has 2 subtypes values + // 0 for BLOB, 1 for CLOB + // see http://www.firebirdfaq.org/faq48/ + // User-defined subtypes are negative. + // Use a number for image which is very unlikely to be defined by a + // user. + enum class BlobSubtype { + Blob = 0, + Clob = 1, + Image = -9546 + }; + + /** + * Numeric and decimal types can be identified by their subtype in + * Firebird API. 1 for NUMERIC, 2 for DECIMAL. + */ + enum class NumberSubType { + Other = 0, + Numeric = 1, + Decimal = 2 + }; + + class ColumnTypeInfo { +private: + short m_aType; + short m_aSubType; + short m_nScale; + OUString m_sCharsetName; +public: + /** + * @param tType SQL type of column defined by Firebird (e.g. + * SQL_DOUBLE) + * @param aSubType SQL sub type as in firebird API. See + * NumberSubType. + * @param scale: Scale of the number. It is ignored in case it's not + * a number. Scale obtained from the Firebird API is negative, so + * that should be negated before passing to this constructor. + * + */ + explicit ColumnTypeInfo( short aType, short aSubType = 0, + short nScale = 0, const OUString& sCharset = OUString() ) + : m_aType(aType) + , m_aSubType(aSubType) + , m_nScale(nScale) + , m_sCharsetName(sCharset) {} + explicit ColumnTypeInfo( short aType, const OUString& sCharset ) + : m_aType(aType) + , m_aSubType(0) + , m_nScale(0) + , m_sCharsetName(sCharset) {} + short getType() const { return m_aType; } + short getSubType() const { return m_aSubType; } + short getScale() const { return m_nScale; } + OUString const & getCharacterSet() const { return m_sCharsetName; } + + sal_Int32 getSdbcType() const; + OUString getColumnTypeName() const; + + }; + + /** + * Make sure an identifier is safe to use within the database. Currently + * firebird seems to return identifiers with 93 character (instead of + * 31), whereby the name is simply padded with trailing whitespace. + * This removes all trailing whitespace (i.e. if necessary so that + * the length is below 31 characters). Firebird automatically compensates + * for such shorter strings, however any trailing padding makes the gui + * editing of such names harder, hence we remove all trailing whitespace. + */ + OUString sanitizeIdentifier(const OUString& rIdentifier); + + inline bool IndicatesError(const ISC_STATUS_ARRAY& rStatusVector) + { + return rStatusVector[0]==1 && rStatusVector[1]; // indicates error; + } + + OUString StatusVectorToString(const ISC_STATUS_ARRAY& rStatusVector, + const OUString& rCause); + + /** + * Evaluate a firebird status vector and throw exceptions as necessary. + * The content of the status vector is included in the thrown exception. + * + * @throws css::sdbc::SQLException + */ + void evaluateStatusVector(const ISC_STATUS_ARRAY& rStatusVector, + const OUString& aCause, + const css::uno::Reference< css::uno::XInterface >& _rxContext); + + /** + * Internally (i.e. in RDB$FIELD_TYPE) firebird stores the data type + * for a column as defined in blr_*, however in the firebird + * api the SQL_* types are used, hence we need to be able to convert + * between the two when retrieving column metadata. + */ + short getFBTypeFromBlrType(short blrType); + + void mallocSQLVAR(XSQLDA* pSqlda); + + void freeSQLVAR(XSQLDA* pSqlda); + + OUString escapeWith( const OUString& sText, const char aKey, const char aEscapeChar); + sal_Int64 pow10Integer( int nDecimalCount ); + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_FIREBIRD_UTIL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/firebird_sdbc.component b/connectivity/source/drivers/firebird/firebird_sdbc.component new file mode 100644 index 000000000..b56d4e0bc --- /dev/null +++ b/connectivity/source/drivers/firebird/firebird_sdbc.component @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * +--> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="firebird_sdbc" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.firebird.Driver"> + <service name="com.sun.star.sdbc.Driver"/> + <service name="com.sun.star.sdbcx.Driver"/> + </implementation> +</component> + diff --git a/connectivity/source/drivers/flat/ECatalog.cxx b/connectivity/source/drivers/flat/ECatalog.cxx new file mode 100644 index 000000000..b656b29c2 --- /dev/null +++ b/connectivity/source/drivers/flat/ECatalog.cxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <flat/ECatalog.hxx> + +#include <flat/EConnection.hxx> +#include <flat/ETables.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::flat; + +OFlatCatalog::OFlatCatalog(OFlatConnection* _pCon) : file::OFileCatalog(_pCon) +{ +} + +void OFlatCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + Sequence< OUString > aTypes; + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(), + "%", "%", aTypes); + + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while(xResult->next()) + aVector.push_back(xRow->getString(3)); + } + if(m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset( new OFlatTables(m_xMetaData,*this,m_aMutex,aVector) ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/EColumns.cxx b/connectivity/source/drivers/flat/EColumns.cxx new file mode 100644 index 000000000..a9e210321 --- /dev/null +++ b/connectivity/source/drivers/flat/EColumns.cxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <flat/EColumns.hxx> +#include <flat/ETable.hxx> + +using namespace connectivity::flat; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; + + +sdbcx::ObjectType OFlatColumns::createObject(const OUString& _rName) +{ + OFlatTable* pTable = static_cast<OFlatTable*>(m_pTable); + const ::rtl::Reference<OSQLColumns>& aCols = pTable->getTableColumns(); + OSQLColumns::const_iterator aIter = find(aCols->begin(),aCols->end(),_rName,::comphelper::UStringMixEqual(isCaseSensitive())); + sdbcx::ObjectType xRet; + if(aIter != aCols->end()) + xRet = sdbcx::ObjectType(*aIter,UNO_QUERY); + return xRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/EConnection.cxx b/connectivity/source/drivers/flat/EConnection.cxx new file mode 100644 index 000000000..5127d903c --- /dev/null +++ b/connectivity/source/drivers/flat/EConnection.cxx @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <flat/EConnection.hxx> +#include <flat/EDatabaseMetaData.hxx> +#include <flat/ECatalog.hxx> +#include <flat/EDriver.hxx> +#include <flat/EPreparedStatement.hxx> +#include <flat/EStatement.hxx> +#include <connectivity/dbexception.hxx> +#include <sal/log.hxx> + +using namespace connectivity::flat; +using namespace connectivity::file; + +typedef connectivity::file::OConnection OConnection_B; + + +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::lang; + + +OFlatConnection::OFlatConnection(ODriver* _pDriver) : OConnection(_pDriver) + ,m_nMaxRowsToScan(50) + ,m_bHeaderLine(true) + ,m_cFieldDelimiter(';') + ,m_cStringDelimiter('"') + ,m_cDecimalDelimiter(',') + ,m_cThousandDelimiter('.') +{ +} + +OFlatConnection::~OFlatConnection() +{ +} + +// XServiceInfo + +IMPLEMENT_SERVICE_INFO(OFlatConnection, "com.sun.star.sdbc.drivers.flat.Connection", "com.sun.star.sdbc.Connection") + + +void OFlatConnection::construct(const OUString& url,const Sequence< PropertyValue >& info) +{ + osl_atomic_increment( &m_refCount ); + + const PropertyValue *pBegin = info.getConstArray(); + const PropertyValue *pEnd = pBegin + info.getLength(); + for(;pBegin != pEnd;++pBegin) + { + if(pBegin->Name == "HeaderLine") + { + if( ! (pBegin->Value >>= m_bHeaderLine) ) + SAL_WARN("connectivity.flat", "construct: unable to get property HeaderLine"); + } + else if(pBegin->Name == "FieldDelimiter") + { + OUString aVal; + if( ! (pBegin->Value >>= aVal) ) + SAL_WARN("connectivity.flat", "construct: unable to get property FieldDelimiter"); + + m_cFieldDelimiter = aVal.toChar(); + } + else if(pBegin->Name == "StringDelimiter") + { + OUString aVal; + if( ! (pBegin->Value >>= aVal) ) + SAL_WARN("connectivity.flat", "construct: unable to get property StringDelimiter"); + + m_cStringDelimiter = aVal.toChar(); + } + else if(pBegin->Name == "DecimalDelimiter") + { + OUString aVal; + if( ! (pBegin->Value >>= aVal) ) + SAL_WARN("connectivity.flat", "construct: unable to get property DecimalDelimiter"); + + m_cDecimalDelimiter = aVal.toChar(); + } + else if(pBegin->Name == "ThousandDelimiter") + { + OUString aVal; + if( ! (pBegin->Value >>= aVal) ) + SAL_WARN("connectivity.flat", "construct: unable to get property ThousandDelimiter"); + + m_cThousandDelimiter = aVal.toChar(); + } + else if ( pBegin->Name == "MaxRowScan" ) + { + pBegin->Value >>= m_nMaxRowsToScan; + } + } + + osl_atomic_decrement( &m_refCount ); + OConnection::construct(url,info); + m_bShowDeleted = true; // we do not supported rows for this type +} + +Reference< XDatabaseMetaData > SAL_CALL OFlatConnection::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_B::rBHelper.bDisposed); + + + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + xMetaData = new OFlatDatabaseMetaData(this); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +css::uno::Reference< XTablesSupplier > OFlatConnection::createCatalog() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XTablesSupplier > xTab = m_xCatalog; + if(!xTab.is()) + { + OFlatCatalog *pCat = new OFlatCatalog(this); + xTab = pCat; + m_xCatalog = xTab; + } + return xTab; +} + +Reference< XStatement > SAL_CALL OFlatConnection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_B::rBHelper.bDisposed); + + OFlatStatement* pStmt = new OFlatStatement(this); + + Reference< XStatement > xStmt = pStmt; + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return xStmt; +} + +Reference< XPreparedStatement > SAL_CALL OFlatConnection::prepareStatement( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_B::rBHelper.bDisposed); + + + OFlatPreparedStatement* pStmt = new OFlatPreparedStatement(this); + Reference< XPreparedStatement > xStmt = pStmt; + pStmt->construct(sql); + + m_aStatements.push_back(WeakReferenceHelper(*pStmt)); + return xStmt; +} + +Reference< XPreparedStatement > SAL_CALL OFlatConnection::prepareCall( const OUString& /*sql*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_B::rBHelper.bDisposed); + + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this ); + return nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/EDatabaseMetaData.cxx b/connectivity/source/drivers/flat/EDatabaseMetaData.cxx new file mode 100644 index 000000000..b69b34e36 --- /dev/null +++ b/connectivity/source/drivers/flat/EDatabaseMetaData.cxx @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <flat/EDatabaseMetaData.hxx> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <FDatabaseMetaDataResultSet.hxx> +#include <comphelper/types.hxx> + +using namespace ::comphelper; + +using namespace connectivity; +using namespace connectivity::flat; +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; + + +OFlatDatabaseMetaData::OFlatDatabaseMetaData(::connectivity::file::OConnection* _pCon) :ODatabaseMetaData(_pCon) +{ +} + +OFlatDatabaseMetaData::~OFlatDatabaseMetaData() +{ +} + +Reference< XResultSet > OFlatDatabaseMetaData::impl_getTypeInfo_throw( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTypeInfo); + Reference< XResultSet > xRef = pResult; + + static ODatabaseMetaDataResultSet::ORows aRows = [&]() + { + ODatabaseMetaDataResultSet::ORows tmp; + ODatabaseMetaDataResultSet::ORow aRow; + + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(OUString("CHAR"))); + aRow.push_back(new ORowSetValueDecorator(DataType::CHAR)); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(254))); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(ColumnValue::NULLABLE))); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(ColumnSearch::CHAR))); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(10))); + + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("VARCHAR")); + aRow[2] = new ORowSetValueDecorator(DataType::VARCHAR); + aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); + aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); + tmp.push_back(aRow); + + + aRow[1] = new ORowSetValueDecorator(OUString("LONGVARCHAR")); + aRow[2] = new ORowSetValueDecorator(DataType::LONGVARCHAR); + aRow[3] = new ORowSetValueDecorator(sal_Int32(65535)); + aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); + aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("DATE")); + aRow[2] = new ORowSetValueDecorator(DataType::DATE); + aRow[3] = new ORowSetValueDecorator(sal_Int32(10)); + aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); + aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("TIME")); + aRow[2] = new ORowSetValueDecorator(DataType::TIME); + aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); + aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); + aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("TIMESTAMP")); + aRow[2] = new ORowSetValueDecorator(DataType::TIMESTAMP); + aRow[3] = new ORowSetValueDecorator(sal_Int32(19)); + aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); + aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("BOOL")); + aRow[2] = new ORowSetValueDecorator(DataType::BIT); + aRow[3] = ODatabaseMetaDataResultSet::get1Value(); + aRow[9] = ODatabaseMetaDataResultSet::getBasicValue(); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("DECIMAL")); + aRow[2] = new ORowSetValueDecorator(DataType::DECIMAL); + aRow[3] = new ORowSetValueDecorator(sal_Int32(20)); + aRow[15] = new ORowSetValueDecorator(sal_Int32(15)); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("DOUBLE")); + aRow[2] = new ORowSetValueDecorator(DataType::DOUBLE); + aRow[3] = new ORowSetValueDecorator(sal_Int32(20)); + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("NUMERIC")); + aRow[2] = new ORowSetValueDecorator(DataType::NUMERIC); + aRow[3] = new ORowSetValueDecorator(sal_Int32(20)); + aRow[15] = new ORowSetValueDecorator(sal_Int32(20)); + tmp.push_back(aRow); + + return tmp; + }(); + + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > SAL_CALL OFlatDatabaseMetaData::getColumns( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XTablesSupplier > xTables = m_pConnection->createCatalog(); + if(!xTables.is()) + throw SQLException(); + + Reference< XNameAccess> xNames = xTables->getTables(); + if(!xNames.is()) + throw SQLException(); + + ODatabaseMetaDataResultSet::ORows aRows; + ODatabaseMetaDataResultSet::ORow aRow(19); + aRow[10] = new ORowSetValueDecorator(sal_Int32(10)); + Sequence< OUString> aTabNames(xNames->getElementNames()); + const OUString* pTabBegin = aTabNames.getConstArray(); + const OUString* pTabEnd = pTabBegin + aTabNames.getLength(); + for(;pTabBegin != pTabEnd;++pTabBegin) + { + if(match(tableNamePattern,*pTabBegin,'\0')) + { + Reference< XColumnsSupplier> xTable( + xNames->getByName(*pTabBegin), css::uno::UNO_QUERY); + aRow[3] = new ORowSetValueDecorator(*pTabBegin); + + Reference< XNameAccess> xColumns = xTable->getColumns(); + if(!xColumns.is()) + throw SQLException(); + + Sequence< OUString> aColNames(xColumns->getElementNames()); + + const OUString* pBegin = aColNames.getConstArray(); + const OUString* pEnd = pBegin + aColNames.getLength(); + Reference< XPropertySet> xColumn; + for(sal_Int32 i=1;pBegin != pEnd;++pBegin,++i) + { + if(match(columnNamePattern,*pBegin,'\0')) + { + aRow[4] = new ORowSetValueDecorator(*pBegin); + + xColumn.set( + xColumns->getByName(*pBegin), css::uno::UNO_QUERY); + OSL_ENSURE(xColumn.is(),"Columns contains a column who isn't a fastpropertyset!"); + aRow[5] = new ORowSetValueDecorator(getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)))); + aRow[6] = new ORowSetValueDecorator(getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME)))); + aRow[7] = new ORowSetValueDecorator(getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION)))); + aRow[9] = new ORowSetValueDecorator(getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE)))); + aRow[11] = new ORowSetValueDecorator(getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE)))); + aRow[13] = new ORowSetValueDecorator(getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE)))); + + switch(static_cast<sal_Int32>(aRow[5]->getValue())) + { + case DataType::CHAR: + case DataType::VARCHAR: + aRow[16] = new ORowSetValueDecorator(sal_Int32(254)); + break; + case DataType::LONGVARCHAR: + aRow[16] = new ORowSetValueDecorator(sal_Int32(65535)); + break; + default: + aRow[16] = new ORowSetValueDecorator(sal_Int32(0)); + } + aRow[17] = new ORowSetValueDecorator(i); + switch(sal_Int32(aRow[11]->getValue())) + { + case ColumnValue::NO_NULLS: + aRow[18] = new ORowSetValueDecorator(OUString("NO")); + break; + case ColumnValue::NULLABLE: + aRow[18] = new ORowSetValueDecorator(OUString("YES")); + break; + default: + aRow[18] = new ORowSetValueDecorator(OUString()); + } + aRows.push_back(aRow); + } + } + } + } + + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eColumns); + Reference< XResultSet > xRef = pResult; + pResult->setRows(aRows); + + return xRef; +} + +OUString SAL_CALL OFlatDatabaseMetaData::getURL( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return "sdbc:flat:" + m_pConnection->getURL(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/EDriver.cxx b/connectivity/source/drivers/flat/EDriver.cxx new file mode 100644 index 000000000..e10f29da7 --- /dev/null +++ b/connectivity/source/drivers/flat/EDriver.cxx @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <flat/EDriver.hxx> +#include <flat/EConnection.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <connectivity/dbexception.hxx> +#include <comphelper/sequence.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> +#include <comphelper/processfactory.hxx> + + +using namespace connectivity::flat; +using namespace connectivity::file; +using namespace css::uno; +using namespace css::beans; +using namespace css::sdbcx; +using namespace css::sdbc; +using namespace css::lang; + + +// static ServiceInfo + +OUString ODriver::getImplementationName_Static( ) +{ + return "com.sun.star.comp.sdbc.flat.ODriver"; +} + + +OUString SAL_CALL ODriver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + + +css::uno::Reference< css::uno::XInterface > connectivity::flat::ODriver_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory) +{ + return *(new ODriver( comphelper::getComponentContext(_rxFactory) )); +} + +Reference< XConnection > SAL_CALL ODriver::connect( const OUString& url, const Sequence< PropertyValue >& info ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if (ODriver_BASE::rBHelper.bDisposed) + throw DisposedException(); + + if ( ! acceptsURL(url) ) + return nullptr; + + OFlatConnection* pCon = new OFlatConnection(this); + pCon->construct(url,info); + Reference< XConnection > xCon = pCon; + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return xCon; +} + +sal_Bool SAL_CALL ODriver::acceptsURL( const OUString& url ) +{ + return url.startsWith("sdbc:flat:"); +} + +Sequence< DriverPropertyInfo > SAL_CALL ODriver::getPropertyInfo( const OUString& url, const Sequence< PropertyValue >& info ) +{ + if ( acceptsURL(url) ) + { + std::vector< DriverPropertyInfo > aDriverInfo; + + Sequence< OUString > aBoolean(2); + aBoolean[0] = "0"; + aBoolean[1] = "1"; + + aDriverInfo.push_back(DriverPropertyInfo( + "FieldDelimiter" + ,"Field separator." + ,false + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "HeaderLine" + ,"Text contains headers." + ,false + ,"0" + ,aBoolean) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "StringDelimiter" + ,"Text separator." + ,false + ,"0" + ,aBoolean) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "DecimalDelimiter" + ,"Decimal separator." + ,false + ,"0" + ,aBoolean) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "ThousandDelimiter" + ,"Thousands separator." + ,false + ,"0" + ,aBoolean) + ); + return ::comphelper::concatSequences(OFileDriver::getPropertyInfo(url,info ), + Sequence< DriverPropertyInfo >(aDriverInfo.data(),aDriverInfo.size())); + } + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + return Sequence< DriverPropertyInfo >(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/EPreparedStatement.cxx b/connectivity/source/drivers/flat/EPreparedStatement.cxx new file mode 100644 index 000000000..64a0b3a1c --- /dev/null +++ b/connectivity/source/drivers/flat/EPreparedStatement.cxx @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <flat/EPreparedStatement.hxx> +#include <flat/EResultSet.hxx> + +using namespace connectivity::flat; +using namespace connectivity::file; +using namespace ::com::sun::star::uno; + +OResultSet* OFlatPreparedStatement::createResultSet() +{ + return new OFlatResultSet(this,m_aSQLIterator); +} + +IMPLEMENT_SERVICE_INFO(OFlatPreparedStatement,"com.sun.star.sdbc.driver.flat.PreparedStatement","com.sun.star.sdbc.PreparedStatement"); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/EResultSet.cxx b/connectivity/source/drivers/flat/EResultSet.cxx new file mode 100644 index 000000000..aca803041 --- /dev/null +++ b/connectivity/source/drivers/flat/EResultSet.cxx @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <com/sun/star/sdbcx/XDeleteRows.hpp> +#include <flat/EResultSet.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace ::comphelper; + +using namespace connectivity::flat; +using namespace connectivity::file; +using namespace ::cppu; +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; + + +OFlatResultSet::OFlatResultSet( OStatement_Base* pStmt,connectivity::OSQLParseTreeIterator& _aSQLIterator) + : file::OResultSet(pStmt,_aSQLIterator) + ,m_bBookmarkable(true) +{ + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), PROPERTY_ID_ISBOOKMARKABLE, PropertyAttribute::READONLY,&m_bBookmarkable, cppu::UnoType<bool>::get()); +} + +OUString SAL_CALL OFlatResultSet::getImplementationName( ) +{ + return "com.sun.star.sdbcx.flat.ResultSet"; +} + +Sequence< OUString > SAL_CALL OFlatResultSet::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdbc.ResultSet", "com.sun.star.sdbcx.ResultSet" }; +} + +sal_Bool SAL_CALL OFlatResultSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Any SAL_CALL OFlatResultSet::queryInterface( const Type & rType ) +{ + if(rType == cppu::UnoType<XDeleteRows>::get()|| rType == cppu::UnoType<XResultSetUpdate>::get() + || rType == cppu::UnoType<XRowUpdate>::get()) + return Any(); + + const Any aRet = OResultSet::queryInterface(rType); + return aRet.hasValue() ? aRet : OFlatResultSet_BASE::queryInterface(rType); +} + +Sequence< Type > SAL_CALL OFlatResultSet::getTypes( ) +{ + Sequence< Type > aTypes = OResultSet::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<XDeleteRows>::get()|| + *pBegin == cppu::UnoType<XResultSetUpdate>::get()|| + *pBegin == cppu::UnoType<XRowUpdate>::get())) + { + aOwnTypes.push_back(*pBegin); + } + } + Sequence< Type > aRet(aOwnTypes.data(), aOwnTypes.size()); + return ::comphelper::concatSequences(aRet,OFlatResultSet_BASE::getTypes()); +} + + +// XRowLocate +Any SAL_CALL OFlatResultSet::getBookmark( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return makeAny(static_cast<sal_Int32>((*m_aRow)[0]->getValue())); +} + +sal_Bool SAL_CALL OFlatResultSet::moveToBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + m_bRowDeleted = m_bRowInserted = m_bRowUpdated = false; + + return Move(IResultSetHelper::BOOKMARK,comphelper::getINT32(bookmark),true); +} + +sal_Bool SAL_CALL OFlatResultSet::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + m_bRowDeleted = m_bRowInserted = m_bRowUpdated = false; + + Move(IResultSetHelper::BOOKMARK,comphelper::getINT32(bookmark),false); + + return relative(rows); +} + + +sal_Int32 SAL_CALL OFlatResultSet::compareBookmarks( const Any& lhs, const Any& rhs ) +{ + return (lhs == rhs) ? CompareBookmark::EQUAL : CompareBookmark::NOT_EQUAL; +} + +sal_Bool SAL_CALL OFlatResultSet::hasOrderedBookmarks( ) +{ + return true; +} + +sal_Int32 SAL_CALL OFlatResultSet::hashBookmark( const Any& bookmark ) +{ + return comphelper::getINT32(bookmark); +} + +IPropertyArrayHelper* OFlatResultSet::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +IPropertyArrayHelper & OFlatResultSet::getInfoHelper() +{ + return *OFlatResultSet_BASE3::getArrayHelper(); +} + +void SAL_CALL OFlatResultSet::acquire() throw() +{ + OFlatResultSet_BASE2::acquire(); +} + +void SAL_CALL OFlatResultSet::release() throw() +{ + OFlatResultSet_BASE2::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OFlatResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/EStatement.cxx b/connectivity/source/drivers/flat/EStatement.cxx new file mode 100644 index 000000000..777463905 --- /dev/null +++ b/connectivity/source/drivers/flat/EStatement.cxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <flat/EStatement.hxx> +#include <flat/EResultSet.hxx> + +using namespace connectivity::flat; +using namespace connectivity::file; +using namespace css::uno; + +OResultSet* OFlatStatement::createResultSet() +{ + return new OFlatResultSet(this,m_aSQLIterator); +} + +IMPLEMENT_SERVICE_INFO(OFlatStatement,"com.sun.star.sdbc.driver.flat.Statement","com.sun.star.sdbc.Statement"); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/ETable.cxx b/connectivity/source/drivers/flat/ETable.cxx new file mode 100644 index 000000000..0dceba05b --- /dev/null +++ b/connectivity/source/drivers/flat/ETable.cxx @@ -0,0 +1,964 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <flat/ETable.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <flat/EConnection.hxx> +#include <flat/EColumns.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <tools/solar.h> +#include <tools/urlobj.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/numbers.hxx> +#include <comphelper/servicehelper.hxx> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/util/NumberFormatsSupplier.hpp> +#include <i18nlangtag/languagetag.hxx> +#include <connectivity/dbconversion.hxx> +#include <connectivity/sdbcx/VColumn.hxx> +#include <file/quotedstring.hxx> +#include <file/FDriver.hxx> +#include <unotools/syslocale.hxx> +#include <unotools/charclass.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::flat; +using namespace connectivity::file; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using std::vector; +using std::lower_bound; + + +void OFlatTable::fillColumns(const css::lang::Locale& _aLocale) +{ + m_bNeedToReadLine = true; // we overwrite m_aCurrentLine, seek the stream, ... + m_pFileStream->Seek(0); + m_aCurrentLine = QuotedTokenizedString(); + bool bRead = true; + + const OFlatConnection* const pConnection = getFlatConnection(); + const bool bHasHeaderLine = pConnection->isHeaderLine(); + + QuotedTokenizedString aHeaderLine; + TRowPositionInFile rowPos(0, 0); + sal_Int32 rowNum(0); + if ( bHasHeaderLine ) + { + bRead = readLine(&rowPos.second, &rowPos.first, true); + if(bRead) + aHeaderLine = m_aCurrentLine; + } + setRowPos(rowNum++, rowPos); + + // read first row + if(bRead) + { + bRead = readLine(&rowPos.second, &rowPos.first); + if(bRead) + setRowPos(rowNum++, rowPos); + } + + if ( !bHasHeaderLine || !aHeaderLine.Len()) + { + // use first non-empty row as headerline because we need the number of columns + while(bRead && m_aCurrentLine.Len() == 0) + { + bRead = readLine(&rowPos.second, &rowPos.first); + if(bRead) + setRowPos(rowNum++, rowPos); + } + aHeaderLine = m_aCurrentLine; + } + // column count + const sal_Int32 nFieldCount = aHeaderLine.GetTokenCount(m_cFieldDelimiter,m_cStringDelimiter); + + if(!m_aColumns.is()) + m_aColumns = new OSQLColumns(); + else + m_aColumns->clear(); + + m_aTypes.clear(); + m_aPrecisions.clear(); + m_aScales.clear(); + // reserve some space + m_aColumns->reserve(nFieldCount+1); + m_aTypes.assign(nFieldCount+1,DataType::SQLNULL); + m_aPrecisions.assign(nFieldCount+1,-1); + m_aScales.assign(nFieldCount+1,-1); + + const bool bCase = m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers(); + CharClass aCharClass( pConnection->getDriver()->getComponentContext(), LanguageTag( _aLocale)); + // read description + const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter(); + const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter(); + ::comphelper::UStringMixEqual aCase(bCase); + vector<OUString> aColumnNames; + vector<OUString> aTypeNames; + aTypeNames.resize(nFieldCount); + const sal_Int32 nMaxRowsToScan = pConnection->getMaxRowsToScan(); + sal_Int32 nRowCount = 0; + + do + { + sal_Int32 nStartPosHeaderLine = 0; // use for efficient way to get the tokens + sal_Int32 nStartPosFirstLine = 0; // use for efficient way to get the tokens + sal_Int32 nStartPosFirstLine2 = 0; + for( sal_Int32 i = 0; i < nFieldCount; i++ ) + { + if ( nRowCount == 0) + { + OUString aColumnName; + if ( bHasHeaderLine ) + { + aColumnName = aHeaderLine.GetTokenSpecial(nStartPosHeaderLine,m_cFieldDelimiter,m_cStringDelimiter); + } + if ( aColumnName.isEmpty() ) + { + aColumnName = "C" + OUString::number(i+1); + } + aColumnNames.push_back(aColumnName); + } + if(bRead) + { + impl_fillColumnInfo_nothrow(m_aCurrentLine, nStartPosFirstLine, nStartPosFirstLine2, + m_aTypes[i], m_aPrecisions[i], m_aScales[i], aTypeNames[i], + cDecimalDelimiter, cThousandDelimiter, aCharClass); + } + } + ++nRowCount; + bRead = readLine(&rowPos.second, &rowPos.first); + if(bRead) + setRowPos(rowNum++, rowPos); + } + while(nRowCount < nMaxRowsToScan && bRead); + + for( sal_Int32 i = 0; i < nFieldCount; i++ ) + { + // check if the columname already exists + OUString aAlias(aColumnNames[i]); + OSQLColumns::const_iterator aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase); + sal_Int32 nExprCnt = 0; + while(aFind != m_aColumns->end()) + { + aAlias = aColumnNames[i] + OUString::number(++nExprCnt); + aFind = connectivity::find(m_aColumns->begin(),m_aColumns->end(),aAlias,aCase); + } + + sdbcx::OColumn* pColumn = new sdbcx::OColumn(aAlias,aTypeNames[i],OUString(),OUString(), + ColumnValue::NULLABLE, + m_aPrecisions[i], + m_aScales[i], + m_aTypes[i], + false, + false, + false, + bCase, + m_CatalogName, getSchema(), getName()); + Reference< XPropertySet> xCol = pColumn; + m_aColumns->push_back(xCol); + } + + m_pFileStream->Seek(m_aRowPosToFilePos[0].second); +} + +void OFlatTable::impl_fillColumnInfo_nothrow(QuotedTokenizedString const & aFirstLine, sal_Int32& nStartPosFirstLine, sal_Int32& nStartPosFirstLine2, + sal_Int32& io_nType, sal_Int32& io_nPrecisions, sal_Int32& io_nScales, OUString& o_sTypeName, + const sal_Unicode cDecimalDelimiter, const sal_Unicode cThousandDelimiter, const CharClass& aCharClass) +{ + if ( io_nType != DataType::VARCHAR ) + { + bool bNumeric = io_nType == DataType::SQLNULL || io_nType == DataType::DOUBLE || io_nType == DataType::DECIMAL || io_nType == DataType::INTEGER; + sal_uLong nIndex = 0; + + if ( bNumeric ) + { + // first without fielddelimiter + OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter); + if (aField.isEmpty() || + (m_cStringDelimiter && m_cStringDelimiter == aField[0])) + { + bNumeric = false; + if ( m_cStringDelimiter != '\0' ) + aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter); + else + nStartPosFirstLine2 = nStartPosFirstLine; + } + else + { + OUString aField2; + if ( m_cStringDelimiter != '\0' ) + aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter); + else + aField2 = aField; + + if (aField2.isEmpty()) + { + bNumeric = false; + } + else + { + bNumeric = true; + sal_Int32 nDot = 0; + sal_Int32 nDecimalDelCount = 0; + sal_Int32 nSpaceCount = 0; + for( sal_Int32 j = 0; j < aField2.getLength(); j++ ) + { + const sal_Unicode c = aField2[j]; + if ( j == nSpaceCount && m_cFieldDelimiter != 32 && c == 32 ) + { + ++nSpaceCount; + continue; + } + // just digits, decimal- and thousands-delimiter? + if ( ( !cDecimalDelimiter || c != cDecimalDelimiter ) && + ( !cThousandDelimiter || c != cThousandDelimiter ) && + !aCharClass.isDigit(aField2,j) && + ( j != 0 || (c != '+' && c != '-' ) ) ) + { + bNumeric = false; + break; + } + if (cDecimalDelimiter && c == cDecimalDelimiter) + { + io_nPrecisions = 15; // we have a decimal value + io_nScales = 2; + ++nDecimalDelCount; + } // if (cDecimalDelimiter && c == cDecimalDelimiter) + if ( c == '.' ) + ++nDot; + } + + if (nDecimalDelCount > 1 || nDot > 1 ) // if there is more than one dot it isn't a number + bNumeric = false; + if (bNumeric && cThousandDelimiter) + { + // Is the delimiter correct? + const OUString aValue = aField2.getToken(0,cDecimalDelimiter); + for( sal_Int32 j = aValue.getLength() - 4; j >= 0; j -= 4) + { + const sal_Unicode c = aValue[j]; + // just digits, decimal- and thousands-delimiter? + if (c == cThousandDelimiter && j) + continue; + else + { + bNumeric = false; + break; + } + } + } + + // now also check for a date field + if (!bNumeric) + { + try + { + nIndex = m_xNumberFormatter->detectNumberFormat(css::util::NumberFormat::ALL,aField2); + } + catch(Exception&) + { + } + } + } + } + } + else if ( io_nType == DataType::DATE || io_nType == DataType::TIMESTAMP || io_nType == DataType::TIME) + { + OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter); + if (aField.isEmpty() || + (m_cStringDelimiter && m_cStringDelimiter == aField[0])) + { + } + else + { + OUString aField2; + if ( m_cStringDelimiter != '\0' ) + aField2 = aFirstLine.GetTokenSpecial(nStartPosFirstLine2,m_cFieldDelimiter,m_cStringDelimiter); + else + aField2 = aField; + if (!aField2.isEmpty() ) + { + try + { + nIndex = m_xNumberFormatter->detectNumberFormat(css::util::NumberFormat::ALL,aField2); + } + catch(Exception&) + { + } + } + } + } + + if (bNumeric) + { + if (cDecimalDelimiter) + { + if(io_nPrecisions) + { + io_nType = DataType::DECIMAL; + o_sTypeName = "DECIMAL"; + } + else + { + io_nType = DataType::DOUBLE; + o_sTypeName = "DOUBLE"; + } + } + else + { + io_nType = DataType::INTEGER; + io_nPrecisions = 0; + io_nScales = 0; + } + } + else + { + switch (comphelper::getNumberFormatType(m_xNumberFormatter,nIndex)) + { + case css::util::NumberFormat::DATE: + io_nType = DataType::DATE; + o_sTypeName = "DATE"; + break; + case css::util::NumberFormat::DATETIME: + io_nType = DataType::TIMESTAMP; + o_sTypeName = "TIMESTAMP"; + break; + case css::util::NumberFormat::TIME: + io_nType = DataType::TIME; + o_sTypeName = "TIME"; + break; + default: + io_nType = DataType::VARCHAR; + io_nPrecisions = 0; // nyi: Data can be longer! + io_nScales = 0; + o_sTypeName = "VARCHAR"; + }; + } + } + else + { + OUString aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine,m_cFieldDelimiter); + if (aField.isEmpty() || + (m_cStringDelimiter && m_cStringDelimiter == aField[0])) + { + if ( m_cStringDelimiter != '\0' ) + aField = aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter); + else + nStartPosFirstLine2 = nStartPosFirstLine; + } + else + { + if ( m_cStringDelimiter != '\0' ) + aFirstLine.GetTokenSpecial(nStartPosFirstLine2, m_cFieldDelimiter, m_cStringDelimiter); + } + } +} + +OFlatTable::OFlatTable(sdbcx::OCollection* _pTables,OFlatConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName + ) : OFlatTable_BASE(_pTables,_pConnection,Name, + Type, + Description, + SchemaName, + CatalogName) + ,m_nRowPos(0) + ,m_nMaxRowCount(0) + ,m_cStringDelimiter(_pConnection->getStringDelimiter()) + ,m_cFieldDelimiter(_pConnection->getFieldDelimiter()) + ,m_bNeedToReadLine(false) +{ + +} + +void OFlatTable::construct() +{ + SvtSysLocale aLocale; + css::lang::Locale aAppLocale(aLocale.GetLanguageTag().getLocale()); + + Reference< XNumberFormatsSupplier > xSupplier = NumberFormatsSupplier::createWithLocale( m_pConnection->getDriver()->getComponentContext(), aAppLocale ); + m_xNumberFormatter.set( NumberFormatter::create( m_pConnection->getDriver()->getComponentContext()), UNO_QUERY_THROW); + m_xNumberFormatter->attachNumberFormatsSupplier(xSupplier); + Reference<XPropertySet> xProp = xSupplier->getNumberFormatSettings(); + xProp->getPropertyValue("NullDate") >>= m_aNullDate; + + INetURLObject aURL; + aURL.SetURL(getEntry()); + + if(aURL.getExtension() != m_pConnection->getExtension()) + aURL.setExtension(m_pConnection->getExtension()); + + OUString aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + + m_pFileStream = createStream_simpleError( aFileName, StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYWRITE); + + if(!m_pFileStream) + m_pFileStream = createStream_simpleError( aFileName, StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYNONE); + + if(!m_pFileStream) + return; + + sal_uInt64 const nSize = m_pFileStream->remainingSize(); + + // Buffersize is dependent on the file-size + m_pFileStream->SetBufferSize(nSize > 1000000 ? 32768 : + nSize > 100000 ? 16384 : + nSize > 10000 ? 4096 : 1024); + + fillColumns(aAppLocale); + + refreshColumns(); +} + +OUString OFlatTable::getEntry() const +{ + OUString sURL; + try + { + Reference< XResultSet > xDir = m_pConnection->getDir()->getStaticResultSet(); + Reference< XRow> xRow(xDir,UNO_QUERY); + OUString sName; + OUString sExt; + + INetURLObject aURL; + xDir->beforeFirst(); + while(xDir->next()) + { + sName = xRow->getString(1); + aURL.SetSmartProtocol(INetProtocol::File); + OUString sUrl = m_pConnection->getURL() + "/" + sName; + aURL.SetSmartURL( sUrl ); + + // cut the extension + sExt = aURL.getExtension(); + + // name and extension have to coincide + if ( m_pConnection->matchesExtension( sExt ) ) + { + if ( !sExt.isEmpty() ) + sName = sName.replaceAt(sName.getLength() - (sExt.getLength() + 1), sExt.getLength()+1, OUString()); + if ( sName == m_Name ) + { + Reference< XContentAccess > xContentAccess( xDir, UNO_QUERY ); + sURL = xContentAccess->queryContentIdentifierString(); + break; + } + } + } + xDir->beforeFirst(); // move back to before first record + } + catch(const Exception&) + { + OSL_ASSERT(false); + } + return sURL; +} + +void OFlatTable::refreshColumns() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ::std::vector< OUString> aVector; + aVector.reserve(m_aColumns->size()); + + for (auto const& column : *m_aColumns) + aVector.push_back(Reference< XNamed>(column,UNO_QUERY_THROW)->getName()); + + if(m_xColumns) + m_xColumns->reFill(aVector); + else + m_xColumns = new OFlatColumns(this,m_aMutex,aVector); +} + + +void SAL_CALL OFlatTable::disposing() +{ + OFileTable::disposing(); + ::osl::MutexGuard aGuard(m_aMutex); + m_aColumns = nullptr; +} + +Sequence< Type > SAL_CALL OFlatTable::getTypes( ) +{ + Sequence< Type > aTypes = OTable_TYPEDEF::getTypes(); + 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<XKeysSupplier>::get()|| + *pBegin == cppu::UnoType<XRename>::get()|| + *pBegin == cppu::UnoType<XIndexesSupplier>::get()|| + *pBegin == cppu::UnoType<XAlterTable>::get()|| + *pBegin == cppu::UnoType<XDataDescriptorFactory>::get())) + { + aOwnTypes.push_back(*pBegin); + } + } + return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size()); +} + + +Any SAL_CALL OFlatTable::queryInterface( const Type & rType ) +{ + if( rType == cppu::UnoType<XKeysSupplier>::get()|| + rType == cppu::UnoType<XIndexesSupplier>::get()|| + rType == cppu::UnoType<XRename>::get()|| + rType == cppu::UnoType<XAlterTable>::get()|| + rType == cppu::UnoType<XDataDescriptorFactory>::get()) + return Any(); + + Any aRet = OTable_TYPEDEF::queryInterface(rType); + return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< css::lang::XUnoTunnel*> (this)); +} + + +Sequence< sal_Int8 > OFlatTable::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OFlatTable::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return (isUnoTunnelId<OFlatTable>(rId)) + ? reinterpret_cast< sal_Int64 >( this ) + : OFlatTable_BASE::getSomething(rId); +} + +bool OFlatTable::fetchRow(OValueRefRow& _rRow, const OSQLColumns & _rCols, bool bRetrieveData) +{ + *(*_rRow)[0] = m_nFilePos; + + if (!bRetrieveData) + return true; + + bool result = false; + if ( m_bNeedToReadLine ) + { + m_pFileStream->Seek(m_nFilePos); + TRowPositionInFile rowPos(0, 0); + if(readLine(&rowPos.second, &rowPos.first)) + { + setRowPos(m_nRowPos, rowPos); + m_bNeedToReadLine = false; + result = true; + } + // else let run through so that we set _rRow to all NULL + } + + const OFlatConnection * const pConnection = getFlatConnection(); + const sal_Unicode cDecimalDelimiter = pConnection->getDecimalDelimiter(); + const sal_Unicode cThousandDelimiter = pConnection->getThousandDelimiter(); + // Fields: + sal_Int32 nStartPos = 0; + OSQLColumns::const_iterator aIter = _rCols.begin(); + OSQLColumns::const_iterator aEnd = _rCols.end(); + const OValueRefVector::size_type nCount = _rRow->size(); + for (OValueRefVector::size_type i = 1; + aIter != aEnd && i < nCount; + ++aIter, i++) + { + OUString aStr = m_aCurrentLine.GetTokenSpecial(nStartPos,m_cFieldDelimiter,m_cStringDelimiter); + + if (aStr.isEmpty()) + { + (*_rRow)[i]->setNull(); + } + else + { + sal_Int32 nType = m_aTypes[i-1]; + switch(nType) + { + case DataType::TIMESTAMP: + case DataType::DATE: + case DataType::TIME: + { + try + { + double nRes = m_xNumberFormatter->convertStringToNumber(css::util::NumberFormat::ALL,aStr); + + switch(nType) + { + case DataType::DATE: + *(*_rRow)[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(nRes,m_aNullDate)); + break; + case DataType::TIMESTAMP: + *(*_rRow)[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(nRes,m_aNullDate)); + break; + default: + *(*_rRow)[i] = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(nRes)); + } + } + catch(Exception&) + { + (*_rRow)[i]->setNull(); + } + } break; + case DataType::DOUBLE: + case DataType::INTEGER: + case DataType::DECIMAL: + case DataType::NUMERIC: + { + + OUString aStrConverted; + if ( DataType::INTEGER != nType ) + { + OSL_ENSURE((cDecimalDelimiter && nType != DataType::INTEGER) || + (!cDecimalDelimiter && nType == DataType::INTEGER), + "Wrong type"); + + OUStringBuffer aBuf(aStr.getLength()); + // convert to Standard-Notation (DecimalPOINT without thousands-comma): + for (sal_Int32 j = 0; j < aStr.getLength(); ++j) + { + const sal_Unicode cChar = aStr[j]; + if (cDecimalDelimiter && cChar == cDecimalDelimiter) + aBuf.append('.'); + else if ( cChar == '.' ) // special case, if decimal separator isn't '.' we have to put the string after it + continue; + else if (cThousandDelimiter && cChar == cThousandDelimiter) + { + // leave out + } + else + aBuf.append(cChar); + } // for (j = 0; j < aStr.(); ++j) + aStrConverted = aBuf.makeStringAndClear(); + } // if ( DataType::INTEGER != nType ) + else + { + if ( cThousandDelimiter ) + aStrConverted = aStr.replaceAll(OUStringChar(cThousandDelimiter), ""); + else + aStrConverted = aStr; + } + const double nVal = ::rtl::math::stringToDouble(aStrConverted,'.',','); + + // #99178# OJ + if ( DataType::DECIMAL == nType || DataType::NUMERIC == nType ) + *(*_rRow)[i] = OUString(OUString::number(nVal)); + else + *(*_rRow)[i] = nVal; + } break; + + default: + { + // Copy Value as String in Row-Variable + *(*_rRow)[i] = ORowSetValue(aStr); + } + break; + } // switch(nType) + (*_rRow)[i]->setTypeKind(nType); + } + } + return result; +} + + +void OFlatTable::refreshHeader() +{ + SAL_INFO( "connectivity.flat", "flat lionel@mamane.lu OFlatTable::refreshHeader" ); +} + + +namespace +{ + template< typename Tp, typename Te> struct RangeBefore + { + bool operator() (const Tp &p, const Te &e) + { + assert(p.first <= p.second); + return p.second <= e; + } + }; +} + +bool OFlatTable::seekRow(IResultSetHelper::Movement eCursorPosition, sal_Int32 nOffset, sal_Int32& nCurPos) +{ + OSL_ENSURE(m_pFileStream,"OFlatTable::seekRow: FileStream is NULL!"); + + + switch(eCursorPosition) + { + case IResultSetHelper::FIRST: + m_nRowPos = 0; + [[fallthrough]]; + case IResultSetHelper::NEXT: + { + assert(m_nRowPos >= 0); + if(m_nMaxRowCount != 0 && m_nRowPos > m_nMaxRowCount) + return false; + ++m_nRowPos; + if(m_aRowPosToFilePos.size() > o3tl::make_unsigned(m_nRowPos)) + { + m_bNeedToReadLine = true; + m_nFilePos = m_aRowPosToFilePos[m_nRowPos].first; + nCurPos = m_aRowPosToFilePos[m_nRowPos].second; + } + else + { + assert(m_aRowPosToFilePos.size() == static_cast< vector< TRowPositionInFile >::size_type >(m_nRowPos)); + const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back()); + // Our ResultSet is allowed to disagree with us only + // on the position of the first line + // (because of the special case of the header...) + assert(m_nRowPos == 1 || nCurPos == lastRowPos.second); + + m_nFilePos = lastRowPos.second; + m_pFileStream->Seek(m_nFilePos); + + TRowPositionInFile newRowPos; + if(!readLine(&newRowPos.second, &newRowPos.first)) + { + m_nMaxRowCount = m_nRowPos - 1; + return false; + } + + nCurPos = newRowPos.second; + setRowPos(m_nRowPos, newRowPos); + } + } + + break; + case IResultSetHelper::PRIOR: + assert(m_nRowPos >= 0); + + if(m_nRowPos == 0) + return false; + + --m_nRowPos; + { + assert (m_nRowPos >= 0); + assert(m_aRowPosToFilePos.size() >= o3tl::make_unsigned(m_nRowPos)); + const TRowPositionInFile &aPositions(m_aRowPosToFilePos[m_nRowPos]); + m_nFilePos = aPositions.first; + nCurPos = aPositions.second; + m_bNeedToReadLine = true; + } + + break; + case IResultSetHelper::LAST: + if (m_nMaxRowCount == 0) + { + while(seekRow(IResultSetHelper::NEXT, 1, nCurPos)) ; // run through after last row + } + // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table + return seekRow(IResultSetHelper::ABSOLUTE1, m_nMaxRowCount, nCurPos); + break; + case IResultSetHelper::RELATIVE1: + { + const sal_Int32 nNewRowPos = m_nRowPos + nOffset; + if (nNewRowPos < 0) + return false; + // ABSOLUTE will take care of case nNewRowPos > nMaxRowCount + return seekRow(IResultSetHelper::ABSOLUTE1, nNewRowPos, nCurPos); + } + case IResultSetHelper::ABSOLUTE1: + { + if(nOffset < 0) + { + if (m_nMaxRowCount == 0) + { + if (!seekRow(IResultSetHelper::LAST, 0, nCurPos)) + return false; + } + // m_nMaxRowCount can still be zero, but now it means there a genuinely zero rows in the table + nOffset = m_nMaxRowCount + nOffset; + } + if(nOffset < 0) + { + seekRow(IResultSetHelper::ABSOLUTE1, 0, nCurPos); + return false; + } + if(m_nMaxRowCount && nOffset > m_nMaxRowCount) + { + m_nRowPos = m_nMaxRowCount + 1; + const TRowPositionInFile &lastRowPos(m_aRowPosToFilePos.back()); + m_nFilePos = lastRowPos.second; + nCurPos = lastRowPos.second; + return false; + } + + assert(m_nRowPos >=0); + assert(m_aRowPosToFilePos.size() > o3tl::make_unsigned(m_nRowPos)); + assert(nOffset >= 0); + if(m_aRowPosToFilePos.size() > o3tl::make_unsigned(nOffset)) + { + m_nFilePos = m_aRowPosToFilePos[nOffset].first; + nCurPos = m_aRowPosToFilePos[nOffset].second; + m_nRowPos = nOffset; + m_bNeedToReadLine = true; + } + else + { + assert(m_nRowPos < nOffset); + while(m_nRowPos < nOffset) + { + if(!seekRow(IResultSetHelper::NEXT, 1, nCurPos)) + return false; + } + assert(m_nRowPos == nOffset); + } + } + + break; + case IResultSetHelper::BOOKMARK: + { + vector< TRowPositionInFile >::const_iterator aFind = lower_bound(m_aRowPosToFilePos.begin(), + m_aRowPosToFilePos.end(), + nOffset, + RangeBefore< TRowPositionInFile, sal_Int32 >()); + + if(aFind == m_aRowPosToFilePos.end() || aFind->first != nOffset) + //invalid bookmark + return false; + + m_bNeedToReadLine = true; + m_nFilePos = aFind->first; + nCurPos = aFind->second; + m_nRowPos = aFind - m_aRowPosToFilePos.begin(); + break; + } + } + + return true; +} + + +bool OFlatTable::readLine(sal_Int32 * const pEndPos, sal_Int32 * const pStartPos, const bool nonEmpty) +{ + const rtl_TextEncoding nEncoding = m_pConnection->getTextEncoding(); + m_aCurrentLine = QuotedTokenizedString(); + do + { + if (pStartPos) + *pStartPos = static_cast<sal_Int32>(m_pFileStream->Tell()); + m_pFileStream->ReadByteStringLine(m_aCurrentLine, nEncoding); + if (m_pFileStream->eof()) + return false; + + QuotedTokenizedString sLine = m_aCurrentLine; // check if the string continues on next line + sal_Int32 nLastOffset = 0; + bool isQuoted = false; + bool isFieldStarting = true; + while (true) + { + bool wasQuote = false; + const sal_Unicode *p = sLine.GetString().getStr() + nLastOffset; + while (*p) + { + if (isQuoted) + { + if (*p == m_cStringDelimiter) + wasQuote = !wasQuote; + else + { + if (wasQuote) + { + wasQuote = false; + isQuoted = false; + if (*p == m_cFieldDelimiter) + isFieldStarting = true; + } + } + } + else + { + if (isFieldStarting) + { + isFieldStarting = false; + if (*p == m_cStringDelimiter) + isQuoted = true; + else if (*p == m_cFieldDelimiter) + isFieldStarting = true; + } + else if (*p == m_cFieldDelimiter) + isFieldStarting = true; + } + ++p; + } + + if (wasQuote) + isQuoted = false; + + if (isQuoted) + { + nLastOffset = sLine.Len(); + m_pFileStream->ReadByteStringLine(sLine,nEncoding); + if ( !m_pFileStream->eof() ) + { + OUString aStr = m_aCurrentLine.GetString() + "\n" + sLine.GetString(); + m_aCurrentLine.SetString(aStr); + sLine = m_aCurrentLine; + } + else + break; + } + else + break; + } + } + while(nonEmpty && m_aCurrentLine.Len() == 0); + + if(pEndPos) + *pEndPos = static_cast<sal_Int32>(m_pFileStream->Tell()); + return true; +} + + +void OFlatTable::setRowPos(const vector<TRowPositionInFile>::size_type rowNum, const TRowPositionInFile &rowPos) +{ + assert(m_aRowPosToFilePos.size() >= rowNum); + if(m_aRowPosToFilePos.size() == rowNum) + m_aRowPosToFilePos.push_back(rowPos); + else + { + SAL_WARN_IF(m_aRowPosToFilePos[rowNum] != rowPos, + "connectivity.flat", + "Setting position for row " << rowNum << " to (" << rowPos.first << ", " << rowPos.second << "), " + "but already had different position (" << m_aRowPosToFilePos[rowNum].first << ", " << m_aRowPosToFilePos[rowNum].second << ")"); + m_aRowPosToFilePos[rowNum] = rowPos; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/ETables.cxx b/connectivity/source/drivers/flat/ETables.cxx new file mode 100644 index 000000000..81f366a9e --- /dev/null +++ b/connectivity/source/drivers/flat/ETables.cxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <flat/ETables.hxx> +#include <flat/ETable.hxx> +#include <file/FCatalog.hxx> + +using namespace connectivity; +using namespace ::comphelper; +using namespace connectivity::flat; +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::lang; +using namespace ::com::sun::star::container; + +sdbcx::ObjectType OFlatTables::createObject(const OUString& _rName) +{ + OFlatTable* pRet = new OFlatTable(this, static_cast<OFlatConnection*>(static_cast<OFileCatalog&>(m_rParent).getConnection()), + _rName,"TABLE"); + sdbcx::ObjectType xRet = pRet; + pRet->construct(); + return xRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/Eservices.cxx b/connectivity/source/drivers/flat/Eservices.cxx new file mode 100644 index 000000000..e74db8791 --- /dev/null +++ b/connectivity/source/drivers/flat/Eservices.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <flat/EDriver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::flat; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* flat_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + ODriver::getImplementationName_Static(), + ODriver::getSupportedServiceNames_Static(), + ODriver_CreateInstance, ::cppu::createSingleFactory) + ; + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/flat/flat.component b/connectivity/source/drivers/flat/flat.component new file mode 100644 index 000000000..6fa7fb8e6 --- /dev/null +++ b/connectivity/source/drivers/flat/flat.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="flat" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.flat.ODriver"> + <service name="com.sun.star.sdbc.Driver"/> + <service name="com.sun.star.sdbcx.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/hsqldb/HCatalog.cxx b/connectivity/source/drivers/hsqldb/HCatalog.cxx new file mode 100644 index 000000000..029e60f94 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HCatalog.cxx @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <hsqldb/HCatalog.hxx> +#include <hsqldb/HUsers.hxx> +#include <hsqldb/HTables.hxx> +#include <hsqldb/HViews.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <comphelper/types.hxx> + + +using namespace connectivity; +using namespace connectivity::hsqldb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OHCatalog::OHCatalog(const Reference< XConnection >& _xConnection) : sdbcx::OCatalog(_xConnection) + ,m_xConnection(_xConnection) +{ +} + +void OHCatalog::refreshObjects(const Sequence< OUString >& _sKindOfObject,::std::vector< OUString>& _rNames) +{ + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(), + "%", + "%", + _sKindOfObject); + fillNames(xResult,_rNames); +} + +void OHCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + + Sequence< OUString > sTableTypes(2); + sTableTypes[0] = "VIEW"; + sTableTypes[1] = "TABLE"; + + refreshObjects(sTableTypes,aVector); + + if ( m_pTables ) + m_pTables->reFill(aVector); + else + m_pTables.reset( new OTables(m_xMetaData,*this,m_aMutex,aVector) ); +} + +void OHCatalog::refreshViews() +{ + Sequence< OUString > aTypes { "VIEW" }; + + bool bSupportsViews = false; + try + { + Reference<XResultSet> xRes = m_xMetaData->getTableTypes(); + Reference<XRow> xRow(xRes,UNO_QUERY); + while ( xRow.is() && xRes->next() ) + { + if ( (bSupportsViews = xRow->getString(1).equalsIgnoreAsciiCase(aTypes[0])) ) + { + break; + } + } + } + catch(const SQLException&) + { + } + + ::std::vector< OUString> aVector; + if ( bSupportsViews ) + refreshObjects(aTypes,aVector); + + if ( m_pViews ) + m_pViews->reFill(aVector); + else + m_pViews.reset( new HViews( m_xConnection, *this, m_aMutex, aVector ) ); +} + +void OHCatalog::refreshGroups() +{ +} + +void OHCatalog::refreshUsers() +{ + ::std::vector< OUString> aVector; + Reference< XStatement > xStmt = m_xConnection->createStatement( ); + Reference< XResultSet > xResult = xStmt->executeQuery("select User from hsqldb.user group by User"); + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while( xResult->next() ) + aVector.push_back(xRow->getString(1)); + ::comphelper::disposeComponent(xResult); + } + ::comphelper::disposeComponent(xStmt); + + if(m_pUsers) + m_pUsers->reFill(aVector); + else + m_pUsers.reset( new OUsers(*this,m_aMutex,aVector,m_xConnection,this) ); +} + +Any SAL_CALL OHCatalog::queryInterface( const Type & rType ) +{ + if ( rType == cppu::UnoType<XGroupsSupplier>::get()) + return Any(); + + return OCatalog::queryInterface(rType); +} + +Sequence< Type > SAL_CALL OHCatalog::getTypes( ) +{ + Sequence< Type > aTypes = OCatalog::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()) + { + aOwnTypes.push_back(*pBegin); + } + } + return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HColumns.cxx b/connectivity/source/drivers/hsqldb/HColumns.cxx new file mode 100644 index 000000000..3f03c3616 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HColumns.cxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <hsqldb/HColumns.hxx> +#include <TConnection.hxx> + + +using namespace ::comphelper; +using namespace connectivity::hsqldb; +using namespace connectivity::sdbcx; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OHSQLColumns::OHSQLColumns( ::cppu::OWeakObject& _rParent + ,::osl::Mutex& _rMutex + ,const ::std::vector< OUString> &_rVector + ) : OColumnsHelper(_rParent,true/*_bCase*/,_rMutex,_rVector,true/*_bUseHardRef*/) +{ +} + +Reference< XPropertySet > OHSQLColumns::createDescriptor() +{ + return new OHSQLColumn; +} + + +OHSQLColumn::OHSQLColumn() + : connectivity::sdbcx::OColumn( true/*_bCase*/ ) +{ + construct(); +} + +void OHSQLColumn::construct() +{ + m_sAutoIncrement = "IDENTITY"; + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION),PROPERTY_ID_AUTOINCREMENTCREATION,0,&m_sAutoIncrement, cppu::UnoType<decltype(m_sAutoIncrement)>::get()); +} + +::cppu::IPropertyArrayHelper* OHSQLColumn::createArrayHelper( sal_Int32 /*_nId*/ ) const +{ + return doCreateArrayHelper(); +} + +::cppu::IPropertyArrayHelper & SAL_CALL OHSQLColumn::getInfoHelper() +{ + return *OHSQLColumn_PROP::getArrayHelper(isNew() ? 1 : 0); +} + +Sequence< OUString > SAL_CALL OHSQLColumn::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdbcx.Column" }; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HConnection.cxx b/connectivity/source/drivers/hsqldb/HConnection.cxx new file mode 100644 index 000000000..b6cbc1e48 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HConnection.cxx @@ -0,0 +1,340 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HConnection.hxx> +#include <hsqldb/HTools.hxx> +#include <bitmaps.hlst> + +#include <connectivity/dbtools.hxx> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData2.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> + +#include <resource/sharedresources.hxx> +#include <strings.hrc> + +using ::com::sun::star::util::XFlushListener; +using ::com::sun::star::lang::EventObject; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::XComponentContext; +using ::com::sun::star::sdbc::XStatement; +using ::com::sun::star::sdbc::XConnection; +using ::com::sun::star::sdbcx::XDataDefinitionSupplier; +using ::com::sun::star::sdbcx::XTablesSupplier; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::WrappedTargetException; +using ::com::sun::star::sdbc::XDriver; +using ::com::sun::star::graphic::XGraphic; +using ::com::sun::star::graphic::GraphicProvider; +using ::com::sun::star::graphic::XGraphicProvider; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::lang::IllegalArgumentException; +using ::com::sun::star::sdbc::XResultSet; +using ::com::sun::star::sdbc::XDatabaseMetaData; +using ::com::sun::star::sdbc::XDatabaseMetaData2; +using ::com::sun::star::sdbc::XRow; +using ::com::sun::star::sdb::application::XDatabaseDocumentUI; +using ::com::sun::star::beans::PropertyValue; + + +namespace connectivity::hsqldb +{ + void SAL_CALL OHsqlConnection::disposing() + { + m_aFlushListeners.disposeAndClear( EventObject( *this ) ); + OHsqlConnection_BASE::disposing(); + OConnectionWrapper::disposing(); + } + + OHsqlConnection::OHsqlConnection( const Reference< XDriver >& _rxDriver, + const Reference< XConnection >& _xConnection ,const Reference< XComponentContext >& _rxContext ) + :OHsqlConnection_BASE( m_aMutex ) + ,m_aFlushListeners( m_aMutex ) + ,m_xDriver( _rxDriver ) + ,m_xContext( _rxContext ) + ,m_bIni(true) + ,m_bReadOnly(false) + { + setDelegation(_xConnection,_rxContext,m_refCount); + } + + OHsqlConnection::~OHsqlConnection() + { + if ( !OHsqlConnection_BASE::rBHelper.bDisposed ) + { + osl_atomic_increment( &m_refCount ); + dispose(); + } + } + + IMPLEMENT_FORWARD_XINTERFACE2(OHsqlConnection,OHsqlConnection_BASE,OConnectionWrapper) + IMPLEMENT_SERVICE_INFO(OHsqlConnection, "com.sun.star.sdbc.drivers.hsqldb.OHsqlConnection", "com.sun.star.sdbc.Connection") + IMPLEMENT_FORWARD_XTYPEPROVIDER2(OHsqlConnection,OHsqlConnection_BASE,OConnectionWrapper) + + + ::osl::Mutex& OHsqlConnection::getMutex() const + { + return m_aMutex; + } + + + void OHsqlConnection::checkDisposed() const + { + ::connectivity::checkDisposed( rBHelper.bDisposed ); + } + + // XFlushable + + void SAL_CALL OHsqlConnection::flush( ) + { + MethodGuard aGuard( *this ); + + try + { + if ( m_xConnection.is() ) + { + if ( m_bIni ) + { + m_bIni = false; + Reference< XDatabaseMetaData2 > xMeta2(m_xConnection->getMetaData(),UNO_QUERY_THROW); + const Sequence< PropertyValue > aInfo = xMeta2->getConnectionInfo(); + const PropertyValue* pIter = aInfo.getConstArray(); + const PropertyValue* pEnd = pIter + aInfo.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( pIter->Name == "readonly" ) + m_bReadOnly = true; + } + } + try + { + if ( !m_bReadOnly ) + { + Reference< XStatement > xStmt( m_xConnection->createStatement(), css::uno::UNO_SET_THROW ); + xStmt->execute( "CHECKPOINT DEFRAG" ); + } + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } + } + + EventObject aFlushedEvent( *this ); + m_aFlushListeners.notifyEach( &XFlushListener::flushed, aFlushedEvent ); + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } + } + + + void SAL_CALL OHsqlConnection::addFlushListener( const Reference< XFlushListener >& l ) + { + MethodGuard aGuard( *this ); + m_aFlushListeners.addInterface( l ); + } + + + void SAL_CALL OHsqlConnection::removeFlushListener( const Reference< XFlushListener >& l ) + { + MethodGuard aGuard( *this ); + m_aFlushListeners.removeInterface( l ); + } + + + Reference< XGraphic > SAL_CALL OHsqlConnection::getTableIcon( const OUString& TableName, ::sal_Int32 /*_ColorMode*/ ) + { + MethodGuard aGuard( *this ); + + impl_checkExistingTable_throw( TableName ); + if ( !impl_isTextTable_nothrow( TableName ) ) + return nullptr; + + return impl_getTextTableIcon_nothrow(); + } + + + Reference< XInterface > SAL_CALL OHsqlConnection::getTableEditor( const Reference< XDatabaseDocumentUI >& DocumentUI, const OUString& TableName ) + { + MethodGuard aGuard( *this ); + + impl_checkExistingTable_throw( TableName ); + if ( !impl_isTextTable_nothrow( TableName ) ) + return nullptr; + + if ( !DocumentUI.is() ) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_NO_DOCUMENTUI)); + throw IllegalArgumentException( + sError, + *this, + 0 + ); + } // if ( !_DocumentUI.is() ) + + +// Reference< XExecutableDialog > xEditor = impl_createLinkedTableEditor_throw( _DocumentUI, _TableName ); +// return xEditor.get(); + return nullptr; + // editor not yet implemented in this CWS + } + + + Reference< XNameAccess > OHsqlConnection::impl_getTableContainer_throw() + { + Reference< XNameAccess > xTables; + try + { + Reference< XConnection > xMe( *this, UNO_QUERY ); + Reference< XDataDefinitionSupplier > xDefinitionsSupp( m_xDriver, UNO_QUERY_THROW ); + Reference< XTablesSupplier > xTablesSupp( xDefinitionsSupp->getDataDefinitionByConnection( xMe ), css::uno::UNO_SET_THROW ); + xTables.set( xTablesSupp->getTables(), css::uno::UNO_SET_THROW ); + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_NO_TABLE_CONTAINER)); + throw WrappedTargetException( sError ,*this, anyEx ); + } + + SAL_WARN_IF( !xTables.is(), "connectivity.hsqldb", "OHsqlConnection::impl_getTableContainer_throw: post condition not met!" ); + return xTables; + } + + //TODO: resource + + void OHsqlConnection::impl_checkExistingTable_throw( const OUString& _rTableName ) + { + bool bDoesExist = false; + try + { + Reference< XNameAccess > xTables( impl_getTableContainer_throw(), css::uno::UNO_SET_THROW ); + bDoesExist = xTables->hasByName( _rTableName ); + } + catch( const Exception& ) + { + // that's a serious error in impl_getTableContainer_throw, or hasByName, however, we're only + // allowed to throw an IllegalArgumentException ourself + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } + + if ( !bDoesExist ) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceStringWithSubstitution( + STR_NO_TABLENAME, + "$tablename$", _rTableName + )); + throw IllegalArgumentException( sError,*this, 0 ); + } // if ( !bDoesExist ) + } + + + bool OHsqlConnection::impl_isTextTable_nothrow( const OUString& _rTableName ) + { + bool bIsTextTable = false; + try + { + Reference< XConnection > xMe( *this, UNO_QUERY_THROW ); + + // split the fully qualified name + Reference< XDatabaseMetaData > xMetaData( xMe->getMetaData(), css::uno::UNO_SET_THROW ); + OUString sCatalog, sSchema, sName; + ::dbtools::qualifiedNameComponents( xMetaData, _rTableName, sCatalog, sSchema, sName, ::dbtools::EComposeRule::Complete ); + + // get the table information + OUStringBuffer sSQL; + sSQL.append( "SELECT HSQLDB_TYPE FROM INFORMATION_SCHEMA.SYSTEM_TABLES" ); + HTools::appendTableFilterCrit( sSQL, sCatalog, sSchema, sName, true ); + sSQL.append( " AND TABLE_TYPE = 'TABLE'" ); + + Reference< XStatement > xStatement( xMe->createStatement(), css::uno::UNO_SET_THROW ); + Reference< XResultSet > xTableHsqlType( xStatement->executeQuery( sSQL.makeStringAndClear() ), css::uno::UNO_SET_THROW ); + + if ( xTableHsqlType->next() ) // might not succeed in case of VIEWs + { + Reference< XRow > xValueAccess( xTableHsqlType, UNO_QUERY_THROW ); + OUString sTableType = xValueAccess->getString( 1 ); + bIsTextTable = sTableType == "TEXT"; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } + + return bIsTextTable; + } + + + Reference< XGraphic > OHsqlConnection::impl_getTextTableIcon_nothrow() + { + Reference< XGraphic > xGraphic; + try + { + // create a graphic provider + Reference< XGraphicProvider > xProvider; + if ( m_xContext.is() ) + xProvider.set( GraphicProvider::create(m_xContext) ); + + // assemble the image URL + OUString sImageURL = + // load the graphic from the global graphic repository + "private:graphicrepository/" + // the relative path within the images.zip + LINKED_TEXT_TABLE_IMAGE_RESOURCE; + + // ask the provider to obtain a graphic + Sequence< PropertyValue > aMediaProperties( 1 ); + aMediaProperties[0].Name = "URL"; + aMediaProperties[0].Value <<= sImageURL; + xGraphic = xProvider->queryGraphic( aMediaProperties ); + OSL_ENSURE( xGraphic.is(), "OHsqlConnection::impl_getTextTableIcon_nothrow: the provider did not give us a graphic object!" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } + return xGraphic; + } + +} // namespace connectivity::hsqldb + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HDriver.cxx b/connectivity/source/drivers/hsqldb/HDriver.cxx new file mode 100644 index 000000000..05b9478a9 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HDriver.cxx @@ -0,0 +1,870 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HDriver.hxx> +#include <hsqldb/HConnection.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <connectivity/dbexception.hxx> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/sdbc/DriverManager.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/embed/XTransactionBroadcaster.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <TConnection.hxx> +#include <hsqldb/HStorageMap.hxx> +#include <jvmfwk/framework.hxx> +#include <com/sun/star/reflection/XProxyFactory.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include "HTerminateListener.hxx" +#include <hsqldb/HCatalog.hxx> +#include <rtl/ustrbuf.hxx> +#include <osl/file.h> +#include <osl/process.h> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/types.hxx> +#include <unotools/confignode.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <tools/diagnose_ex.h> + +#include <memory> + + +namespace connectivity +{ + + using namespace hsqldb; + using namespace css::uno; + using namespace css::sdbc; + using namespace css::sdbcx; + using namespace css::beans; + using namespace css::frame; + using namespace css::lang; + using namespace css::embed; + using namespace css::io; + using namespace css::util; + using namespace css::reflection; + + namespace hsqldb + { + Reference< XInterface > ODriverDelegator_CreateInstance(const Reference< css::lang::XMultiServiceFactory >& _rxFac) + { + return *(new ODriverDelegator(comphelper::getComponentContext(_rxFac))); + } + } + + + ODriverDelegator::ODriverDelegator(const Reference< XComponentContext >& _rxContext) + : ODriverDelegator_BASE(m_aMutex) + ,m_xContext(_rxContext) + ,m_bInShutDownConnections(false) + { + } + + + ODriverDelegator::~ODriverDelegator() + { + try + { + ::comphelper::disposeComponent(m_xDriver); + } + catch(const Exception&) + { + } + } + + + void SAL_CALL ODriverDelegator::disposing() + { + ::osl::MutexGuard aGuard(m_aMutex); + + try + { + for (const auto& rConnection : m_aConnections) + { + Reference<XInterface > xTemp = rConnection.first.get(); + ::comphelper::disposeComponent(xTemp); + } + } + catch(Exception&) + { + // not interested in + } + m_aConnections.clear(); + TWeakPairVector().swap(m_aConnections); + + cppu::WeakComponentImplHelperBase::disposing(); + } + + Reference< XDriver > const & ODriverDelegator::loadDriver( ) + { + if ( !m_xDriver.is() ) + { + Reference<XDriverManager2> xDriverAccess = DriverManager::create( m_xContext ); + m_xDriver = xDriverAccess->getDriverByURL("jdbc:hsqldb:db"); + } + + return m_xDriver; + } + + + namespace + { + OUString lcl_getPermittedJavaMethods_nothrow( const Reference< XComponentContext >& _rxContext ) + { + OUString aConfigPath = + "/org.openoffice.Office.DataAccess/DriverSettings/" + + ODriverDelegator::getImplementationName_Static() + + "/PermittedJavaMethods"; + ::utl::OConfigurationTreeRoot aConfig( ::utl::OConfigurationTreeRoot::createWithComponentContext( + _rxContext, aConfigPath ) ); + + OUStringBuffer aPermittedMethods; + const Sequence< OUString > aNodeNames( aConfig.getNodeNames() ); + for ( auto const & nodeName : aNodeNames ) + { + OUString sPermittedMethod; + OSL_VERIFY( aConfig.getNodeValue( nodeName ) >>= sPermittedMethod ); + + if ( !aPermittedMethods.isEmpty() ) + aPermittedMethods.append( ';' ); + aPermittedMethods.append( sPermittedMethod ); + } + + return aPermittedMethods.makeStringAndClear(); + } + } + + + Reference< XConnection > SAL_CALL ODriverDelegator::connect( const OUString& url, const Sequence< PropertyValue >& info ) + { + Reference< XConnection > xConnection; + if ( acceptsURL(url) ) + { + Reference< XDriver > xDriver = loadDriver(); + if ( xDriver.is() ) + { + OUString sURL; + Reference<XStorage> xStorage; + const PropertyValue* pIter = info.getConstArray(); + const PropertyValue* pEnd = pIter + info.getLength(); + + for (;pIter != pEnd; ++pIter) + { + if ( pIter->Name == "Storage" ) + { + xStorage.set(pIter->Value,UNO_QUERY); + } + else if ( pIter->Name == "URL" ) + { + pIter->Value >>= sURL; + } + } + + if ( !xStorage.is() || sURL.isEmpty() ) + { + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_NO_STORAGE); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + + OUString sSystemPath; + osl_getSystemPathFromFileURL( sURL.pData, &sSystemPath.pData ); + if ( sURL.isEmpty() || sSystemPath.isEmpty() ) + { + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_INVALID_FILE_URL); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + + bool bIsNewDatabase = !xStorage->hasElements(); + + ::comphelper::NamedValueCollection aProperties; + + // properties for accessing the embedded storage + OUString sKey = StorageContainer::registerStorage( xStorage, sSystemPath ); + aProperties.put( "storage_key", sKey ); + aProperties.put( "storage_class_name", + OUString( "com.sun.star.sdbcx.comp.hsqldb.StorageAccess" ) ); + aProperties.put( "fileaccess_class_name", + OUString( "com.sun.star.sdbcx.comp.hsqldb.StorageFileAccess" ) ); + + // JDBC driver and driver's classpath + aProperties.put( "JavaDriverClass", + OUString( "org.hsqldb.jdbcDriver" ) ); + aProperties.put( "JavaDriverClassPath", + OUString( +#ifdef SYSTEM_HSQLDB + HSQLDB_JAR +#else + "vnd.sun.star.expand:$LO_JAVA_DIR/hsqldb.jar" +#endif + " vnd.sun.star.expand:$LO_JAVA_DIR/sdbc_hsqldb.jar" + ) ); + + // auto increment handling + aProperties.put( "IsAutoRetrievingEnabled", true ); + aProperties.put( "AutoRetrievingStatement", + OUString( "CALL IDENTITY()" ) ); + aProperties.put( "IgnoreDriverPrivileges", true ); + + // don't want to expose HSQLDB's schema capabilities which exist since 1.8.0RC10 + aProperties.put( "default_schema", + OUString( "true" ) ); + + // security: permitted Java classes + NamedValue aPermittedClasses( + "hsqldb.method_class_names", + makeAny( lcl_getPermittedJavaMethods_nothrow( m_xContext ) ) + ); + aProperties.put( "SystemProperties", Sequence< NamedValue >( &aPermittedClasses, 1 ) ); + + const OUString sProperties( "properties" ); + OUString sMessage; + try + { + if ( !bIsNewDatabase && xStorage->isStreamElement(sProperties) ) + { + Reference<XStream > xStream = xStorage->openStreamElement(sProperties,ElementModes::READ); + if ( xStream.is() ) + { + std::unique_ptr<SvStream> pStream( ::utl::UcbStreamHelper::CreateStream(xStream) ); + if (pStream) + { + OString sLine; + OString sVersionString; + while ( pStream->ReadLine(sLine) ) + { + if ( sLine.isEmpty() ) + continue; + sal_Int32 nIdx {0}; + const OString sIniKey = sLine.getToken(0, '=', nIdx); + const OString sValue = sLine.getToken(0, '=', nIdx); + if( sIniKey == "hsqldb.compatible_version" ) + { + sVersionString = sValue; + } + else + { + if (sIniKey == "version" && sVersionString.isEmpty()) + { + sVersionString = sValue; + } + } + } + if (!sVersionString.isEmpty()) + { + sal_Int32 nIdx {0}; + const sal_Int32 nMajor = sVersionString.getToken(0, '.', nIdx).toInt32(); + const sal_Int32 nMinor = sVersionString.getToken(0, '.', nIdx).toInt32(); + const sal_Int32 nMicro = sVersionString.getToken(0, '.', nIdx).toInt32(); + if ( nMajor > 1 + || ( nMajor == 1 && nMinor > 8 ) + || ( nMajor == 1 && nMinor == 8 && nMicro > 0 ) ) + { + ::connectivity::SharedResources aResources; + sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION); + } + } + } + } // if ( xStream.is() ) + ::comphelper::disposeComponent(xStream); + } + } + catch(Exception&) + { + } + if ( !sMessage.isEmpty() ) + { + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + + // readonly? + Reference<XPropertySet> xProp(xStorage,UNO_QUERY); + if ( xProp.is() ) + { + sal_Int32 nMode = 0; + xProp->getPropertyValue("OpenMode") >>= nMode; + if ( (nMode & ElementModes::WRITE) != ElementModes::WRITE ) + { + aProperties.put( "readonly", OUString( "true" ) ); + } + } + + Sequence< PropertyValue > aConnectionArgs; + aProperties >>= aConnectionArgs; + OUString sConnectURL = "jdbc:hsqldb:" + sSystemPath; + Reference<XConnection> xOrig; + try + { + xOrig = xDriver->connect( sConnectURL, aConnectionArgs ); + } + catch(const Exception&) + { + StorageContainer::revokeStorage(sKey,nullptr); + throw; + } + + // if the storage is completely empty, then we just created a new HSQLDB + // In this case, do some initializations. + if ( bIsNewDatabase && xOrig.is() ) + onConnectedNewDatabase( xOrig ); + + if ( xOrig.is() ) + { + // now we have to set the URL to get the correct answer for metadata()->getURL() + auto pMetaConnection = comphelper::getUnoTunnelImplementation<OMetaConnection>(xOrig); + if ( pMetaConnection ) + pMetaConnection->setURL(url); + + Reference<XComponent> xComp(xOrig,UNO_QUERY); + if ( xComp.is() ) + xComp->addEventListener(this); + + // we want to close all connections when the office shuts down + static Reference< XTerminateListener> s_xTerminateListener = [&]() + { + Reference< XDesktop2 > xDesktop = Desktop::create( m_xContext ); + + auto tmp = new OConnectionController(this); + xDesktop->addTerminateListener(tmp); + return tmp; + }(); + Reference< XComponent> xIfc = new OHsqlConnection( this, xOrig, m_xContext ); + xConnection.set(xIfc,UNO_QUERY); + m_aConnections.push_back(TWeakPair(WeakReferenceHelper(xOrig),TWeakConnectionPair(sKey,TWeakRefPair(WeakReferenceHelper(xConnection),WeakReferenceHelper())))); + + Reference<XTransactionBroadcaster> xBroad(xStorage,UNO_QUERY); + if ( xBroad.is() ) + { + Reference<XTransactionListener> xListener(*this,UNO_QUERY); + xBroad->addTransactionListener(xListener); + } + } + } + } + return xConnection; + } + + + sal_Bool SAL_CALL ODriverDelegator::acceptsURL( const OUString& url ) + { + bool bEnabled = false; + javaFrameworkError e = jfw_getEnabled(&bEnabled); + switch (e) { + case JFW_E_NONE: + break; + case JFW_E_DIRECT_MODE: + SAL_INFO( + "connectivity.hsqldb", + "jfw_getEnabled: JFW_E_DIRECT_MODE, assuming true"); + bEnabled = true; + break; + default: + SAL_WARN( + "connectivity.hsqldb", "jfw_getEnabled: error code " << +e); + break; + } + return bEnabled && url == "sdbc:embedded:hsqldb"; + } + + + Sequence< DriverPropertyInfo > SAL_CALL ODriverDelegator::getPropertyInfo( const OUString& url, const Sequence< PropertyValue >& /*info*/ ) + { + if ( !acceptsURL(url) ) + return Sequence< DriverPropertyInfo >(); + std::vector< DriverPropertyInfo > aDriverInfo; + aDriverInfo.push_back(DriverPropertyInfo( + "Storage" + ,"Defines the storage where the database will be stored." + ,true + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "URL" + ,"Defines the url of the data source." + ,true + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "AutoRetrievingStatement" + ,"Defines the statement which will be executed to retrieve auto increment values." + ,false + ,"CALL IDENTITY()" + ,Sequence< OUString >()) + ); + return Sequence< DriverPropertyInfo >(aDriverInfo.data(),aDriverInfo.size()); + } + + + sal_Int32 SAL_CALL ODriverDelegator::getMajorVersion( ) + { + return 1; + } + + + sal_Int32 SAL_CALL ODriverDelegator::getMinorVersion( ) + { + return 0; + } + + + Reference< XTablesSupplier > SAL_CALL ODriverDelegator::getDataDefinitionByConnection( const Reference< XConnection >& connection ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(ODriverDelegator_BASE::rBHelper.bDisposed); + + Reference< XTablesSupplier > xTab; + + TWeakPairVector::iterator i = std::find_if(m_aConnections.begin(), m_aConnections.end(), + [&connection](const TWeakPairVector::value_type& rConnection) { + return rConnection.second.second.first.get() == connection.get(); }); + if (i != m_aConnections.end()) + { + xTab.set(i->second.second.second,UNO_QUERY); + if ( !xTab.is() ) + { + xTab = new OHCatalog(connection); + i->second.second.second = WeakReferenceHelper(xTab); + } + } + + return xTab; + } + + + Reference< XTablesSupplier > SAL_CALL ODriverDelegator::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)); + } + + // XServiceInfo + + + OUString ODriverDelegator::getImplementationName_Static( ) + { + return "com.sun.star.sdbcx.comp.hsqldb.Driver"; + } + + Sequence< OUString > ODriverDelegator::getSupportedServiceNames_Static( ) + { + return { "com.sun.star.sdbc.Driver", "com.sun.star.sdbcx.Driver" }; + } + + OUString SAL_CALL ODriverDelegator::getImplementationName( ) + { + return getImplementationName_Static(); + } + + sal_Bool SAL_CALL ODriverDelegator::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + Sequence< OUString > SAL_CALL ODriverDelegator::getSupportedServiceNames( ) + { + return getSupportedServiceNames_Static(); + } + + void SAL_CALL ODriverDelegator::createCatalog( const Sequence< PropertyValue >& /*info*/ ) + { + ::dbtools::throwFeatureNotImplementedSQLException( "XCreateCatalog::createCatalog", *this ); + } + + void ODriverDelegator::shutdownConnection(const TWeakPairVector::iterator& _aIter ) + { + OSL_ENSURE(m_aConnections.end() != _aIter,"Iterator equals .end()"); + bool bLastOne = true; + try + { + Reference<XConnection> _xConnection(_aIter->first.get(),UNO_QUERY); + + if ( _xConnection.is() ) + { + Reference<XStatement> xStmt = _xConnection->createStatement(); + if ( xStmt.is() ) + { + Reference<XResultSet> xRes = xStmt->executeQuery("SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_SESSIONS WHERE USER_NAME ='SA'"); + Reference<XRow> xRow(xRes,UNO_QUERY); + if ( xRow.is() && xRes->next() ) + bLastOne = xRow->getInt(1) == 1; + if ( bLastOne ) + xStmt->execute("SHUTDOWN"); + } + } + } + catch(Exception&) + { + } + if ( bLastOne ) + { + // Reference<XTransactionListener> xListener(*this,UNO_QUERY); + // a shutdown should commit all changes to the db files + StorageContainer::revokeStorage(_aIter->second.first,nullptr); + } + if ( !m_bInShutDownConnections ) + m_aConnections.erase(_aIter); + } + + void SAL_CALL ODriverDelegator::disposing( const css::lang::EventObject& Source ) + { + ::osl::MutexGuard aGuard(m_aMutex); + Reference<XConnection> xCon(Source.Source,UNO_QUERY); + if ( xCon.is() ) + { + TWeakPairVector::iterator i = std::find_if(m_aConnections.begin(), m_aConnections.end(), + [&xCon](const TWeakPairVector::value_type& rConnection) { return rConnection.first.get() == xCon.get(); }); + + if (i != m_aConnections.end()) + shutdownConnection(i); + } + else + { + Reference< XStorage> xStorage(Source.Source,UNO_QUERY); + if ( xStorage.is() ) + { + OUString sKey = StorageContainer::getRegisteredKey(xStorage); + TWeakPairVector::iterator i = std::find_if(m_aConnections.begin(),m_aConnections.end(), + [&sKey] (const TWeakPairVector::value_type& conn) { + return conn.second.first == sKey; + }); + + if ( i != m_aConnections.end() ) + shutdownConnection(i); + } + } + } + + void ODriverDelegator::shutdownConnections() + { + m_bInShutDownConnections = true; + for (const auto& rConnection : m_aConnections) + { + try + { + Reference<XConnection> xCon(rConnection.first,UNO_QUERY); + ::comphelper::disposeComponent(xCon); + } + catch(Exception&) + { + } + } + m_aConnections.clear(); + m_bInShutDownConnections = true; + } + + void ODriverDelegator::flushConnections() + { + for (const auto& rConnection : m_aConnections) + { + try + { + Reference<XFlushable> xCon(rConnection.second.second.first.get(),UNO_QUERY); + if (xCon.is()) + xCon->flush(); + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } + } + } + + void SAL_CALL ODriverDelegator::preCommit( const css::lang::EventObject& aEvent ) + { + ::osl::MutexGuard aGuard(m_aMutex); + + Reference< XStorage> xStorage(aEvent.Source,UNO_QUERY); + OUString sKey = StorageContainer::getRegisteredKey(xStorage); + if ( sKey.isEmpty() ) + return; + + TWeakPairVector::const_iterator i = std::find_if(m_aConnections.begin(), m_aConnections.end(), + [&sKey] (const TWeakPairVector::value_type& conn) { + return conn.second.first == sKey; + }); + + OSL_ENSURE( i != m_aConnections.end(), "ODriverDelegator::preCommit: they're committing a storage which I do not know!" ); + if ( i == m_aConnections.end() ) + return; + + try + { + Reference<XConnection> xConnection(i->first,UNO_QUERY); + if ( xConnection.is() ) + { + Reference< XStatement> xStmt = xConnection->createStatement(); + OSL_ENSURE( xStmt.is(), "ODriverDelegator::preCommit: no statement!" ); + if ( xStmt.is() ) + xStmt->execute( "SET WRITE_DELAY 0" ); + + bool bPreviousAutoCommit = xConnection->getAutoCommit(); + xConnection->setAutoCommit( false ); + xConnection->commit(); + xConnection->setAutoCommit( bPreviousAutoCommit ); + + if ( xStmt.is() ) + xStmt->execute( "SET WRITE_DELAY 60" ); + } + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION( "connectivity.hsqldb", "ODriverDelegator::preCommit" ); + } + } + + void SAL_CALL ODriverDelegator::commited( const css::lang::EventObject& /*aEvent*/ ) + { + } + + void SAL_CALL ODriverDelegator::preRevert( const css::lang::EventObject& /*aEvent*/ ) + { + } + + void SAL_CALL ODriverDelegator::reverted( const css::lang::EventObject& /*aEvent*/ ) + { + } + + namespace + { + + const char* lcl_getCollationForLocale( const OUString& _rLocaleString, bool _bAcceptCountryMismatch = false ) + { + static const char* pTranslations[] = + { + "af-ZA", "Afrikaans", + "am-ET", "Amharic", + "ar", "Arabic", + "as-IN", "Assamese", + "az-AZ", "Azerbaijani_Latin", + "az-cyrillic", "Azerbaijani_Cyrillic", + "be-BY", "Belarusian", + "bg-BG", "Bulgarian", + "bn-IN", "Bengali", + "bo-CN", "Tibetan", + "bs-BA", "Bosnian", + "ca-ES", "Catalan", + "cs-CZ", "Czech", + "cy-GB", "Welsh", + "da-DK", "Danish", + "de-DE", "German", + "el-GR", "Greek", + "en-US", "Latin1_General", + "es-ES", "Spanish", + "et-EE", "Estonian", + "eu", "Basque", + "fi-FI", "Finnish", + "fr-FR", "French", + "gn-PY", "Guarani", + "gu-IN", "Gujarati", + "ha-NG", "Hausa", + "he-IL", "Hebrew", + "hi-IN", "Hindi", + "hr-HR", "Croatian", + "hu-HU", "Hungarian", + "hy-AM", "Armenian", + "id-ID", "Indonesian", + "ig-NG", "Igbo", + "is-IS", "Icelandic", + "it-IT", "Italian", + "iu-CA", "Inuktitut", + "ja-JP", "Japanese", + "ka-GE", "Georgian", + "kk-KZ", "Kazakh", + "km-KH", "Khmer", + "kn-IN", "Kannada", + "ko-KR", "Korean", + "kok-IN", "Konkani", + "ks", "Kashmiri", + "ky-KG", "Kirghiz", + "lo-LA", "Lao", + "lt-LT", "Lithuanian", + "lv-LV", "Latvian", + "mi-NZ", "Maori", + "mk-MK", "Macedonian", + "ml-IN", "Malayalam", + "mn-MN", "Mongolian", + "mni-IN", "Manipuri", + "mr-IN", "Marathi", + "ms-MY", "Malay", + "mt-MT", "Maltese", + "my-MM", "Burmese", + "nb-NO", "Danish_Norwegian", + "ne-NP", "Nepali", + "nl-NL", "Dutch", + "nn-NO", "Norwegian", + "or-IN", "Odia", + "pa-IN", "Punjabi", + "pl-PL", "Polish", + "ps-AF", "Pashto", + "pt-PT", "Portuguese", + "ro-RO", "Romanian", + "ru-RU", "Russian", + "sa-IN", "Sanskrit", + "sd-IN", "Sindhi", + "sk-SK", "Slovak", + "sl-SI", "Slovenian", + "so-SO", "Somali", + "sq-AL", "Albanian", + "sr-YU", "Serbian_Cyrillic", + "sv-SE", "Swedish", + "sw-KE", "Swahili", + "ta-IN", "Tamil", + "te-IN", "Telugu", + "tg-TJ", "Tajik", + "th-TH", "Thai", + "tk-TM", "Turkmen", + "tn-BW", "Tswana", + "tr-TR", "Turkish", + "tt-RU", "Tatar", + "uk-UA", "Ukrainian", + "ur-PK", "Urdu", + "uz-UZ", "Uzbek_Latin", + "ven-ZA", "Venda", + "vi-VN", "Vietnamese", + "yo-NG", "Yoruba", + "zh-CN", "Chinese", + "zu-ZA", "Zulu", + nullptr, nullptr + }; + + OUString sLocaleString( _rLocaleString ); + char nCompareTermination = 0; + + if ( _bAcceptCountryMismatch ) + { + // strip the country part from the compare string + sal_Int32 nCountrySep = sLocaleString.indexOf( '-' ); + if ( nCountrySep > -1 ) + sLocaleString = sLocaleString.copy( 0, nCountrySep ); + + // the entries in the translation table are compared until the + // - character only, not until the terminating 0 + nCompareTermination = '-'; + } + + const char** pLookup = pTranslations; + for ( ; *pLookup; pLookup +=2 ) + { + sal_Int32 nCompareUntil = 0; + while ( (*pLookup)[ nCompareUntil ] != nCompareTermination && (*pLookup)[ nCompareUntil ] != 0 ) + ++nCompareUntil; + + if ( sLocaleString.equalsAsciiL( *pLookup, nCompareUntil ) ) + return *( pLookup + 1 ); + } + + if ( !_bAcceptCountryMismatch ) + // second round, this time without matching the country + return lcl_getCollationForLocale( _rLocaleString, true ); + + OSL_FAIL( "lcl_getCollationForLocale: unknown locale string, falling back to Latin1_General!" ); + return "Latin1_General"; + } + + + OUString lcl_getSystemLocale( const Reference< XComponentContext >& _rxContext ) + { + OUString sLocaleString = "en-US"; + try + { + + Reference< XMultiServiceFactory > xConfigProvider( + css::configuration::theDefaultProvider::get( _rxContext ) ); + + + // arguments for creating the config access + Sequence<Any> aArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(OUString("/org.openoffice.Setup/L10N" ))}, // the path to the node to open + {"depth", Any(sal_Int32(-1))}, // the depth: -1 means unlimited + })); + // create the access + Reference< XPropertySet > xNode( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments ), + UNO_QUERY ); + OSL_ENSURE( xNode.is(), "lcl_getSystemLocale: invalid access returned (should throw an exception instead)!" ); + + + // ask for the system locale setting + if ( xNode.is() ) + xNode->getPropertyValue("ooSetupSystemLocale") >>= sLocaleString; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.hsqldb", "lcl_getSystemLocale" ); + } + if ( sLocaleString.isEmpty() ) + { + rtl_Locale* pProcessLocale = nullptr; + osl_getProcessLocale( &pProcessLocale ); + sLocaleString = LanguageTag( *pProcessLocale).getBcp47(); + } + return sLocaleString; + } + } + + void ODriverDelegator::onConnectedNewDatabase( const Reference< XConnection >& _rxConnection ) + { + try + { + Reference< XStatement > xStatement = _rxConnection->createStatement(); + OSL_ENSURE( xStatement.is(), "ODriverDelegator::onConnectedNewDatabase: could not create a statement!" ); + if ( xStatement.is() ) + { + OUStringBuffer aStatement; + aStatement.append( "SET DATABASE COLLATION \"" ); + aStatement.appendAscii( lcl_getCollationForLocale( lcl_getSystemLocale( m_xContext ) ) ); + aStatement.append( "\"" ); + + xStatement->execute( aStatement.makeStringAndClear() ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "connectivity.hsqldb", "ODriverDelegator::onConnectedNewDatabase" ); + } + } + + +} // namespace connectivity + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HStorageAccess.cxx b/connectivity/source/drivers/hsqldb/HStorageAccess.cxx new file mode 100644 index 000000000..60e3fd0ee --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HStorageAccess.cxx @@ -0,0 +1,508 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HStorageAccess.hxx> +#include <com/sun/star/embed/XStorage.hpp> +#include <hsqldb/HStorageMap.hxx> +#include "accesslog.hxx" +#include <osl/diagnose.h> + +#include <string.h> + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::connectivity::hsqldb; + +#define ThrowException(env, type, msg) { \ + env->ThrowNew(env->FindClass(type), msg); } + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: openStream + * Signature: (Ljava/lang/String;Ljava/lang/String;I)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_openStream + (JNIEnv * env, jobject /*obj_this*/,jstring name, jstring key, jint mode) +{ +#ifdef HSQLDB_DBG + { + OperationLogFile( env, name, "data" ).logOperation( "openStream" ); + LogFile( env, name, "data" ).create(); + } +#endif + + StorageContainer::registerStream(env,name,key,mode); +} + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: close + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_close + (JNIEnv * env, jobject /*obj_this*/,jstring name, jstring key) +{ +#ifdef HSQLDB_DBG + { + OUString sKey = StorageContainer::jstring2ustring(env,key); + OUString sName = StorageContainer::jstring2ustring(env,name); + } +#endif + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XOutputStream> xFlush = pHelper ? pHelper->getOutputStream() : Reference< XOutputStream>(); + if ( xFlush.is() ) + try + { + xFlush->flush(); + } + catch(const Exception&) + { + OSL_FAIL( "NativeStorageAccess::close: caught an exception while flushing!" ); + } +#ifdef HSQLDB_DBG + { + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "close" ); + aOpLog.close(); + + LogFile aDataLog( env, name, "data" ); + aDataLog.close(); + } +#endif + + StorageContainer::revokeStream(env,name,key); +} + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: getFilePointer + * Signature: (Ljava/lang/String;Ljava/lang/String;)J + */ +extern "C" SAL_JNI_EXPORT jlong JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_getFilePointer + (JNIEnv * env, jobject /*obj_this*/,jstring name, jstring key) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "getFilePointer" ); +#endif + + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + OSL_ENSURE(pHelper.get(),"No stream helper!"); + + jlong nReturn = pHelper ? pHelper->getSeek()->getPosition() : jlong(0); +#ifdef HSQLDB_DBG + aOpLog.logReturn( nReturn ); +#endif + return nReturn; +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: length + * Signature: (Ljava/lang/String;Ljava/lang/String;)J + */ +extern "C" SAL_JNI_EXPORT jlong JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_length + (JNIEnv * env, jobject /*obj_this*/,jstring name, jstring key) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "length" ); +#endif + + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + OSL_ENSURE(pHelper.get(),"No stream helper!"); + + jlong nReturn = pHelper ? pHelper->getSeek()->getLength() :jlong(0); +#ifdef HSQLDB_DBG + aOpLog.logReturn( nReturn ); +#endif + return nReturn; +} + + +jint read_from_storage_stream( JNIEnv * env, jstring name, jstring key ) +{ + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XInputStream> xIn = pHelper ? pHelper->getInputStream() : Reference< XInputStream>(); + OSL_ENSURE(xIn.is(),"Input stream is NULL!"); + if ( xIn.is() ) + { + Sequence< ::sal_Int8 > aData(1); + sal_Int32 nBytesRead = -1; + try + { + nBytesRead = xIn->readBytes(aData,1); + } + catch(const Exception& e) + { + StorageContainer::throwJavaException(e,env); + return -1; + + } + if (nBytesRead <= 0) + { + return -1; + } + else + { + return static_cast<unsigned char>(aData[0]); + } + } + return -1; +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: read + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ +extern "C" SAL_JNI_EXPORT jint JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_read__Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv* env, jobject /*obj_this*/, jstring name, jstring key) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "read" ); + + DataLogFile aDataLog( env, name, "data" ); + return read_from_storage_stream( env, obj_this, name, key, &aDataLog ); +#else + return read_from_storage_stream( env, name, key ); +#endif +} + + +jint read_from_storage_stream_into_buffer( JNIEnv * env, jstring name, jstring key, jbyteArray buffer, jint off, jint len ) +{ +#ifdef HSQLDB_DBG + { + OUString sKey = StorageContainer::jstring2ustring(env,key); + OUString sName = StorageContainer::jstring2ustring(env,name); + } +#endif + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XInputStream> xIn = pHelper ? pHelper->getInputStream() : Reference< XInputStream>(); + OSL_ENSURE(xIn.is(),"Input stream is NULL!"); + if ( xIn.is() ) + { + jsize nLen = env->GetArrayLength(buffer); + if ( nLen < len || len <= 0 ) + { + ThrowException( env, + "java/io/IOException", + "len is greater or equal to the buffer size"); + return -1; + } + sal_Int32 nBytesRead = -1; + + Sequence< ::sal_Int8 > aData(nLen); + try + { + nBytesRead = xIn->readBytes(aData, len); + } + catch(const Exception& e) + { + StorageContainer::throwJavaException(e,env); + return -1; + } + + if (nBytesRead <= 0) + return -1; + env->SetByteArrayRegion(buffer,off,nBytesRead,reinterpret_cast<jbyte*>(&aData[0])); + + return nBytesRead; + } + ThrowException( env, + "java/io/IOException", + "Stream is not valid"); + return -1; +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: read + * Signature: (Ljava/lang/String;Ljava/lang/String;[BII)I + */ +extern "C" SAL_JNI_EXPORT jint JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_read__Ljava_lang_String_2Ljava_lang_String_2_3BII + (JNIEnv * env, jobject obj_this,jstring name, jstring key, jbyteArray buffer, jint off, jint len) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "read( byte[], int, int )" ); + + DataLogFile aDataLog( env, name, "data" ); + return read_from_storage_stream_into_buffer( env, obj_this, name, key, buffer, off, len, &aDataLog ); +#else + (void)obj_this; + return read_from_storage_stream_into_buffer( env, name, key, buffer, off, len ); +#endif +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: readInt + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ +extern "C" SAL_JNI_EXPORT jint JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_readInt + (JNIEnv * env, jobject /*obj_this*/,jstring name, jstring key) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "readInt" ); +#endif + + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XInputStream> xIn = pHelper ? pHelper->getInputStream() : Reference< XInputStream>(); + OSL_ENSURE(xIn.is(),"Input stream is NULL!"); + if ( xIn.is() ) + { + Sequence< ::sal_Int8 > aData(4); + sal_Int32 nBytesRead = -1; + try + { + nBytesRead = xIn->readBytes(aData, 4); + } + catch(const Exception& e) + { + StorageContainer::throwJavaException(e,env); + return -1; + } + + if ( nBytesRead != 4 ) { + ThrowException( env, + "java/io/IOException", + "Bytes read != 4"); + return -1; + } + + Sequence< sal_Int32 > ch(4); + for(sal_Int32 i = 0;i < 4; ++i) + { + ch[i] = static_cast<unsigned char>(aData[i]); + } + + if ((ch[0] | ch[1] | ch[2] | ch[3]) < 0) + { + ThrowException( env, + "java/io/IOException", + "One byte is < 0"); + return -1; + } + jint nRet = (ch[0] << 24) + (ch[1] << 16) + (ch[2] << 8) + (ch[3] << 0); +#ifdef HSQLDB_DBG + DataLogFile aDataLog( env, name, "data" ); + aDataLog.write( nRet ); + + aOpLog.logReturn( nRet ); +#endif + return nRet; + } + ThrowException( env, + "java/io/IOException", + "No InputStream"); + return -1; +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: seek + * Signature: (Ljava/lang/String;Ljava/lang/String;J)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_seek + (JNIEnv * env, jobject /*obj_this*/,jstring name, jstring key, jlong position) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "seek", position ); +#endif + + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XSeekable> xSeek = pHelper ? pHelper->getSeek() : Reference< XSeekable>(); + + OSL_ENSURE(xSeek.is(),"No Seekable stream!"); + if ( !xSeek.is() ) + return; + +#ifdef HSQLDB_DBG + DataLogFile aDataLog( env, name, "data" ); +#endif + + ::sal_Int64 nLen = xSeek->getLength(); + if ( nLen < position) + { + static const ::sal_Int64 BUFFER_SIZE = 9192; + #ifdef HSQLDB_DBG + aDataLog.seek( nLen ); + #endif + xSeek->seek(nLen); + Reference< XOutputStream> xOut = pHelper->getOutputStream(); + OSL_ENSURE(xOut.is(),"No output stream!"); + + ::sal_Int64 diff = position - nLen; + sal_Int32 n; + while( diff != 0 ) + { + if ( BUFFER_SIZE < diff ) + { + n = static_cast<sal_Int32>(BUFFER_SIZE); + diff = diff - BUFFER_SIZE; + } + else + { + n = static_cast<sal_Int32>(diff); + diff = 0; + } + Sequence< ::sal_Int8 > aData(n); + memset(aData.getArray(),0,n); + xOut->writeBytes(aData); + #ifdef HSQLDB_DBG + aDataLog.write( aData.getConstArray(), n ); + #endif + } + } + xSeek->seek(position); + OSL_ENSURE(xSeek->getPosition() == position,"Wrong position after seeking the stream"); + +#ifdef HSQLDB_DBG + aDataLog.seek( position ); + OSL_ENSURE( xSeek->getPosition() == aDataLog.tell(), "Wrong position after seeking the stream" ); +#endif +} + + +void write_to_storage_stream_from_buffer( JNIEnv* env, jstring name, jstring key, jbyteArray buffer, jint off, jint len ) +{ + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XOutputStream> xOut = pHelper ? pHelper->getOutputStream() : Reference< XOutputStream>(); + OSL_ENSURE(xOut.is(),"Stream is NULL"); + + try + { + if ( xOut.is() ) + { + jbyte *buf = env->GetByteArrayElements(buffer,nullptr); + if (env->ExceptionCheck()) + { + env->ExceptionClear(); + OSL_FAIL("ExceptionClear"); + } + OSL_ENSURE(buf,"buf is NULL"); + if ( buf && len > 0 && len <= env->GetArrayLength(buffer)) + { + Sequence< ::sal_Int8 > aData(reinterpret_cast<sal_Int8 *>(buf + off),len); + env->ReleaseByteArrayElements(buffer, buf, JNI_ABORT); + xOut->writeBytes(aData); + } + } + else + { + ThrowException( env, + "java/io/IOException", + "No OutputStream"); + } + } + catch(const Exception& e) + { + OSL_FAIL("Exception caught! : write [BII)V"); + StorageContainer::throwJavaException(e,env); + } +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: write + * Signature: (Ljava/lang/String;Ljava/lang/String;[BII)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_write + (JNIEnv * env, jobject obj_this,jstring name, jstring key, jbyteArray buffer, jint off, jint len) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "write( byte[], int, int )" ); + + DataLogFile aDataLog( env, name, "data" ); + write_to_storage_stream_from_buffer( env, obj_this, name, key, buffer, off, len, &aDataLog ); +#else + (void)obj_this; + write_to_storage_stream_from_buffer( env, name, key, buffer, off, len ); +#endif +} + + +void write_to_storage_stream( JNIEnv* env, jstring name, jstring key, jint v ) +{ + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XOutputStream> xOut = pHelper ? pHelper->getOutputStream() : Reference< XOutputStream>(); + OSL_ENSURE(xOut.is(),"Stream is NULL"); + try + { + if ( xOut.is() ) + { + Sequence< ::sal_Int8 > oneByte(4); + oneByte[0] = static_cast<sal_Int8>((v >> 24) & 0xFF); + oneByte[1] = static_cast<sal_Int8>((v >> 16) & 0xFF); + oneByte[2] = static_cast<sal_Int8>((v >> 8) & 0xFF); + oneByte[3] = static_cast<sal_Int8>((v >> 0) & 0xFF); + + xOut->writeBytes(oneByte); + } + else + { + ThrowException( env, + "java/io/IOException", + "No OutputStream"); + } + } + catch(const Exception& e) + { + OSL_FAIL("Exception caught! : writeBytes(aData);"); + StorageContainer::throwJavaException(e,env); + } +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess + * Method: writeInt + * Signature: (Ljava/lang/String;Ljava/lang/String;I)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_NativeStorageAccess_writeInt + (JNIEnv * env, jobject obj_this,jstring name, jstring key, jint v) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "data" ); + aOpLog.logOperation( "writeInt" ); + + DataLogFile aDataLog( env, name, "data" ); + write_to_storage_stream( env, name, key, v, &aDataLog ); +#else + (void)obj_this; + write_to_storage_stream( env, name, key, v ); +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HStorageMap.cxx b/connectivity/source/drivers/hsqldb/HStorageMap.cxx new file mode 100644 index 000000000..d10ee29a6 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HStorageMap.cxx @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HStorageMap.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/embed/XTransactionBroadcaster.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <uno/mapping.hxx> +#include <algorithm> + +namespace connectivity::hsqldb +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::embed; + using namespace ::com::sun::star::io; + + StreamHelper::StreamHelper(const Reference< XStream>& _xStream) + : m_xStream(_xStream) + { + } + + StreamHelper::~StreamHelper() + { + try + { + m_xStream.clear(); + m_xSeek.clear(); + if ( m_xInputStream.is() ) + { + m_xInputStream->closeInput(); + m_xInputStream.clear(); + } + // this is done implicitly by the closing of the input stream + else if ( m_xOutputStream.is() ) + { + m_xOutputStream->closeOutput(); + try + { + ::comphelper::disposeComponent(m_xOutputStream); + } + catch(const DisposedException&) + { + } + catch(const Exception&) + { + OSL_FAIL("Could not dispose OutputStream"); + } + m_xOutputStream.clear(); + } + } + catch(const Exception&) + { + OSL_FAIL("Exception caught!"); + } + } + + Reference< XInputStream> const & StreamHelper::getInputStream() + { + if ( !m_xInputStream.is() ) + m_xInputStream = m_xStream->getInputStream(); + return m_xInputStream; + } + + Reference< XOutputStream> const & StreamHelper::getOutputStream() + { + if ( !m_xOutputStream.is() ) + m_xOutputStream = m_xStream->getOutputStream(); + return m_xOutputStream; + } + + Reference< XSeekable> const & StreamHelper::getSeek() + { + if ( !m_xSeek.is() ) + m_xSeek.set(m_xStream,UNO_QUERY); + return m_xSeek; + } + + css::uno::Reference<css::embed::XStorage> StorageData::mapStorage() + const + { + css::uno::Environment env(css::uno::Environment::getCurrent()); + if (!(env.is() && storageEnvironment.is())) { + throw css::uno::RuntimeException("cannot get environments"); + } + if (env.get() == storageEnvironment.get()) { + return storage; + } else { + css::uno::Mapping map(storageEnvironment, env); + if (!map.is()) { + throw css::uno::RuntimeException("cannot get mapping"); + } + css::uno::Reference<css::embed::XStorage> mapped; + map.mapInterface( + reinterpret_cast<void **>(&mapped), storage.get(), + cppu::UnoType<css::embed::XStorage>::get()); + return mapped; + } + } + + static TStorages& lcl_getStorageMap() + { + static TStorages s_aMap; + return s_aMap; + } + + static OUString lcl_getNextCount() + { + static sal_Int32 s_nCount = 0; + return OUString::number(s_nCount++); + } + + OUString StorageContainer::removeURLPrefix(const OUString& _sURL,const OUString& _sFileURL) + { + return _sURL.copy(_sFileURL.getLength()+1); + } + + OUString StorageContainer::removeOldURLPrefix(const OUString& _sURL) + { + OUString sRet = _sURL; +#if defined(_WIN32) + sal_Int32 nIndex = sRet.lastIndexOf('\\'); +#else + sal_Int32 nIndex = sRet.lastIndexOf('/'); +#endif + if ( nIndex != -1 ) + { + sRet = _sURL.copy(nIndex+1); + } + return sRet; + + } + /*****************************************************************************/ + /* convert jstring to rtl_uString */ + + OUString StorageContainer::jstring2ustring(JNIEnv * env, jstring jstr) + { + if (env->ExceptionCheck()) + { + env->ExceptionClear(); + OSL_FAIL("ExceptionClear"); + } + OUString aStr; + if ( jstr ) + { + jboolean bCopy(true); + const jchar* pChar = env->GetStringChars(jstr,&bCopy); + jsize len = env->GetStringLength(jstr); + aStr = OUString( + reinterpret_cast<sal_Unicode const *>(pChar), len); + + if(bCopy) + env->ReleaseStringChars(jstr,pChar); + } + + if (env->ExceptionCheck()) + { + env->ExceptionClear(); + OSL_FAIL("ExceptionClear"); + } + return aStr; + } + + + OUString StorageContainer::registerStorage(const Reference< XStorage>& _xStorage,const OUString& _sURL) + { + OSL_ENSURE(_xStorage.is(),"Storage is NULL!"); + TStorages& rMap = lcl_getStorageMap(); + // check if the storage is already in our map + TStorages::const_iterator aFind = std::find_if(rMap.begin(),rMap.end(), + [&_xStorage] (const TStorages::value_type& storage) { + return storage.second.mapStorage() == _xStorage; + }); + + if ( aFind == rMap.end() ) + { + aFind = rMap.insert(TStorages::value_type(lcl_getNextCount(), {_xStorage, css::uno::Environment::getCurrent(), _sURL, TStreamMap()})).first; + } + + return aFind->first; + } + + TStorages::mapped_type StorageContainer::getRegisteredStorage(const OUString& _sKey) + { + TStorages::mapped_type aRet; + TStorages& rMap = lcl_getStorageMap(); + TStorages::const_iterator aFind = rMap.find(_sKey); + OSL_ENSURE(aFind != rMap.end(),"Storage could not be found in list!"); + if ( aFind != rMap.end() ) + aRet = aFind->second; + + return aRet; + } + + OUString StorageContainer::getRegisteredKey(const Reference< XStorage>& _xStorage) + { + OUString sKey; + OSL_ENSURE(_xStorage.is(),"Storage is NULL!"); + TStorages& rMap = lcl_getStorageMap(); + // check if the storage is already in our map + TStorages::const_iterator aFind = std::find_if(rMap.begin(),rMap.end(), + [&_xStorage] (const TStorages::value_type& storage) { + return storage.second.mapStorage() == _xStorage; + }); + + if ( aFind != rMap.end() ) + sKey = aFind->first; + return sKey; + } + + void StorageContainer::revokeStorage(const OUString& _sKey,const Reference<XTransactionListener>& _xListener) + { + TStorages& rMap = lcl_getStorageMap(); + TStorages::iterator aFind = rMap.find(_sKey); + if ( aFind == rMap.end() ) + return; + + try + { + if ( _xListener.is() ) + { + Reference<XTransactionBroadcaster> xBroad(aFind->second.mapStorage(),UNO_QUERY); + if ( xBroad.is() ) + xBroad->removeTransactionListener(_xListener); + Reference<XTransactedObject> xTrans(aFind->second.mapStorage(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->commit(); + } + } + catch(const Exception&) + { + } + rMap.erase(aFind); + } + + TStreamMap::mapped_type StorageContainer::registerStream(JNIEnv * env,jstring name, jstring key,sal_Int32 _nMode) + { + TStreamMap::mapped_type pHelper; + TStorages& rMap = lcl_getStorageMap(); + OUString sKey = jstring2ustring(env,key); + TStorages::iterator aFind = rMap.find(sKey); + OSL_ENSURE(aFind != rMap.end(),"Storage could not be found in list!"); + if ( aFind != rMap.end() ) + { + TStorages::mapped_type aStoragePair = StorageContainer::getRegisteredStorage(sKey); + auto storage = aStoragePair.mapStorage(); + OSL_ENSURE(storage.is(),"No Storage available!"); + if ( storage.is() ) + { + OUString sOrgName = StorageContainer::jstring2ustring(env,name); + OUString sName = removeURLPrefix(sOrgName,aStoragePair.url); + TStreamMap::iterator aStreamFind = aFind->second.streams.find(sName); + OSL_ENSURE( aStreamFind == aFind->second.streams.end(),"A Stream was already registered for this object!"); + if ( aStreamFind != aFind->second.streams.end() ) + { + pHelper = aStreamFind->second; + } + else + { + try + { + try + { + pHelper = std::make_shared<StreamHelper>(storage->openStreamElement(sName,_nMode)); + } + catch(const Exception&) + { + OUString sStrippedName = removeOldURLPrefix(sOrgName); + + if ( (_nMode & ElementModes::WRITE) != ElementModes::WRITE ) + { + bool bIsStream = true; + try + { + bIsStream = storage->isStreamElement(sStrippedName); + } + catch(const Exception&) + { + bIsStream = false; + } + if ( !bIsStream ) + return pHelper; // readonly file without data stream + } + pHelper = std::make_shared<StreamHelper>(storage->openStreamElement( sStrippedName, _nMode ) ); + } + aFind->second.streams.emplace(sName,pHelper); + } + catch(const Exception& e) + { + SAL_WARN( "connectivity.hsqldb", "[HSQLDB-SDBC] caught an exception while opening a stream\n" + "Name: " << sName + << "\nMode: 0x" << ( _nMode < 16 ? "0" : "") + << std::hex << _nMode ); + StorageContainer::throwJavaException(e,env); + } + } + } + } + return pHelper; + } + + void StorageContainer::revokeStream( JNIEnv * env,jstring name, jstring key) + { + TStorages& rMap = lcl_getStorageMap(); + TStorages::iterator aFind = rMap.find(jstring2ustring(env,key)); + OSL_ENSURE(aFind != rMap.end(),"Storage could not be found in list!"); + if ( aFind != rMap.end() ) + aFind->second.streams.erase(removeURLPrefix(jstring2ustring(env,name),aFind->second.url)); + } + + TStreamMap::mapped_type StorageContainer::getRegisteredStream( JNIEnv * env,jstring name, jstring key) + { + TStreamMap::mapped_type pRet; + TStorages& rMap = lcl_getStorageMap(); + TStorages::const_iterator aFind = rMap.find(jstring2ustring(env,key)); + OSL_ENSURE(aFind != rMap.end(),"Storage could not be found in list!"); + if ( aFind != rMap.end() ) + { + TStreamMap::const_iterator aStreamFind = aFind->second.streams.find(removeURLPrefix(jstring2ustring(env,name),aFind->second.url)); + if ( aStreamFind != aFind->second.streams.end() ) + pRet = aStreamFind->second; + } + + return pRet; + } + + void StorageContainer::throwJavaException(const Exception& _aException,JNIEnv * env) + { + if (env->ExceptionCheck()) + env->ExceptionClear(); + SAL_WARN("connectivity.hsqldb", "forwarding Exception: " << _aException ); + OString cstr( OUStringToOString(_aException.Message, RTL_TEXTENCODING_JAVA_UTF8 ) ); + env->ThrowNew(env->FindClass("java/io/IOException"), cstr.getStr()); + } + +} // namespace + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HTable.cxx b/connectivity/source/drivers/hsqldb/HTable.cxx new file mode 100644 index 000000000..f8dee57c5 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HTable.cxx @@ -0,0 +1,390 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HTable.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <comphelper/property.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/TKeys.hxx> +#include <connectivity/TIndexes.hxx> +#include <hsqldb/HColumns.hxx> +#include <TConnection.hxx> + +#include <tools/diagnose_ex.h> + + +using namespace ::comphelper; +using namespace connectivity::hsqldb; +using namespace connectivity::sdbcx; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OHSQLTable::OHSQLTable( sdbcx::OCollection* _pTables, + const Reference< XConnection >& _xConnection) + :OTableHelper(_pTables,_xConnection,true) +{ + // we create a new table here, so we should have all the rights or ;-) + m_nPrivileges = Privilege::DROP | + Privilege::REFERENCE | + Privilege::ALTER | + Privilege::CREATE | + Privilege::READ | + Privilege::DELETE | + Privilege::UPDATE | + Privilege::INSERT | + Privilege::SELECT; + construct(); +} + +OHSQLTable::OHSQLTable( sdbcx::OCollection* _pTables, + const Reference< XConnection >& _xConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName, + sal_Int32 _nPrivileges + ) : OTableHelper( _pTables, + _xConnection, + true, + Name, + Type, + Description, + SchemaName, + CatalogName) + , m_nPrivileges(_nPrivileges) +{ + construct(); +} + +void OHSQLTable::construct() +{ + OTableHelper::construct(); + if ( !isNew() ) + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRIVILEGES), PROPERTY_ID_PRIVILEGES,PropertyAttribute::READONLY,&m_nPrivileges, cppu::UnoType<decltype(m_nPrivileges)>::get()); +} + +::cppu::IPropertyArrayHelper* OHSQLTable::createArrayHelper( sal_Int32 /*_nId*/ ) const +{ + return doCreateArrayHelper(); +} + +::cppu::IPropertyArrayHelper & OHSQLTable::getInfoHelper() +{ + return *static_cast<OHSQLTable_PROP*>(this)->getArrayHelper(isNew() ? 1 : 0); +} + +sdbcx::OCollection* OHSQLTable::createColumns(const ::std::vector< OUString>& _rNames) +{ + OHSQLColumns* pColumns = new OHSQLColumns(*this,m_aMutex,_rNames); + pColumns->setParent(this); + return pColumns; +} + +sdbcx::OCollection* OHSQLTable::createKeys(const ::std::vector< OUString>& _rNames) +{ + return new OKeysHelper(this,m_aMutex,_rNames); +} + +sdbcx::OCollection* OHSQLTable::createIndexes(const ::std::vector< OUString>& _rNames) +{ + return new OIndexesHelper(this,m_aMutex,_rNames); +} + +Sequence< sal_Int8 > OHSQLTable::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OHSQLTable::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return (isUnoTunnelId<OHSQLTable>(rId)) + ? reinterpret_cast< sal_Int64 >( this ) + : OTable_TYPEDEF::getSomething(rId); +} + +// XAlterTable +void SAL_CALL OHSQLTable::alterColumnByName( const OUString& colName, const Reference< XPropertySet >& descriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed( +#ifdef __GNUC__ + ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed +#else + rBHelper.bDisposed +#endif + ); + + if ( !m_xColumns || !m_xColumns->hasByName(colName) ) + throw NoSuchElementException(colName,*this); + + + if ( !isNew() ) + { + // first we have to check what should be altered + Reference<XPropertySet> xProp; + m_xColumns->getByName(colName) >>= xProp; + // first check the types + sal_Int32 nOldType = 0,nNewType = 0,nOldPrec = 0,nNewPrec = 0,nOldScale = 0,nNewScale = 0; + OUString sOldTypeName, sNewTypeName; + + ::dbtools::OPropertyMap& rProp = OMetaConnection::getPropMap(); + + // type/typename + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPE)) >>= nOldType; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPE)) >>= nNewType; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME)) >>= sOldTypeName; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME))>>= sNewTypeName; + + // and precision and scale + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_PRECISION)) >>= nOldPrec; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_PRECISION))>>= nNewPrec; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_SCALE)) >>= nOldScale; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_SCALE)) >>= nNewScale; + + // second: check the "is nullable" value + sal_Int32 nOldNullable = 0,nNewNullable = 0; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISNULLABLE)) >>= nOldNullable; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISNULLABLE)) >>= nNewNullable; + + // check also the auto_increment + bool bOldAutoIncrement = false,bAutoIncrement = false; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT)) >>= bOldAutoIncrement; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT)) >>= bAutoIncrement; + + // now we should look if the name of the column changed + OUString sNewColumnName; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_NAME)) >>= sNewColumnName; + if ( sNewColumnName != colName ) + { + const OUString sQuote = getMetaData()->getIdentifierQuoteString( ); + + OUString sSql = getAlterTableColumnPart() + + " ALTER COLUMN " + + ::dbtools::quoteName(sQuote,colName) + + " RENAME TO " + + ::dbtools::quoteName(sQuote,sNewColumnName); + + executeStatement(sSql); + } + + if ( nOldType != nNewType + || sOldTypeName != sNewTypeName + || nOldPrec != nNewPrec + || nOldScale != nNewScale + || nNewNullable != nOldNullable + || bOldAutoIncrement != bAutoIncrement ) + { + // special handling because they change the type names to distinguish + // if a column should be an auto_increment one + if ( bOldAutoIncrement != bAutoIncrement ) + { + /// TODO: insert special handling for auto increment "IDENTITY" and primary key + } + alterColumnType(nNewType,sNewColumnName,descriptor); + } + + // third: check the default values + OUString sNewDefault,sOldDefault; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DEFAULTVALUE)) >>= sOldDefault; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DEFAULTVALUE)) >>= sNewDefault; + + if(!sOldDefault.isEmpty()) + { + dropDefaultValue(colName); + if(!sNewDefault.isEmpty() && sOldDefault != sNewDefault) + alterDefaultValue(sNewDefault,sNewColumnName); + } + else if(sOldDefault.isEmpty() && !sNewDefault.isEmpty()) + alterDefaultValue(sNewDefault,sNewColumnName); + + m_xColumns->refresh(); + } + else + { + if(m_xColumns) + { + m_xColumns->dropByName(colName); + m_xColumns->appendByDescriptor(descriptor); + } + } + +} + +void OHSQLTable::alterColumnType(sal_Int32 nNewType,const OUString& _rColName, const Reference<XPropertySet>& _xDescriptor) +{ + OUString sSql = getAlterTableColumnPart() + " ALTER COLUMN "; +#if OSL_DEBUG_LEVEL > 0 + try + { + OUString sDescriptorName; + OSL_ENSURE( _xDescriptor.is() + && ( _xDescriptor->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_NAME ) ) >>= sDescriptorName ) + && ( sDescriptorName == _rColName ), + "OHSQLTable::alterColumnType: unexpected column name!" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } +#else + (void)_rColName; +#endif + + OHSQLColumn* pColumn = new OHSQLColumn; + Reference<XPropertySet> xProp = pColumn; + ::comphelper::copyProperties(_xDescriptor,xProp); + xProp->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE),makeAny(nNewType)); + + sSql += ::dbtools::createStandardColumnPart(xProp,getConnection()); + executeStatement(sSql); +} + +void OHSQLTable::alterDefaultValue(const OUString& _sNewDefault,const OUString& _rColName) +{ + const OUString sQuote = getMetaData()->getIdentifierQuoteString( ); + OUString sSql = getAlterTableColumnPart() + + " ALTER COLUMN " + + ::dbtools::quoteName(sQuote,_rColName) + + " SET DEFAULT '" + _sNewDefault + "'"; + + executeStatement(sSql); +} + +void OHSQLTable::dropDefaultValue(const OUString& _rColName) +{ + const OUString sQuote = getMetaData()->getIdentifierQuoteString( ); + OUString sSql = getAlterTableColumnPart() + + " ALTER COLUMN " + + ::dbtools::quoteName(sQuote,_rColName) + + " DROP DEFAULT"; + + executeStatement(sSql); +} + +OUString OHSQLTable::getAlterTableColumnPart() const +{ + OUString sSql( "ALTER TABLE " ); + + OUString sComposedName( ::dbtools::composeTableName( getMetaData(), m_CatalogName, m_SchemaName, m_Name, true, ::dbtools::EComposeRule::InTableDefinitions ) ); + sSql += sComposedName; + + return sSql; +} + +void OHSQLTable::executeStatement(const OUString& _rStatement ) +{ + OUString sSQL = _rStatement; + if(sSQL.endsWith(",")) + sSQL = sSQL.replaceAt(sSQL.getLength()-1, 1, ")"); + + Reference< XStatement > xStmt = getConnection()->createStatement( ); + if ( xStmt.is() ) + { + try { xStmt->execute(sSQL); } + catch( const Exception& ) + { + ::comphelper::disposeComponent(xStmt); + throw; + } + ::comphelper::disposeComponent(xStmt); + } +} + +Sequence< Type > SAL_CALL OHSQLTable::getTypes( ) +{ + if ( m_Type == "VIEW" ) + { + Sequence< Type > aTypes = OTableHelper::getTypes(); + std::vector<Type> aOwnTypes; + aOwnTypes.reserve(aTypes.getLength()); + const Type* pIter = aTypes.getConstArray(); + const Type* pEnd = pIter + aTypes.getLength(); + for(;pIter != pEnd;++pIter) + { + if( *pIter != cppu::UnoType<XRename>::get()) + { + aOwnTypes.push_back(*pIter); + } + } + return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size()); + } + return OTableHelper::getTypes(); +} + +// XRename +void SAL_CALL OHSQLTable::rename( const OUString& newName ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed( +#ifdef __GNUC__ + ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed +#else + rBHelper.bDisposed +#endif + ); + + if(!isNew()) + { + OUString sSql = "ALTER "; + if ( m_Type == "VIEW" ) + sSql += " VIEW "; + else + sSql += " TABLE "; + + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(getMetaData(),newName,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + + sSql += + ::dbtools::composeTableName( getMetaData(), m_CatalogName, m_SchemaName, m_Name, true, ::dbtools::EComposeRule::InDataManipulation ) + + " RENAME TO " + + ::dbtools::composeTableName( getMetaData(), sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InDataManipulation ); + + executeStatement(sSql); + + ::connectivity::OTable_TYPEDEF::rename(newName); + } + else + ::dbtools::qualifiedNameComponents(getMetaData(),newName,m_CatalogName,m_SchemaName,m_Name,::dbtools::EComposeRule::InTableDefinitions); +} + + +Any SAL_CALL OHSQLTable::queryInterface( const Type & rType ) +{ + if( m_Type == "VIEW" && rType == cppu::UnoType<XRename>::get()) + return Any(); + + return OTableHelper::queryInterface(rType); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HTables.cxx b/connectivity/source/drivers/hsqldb/HTables.cxx new file mode 100644 index 000000000..787af894b --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HTables.cxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <hsqldb/HTables.hxx> +#include <hsqldb/HViews.hxx> +#include <hsqldb/HTable.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <hsqldb/HCatalog.hxx> +#include <connectivity/dbtools.hxx> +#include <comphelper/types.hxx> +#include <TConnection.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace ::cppu; +using namespace connectivity::hsqldb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace dbtools; + +sdbcx::ObjectType OTables::createObject(const OUString& _rName) +{ + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData,_rName,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + + Sequence< OUString > sTableTypes(3); + sTableTypes[0] = "VIEW"; + sTableTypes[1] = "TABLE"; + sTableTypes[2] = "%"; // just to be sure to include anything else... + + Any aCatalog; + if ( !sCatalog.isEmpty() ) + aCatalog <<= sCatalog; + Reference< XResultSet > xResult = m_xMetaData->getTables(aCatalog,sSchema,sTable,sTableTypes); + + sdbcx::ObjectType xRet; + if ( xResult.is() ) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + if ( xResult->next() ) // there can be only one table with this name + { + sal_Int32 nPrivileges = ::dbtools::getTablePrivileges( m_xMetaData, sCatalog, sSchema, sTable ); + if ( m_xMetaData->isReadOnly() ) + nPrivileges &= ~( Privilege::INSERT | Privilege::UPDATE | Privilege::DELETE | Privilege::CREATE | Privilege::ALTER | Privilege::DROP ); + + // obtain privileges + OHSQLTable* pRet = new OHSQLTable( this + ,static_cast<OHCatalog&>(m_rParent).getConnection() + ,sTable + ,xRow->getString(4) + ,xRow->getString(5) + ,sSchema + ,sCatalog + ,nPrivileges); + xRet = pRet; + } + ::comphelper::disposeComponent(xResult); + } + + return xRet; +} + +void OTables::impl_refresh( ) +{ + static_cast<OHCatalog&>(m_rParent).refreshTables(); +} + +void OTables::disposing() +{ + m_xMetaData.clear(); + OCollection::disposing(); +} + +Reference< XPropertySet > OTables::createDescriptor() +{ + return new OHSQLTable(this,static_cast<OHCatalog&>(m_rParent).getConnection()); +} + +// XAppend +sdbcx::ObjectType OTables::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + createTable(descriptor); + return createObject( _rForName ); +} + +// XDrop +void OTables::dropObject(sal_Int32 _nPos,const OUString& _sElementName) +{ + Reference< XInterface > xObject( getObject( _nPos ) ); + bool bIsNew = connectivity::sdbcx::ODescriptor::isNew( xObject ); + if (bIsNew) + return; + + Reference< XConnection > xConnection = static_cast<OHCatalog&>(m_rParent).getConnection(); + + + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData,_sElementName,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + + OUString aSql( "DROP " ); + + Reference<XPropertySet> xProp(xObject,UNO_QUERY); + bool bIsView; + if((bIsView = (xProp.is() && ::comphelper::getString(xProp->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))) == "VIEW"))) // here we have a view + aSql += "VIEW "; + else + aSql += "TABLE "; + + OUString sComposedName( + ::dbtools::composeTableName( m_xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InDataManipulation ) ); + aSql += sComposedName; + Reference< XStatement > xStmt = xConnection->createStatement( ); + if ( xStmt.is() ) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + // if no exception was thrown we must delete it from the views + if ( bIsView ) + { + HViews* pViews = static_cast<HViews*>(static_cast<OHCatalog&>(m_rParent).getPrivateViews()); + if ( pViews && pViews->hasByName(_sElementName) ) + pViews->dropByNameImpl(_sElementName); + } +} + +void OTables::createTable( const Reference< XPropertySet >& descriptor ) +{ + Reference< XConnection > xConnection = static_cast<OHCatalog&>(m_rParent).getConnection(); + OUString aSql = ::dbtools::createSqlCreateTableStatement(descriptor,xConnection); + + Reference< XStatement > xStmt = xConnection->createStatement( ); + if ( xStmt.is() ) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } +} + +void OTables::appendNew(const OUString& _rsNewTable) +{ + insertElement(_rsNewTable,nullptr); + + // notify our container listeners + ContainerEvent aEvent(static_cast<XContainer*>(this), makeAny(_rsNewTable), Any(), Any()); + OInterfaceIteratorHelper2 aListenerLoop(m_aContainerListeners); + while (aListenerLoop.hasMoreElements()) + static_cast<XContainerListener*>(aListenerLoop.next())->elementInserted(aEvent); +} + +OUString OTables::getNameForObject(const sdbcx::ObjectType& _xObject) +{ + OSL_ENSURE(_xObject.is(),"OTables::getNameForObject: Object is NULL!"); + return ::dbtools::composeTableName( m_xMetaData, _xObject, ::dbtools::EComposeRule::InDataManipulation, false ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HTerminateListener.cxx b/connectivity/source/drivers/hsqldb/HTerminateListener.cxx new file mode 100644 index 000000000..df325efb7 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HTerminateListener.cxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "HTerminateListener.hxx" +#include <hsqldb/HDriver.hxx> + + +namespace connectivity +{ + + using namespace hsqldb; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + +// XEventListener +void SAL_CALL OConnectionController::disposing( const EventObject& /*Source*/ ) +{ +} + +// XTerminateListener +void SAL_CALL OConnectionController::queryTermination( const EventObject& /*aEvent*/ ) +{ + m_pDriver->flushConnections(); +} + +void SAL_CALL OConnectionController::notifyTermination( const EventObject& /*aEvent*/ ) +{ + m_pDriver->shutdownConnections(); +} + + +} // namespace connectivity + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HTerminateListener.hxx b/connectivity/source/drivers/hsqldb/HTerminateListener.hxx new file mode 100644 index 000000000..62e8ec79d --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HTerminateListener.hxx @@ -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 . + */ +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_HSQLDB_HTERMINATELISTENER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_HSQLDB_HTERMINATELISTENER_HXX + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/frame/XTerminateListener.hpp> + + +namespace connectivity +{ + + + namespace hsqldb + { + class ODriverDelegator; + class OConnectionController : public ::cppu::WeakImplHelper< css::frame::XTerminateListener > + { + ODriverDelegator* m_pDriver; + protected: + virtual ~OConnectionController() override {m_pDriver = nullptr;} + public: + explicit OConnectionController(ODriverDelegator* _pDriver) : m_pDriver(_pDriver){} + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XTerminateListener + virtual void SAL_CALL queryTermination( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL notifyTermination( const css::lang::EventObject& aEvent ) override; + }; + } + +} // namespace connectivity + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_HSQLDB_HTERMINATELISTENER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HTools.cxx b/connectivity/source/drivers/hsqldb/HTools.cxx new file mode 100644 index 000000000..b9854c01c --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HTools.cxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HTools.hxx> + + +namespace connectivity::hsqldb +{ + + void HTools::appendTableFilterCrit( OUStringBuffer& _inout_rBuffer, const OUString& _rCatalog, + const OUString& _rSchema, const OUString& _rName, bool _bShortForm ) + { + _inout_rBuffer.append( " WHERE " ); + if ( !_rCatalog.isEmpty() ) + { + _inout_rBuffer.appendAscii( _bShortForm ? "TABLE_CAT" : "TABLE_CATALOG" ); + _inout_rBuffer.append( " = '" ); + _inout_rBuffer.append ( _rCatalog ); + _inout_rBuffer.append( "' AND " ); + } + if ( !_rSchema.isEmpty() ) + { + _inout_rBuffer.appendAscii( _bShortForm ? "TABLE_SCHEM" : "TABLE_SCHEMA" ); + _inout_rBuffer.append( " = '" ); + _inout_rBuffer.append ( _rSchema ); + _inout_rBuffer.append( "' AND " ); + } + _inout_rBuffer.append( "TABLE_NAME = '" ); + _inout_rBuffer.append ( _rName ); + _inout_rBuffer.append( "'" ); + } + + +} // namespace connectivity::hsqldb + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HUser.cxx b/connectivity/source/drivers/hsqldb/HUser.cxx new file mode 100644 index 000000000..2ed0c0626 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HUser.cxx @@ -0,0 +1,328 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HUser.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/sdbcx/PrivilegeObject.hpp> +#include <TConnection.hxx> +#include <strings.hrc> + +using namespace connectivity; +using namespace connectivity::hsqldb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OHSQLUser::OHSQLUser( const css::uno::Reference< css::sdbc::XConnection >& _xConnection) : connectivity::sdbcx::OUser(true) + ,m_xConnection(_xConnection) +{ + construct(); +} + +OHSQLUser::OHSQLUser( const css::uno::Reference< css::sdbc::XConnection >& _xConnection, + const OUString& Name + ) : connectivity::sdbcx::OUser(Name,true) + ,m_xConnection(_xConnection) +{ + construct(); +} + +void OHSQLUser::refreshGroups() +{ +} + +OUserExtend::OUserExtend( const css::uno::Reference< css::sdbc::XConnection >& _xConnection) : OHSQLUser(_xConnection) +{ + construct(); +} + +void OUserExtend::construct() +{ + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD), PROPERTY_ID_PASSWORD,0,&m_Password,::cppu::UnoType<OUString>::get()); +} + +cppu::IPropertyArrayHelper* OUserExtend::createArrayHelper() const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new cppu::OPropertyArrayHelper(aProps); +} + +cppu::IPropertyArrayHelper & OUserExtend::getInfoHelper() +{ + return *OUserExtend_PROP::getArrayHelper(); +} +typedef connectivity::sdbcx::OUser_BASE OUser_BASE_RBHELPER; + +sal_Int32 SAL_CALL OHSQLUser::getPrivileges( const OUString& objName, sal_Int32 objType ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_RBHELPER::rBHelper.bDisposed); + + sal_Int32 nRights,nRightsWithGrant; + findPrivilegesAndGrantPrivileges(objName,objType,nRights,nRightsWithGrant); + return nRights; +} + +void OHSQLUser::findPrivilegesAndGrantPrivileges(const OUString& objName, sal_Int32 objType,sal_Int32& nRights,sal_Int32& nRightsWithGrant) +{ + nRightsWithGrant = nRights = 0; + // first we need to create the sql stmt to select the privs + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(xMeta,objName,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + Reference<XResultSet> xRes; + switch(objType) + { + case PrivilegeObject::TABLE: + case PrivilegeObject::VIEW: + { + Any aCatalog; + if ( !sCatalog.isEmpty() ) + aCatalog <<= sCatalog; + xRes = xMeta->getTablePrivileges(aCatalog,sSchema,sTable); + } + break; + + case PrivilegeObject::COLUMN: + { + Any aCatalog; + if ( !sCatalog.isEmpty() ) + aCatalog <<= sCatalog; + xRes = xMeta->getColumnPrivileges(aCatalog,sSchema,sTable,"%"); + } + break; + } + + if ( !xRes.is() ) + return; + + static const char sYes [] = "YES"; + + nRightsWithGrant = nRights = 0; + + Reference<XRow> xCurrentRow(xRes,UNO_QUERY); + while( xCurrentRow.is() && xRes->next() ) + { + OUString sGrantee = xCurrentRow->getString(5); + OUString sPrivilege = xCurrentRow->getString(6); + OUString sGrantable = xCurrentRow->getString(7); + + if (!m_Name.equalsIgnoreAsciiCase(sGrantee)) + continue; + + if (sPrivilege.equalsIgnoreAsciiCase("SELECT")) + { + nRights |= Privilege::SELECT; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::SELECT; + } + else if (sPrivilege.equalsIgnoreAsciiCase("INSERT")) + { + nRights |= Privilege::INSERT; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::INSERT; + } + else if (sPrivilege.equalsIgnoreAsciiCase("UPDATE")) + { + nRights |= Privilege::UPDATE; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::UPDATE; + } + else if (sPrivilege.equalsIgnoreAsciiCase("DELETE")) + { + nRights |= Privilege::DELETE; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::DELETE; + } + else if (sPrivilege.equalsIgnoreAsciiCase("READ")) + { + nRights |= Privilege::READ; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::READ; + } + else if (sPrivilege.equalsIgnoreAsciiCase("CREATE")) + { + nRights |= Privilege::CREATE; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::CREATE; + } + else if (sPrivilege.equalsIgnoreAsciiCase("ALTER")) + { + nRights |= Privilege::ALTER; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::ALTER; + } + else if (sPrivilege.equalsIgnoreAsciiCase("REFERENCE")) + { + nRights |= Privilege::REFERENCE; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::REFERENCE; + } + else if (sPrivilege.equalsIgnoreAsciiCase("DROP")) + { + nRights |= Privilege::DROP; + if ( sGrantable.equalsIgnoreAsciiCase(sYes) ) + nRightsWithGrant |= Privilege::DROP; + } + } + ::comphelper::disposeComponent(xRes); +} + +sal_Int32 SAL_CALL OHSQLUser::getGrantablePrivileges( const OUString& objName, sal_Int32 objType ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_RBHELPER::rBHelper.bDisposed); + + sal_Int32 nRights,nRightsWithGrant; + findPrivilegesAndGrantPrivileges(objName,objType,nRights,nRightsWithGrant); + return nRightsWithGrant; +} + +void SAL_CALL OHSQLUser::grantPrivileges( const OUString& objName, sal_Int32 objType, sal_Int32 objPrivileges ) +{ + if ( objType != PrivilegeObject::TABLE ) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_PRIVILEGE_NOT_GRANTED)); + ::dbtools::throwGenericSQLException(sError,*this); + } // if ( objType != PrivilegeObject::TABLE ) + + + ::osl::MutexGuard aGuard(m_aMutex); + + OUString sPrivs = getPrivilegeString(objPrivileges); + if(!sPrivs.isEmpty()) + { + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + OUString sGrant = "GRANT " + sPrivs + + " ON " + ::dbtools::quoteTableName(xMeta,objName,::dbtools::EComposeRule::InDataManipulation) + + " TO " + ::dbtools::quoteName(xMeta->getIdentifierQuoteString(), m_Name); + + Reference<XStatement> xStmt = m_xConnection->createStatement(); + if(xStmt.is()) + xStmt->execute(sGrant); + ::comphelper::disposeComponent(xStmt); + } +} + +void SAL_CALL OHSQLUser::revokePrivileges( const OUString& objName, sal_Int32 objType, sal_Int32 objPrivileges ) +{ + if ( objType != PrivilegeObject::TABLE ) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_PRIVILEGE_NOT_REVOKED)); + ::dbtools::throwGenericSQLException(sError,*this); + } // if ( objType != PrivilegeObject::TABLE ) + + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_RBHELPER::rBHelper.bDisposed); + OUString sPrivs = getPrivilegeString(objPrivileges); + if(!sPrivs.isEmpty()) + { + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + OUString sGrant = "REVOKE " + sPrivs + + " ON " + ::dbtools::quoteTableName(xMeta,objName,::dbtools::EComposeRule::InDataManipulation) + + " FROM " + ::dbtools::quoteName(xMeta->getIdentifierQuoteString(), m_Name); + + Reference<XStatement> xStmt = m_xConnection->createStatement(); + if(xStmt.is()) + xStmt->execute(sGrant); + ::comphelper::disposeComponent(xStmt); + } +} + +// XUser +void SAL_CALL OHSQLUser::changePassword( const OUString& /*oldPassword*/, const OUString& newPassword ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_RBHELPER::rBHelper.bDisposed); + + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + + if( m_Name != xMeta->getUserName() ) + { + ::dbtools::throwGenericSQLException("HSQLDB can only change password of the current user.", *this); + } + + OUString sAlterPwd = "SET PASSWORD " + + ::dbtools::quoteName(xMeta->getIdentifierQuoteString(), newPassword); + + Reference<XStatement> xStmt = m_xConnection->createStatement(); + if ( xStmt.is() ) + { + xStmt->execute(sAlterPwd); + ::comphelper::disposeComponent(xStmt); + } +} + +OUString OHSQLUser::getPrivilegeString(sal_Int32 nRights) +{ + OUString sPrivs; + if((nRights & Privilege::INSERT) == Privilege::INSERT) + sPrivs += "INSERT"; + + if((nRights & Privilege::DELETE) == Privilege::DELETE) + { + if(!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "DELETE"; + } + + if((nRights & Privilege::UPDATE) == Privilege::UPDATE) + { + if(!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "UPDATE"; + } + + if((nRights & Privilege::ALTER) == Privilege::ALTER) + { + if(!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "ALTER"; + } + + if((nRights & Privilege::SELECT) == Privilege::SELECT) + { + if(!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "SELECT"; + } + + if((nRights & Privilege::REFERENCE) == Privilege::REFERENCE) + { + if(!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "REFERENCES"; + } + + return sPrivs; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HUsers.cxx b/connectivity/source/drivers/hsqldb/HUsers.cxx new file mode 100644 index 000000000..40d1f5243 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HUsers.cxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <hsqldb/HUsers.hxx> +#include <hsqldb/HUser.hxx> +#include <connectivity/sdbcx/IRefreshable.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <TConnection.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::hsqldb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OUsers::OUsers( ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector, + const css::uno::Reference< css::sdbc::XConnection >& _xConnection, + connectivity::sdbcx::IRefreshableUsers* _pParent) + : sdbcx::OCollection(_rParent, true, _rMutex, _rVector) + ,m_xConnection(_xConnection) + ,m_pParent(_pParent) +{ +} + + +sdbcx::ObjectType OUsers::createObject(const OUString& _rName) +{ + return new OHSQLUser(m_xConnection,_rName); +} + +void OUsers::impl_refresh() +{ + m_pParent->refreshUsers(); +} + +Reference< XPropertySet > OUsers::createDescriptor() +{ + OUserExtend* pNew = new OUserExtend(m_xConnection); + return pNew; +} + +// XAppend +sdbcx::ObjectType OUsers::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + OUString aQuote = m_xConnection->getMetaData()->getIdentifierQuoteString( ); + OUString sPassword; + descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD)) >>= sPassword; + OUString aSql = "GRANT USAGE ON * TO " + + ::dbtools::quoteName(aQuote,_rForName) + " @\"%\" "; + if ( !sPassword.isEmpty() ) + { + aSql += " IDENTIFIED BY '" + sPassword + "'"; + } + + Reference< XStatement > xStmt = m_xConnection->createStatement( ); + if(xStmt.is()) + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + + return createObject( _rForName ); +} + +// XDrop +void OUsers::dropObject(sal_Int32 /*nPos*/,const OUString& _sElementName) +{ + OUString aSql( "REVOKE ALL ON * FROM " ); + OUString aQuote = m_xConnection->getMetaData()->getIdentifierQuoteString( ); + aSql += ::dbtools::quoteName(aQuote,_sElementName); + + Reference< XStatement > xStmt = m_xConnection->createStatement( ); + if(xStmt.is()) + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HView.cxx b/connectivity/source/drivers/hsqldb/HView.cxx new file mode 100644 index 000000000..0a09ec0d2 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HView.cxx @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <hsqldb/HView.hxx> +#include <hsqldb/HTools.hxx> + +#include <propertyids.hxx> + +#include <connectivity/dbtools.hxx> + +#include <com/sun/star/lang/WrappedTargetException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/sharedunocomponent.hxx> + + +namespace connectivity::hsqldb +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::Any; + using ::com::sun::star::sdbc::SQLException; + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::lang::WrappedTargetException; + using ::com::sun::star::sdbc::XResultSet; + using ::com::sun::star::sdbc::XStatement; + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::sdbc::XRow; + + HView::HView( const Reference< XConnection >& _rxConnection, bool _bCaseSensitive, + const OUString& _rSchemaName, const OUString& _rName ) + :HView_Base( _bCaseSensitive, _rName, _rxConnection->getMetaData(), OUString(), _rSchemaName, OUString() ) + ,m_xConnection( _rxConnection ) + { + } + + + HView::~HView() + { + } + + + IMPLEMENT_FORWARD_XINTERFACE2( HView, HView_Base, HView_IBASE ) + IMPLEMENT_FORWARD_XTYPEPROVIDER2( HView, HView_Base, HView_IBASE ) + + + void SAL_CALL HView::alterCommand( const OUString& _rNewCommand ) + { + // not really atomic ... as long as we do not have something like + // ALTER VIEW <name> TO <command> + // in HSQL, we need to do it this way ... + // + // I can imagine scenarios where this fails, e.g. when dropping the view + // succeeds, re-creating it fails, some other thread alters a table which + // the view was based on, and then we try to restore the view with the + // original command, which then fails, too. + // + // However, there's not much chance to prevent this kind of errors without + // backend support. + + OUString sQualifiedName( ::dbtools::composeTableName( + m_xMetaData, m_CatalogName, m_SchemaName, m_Name, true, ::dbtools::EComposeRule::InDataManipulation ) ); + + ::utl::SharedUNOComponent< XStatement > xStatement; xStatement.set( m_xConnection->createStatement(), UNO_QUERY_THROW ); + + // create a statement which can be used to re-create the original view, in case + // dropping it succeeds, but creating it with a new statement fails + OUStringBuffer aRestoreCommand; + aRestoreCommand.append( "CREATE VIEW " ); + aRestoreCommand.append ( sQualifiedName ); + aRestoreCommand.append( " AS " ); + aRestoreCommand.append ( impl_getCommand_throwSQLException() ); + OUString sRestoreCommand( aRestoreCommand.makeStringAndClear() ); + + bool bDropSucceeded( false ); + try + { + // drop the existing view + OUString aCommand ="DROP VIEW " + sQualifiedName; + xStatement->execute( aCommand ); + bDropSucceeded = true; + + // create a new one with the same name + aCommand = "CREATE VIEW " + sQualifiedName + " AS " + _rNewCommand; + xStatement->execute( aCommand ); + } + catch( const SQLException& ) + { + if ( bDropSucceeded ) + // drop succeeded, but creation failed -> re-create the view with the original + // statement + xStatement->execute( sRestoreCommand ); + throw; + } + catch( const RuntimeException& ) + { + if ( bDropSucceeded ) + xStatement->execute( sRestoreCommand ); + throw; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + if ( bDropSucceeded ) + xStatement->execute( sRestoreCommand ); + } + } + + + void SAL_CALL HView::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const + { + if ( _nHandle == PROPERTY_ID_COMMAND ) + { + // retrieve the very current command, don't rely on the base classes cached value + // (which we initialized empty, anyway) + _rValue <<= impl_getCommand_wrapSQLException(); + return; + } + + HView_Base::getFastPropertyValue( _rValue, _nHandle ); + } + + OUString HView::impl_getCommand() const + { + OUStringBuffer aCommand; + aCommand.append( "SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.SYSTEM_VIEWS " ); + HTools::appendTableFilterCrit( aCommand, m_CatalogName, m_SchemaName, m_Name, false ); + ::utl::SharedUNOComponent< XStatement > xStatement; xStatement.set( m_xConnection->createStatement(), UNO_QUERY_THROW ); + Reference< XResultSet > xResult( xStatement->executeQuery( aCommand.makeStringAndClear() ), css::uno::UNO_SET_THROW ); + if ( !xResult->next() ) + { + // hmm. There is no view the name as we know it. Can only mean some other instance + // dropped this view meanwhile... + throw DisposedException(); + } + + Reference< XRow > xRow( xResult, UNO_QUERY_THROW ); + return xRow->getString( 1 ); + } + + OUString HView::impl_getCommand_wrapSQLException() const + { + OUString sCommand; + + try + { + sCommand = impl_getCommand(); + } + catch( const RuntimeException& ) + { + throw; + } + catch( const SQLException& e ) + { + throw WrappedTargetException( e.Message, static_cast< XAlterView* >( const_cast< HView* >( this ) ), ::cppu::getCaughtException() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } + + return sCommand; + } + + OUString HView::impl_getCommand_throwSQLException() const + { + OUString sCommand; + + try + { + sCommand = impl_getCommand(); + } + catch( const RuntimeException& ) + { + throw; + } + catch( const SQLException& ) + { + throw; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.hsqldb"); + } + + return sCommand; + } + +} // namespace connectivity::hsqldb + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/HViews.cxx b/connectivity/source/drivers/hsqldb/HViews.cxx new file mode 100644 index 000000000..e67a9fa14 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/HViews.cxx @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HTables.hxx> +#include <hsqldb/HViews.hxx> +#include <hsqldb/HView.hxx> +#include <hsqldb/HCatalog.hxx> +#include <connectivity/dbtools.hxx> +#include <comphelper/types.hxx> +#include <TConnection.hxx> + +using namespace ::comphelper; + +using namespace ::cppu; +using namespace connectivity; +using namespace connectivity::hsqldb; +using namespace css::uno; +using namespace css::beans; +using namespace css::sdbcx; +using namespace css::sdbc; +using namespace css::container; +using namespace css::lang; +using namespace dbtools; +typedef connectivity::sdbcx::OCollection OCollection_TYPE; + + +HViews::HViews( const Reference< XConnection >& _rxConnection, ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector ) + :sdbcx::OCollection( _rParent, true, _rMutex, _rVector ) + ,m_xConnection( _rxConnection ) + ,m_xMetaData( _rxConnection->getMetaData() ) + ,m_bInDrop( false ) +{ +} + + +sdbcx::ObjectType HViews::createObject(const OUString& _rName) +{ + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData, + _rName, + sCatalog, + sSchema, + sTable, + ::dbtools::EComposeRule::InDataManipulation); + return new HView( m_xConnection, isCaseSensitive(), sSchema, sTable ); +} + + +void HViews::impl_refresh( ) +{ + static_cast<OHCatalog&>(m_rParent).refreshTables(); +} + +void HViews::disposing() +{ + m_xMetaData.clear(); + OCollection::disposing(); +} + +Reference< XPropertySet > HViews::createDescriptor() +{ + Reference<XConnection> xConnection = static_cast<OHCatalog&>(m_rParent).getConnection(); + connectivity::sdbcx::OView* pNew = new connectivity::sdbcx::OView(true, xConnection->getMetaData()); + return pNew; +} + +// XAppend +sdbcx::ObjectType HViews::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + createView(descriptor); + return createObject( _rForName ); +} + +// XDrop +void HViews::dropObject(sal_Int32 _nPos,const OUString& /*_sElementName*/) +{ + if ( m_bInDrop ) + return; + + Reference< XInterface > xObject( getObject( _nPos ) ); + bool bIsNew = connectivity::sdbcx::ODescriptor::isNew( xObject ); + if (!bIsNew) + { + OUString aSql( "DROP VIEW" ); + + Reference<XPropertySet> xProp(xObject,UNO_QUERY); + aSql += ::dbtools::composeTableName( m_xMetaData, xProp, ::dbtools::EComposeRule::InTableDefinitions, true ); + + Reference<XConnection> xConnection = static_cast<OHCatalog&>(m_rParent).getConnection(); + Reference< XStatement > xStmt = xConnection->createStatement( ); + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } +} + +void HViews::dropByNameImpl(const OUString& elementName) +{ + m_bInDrop = true; + OCollection_TYPE::dropByName(elementName); + m_bInDrop = false; +} + +void HViews::createView( const Reference< XPropertySet >& descriptor ) +{ + Reference<XConnection> xConnection = static_cast<OHCatalog&>(m_rParent).getConnection(); + + OUString sCommand; + descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_COMMAND)) >>= sCommand; + + OUString aSql = "CREATE VIEW " + + ::dbtools::composeTableName( m_xMetaData, descriptor, ::dbtools::EComposeRule::InTableDefinitions, true ) + + " AS " + sCommand; + + Reference< XStatement > xStmt = xConnection->createStatement( ); + if ( xStmt.is() ) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + + // insert the new view also in the tables collection + OTables* pTables = static_cast<OTables*>(static_cast<OHCatalog&>(m_rParent).getPrivateTables()); + if ( pTables ) + { + OUString sName = ::dbtools::composeTableName( m_xMetaData, descriptor, ::dbtools::EComposeRule::InDataManipulation, false ); + pTables->appendNew(sName); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/Hservices.cxx b/connectivity/source/drivers/hsqldb/Hservices.cxx new file mode 100644 index 000000000..5ca8a0962 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/Hservices.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <hsqldb/HDriver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::hsqldb; +using css::uno::Reference; +using css::uno::Sequence; +using css::lang::XSingleServiceFactory; +using css::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* hsqldb_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + ODriverDelegator::getImplementationName_Static(), + ODriverDelegator::getSupportedServiceNames_Static(), + ODriverDelegator_CreateInstance, ::cppu::createSingleFactory) + ; + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/StorageFileAccess.cxx b/connectivity/source/drivers/hsqldb/StorageFileAccess.cxx new file mode 100644 index 000000000..3e8461a95 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/StorageFileAccess.cxx @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H +#include <config.h> +#endif +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <hsqldb/HStorageMap.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::connectivity::hsqldb; + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageFileAccess + * Method: isStreamElement + * Signature: (Ljava/lang/String;Ljava/lang/String;)Z + */ +extern "C" SAL_JNI_EXPORT jboolean JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageFileAccess_isStreamElement + (JNIEnv * env, jobject /*obj_this*/,jstring key, jstring name) +{ + TStorages::mapped_type aStoragePair = StorageContainer::getRegisteredStorage(StorageContainer::jstring2ustring(env,key)); + auto storage = aStoragePair.mapStorage(); + if ( storage.is() ) + { + try + { + OUString sName = StorageContainer::jstring2ustring(env,name); + try + { + OUString sOldName = StorageContainer::removeOldURLPrefix(sName); + if ( storage->isStreamElement(sOldName) ) + { + try + { + storage->renameElement(sOldName,StorageContainer::removeURLPrefix(sName,aStoragePair.url)); + } + catch(const Exception&) + { + } + } + } + catch(const NoSuchElementException&) + { + } + catch(const IllegalArgumentException&) + { + } + return storage->isStreamElement(StorageContainer::removeURLPrefix(sName,aStoragePair.url)); + } + catch(const NoSuchElementException&) + { + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("connectivity.hsqldb", "forwarding"); + if (env->ExceptionCheck()) + env->ExceptionClear(); + } + } + return JNI_FALSE; +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageFileAccess + * Method: removeElement + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageFileAccess_removeElement + (JNIEnv * env, jobject /*obj_this*/,jstring key, jstring name) +{ +#ifdef HSQLDB_DBG + { + OUString sKey = StorageContainer::jstring2ustring(env,key); + OUString sName = StorageContainer::jstring2ustring(env,name); + } +#endif + TStorages::mapped_type aStoragePair = StorageContainer::getRegisteredStorage(StorageContainer::jstring2ustring(env,key)); + auto storage = aStoragePair.mapStorage(); + if ( !storage.is() ) + return; + + try + { + storage->removeElement(StorageContainer::removeURLPrefix(StorageContainer::jstring2ustring(env,name),aStoragePair.url)); + } + catch(const NoSuchElementException&) + { + if (env->ExceptionCheck()) + env->ExceptionClear(); + } + catch(const Exception& e) + { + TOOLS_WARN_EXCEPTION("connectivity.hsqldb", ""); + StorageContainer::throwJavaException(e,env); + } +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageFileAccess + * Method: renameElement + * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageFileAccess_renameElement + (JNIEnv * env, jobject /*obj_this*/,jstring key, jstring oldname, jstring newname) +{ +#ifdef HSQLDB_DBG + { + OUString sKey = StorageContainer::jstring2ustring(env,key); + OUString sNewName = StorageContainer::jstring2ustring(env,newname); + OUString sOldName = StorageContainer::jstring2ustring(env,oldname); + } +#endif + TStorages::mapped_type aStoragePair = StorageContainer::getRegisteredStorage(StorageContainer::jstring2ustring(env,key)); + auto storage = aStoragePair.mapStorage(); + if ( !storage.is() ) + return; + + try + { + storage->renameElement( + StorageContainer::removeURLPrefix(StorageContainer::jstring2ustring(env,oldname),aStoragePair.url), + StorageContainer::removeURLPrefix(StorageContainer::jstring2ustring(env,newname),aStoragePair.url) + ); +#ifdef HSQLDB_DBG + { + OUString sNewName = StorageContainer::removeURLPrefix(StorageContainer::jstring2ustring(env,newname),aStoragePair.first.second); + OSL_ENSURE(aStoragePair.first.first->isStreamElement(sNewName),"Stream could not be renamed"); + } +#endif + } + catch(const NoSuchElementException&) + { + } + catch(const Exception& e) + { + OSL_FAIL("Exception caught! : Java_com_sun_star_sdbcx_comp_hsqldb_StorageFileAccess_renameElement"); + StorageContainer::throwJavaException(e,env); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/StorageNativeInputStream.cxx b/connectivity/source/drivers/hsqldb/StorageNativeInputStream.cxx new file mode 100644 index 000000000..bdafac4ee --- /dev/null +++ b/connectivity/source/drivers/hsqldb/StorageNativeInputStream.cxx @@ -0,0 +1,291 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H +#include <config.h> +#endif +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/document/XDocumentSubStorageSupplier.hpp> +#include <hsqldb/HStorageAccess.hxx> +#include <hsqldb/HStorageMap.hxx> + +#include <osl/diagnose.h> +#include "accesslog.hxx" + +#include <limits> + + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::connectivity::hsqldb; + +/*****************************************************************************/ +/* exception macros */ + +#define ThrowException(env, type, msg) { \ + env->ThrowNew(env->FindClass(type), msg); } +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream + * Method: openStream + * Signature: (Ljava/lang/String;Ljava/lang/String;I)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream_openStream + (JNIEnv * env, jobject /*obj_this*/,jstring key, jstring name, jint mode) +{ +#ifdef HSQLDB_DBG + { + OperationLogFile( env, name, "input" ).logOperation( "openStream" ); + LogFile( env, name, "input" ).create(); + } +#endif + StorageContainer::registerStream(env,name,key,mode); +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream + * Method: read + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ +extern "C" SAL_JNI_EXPORT jint JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream_read__Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv * env, jobject /*obj_this*/, jstring key, jstring name) +{ +#ifdef HSQLDB_DBG + OperationLogFile( env, name, "input" ).logOperation( "read()" ); + + DataLogFile aDataLog( env, name, "input" ); + return read_from_storage_stream( env, obj_this, name, key, &aDataLog ); +#else + return read_from_storage_stream( env, name, key ); +#endif +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream + * Method: read + * Signature: (Ljava/lang/String;Ljava/lang/String;[BII)I + */ +extern "C" SAL_JNI_EXPORT jint JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream_read__Ljava_lang_String_2Ljava_lang_String_2_3BII + (JNIEnv * env, jobject obj_this, jstring key, jstring name, jbyteArray buffer, jint off, jint len) +{ +#ifdef HSQLDB_DBG + OperationLogFile( env, name, "input" ).logOperation( "read( byte[], int, int )" ); + + DataLogFile aDataLog( env, name, "input" ); + return read_from_storage_stream_into_buffer( env, obj_this, name, key, buffer, off, len, &aDataLog ); +#else + (void)obj_this; + return read_from_storage_stream_into_buffer(env, name,key,buffer,off,len); +#endif +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream + * Method: close + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream_close + (JNIEnv * env, jobject /*obj_this*/,jstring key, jstring name) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "input" ); + aOpLog.logOperation( "close" ); + aOpLog.close(); + + LogFile aDataLog( env, name, "input" ); + aDataLog.close(); +#endif + StorageContainer::revokeStream(env,name,key); +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream + * Method: skip + * Signature: (Ljava/lang/String;Ljava/lang/String;J)J + */ +extern "C" SAL_JNI_EXPORT jlong JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream_skip + (JNIEnv * env, jobject /*obj_this*/,jstring key, jstring name, jlong n) +{ +#ifdef HSQLDB_DBG + OperationLogFile( env, name, "input" ).logOperation( "skip()" ); +#endif + + if ( n < 0 ) + ThrowException( env, + "java/io/IOException", + "n < 0"); + + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + OSL_ENSURE(pHelper.get(),"No stream helper!"); + if ( pHelper ) + { + Reference<XInputStream> xIn = pHelper->getInputStream(); + if ( xIn.is() ) + { + try + { + sal_Int64 tmpLongVal = n; + sal_Int32 tmpIntVal; + + try + { + do { + if (tmpLongVal >= std::numeric_limits<sal_Int64>::max() ) + tmpIntVal = std::numeric_limits<sal_Int32>::max(); + else // Casting is safe here. + tmpIntVal = static_cast<sal_Int32>(tmpLongVal); + + tmpLongVal -= tmpIntVal; + + xIn->skipBytes(tmpIntVal); + + } while (tmpLongVal > 0); + } + catch(const Exception&) + { + } + + return n - tmpLongVal; + } + catch(const Exception& e) + { + OSL_FAIL("Exception caught! : skip();"); + StorageContainer::throwJavaException(e,env); + } + } + } + else + { + ThrowException( env, + "java/io/IOException", + "Stream is not valid"); + } + return 0; +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream + * Method: available + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ +extern "C" SAL_JNI_EXPORT jint JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream_available + (JNIEnv * env, jobject /*obj_this*/,jstring key, jstring name) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "input" ); + aOpLog.logOperation( "available" ); +#endif + + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + OSL_ENSURE(pHelper.get(),"No stream helper!"); + Reference<XInputStream> xIn = pHelper ? pHelper->getInputStream() : Reference<XInputStream>(); + if ( xIn.is() ) + { + try + { + jint nAvailable = xIn->available(); +#ifdef HSQLDB_DBG + aOpLog.logReturn( nAvailable ); +#endif + return nAvailable; + } + catch(const Exception& e) + { + OSL_FAIL("Exception caught! : available();"); + StorageContainer::throwJavaException(e,env); + } + } + else + { + ThrowException( env, + "java/io/IOException", + "Stream is not valid"); + } + return 0; +} + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream + * Method: read + * Signature: (Ljava/lang/String;Ljava/lang/String;[B)I + */ +extern "C" SAL_JNI_EXPORT jint JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeInputStream_read__Ljava_lang_String_2Ljava_lang_String_2_3B + (JNIEnv * env, jobject /*obj_this*/,jstring key, jstring name, jbyteArray buffer) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "input" ); + aOpLog.logOperation( "read( byte[] )" ); + + DataLogFile aDataLog( env, name, "input" ); +#endif + + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XInputStream> xIn = pHelper ? pHelper->getInputStream() : Reference< XInputStream>(); + OSL_ENSURE(xIn.is(),"Input stream is NULL!"); + jint nBytesRead = 0; + if ( xIn.is() ) + { + jsize nLen = env->GetArrayLength(buffer); + Sequence< ::sal_Int8 > aData(nLen); + + try + { + nBytesRead = xIn->readBytes(aData,nLen); + } + catch(const Exception& e) + { + OSL_FAIL("Exception caught! : skip();"); + StorageContainer::throwJavaException(e,env); + } + + // Casting bytesRead to an int is okay, since the user can + // only pass in an integer length to read, so the bytesRead + // must <= len. + + if (nBytesRead <= 0) { +#ifdef HSQLDB_DBG + aOpLog.logReturn( (jint)-1 ); +#endif + return -1; + } + OSL_ENSURE(nLen >= nBytesRead,"Buffer is too small!"); + OSL_ENSURE(aData.getLength() >= nBytesRead,"Buffer is too small!"); + env->SetByteArrayRegion(buffer, 0, nBytesRead, reinterpret_cast<jbyte*>(&aData[0])); +#ifdef HSQLDB_DBG + aDataLog.write( &aData[0], nBytesRead ); +#endif + } +#ifdef HSQLDB_DBG + aOpLog.logReturn( nBytesRead ); +#endif + return nBytesRead; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/StorageNativeOutputStream.cxx b/connectivity/source/drivers/hsqldb/StorageNativeOutputStream.cxx new file mode 100644 index 000000000..f766696e0 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/StorageNativeOutputStream.cxx @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H +#include <config.h> +#endif + +#include <cppuhelper/bootstrap.hxx> +#include <osl/diagnose.h> +#include "accesslog.hxx" +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/document/XDocumentSubStorageSupplier.hpp> +#include <hsqldb/HStorageAccess.hxx> +#include <hsqldb/HStorageMap.hxx> + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::connectivity::hsqldb; + + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream + * Method: openStream + * Signature: (Ljava/lang/String;Ljava/lang/String;I)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream_openStream + (JNIEnv * env, jobject /*obj_this*/, jstring name, jstring key, jint mode) +{ +#ifdef HSQLDB_DBG + { + OperationLogFile( env, name, "output" ).logOperation( "openStream" ); + LogFile( env, name, "output" ).create(); + } +#endif + StorageContainer::registerStream(env,name,key,mode); +} +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream + * Method: write + * Signature: (Ljava/lang/String;Ljava/lang/String;[BII)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream_write__Ljava_lang_String_2Ljava_lang_String_2_3BII + (JNIEnv * env, jobject obj_this, jstring key, jstring name, jbyteArray buffer, jint off, jint len) +{ +#ifdef HSQLDB_DBG + OperationLogFile( env, name, "output" ).logOperation( "write( byte[], int, int )" ); + + DataLogFile aDataLog( env, name, "output" ); + write_to_storage_stream_from_buffer( env, obj_this, name, key, buffer, off, len, &aDataLog ); +#else + (void)obj_this; + write_to_storage_stream_from_buffer( env, name, key, buffer, off, len ); +#endif +} + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream + * Method: write + * Signature: (Ljava/lang/String;Ljava/lang/String;[B)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream_write__Ljava_lang_String_2Ljava_lang_String_2_3B + (JNIEnv * env, jobject obj_this, jstring key, jstring name, jbyteArray buffer) +{ +#ifdef HSQLDB_DBG + OperationLogFile( env, name, "output" ).logOperation( "write( byte[] )" ); + + DataLogFile aDataLog( env, name, "output" ); + write_to_storage_stream_from_buffer( env, obj_this, name, key, buffer, 0, env->GetArrayLength( buffer ), &aDataLog ); +#else + (void)obj_this; + write_to_storage_stream_from_buffer( env, name, key, buffer, 0, env->GetArrayLength( buffer ) ); +#endif +} + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream + * Method: close + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream_close + (JNIEnv * env, jobject /*obj_this*/, jstring key, jstring name) +{ +#ifdef HSQLDB_DBG + OperationLogFile aOpLog( env, name, "output" ); + aOpLog.logOperation( "close" ); + + LogFile aDataLog( env, name, "output" ); +#endif + + std::shared_ptr<StreamHelper> pHelper = StorageContainer::getRegisteredStream(env,name,key); + Reference< XOutputStream> xFlush = pHelper ? pHelper->getOutputStream() : Reference< XOutputStream>(); + if ( xFlush.is() ) + try + { + xFlush->flush(); + } + catch(Exception&) + {} + +#ifdef HSQLDB_DBG + aDataLog.close(); + aOpLog.close(); +#endif + StorageContainer::revokeStream(env,name,key); +} + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream + * Method: write + * Signature: (Ljava/lang/String;Ljava/lang/String;I)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream_write__Ljava_lang_String_2Ljava_lang_String_2I + (JNIEnv * env, jobject obj_this, jstring key, jstring name,jint b) +{ +#ifdef HSQLDB_DBG + OperationLogFile( env, name, "output" ).logOperation( "write( int )" ); + + DataLogFile aDataLog( env, name, "output" ); + write_to_storage_stream( env, name, key, b, &aDataLog ); +#else + (void)obj_this; + write_to_storage_stream( env, name, key, b ); +#endif +} + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream + * Method: flush + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream_flush + (JNIEnv * env, jobject /*obj_this*/, jstring key, jstring name) +{ +#ifdef HSQLDB_DBG + OperationLogFile( env, name, "output" ).logOperation( "flush" ); + + OUString sKey = StorageContainer::jstring2ustring(env,key); + OUString sName = StorageContainer::jstring2ustring(env,name); +#else + (void) env; + (void) key; + (void) name; +#endif +} + +/* + * Class: com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream + * Method: sync + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +extern "C" SAL_JNI_EXPORT void JNICALL Java_com_sun_star_sdbcx_comp_hsqldb_StorageNativeOutputStream_sync + (JNIEnv * env, jobject /*obj_this*/, jstring key, jstring name) +{ +#ifdef HSQLDB_DBG + OperationLogFile( env, name, "output" ).logOperation( "sync" ); +#endif + std::shared_ptr< StreamHelper > pStream = StorageContainer::getRegisteredStream( env, name, key ); + Reference< XOutputStream > xFlush = pStream ? pStream->getOutputStream() : Reference< XOutputStream>(); + OSL_ENSURE( xFlush.is(), "StorageNativeOutputStream::sync: could not retrieve an output stream!" ); + if ( xFlush.is() ) + { + try + { + xFlush->flush(); + } + catch(Exception&) + { + OSL_FAIL( "StorageNativeOutputStream::sync: could not flush output stream!" ); + } + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/accesslog.cxx b/connectivity/source/drivers/hsqldb/accesslog.cxx new file mode 100644 index 000000000..f7e0f10ee --- /dev/null +++ b/connectivity/source/drivers/hsqldb/accesslog.cxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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> + +#ifdef HSQLDB_DBG + +#include <map> + +#include "accesslog.hxx" +#include "hsqldb/HStorageMap.hxx" + +#include <osl/thread.h> + +namespace connectivity::hsqldb +{ + typedef std::map<OUString, FILE *> TDebugStreamMap; + TDebugStreamMap& getStreams() + { + static TDebugStreamMap streams; + return streams; + } + + + LogFile::LogFile( JNIEnv* env, jstring streamName, const char* _pAsciiSuffix ) + : m_sFileName(StorageContainer::jstring2ustring(env,streamName) + + "." + OUString::createFromAscii( _pAsciiSuffix ) ) + { + } + + + FILE*& LogFile::getLogFile() + { + FILE*& pLogFile = getStreams()[m_sFileName]; + if ( !pLogFile ) + { + OString sByteLogName = OUStringToOString(m_sFileName,osl_getThreadTextEncoding()); + pLogFile = fopen( sByteLogName.getStr(), "a+" ); + } + return pLogFile; + } + + + void LogFile::writeString( const char* _pString, bool _bEndLine ) + { + FILE* pLogFile = getLogFile(); + fwrite( _pString, sizeof( *_pString ), strlen( _pString ), pLogFile ); + if ( _bEndLine ) + fwrite( "\n", sizeof( *_pString ), strlen( "\n" ), pLogFile ); + fflush( pLogFile ); + } + + + void LogFile::close() + { + fclose( getLogFile() ); + getLogFile() = NULL; + } +} } +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/accesslog.hxx b/connectivity/source/drivers/hsqldb/accesslog.hxx new file mode 100644 index 000000000..a4dc41f2d --- /dev/null +++ b/connectivity/source/drivers/hsqldb/accesslog.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_HSQLDB_ACCESSLOG_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_HSQLDB_ACCESSLOG_HXX + +#ifdef HSQLDB_DBG + +#include <jni.h> +#include <rtl/ustring.hxx> +#include <rtl/string.hxx> + +namespace connectivity::hsqldb +{ + class LogFile + { + private: + OUString m_sFileName; + + public: + LogFile( JNIEnv* env, jstring streamName, const char* _pAsciiSuffix ); + + public: + void writeString( const char* _pString, bool _bEndLine = true ); + void create() { getLogFile(); } + virtual void close(); + + protected: + FILE*& getLogFile(); + }; + + class OperationLogFile : public LogFile + { + public: + OperationLogFile( JNIEnv* env, jstring streamName, const char* _pAsciiSuffix ) + :LogFile( env, streamName, ( OString( _pAsciiSuffix ) += ".op" ).getStr() ) + { + } + + void logOperation( const char* _pOp ) + { + writeString( _pOp, true ); + } + + void logOperation( const char* _pOp, jlong _nLongArg ) + { + OString sLine( _pOp ); + sLine += "( "; + sLine += OString::number( _nLongArg ); + sLine += " )"; + writeString( sLine.getStr(), true ); + } + + void logReturn( jlong _nRetVal ) + { + OString sLine( " -> " ); + sLine += OString::number( _nRetVal ); + writeString( sLine.getStr(), true ); + } + + void logReturn( jint _nRetVal ) + { + OString sLine( " -> " ); + sLine += OString::number( _nRetVal ); + writeString( sLine.getStr(), true ); + } + + virtual void close() + { + writeString( "-------------------------------", true ); + writeString( "", true ); + LogFile::close(); + } + }; + + class DataLogFile : public LogFile + { + public: + DataLogFile( JNIEnv* env, jstring streamName, const char* _pAsciiSuffix ) + :LogFile( env, streamName, _pAsciiSuffix ) + { + } + + void write( jint value ) + { + fputc( value, getLogFile() ); + fflush( getLogFile() ); + } + + void write( const sal_Int8* buffer, sal_Int32 bytesRead ) + { + fwrite( buffer, sizeof(sal_Int8), bytesRead, getLogFile() ); + fflush( getLogFile() ); + } + + sal_Int64 seek( sal_Int64 pos ) + { + FILE* pFile = getLogFile(); + fseek( pFile, 0, SEEK_END ); + if ( ftell( pFile ) < pos ) + { + sal_Int8 filler( 0 ); + while ( ftell( pFile ) < pos ) + fwrite( &filler, sizeof( sal_Int8 ), 1, pFile ); + fflush( pFile ); + } + fseek( pFile, pos, SEEK_SET ); + return ftell( pFile ); + } + + sal_Int64 tell() + { + return ftell( getLogFile() ); + } + }; + +} } +#endif + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_HSQLDB_ACCESSLOG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/hsqldb/hsqldb.component b/connectivity/source/drivers/hsqldb/hsqldb.component new file mode 100644 index 000000000..ab8318861 --- /dev/null +++ b/connectivity/source/drivers/hsqldb/hsqldb.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="hsqldb" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.sdbcx.comp.hsqldb.Driver"> + <service name="com.sun.star.sdbc.Driver"/> + <service name="com.sun.star.sdbcx.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/jdbc/Array.cxx b/connectivity/source/drivers/jdbc/Array.cxx new file mode 100644 index 000000000..d03b01928 --- /dev/null +++ b/connectivity/source/drivers/jdbc/Array.cxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <java/sql/Array.hxx> +#include <java/tools.hxx> +#include <osl/diagnose.h> + +using namespace connectivity; + +//************ Class: java.sql.Array + + +jclass java_sql_Array::theClass = nullptr; + +java_sql_Array::~java_sql_Array() +{} + +jclass java_sql_Array::getMyClass() const +{ + // the class must be fetched once, therefore it's static + if( !theClass ) + theClass = findMyClass("java/sql/Array"); + + return theClass; +} + +OUString SAL_CALL java_sql_Array::getBaseTypeName( ) +{ + static jmethodID mID(nullptr); + return callStringMethod("getBaseTypeName",mID); +} + +sal_Int32 SAL_CALL java_sql_Array::getBaseType( ) +{ + static jmethodID mID(nullptr); + return callIntMethod_ThrowSQL("getBaseType", mID); +} + +css::uno::Sequence< css::uno::Any > SAL_CALL java_sql_Array::getArray( const css::uno::Reference< css::container::XNameAccess >& typeMap ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + jobject obj = convertTypeMapToJavaMap(typeMap); + static const char * const cSignature = "(Ljava/util/Map;)[Ljava/lang/Object;"; + static const char * const cMethodName = "getArray"; + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + // submit Java-Call + t.pEnv->CallObjectMethod( object, mID, obj); + ThrowSQLException(t.pEnv,*this); + // and clean up + t.pEnv->DeleteLocalRef(obj); + } //t.pEnv + return css::uno::Sequence< css::uno::Any >(); +} + +css::uno::Sequence< css::uno::Any > SAL_CALL java_sql_Array::getArrayAtIndex( sal_Int32 index, sal_Int32 count, const css::uno::Reference< css::container::XNameAccess >& typeMap ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + jobject obj = convertTypeMapToJavaMap(typeMap); + static const char * const cSignature = "(IILjava/util/Map;)[Ljava/lang/Object;"; + static const char * const cMethodName = "getArray"; + // submit Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + t.pEnv->CallObjectMethod( object, mID, index,count,obj); + ThrowSQLException(t.pEnv,*this); + // and clean up + t.pEnv->DeleteLocalRef(obj); + } + return css::uno::Sequence< css::uno::Any >(); +} + +css::uno::Reference< css::sdbc::XResultSet > SAL_CALL java_sql_Array::getResultSet( const css::uno::Reference< css::container::XNameAccess >& typeMap ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + // convert Parameter + jobject obj = convertTypeMapToJavaMap(typeMap); + // initialize temporary variable + static const char * const cSignature = "(Ljava/util/Map;)Ljava/sql/ResultSet;"; + static const char * const cMethodName = "getResultSet"; + // submit Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + t.pEnv->CallObjectMethod( object, mID, obj); + ThrowSQLException(t.pEnv,*this); + // and cleanup + t.pEnv->DeleteLocalRef(obj); + } + return nullptr; +} + +css::uno::Reference< css::sdbc::XResultSet > SAL_CALL java_sql_Array::getResultSetAtIndex( sal_Int32 index, sal_Int32 count, const css::uno::Reference< css::container::XNameAccess >& typeMap ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + // convert parameter + jobject obj = convertTypeMapToJavaMap(typeMap); + // initialize temporary variable + static const char * const cSignature = "(Ljava/util/Map;)Ljava/sql/ResultSet;"; + static const char * const cMethodName = "getResultSetAtIndex"; + // submit Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + t.pEnv->CallObjectMethod( object, mID, index,count,obj); + ThrowSQLException(t.pEnv,*this); + // and cleanup + t.pEnv->DeleteLocalRef(obj); + } + return nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Blob.cxx b/connectivity/source/drivers/jdbc/Blob.cxx new file mode 100644 index 000000000..adacd5d7c --- /dev/null +++ b/connectivity/source/drivers/jdbc/Blob.cxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/Blob.hxx> +#include <java/io/InputStream.hxx> +#include <connectivity/dbexception.hxx> +#include <osl/diagnose.h> + +#include <string.h> + +using namespace connectivity; + +//************ Class: java.sql.Blob + + +jclass java_sql_Blob::theClass = nullptr; +java_sql_Blob::java_sql_Blob( JNIEnv * pEnv, jobject myObj ) + : java_lang_Object( pEnv, myObj ) +{ + SDBThreadAttach::addRef(); +} +java_sql_Blob::~java_sql_Blob() +{ + SDBThreadAttach::releaseRef(); +} + +jclass java_sql_Blob::getMyClass() const +{ + // the class must be fetched only once, therefore it's static + if( !theClass ) + theClass = findMyClass("java/sql/Blob"); + return theClass; +} + +sal_Int64 SAL_CALL java_sql_Blob::length( ) +{ + jlong out(0); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + // initialize temporary variable + static const char * const cSignature = "()J"; + static const char * const cMethodName = "length"; + // submit Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + out = t.pEnv->CallLongMethod( object, mID ); + ThrowSQLException(t.pEnv,*this); + } //t.pEnv + return static_cast<sal_Int64>(out); +} +css::uno::Sequence< sal_Int8 > SAL_CALL java_sql_Blob::getBytes( sal_Int64 pos, sal_Int32 count ) +{ + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + css::uno::Sequence< sal_Int8 > aSeq; + { + // initialize temporary variable + static const char * const cSignature = "(JI)[B"; + static const char * const cMethodName = "getBytes"; + // submit Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + jbyteArray out = static_cast<jbyteArray>(t.pEnv->CallObjectMethod( object, mID,pos,count)); + ThrowSQLException(t.pEnv,*this); + if(out) + { + jboolean p = false; + aSeq.realloc(t.pEnv->GetArrayLength(out)); + memcpy(aSeq.getArray(),t.pEnv->GetByteArrayElements(out,&p),aSeq.getLength()); + t.pEnv->DeleteLocalRef(out); + } + } //t.pEnv + // WARNING: the caller becomes the owner of the returned pointer + return aSeq; +} + +css::uno::Reference< css::io::XInputStream > SAL_CALL java_sql_Blob::getBinaryStream( ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getBinaryStream","()Ljava/io/InputStream;", mID); + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_io_InputStream( t.pEnv, out ); +} + +sal_Int64 SAL_CALL java_sql_Blob::position( const css::uno::Sequence< sal_Int8 >& pattern, sal_Int64 start ) +{ + jlong out(0); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + // initialize temporary variable + static const char * const cSignature = "([BI)J"; + static const char * const cMethodName = "position"; + // submit Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + // convert Parameter + jbyteArray pByteArray = t.pEnv->NewByteArray(pattern.getLength()); + jbyte * patternData = reinterpret_cast<jbyte *>( + const_cast<sal_Int8 *>(pattern.getConstArray())); + // 4th param of Set*ArrayRegion changed from pointer to non-const to + // pointer to const between <http://docs.oracle.com/javase/6/docs/ + // technotes/guides/jni/spec/functions.html#wp22933> and + // <http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/ + // functions.html#wp22933>; work around that difference in a way + // that doesn't trigger loplugin:redundantcast + t.pEnv->SetByteArrayRegion(pByteArray,0,pattern.getLength(),patternData); + out = t.pEnv->CallLongMethod( object, mID, pByteArray,start ); + t.pEnv->DeleteLocalRef(pByteArray); + ThrowSQLException(t.pEnv,*this); + } //t.pEnv + return static_cast<sal_Int64>(out); +} + +sal_Int64 SAL_CALL java_sql_Blob::positionOfBlob( const css::uno::Reference< css::sdbc::XBlob >& /*pattern*/, sal_Int64 /*start*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XBlob::positionOfBlob", *this ); + // this was put here in CWS warnings01. The previous implementation was defective, as it did ignore + // the pattern parameter. Since the effort for proper implementation is rather high - we would need + // to translated patter into a byte[] -, we defer this functionality for the moment (hey, it was + // unusable, anyway) + // #i57457# + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Boolean.cxx b/connectivity/source/drivers/jdbc/Boolean.cxx new file mode 100644 index 000000000..acd1e81ef --- /dev/null +++ b/connectivity/source/drivers/jdbc/Boolean.cxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/lang/Boolean.hxx> +using namespace connectivity; + +//************ Class: java.lang.Boolean + + +jclass java_lang_Boolean::theClass = nullptr; + +java_lang_Boolean::~java_lang_Boolean() +{} +jclass java_lang_Boolean::st_getMyClass() +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/lang/Boolean"); + return theClass; +} + +jclass java_lang_Boolean::getMyClass() const +{ + return st_getMyClass(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/CallableStatement.cxx b/connectivity/source/drivers/jdbc/CallableStatement.cxx new file mode 100644 index 000000000..0994c827e --- /dev/null +++ b/connectivity/source/drivers/jdbc/CallableStatement.cxx @@ -0,0 +1,354 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/CallableStatement.hxx> +#include <java/tools.hxx> +#include <java/sql/Array.hxx> +#include <java/sql/Clob.hxx> +#include <java/sql/Blob.hxx> +#include <java/sql/Connection.hxx> +#include <java/sql/Ref.hxx> +#include <java/sql/Timestamp.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/sequence.hxx> + +#include <string.h> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +IMPLEMENT_SERVICE_INFO(java_sql_CallableStatement,"com.sun.star.sdbcx.ACallableStatement","com.sun.star.sdbc.CallableStatement"); + + +//************ Class: java.sql.CallableStatement + +java_sql_CallableStatement::java_sql_CallableStatement( JNIEnv * pEnv, java_sql_Connection& _rCon,const OUString& sql ) + : java_sql_PreparedStatement( pEnv, _rCon, sql ) +{ +} + +java_sql_CallableStatement::~java_sql_CallableStatement() +{ +} + + +Any SAL_CALL java_sql_CallableStatement::queryInterface( const Type & rType ) +{ + Any aRet = java_sql_PreparedStatement::queryInterface(rType); + return aRet.hasValue() ? aRet : ::cppu::queryInterface(rType,static_cast< css::sdbc::XRow*>(this),static_cast< css::sdbc::XOutParameters*>(this)); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL java_sql_CallableStatement::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<css::sdbc::XRow>::get(), + cppu::UnoType<css::sdbc::XOutParameters>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),java_sql_PreparedStatement::getTypes()); +} + +sal_Bool SAL_CALL java_sql_CallableStatement::wasNull( ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + return callBooleanMethod( "wasNull", mID ); +} + +sal_Bool SAL_CALL java_sql_CallableStatement::getBoolean( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "getBoolean", mID,columnIndex ); +} +sal_Int8 SAL_CALL java_sql_CallableStatement::getByte( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jbyte (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallByteMethod; + return callMethodWithIntArg<jbyte>(pCallMethod,"getByte","(I)B",mID,columnIndex); +} +Sequence< sal_Int8 > SAL_CALL java_sql_CallableStatement::getBytes( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + Sequence< sal_Int8 > aSeq; + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jbyteArray out = static_cast<jbyteArray>(callObjectMethodWithIntArg(t.pEnv,"getBytes","(I)[B", mID, columnIndex)); + if (out) + { + jboolean p = false; + aSeq.realloc(t.pEnv->GetArrayLength(out)); + memcpy(aSeq.getArray(),t.pEnv->GetByteArrayElements(out,&p),aSeq.getLength()); + t.pEnv->DeleteLocalRef(out); + } + return aSeq; +} +css::util::Date SAL_CALL java_sql_CallableStatement::getDate( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getDate","(I)Ljava/sql/Date;", mID, columnIndex); + return out ? static_cast <css::util::Date>(java_sql_Date( t.pEnv, out )) : css::util::Date(); +} +double SAL_CALL java_sql_CallableStatement::getDouble( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + double (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallDoubleMethod; + return callMethodWithIntArg<double>(pCallMethod,"getDouble","(I)D",mID,columnIndex); +} + +float SAL_CALL java_sql_CallableStatement::getFloat( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jfloat (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallFloatMethod; + return callMethodWithIntArg<jfloat>(pCallMethod,"getFloat","(I)F",mID,columnIndex); +} + +sal_Int32 SAL_CALL java_sql_CallableStatement::getInt( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + return callIntMethodWithIntArg_ThrowSQL("getInt",mID,columnIndex); +} + +sal_Int64 SAL_CALL java_sql_CallableStatement::getLong( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jlong (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallLongMethod; + return callMethodWithIntArg<jlong>(pCallMethod,"getLong","(I)J",mID,columnIndex); +} + +Any SAL_CALL java_sql_CallableStatement::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callObjectMethodWithIntArg(t.pEnv,"getObject","(I)Ljava/lang/Object;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return Any(); +} + +sal_Int16 SAL_CALL java_sql_CallableStatement::getShort( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jshort (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallShortMethod; + return callMethodWithIntArg<jshort>(pCallMethod,"getShort","(I)S",mID,columnIndex); +} + +OUString SAL_CALL java_sql_CallableStatement::getString( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getString",mID,columnIndex); +} + + css::util::Time SAL_CALL java_sql_CallableStatement::getTime( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getTime","(I)Ljava/sql/Time;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out ? static_cast <css::util::Time> (java_sql_Time( t.pEnv, out )) : css::util::Time(); +} + + css::util::DateTime SAL_CALL java_sql_CallableStatement::getTimestamp( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getTimestamp","(I)Ljava/sql/Timestamp;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out ? static_cast <css::util::DateTime> (java_sql_Timestamp( t.pEnv, out )) : css::util::DateTime(); +} + +void SAL_CALL java_sql_CallableStatement::registerOutParameter( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + createStatement(t.pEnv); + + // initialize temporary variable + static const char * const cSignature = "(IILjava/lang/String;)V"; + static const char * const cMethodName = "registerOutParameter"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + // Convert Parameter + jdbc::LocalRef< jstring > str( t.env(),convertwchar_tToJavaString(t.pEnv,typeName)); + t.pEnv->CallVoidMethod( object, mID, parameterIndex,sqlType,str.get()); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } +} +void SAL_CALL java_sql_CallableStatement::registerNumericOutParameter( sal_Int32 parameterIndex, sal_Int32 sqlType, sal_Int32 scale ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + createStatement(t.pEnv); + // initialize temporary variable + static const char * const cSignature = "(III)V"; + static const char * const cMethodName = "registerOutParameter"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + t.pEnv->CallVoidMethod( object, mID, parameterIndex,sqlType,scale); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } +} + +jclass java_sql_CallableStatement::theClass = nullptr; + +jclass java_sql_CallableStatement::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/CallableStatement"); + return theClass; +} + +Reference< css::io::XInputStream > SAL_CALL java_sql_CallableStatement::getBinaryStream( sal_Int32 columnIndex ) +{ + Reference< css::sdbc::XBlob > xBlob = getBlob(columnIndex); + return xBlob.is() ? xBlob->getBinaryStream() : Reference< css::io::XInputStream >(); +} +Reference< css::io::XInputStream > SAL_CALL java_sql_CallableStatement::getCharacterStream( sal_Int32 columnIndex ) +{ + Reference< css::sdbc::XClob > xClob = getClob(columnIndex); + return xClob.is() ? xClob->getCharacterStream() : Reference< css::io::XInputStream >(); +} + +Reference< css::sdbc::XArray > SAL_CALL java_sql_CallableStatement::getArray( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getArray","(I)Ljava/sql/Array;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_Array( t.pEnv, out ); +} + +Reference< css::sdbc::XClob > SAL_CALL java_sql_CallableStatement::getClob( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getClob","(I)Ljava/sql/Clob;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_Clob( t.pEnv, out ); +} +Reference< css::sdbc::XBlob > SAL_CALL java_sql_CallableStatement::getBlob( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getBlob","(I)Ljava/sql/Blob;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_Blob( t.pEnv, out ); +} + +Reference< css::sdbc::XRef > SAL_CALL java_sql_CallableStatement::getRef( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getRef","(I)Ljava/sql/Ref;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_Ref( t.pEnv, out ); +} + +void SAL_CALL java_sql_CallableStatement::acquire() throw() +{ + java_sql_PreparedStatement::acquire(); +} + +void SAL_CALL java_sql_CallableStatement::release() throw() +{ + java_sql_PreparedStatement::release(); +} + +void java_sql_CallableStatement::createStatement(JNIEnv* /*_pEnv*/) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + if( !(t.pEnv && !object) ) + return; + + // initialize temporary variable + static const char * const cMethodName = "prepareCall"; + // execute Java-Call + jobject out = nullptr; + // convert Parameter + jdbc::LocalRef< jstring > str( t.env(),convertwchar_tToJavaString(t.pEnv,m_sSqlStatement)); + + static jmethodID mID = [&]() + { + static const char * const cSignature = "(Ljava/lang/String;II)Ljava/sql/CallableStatement;"; + return t.pEnv->GetMethodID( m_pConnection->getMyClass(), cMethodName, cSignature ); + }(); + if( mID ){ + out = t.pEnv->CallObjectMethod( m_pConnection->getJavaObject(), mID, str.get() ,m_nResultSetType,m_nResultSetConcurrency); + } //mID + else + { + static const char * const cSignature2 = "(Ljava/lang/String;)Ljava/sql/CallableStatement;"; + static jmethodID mID2 = t.pEnv->GetMethodID( m_pConnection->getMyClass(), cMethodName, cSignature2 );OSL_ENSURE(mID2,"Unknown method id!"); + if( mID2 ){ + out = t.pEnv->CallObjectMethod( m_pConnection->getJavaObject(), mID2, str.get() ); + } //mID + } + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + + if ( out ) + object = t.pEnv->NewGlobalRef( out ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Class.cxx b/connectivity/source/drivers/jdbc/Class.cxx new file mode 100644 index 000000000..98d37fbcc --- /dev/null +++ b/connectivity/source/drivers/jdbc/Class.cxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/lang/Class.hxx> +#include <rtl/ustring.hxx> + +using namespace connectivity; + +//************ Class: java.lang.Class + + +jclass java_lang_Class::theClass = nullptr; + +java_lang_Class::~java_lang_Class() +{} + +jclass java_lang_Class::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/lang/Class"); + return theClass; +} + +java_lang_Class * java_lang_Class::forName( const OUString& _par0 ) +{ + jobject out(nullptr); + SDBThreadAttach t; + + { + OString sClassName = OUStringToOString(_par0, RTL_TEXTENCODING_JAVA_UTF8); + sClassName = sClassName.replace('.','/'); + out = t.pEnv->FindClass(sClassName.getStr()); + ThrowSQLException(t.pEnv,nullptr); + } //t.pEnv + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_lang_Class( t.pEnv, out ); +} + +jobject java_lang_Class::newInstanceObject() +{ + SDBThreadAttach t; + auto const id = t.pEnv->GetMethodID(static_cast<jclass>(object), "<init>", "()V"); + if (id == nullptr) { + ThrowSQLException(t.pEnv, nullptr); + } + auto const obj = t.pEnv->NewObject(static_cast<jclass>(object), id); + if (obj == nullptr) { + ThrowSQLException(t.pEnv, nullptr); + } + return obj; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Clob.cxx b/connectivity/source/drivers/jdbc/Clob.cxx new file mode 100644 index 000000000..135512d5a --- /dev/null +++ b/connectivity/source/drivers/jdbc/Clob.cxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <java/sql/Clob.hxx> +#include <java/tools.hxx> +#include <java/io/Reader.hxx> +#include <connectivity/dbexception.hxx> +#include <osl/diagnose.h> + +using namespace connectivity; + +//************ Class: java.sql.Clob + + +jclass java_sql_Clob::theClass = nullptr; + +java_sql_Clob::java_sql_Clob( JNIEnv * pEnv, jobject myObj ) + : java_lang_Object( pEnv, myObj ) +{ + SDBThreadAttach::addRef(); +} +java_sql_Clob::~java_sql_Clob() +{ + SDBThreadAttach::releaseRef(); +} + +jclass java_sql_Clob::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/Clob"); + return theClass; +} + +sal_Int64 SAL_CALL java_sql_Clob::length( ) +{ + jlong out(0); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + // initialize temporary variable + static const char * const cSignature = "()J"; + static const char * const cMethodName = "length"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + out = t.pEnv->CallLongMethod( object, mID ); + ThrowSQLException(t.pEnv,*this); + } //t.pEnv + return static_cast<sal_Int64>(out); +} + +OUString SAL_CALL java_sql_Clob::getSubString( sal_Int64 pos, sal_Int32 subStringLength ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + OUString aStr; + { + // initialize temporary variable + static const char * const cSignature = "(JI)Ljava/lang/String;"; + static const char * const cMethodName = "getSubString"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + jstring out = static_cast<jstring>(t.pEnv->CallObjectMethod( object, mID,pos,subStringLength)); + ThrowSQLException(t.pEnv,*this); + aStr = JavaString2String(t.pEnv,out); + } //t.pEnv + // WARNING: the caller becomes the owner of the returned pointer + return aStr; +} + +css::uno::Reference< css::io::XInputStream > SAL_CALL java_sql_Clob::getCharacterStream( ) +{ + SDBThreadAttach t; + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getCharacterStream","()Ljava/io/Reader;", mID); + + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_io_Reader( t.pEnv, out ); +} + +sal_Int64 SAL_CALL java_sql_Clob::position( const OUString& searchstr, sal_Int32 start ) +{ + jlong out(0); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + jvalue args[1]; + // convert Parameter + args[0].l = convertwchar_tToJavaString(t.pEnv,searchstr); + // initialize temporary Variable + static const char * const cSignature = "(Ljava/lang/String;I)J"; + static const char * const cMethodName = "position"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + out = t.pEnv->CallLongMethod( object, mID, args[0].l,start ); + ThrowSQLException(t.pEnv,*this); + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + } //t.pEnv + return static_cast<sal_Int64>(out); +} + +sal_Int64 SAL_CALL java_sql_Clob::positionOfClob( const css::uno::Reference< css::sdbc::XClob >& /*pattern*/, sal_Int64 /*start*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XClob::positionOfClob", *this ); + // this was put here in CWS warnings01. The previous implementation was defective, as it did ignore + // the pattern parameter. Since the effort for proper implementation is rather high - we would need + // to translated patter into a byte[] -, we defer this functionality for the moment (hey, it was + // unusable, anyway) + // 2005-11-15 / #i57457# / frank.schoenheit@sun.com + return 0; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/ConnectionLog.cxx b/connectivity/source/drivers/jdbc/ConnectionLog.cxx new file mode 100644 index 000000000..96a82fb61 --- /dev/null +++ b/connectivity/source/drivers/jdbc/ConnectionLog.cxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <java/sql/ConnectionLog.hxx> + +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> + +#include <stdio.h> + + +namespace connectivity::java::sql { + + + namespace + { + sal_Int32 lcl_getFreeID( ConnectionLog::ObjectType _eType ) + { + static oslInterlockedCount s_nCounts[ ConnectionLog::ObjectTypeCount ] = { 0, 0 }; + return osl_atomic_increment( s_nCounts + _eType ); + } + } + + ConnectionLog::ConnectionLog( const ::comphelper::EventLogger& _rDriverLog ) + :ConnectionLog_Base( _rDriverLog ) + ,m_nObjectID( lcl_getFreeID( CONNECTION ) ) + { + } + + + ConnectionLog::ConnectionLog( const ConnectionLog& _rSourceLog ) + :ConnectionLog_Base( _rSourceLog ) + ,m_nObjectID( _rSourceLog.m_nObjectID ) + { + } + + + ConnectionLog::ConnectionLog( const ConnectionLog& _rSourceLog, ConnectionLog::ObjectType _eType ) + :ConnectionLog_Base( _rSourceLog ) + ,m_nObjectID( lcl_getFreeID( _eType ) ) + { + } + + +} // namespace connectivity::java::sql + + +namespace comphelper::log::convert +{ + + + using ::com::sun::star::util::Date; + using ::com::sun::star::util::Time; + using ::com::sun::star::util::DateTime; + + + OUString convertLogArgToString( const Date& _rDate ) + { + char buffer[ 30 ]; + const size_t buffer_size = sizeof( buffer ); + snprintf( buffer, buffer_size, "%04i-%02i-%02i", + static_cast<int>(_rDate.Year), static_cast<int>(_rDate.Month), static_cast<int>(_rDate.Day) ); + return OUString::createFromAscii( buffer ); + } + + + OUString convertLogArgToString( const css::util::Time& _rTime ) + { + char buffer[ 30 ]; + const size_t buffer_size = sizeof( buffer ); + snprintf( buffer, buffer_size, "%02i:%02i:%02i.%09i", + static_cast<int>(_rTime.Hours), static_cast<int>(_rTime.Minutes), static_cast<int>(_rTime.Seconds), static_cast<int>(_rTime.NanoSeconds) ); + return OUString::createFromAscii( buffer ); + } + + + OUString convertLogArgToString( const DateTime& _rDateTime ) + { + char buffer[ sizeof("-32768-65535-65535 65535:65535:65535.4294967295") ]; + // reserve enough space for hypothetical max length + const size_t buffer_size = sizeof( buffer ); + snprintf( buffer, buffer_size, "%04" SAL_PRIdINT32 "-%02" SAL_PRIuUINT32 "-%02" SAL_PRIuUINT32 " %02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ".%09" SAL_PRIuUINT32, + static_cast<sal_Int32>(_rDateTime.Year), static_cast<sal_uInt32>(_rDateTime.Month), static_cast<sal_uInt32>(_rDateTime.Day), + static_cast<sal_uInt32>(_rDateTime.Hours), static_cast<sal_uInt32>(_rDateTime.Minutes), static_cast<sal_uInt32>(_rDateTime.Seconds), _rDateTime.NanoSeconds ); + return OUString::createFromAscii( buffer ); + } + + +} // comphelper::log::convert + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/ContextClassLoader.cxx b/connectivity/source/drivers/jdbc/ContextClassLoader.cxx new file mode 100644 index 000000000..50e6bc632 --- /dev/null +++ b/connectivity/source/drivers/jdbc/ContextClassLoader.cxx @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/ContextClassLoader.hxx> +#include <java/lang/Object.hxx> + + +namespace connectivity::jdbc +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + + using ::connectivity::java_lang_Object; + + ContextClassLoaderScope::ContextClassLoaderScope( JNIEnv& environment, const GlobalRef< jobject >& newClassLoader, + const ::comphelper::EventLogger& _rLoggerForErrors, const Reference< XInterface >& _rxErrorContext ) + :m_environment( environment ) + ,m_currentThread( environment ) + ,m_oldContextClassLoader( environment ) + ,m_setContextClassLoaderMethod( nullptr ) + { + if ( !newClassLoader.is() ) + return; + + do // artificial loop for easier flow control + { + + LocalRef< jclass > threadClass( m_environment ); + threadClass.set( m_environment.FindClass( "java/lang/Thread" ) ); + if ( !threadClass.is() ) + break; + + jmethodID currentThreadMethod( m_environment.GetStaticMethodID( + threadClass.get(), "currentThread", "()Ljava/lang/Thread;" ) ); + if ( currentThreadMethod == nullptr ) + break; + + m_currentThread.set( m_environment.CallStaticObjectMethod( threadClass.get(), currentThreadMethod ) ); + if ( !m_currentThread.is() ) + break; + + jmethodID getContextClassLoaderMethod( m_environment.GetMethodID( + threadClass.get(), "getContextClassLoader", "()Ljava/lang/ClassLoader;" ) ); + if ( getContextClassLoaderMethod == nullptr ) + break; + m_oldContextClassLoader.set( m_environment.CallObjectMethod( m_currentThread.get(), getContextClassLoaderMethod ) ); + LocalRef< jthrowable > throwable( m_environment, m_environment.ExceptionOccurred() ); + if ( throwable.is() ) + break; + + m_setContextClassLoaderMethod = m_environment.GetMethodID( + threadClass.get(), "setContextClassLoader", "(Ljava/lang/ClassLoader;)V" ); + if ( m_setContextClassLoaderMethod == nullptr ) + break; + + } + while ( false ); + + if ( !isActive() ) + { + java_lang_Object::ThrowLoggedSQLException( _rLoggerForErrors, &environment, _rxErrorContext ); + return; + } + + // set the new class loader + m_environment.CallObjectMethod( m_currentThread.get(), m_setContextClassLoaderMethod, newClassLoader.get() ); + LocalRef< jthrowable > throwable( m_environment, m_environment.ExceptionOccurred() ); + if ( throwable.is() ) + { + m_currentThread.reset(); + m_setContextClassLoaderMethod = nullptr; + java_lang_Object::ThrowLoggedSQLException( _rLoggerForErrors, &environment, _rxErrorContext ); + } + } + + + ContextClassLoaderScope::~ContextClassLoaderScope() + { + if ( isActive() ) + { + LocalRef< jobject > currentThread( m_currentThread.env(), m_currentThread.release() ); + jmethodID setContextClassLoaderMethod( m_setContextClassLoaderMethod ); + m_setContextClassLoaderMethod = nullptr; + + m_environment.CallObjectMethod( currentThread.get(), setContextClassLoaderMethod, m_oldContextClassLoader.get() ); + m_environment.ExceptionClear(); + } + } + +} // namespace connectivity::jdbc + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/DatabaseMetaData.cxx b/connectivity/source/drivers/jdbc/DatabaseMetaData.cxx new file mode 100644 index 000000000..e1918d11c --- /dev/null +++ b/connectivity/source/drivers/jdbc/DatabaseMetaData.cxx @@ -0,0 +1,1459 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/macros.h> +#include <java/sql/DatabaseMetaData.hxx> +#include <java/sql/Connection.hxx> +#include <java/sql/ResultSet.hxx> +#include <java/sql/SQLException.hxx> +#include <java/tools.hxx> +#include <java/lang/String.hxx> +#include <FDatabaseMetaDataResultSet.hxx> +#include <comphelper/types.hxx> +#include <TPrivilegesResultSet.hxx> +#include <strings.hxx> + +using namespace ::comphelper; + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +jclass java_sql_DatabaseMetaData::theClass = nullptr; + +java_sql_DatabaseMetaData::~java_sql_DatabaseMetaData() +{ + SDBThreadAttach::releaseRef(); +} + +jclass java_sql_DatabaseMetaData::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/DatabaseMetaData"); + return theClass; +} + +java_sql_DatabaseMetaData::java_sql_DatabaseMetaData( JNIEnv * pEnv, jobject myObj, java_sql_Connection& _rConnection ) + :ODatabaseMetaDataBase( &_rConnection,_rConnection.getConnectionInfo() ) + ,java_lang_Object( pEnv, myObj ) + ,m_pConnection( &_rConnection ) + ,m_aLogger( _rConnection.getLogger() ) +{ + SDBThreadAttach::addRef(); +} + + +Reference< XResultSet > java_sql_DatabaseMetaData::impl_getTypeInfo_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethod( "getTypeInfo", mID ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getCatalogs( ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethod( "getCatalogs", mID ); +} + +OUString java_sql_DatabaseMetaData::impl_getCatalogSeparator_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getCatalogSeparator", mID ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getSchemas( ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethod( "getSchemas", mID ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getColumnPrivileges( + const Any& catalog, const OUString& schema, const OUString& table, const OUString& columnNamePattern ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethodWithStrings( "getColumnPrivileges", mID, catalog, schema, table, &columnNamePattern ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getColumns( + const Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const OUString& columnNamePattern ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethodWithStrings( "getColumns", mID, catalog, schemaPattern, tableNamePattern, &columnNamePattern ); +} + + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getTables( + const Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const Sequence< OUString >& _types ) +{ + static const char * const cMethodName = "getTables"; + + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, cMethodName ); + + jobject out(nullptr); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + static const char * const cSignature = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)Ljava/sql/ResultSet;"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + OSL_VERIFY( !isExceptionOccurred(t.pEnv) ); + jvalue args[4]; + + args[3].l = nullptr; + sal_Int32 typeFilterCount = _types.getLength(); + if ( typeFilterCount ) + { + jobjectArray pObjArray = t.pEnv->NewObjectArray( static_cast<jsize>(typeFilterCount), java_lang_String::st_getMyClass(), nullptr ); + OSL_VERIFY( !isExceptionOccurred( t.pEnv ) ); + const OUString* typeFilter = _types.getConstArray(); + bool bIncludeAllTypes = false; + for ( sal_Int32 i=0; i<typeFilterCount; ++i, ++typeFilter ) + { + if ( *typeFilter == "%" ) + { + bIncludeAllTypes = true; + break; + } + jstring aT = convertwchar_tToJavaString( t.pEnv, *typeFilter ); + t.pEnv->SetObjectArrayElement( pObjArray, static_cast<jsize>(i), aT ); + OSL_VERIFY( !isExceptionOccurred( t.pEnv ) ); + } + + if ( bIncludeAllTypes ) + { + // the SDBC API allows to pass "%" as table type filter, but in JDBC, "all table types" + // is represented by the table type being <null/> + t.pEnv->DeleteLocalRef( pObjArray ); + OSL_VERIFY( !isExceptionOccurred( t.pEnv ) ); + } + else + { + args[3].l = pObjArray; + } + } + // if we are to display "all catalogs", then respect m_aCatalogRestriction + Any aCatalogFilter( catalog ); + if ( !aCatalogFilter.hasValue() ) + aCatalogFilter = m_pConnection->getCatalogRestriction(); + // similar for schema + Any aSchemaFilter; + if ( schemaPattern == "%" ) + aSchemaFilter = m_pConnection->getSchemaRestriction(); + else + aSchemaFilter <<= schemaPattern; + + args[0].l = aCatalogFilter.hasValue() ? convertwchar_tToJavaString( t.pEnv, ::comphelper::getString( aCatalogFilter ) ) : nullptr; + args[1].l = aSchemaFilter.hasValue() ? convertwchar_tToJavaString( t.pEnv, ::comphelper::getString( aSchemaFilter ) ) : nullptr; + args[2].l = convertwchar_tToJavaString(t.pEnv,tableNamePattern); + out = t.pEnv->CallObjectMethod( object, mID, args[0].l, args[1].l,args[2].l,args[3].l); + jthrowable jThrow = t.pEnv->ExceptionOccurred(); + if ( jThrow ) + t.pEnv->ExceptionClear();// we have to clear the exception here because we want to handle it below + if ( aCatalogFilter.hasValue() ) + { + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + OSL_VERIFY( !isExceptionOccurred( t.pEnv ) ); + } + if(args[1].l) + { + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[1].l)); + OSL_VERIFY( !isExceptionOccurred( t.pEnv ) ); + } + if(!tableNamePattern.isEmpty()) + { + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[2].l)); + OSL_VERIFY( !isExceptionOccurred( t.pEnv ) ); + } + //for(INT16 i=0;i<len;i++) + if ( args[3].l ) + { + t.pEnv->DeleteLocalRef( static_cast<jobjectArray>(args[3].l) ); + OSL_VERIFY( !isExceptionOccurred( t.pEnv ) ); + } + + if ( jThrow ) + { + if ( t.pEnv->IsInstanceOf( jThrow,java_sql_SQLException_BASE::st_getMyClass() ) ) + { + java_sql_SQLException_BASE aException( t.pEnv, jThrow ); + SQLException e( aException.getMessage(), + *this, + aException.getSQLState(), + aException.getErrorCode(), + Any() + ); + throw e; + } + } + } + + if ( !out ) + return nullptr; + + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_SUCCESS, cMethodName ); + return new java_sql_ResultSet( t.pEnv, out, m_aLogger,*m_pConnection); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getProcedureColumns( + const Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern, const OUString& columnNamePattern ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethodWithStrings( "getProcedureColumns", mID, catalog, schemaPattern, procedureNamePattern, &columnNamePattern ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getProcedures( const Any& + catalog, const OUString& schemaPattern, const OUString& procedureNamePattern ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethodWithStrings( "getProcedures", mID, catalog, schemaPattern, procedureNamePattern ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getVersionColumns( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethodWithStrings( "getVersionColumns", mID, catalog, schema, table ); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxBinaryLiteralLength", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxRowSize( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxRowSize", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxCatalogNameLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxCatalogNameLength", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxCharLiteralLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxCharLiteralLength", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxColumnNameLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxColumnNameLength", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxColumnsInIndex( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxColumnsInIndex", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxCursorNameLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxCursorNameLength", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxConnections( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxConnections", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxColumnsInTable( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxColumnsInTable", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxStatementLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxStatementLength", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxTableNameLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxTableNameLength", mID); +} + +sal_Int32 java_sql_DatabaseMetaData::impl_getMaxTablesInSelect_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxTablesInSelect", mID); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getExportedKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethodWithStrings( "getExportedKeys", mID, catalog, schema, table ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getImportedKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethodWithStrings( "getImportedKeys", mID, catalog, schema, table ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getPrimaryKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethodWithStrings( "getPrimaryKeys", mID, catalog, schema, table ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getIndexInfo( + const Any& catalog, const OUString& schema, const OUString& table, + sal_Bool unique, sal_Bool approximate ) +{ + static const char * const cMethodName = "getIndexInfo"; + + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, cMethodName ); + + jobject out(nullptr); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + static const char * const cSignature = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)Ljava/sql/ResultSet;"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + jvalue args[5]; + // convert Parameter + args[0].l = catalog.hasValue() ? convertwchar_tToJavaString(t.pEnv,comphelper::getString(catalog)) : nullptr; + args[1].l = schema.toChar() == '%' ? nullptr : convertwchar_tToJavaString(t.pEnv,schema); + args[2].l = convertwchar_tToJavaString(t.pEnv,table); + args[3].z = unique; + args[4].z = approximate; + out = t.pEnv->CallObjectMethod( object, mID, args[0].l,args[1].l,args[2].l,args[3].z,args[4].z ); + + // and clean up + if(catalog.hasValue()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + if(args[1].l) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[1].l)); + if(!table.isEmpty()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[2].l)); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + if ( !out ) + return nullptr; + + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_SUCCESS, cMethodName ); + return new java_sql_ResultSet( t.pEnv, out, m_aLogger,*m_pConnection); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getBestRowIdentifier( + const Any& catalog, const OUString& schema, const OUString& table, sal_Int32 scope, + sal_Bool nullable ) +{ + static const char * const cMethodName = "getBestRowIdentifier"; + + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, cMethodName ); + + jobject out(nullptr); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + static const char * const cSignature = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZ)Ljava/sql/ResultSet;"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + jvalue args[3]; + // convert Parameter + args[0].l = catalog.hasValue() ? convertwchar_tToJavaString(t.pEnv,comphelper::getString(catalog)) : nullptr; + args[1].l = schema.toChar() == '%' ? nullptr : convertwchar_tToJavaString(t.pEnv,schema); + args[2].l = convertwchar_tToJavaString(t.pEnv,table); + out = t.pEnv->CallObjectMethod( object, mID, args[0].l,args[1].l,args[2].l,scope,nullable); + + // and cleanup + if(catalog.hasValue()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + if(args[1].l) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[1].l)); + if(!table.isEmpty()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[2].l)); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + + if ( !out ) + return nullptr; + + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_SUCCESS, cMethodName ); + return new java_sql_ResultSet( t.pEnv, out, m_aLogger,*m_pConnection); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getTablePrivileges( + const Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern ) +{ + if ( m_pConnection->isIgnoreDriverPrivilegesEnabled() ) + return new OResultSetPrivileges(this,catalog,schemaPattern,tableNamePattern); + + static jmethodID mID(nullptr); + Reference< XResultSet > xReturn( impl_callResultSetMethodWithStrings( "getTablePrivileges", mID, catalog, schemaPattern, tableNamePattern ) ); + + if ( xReturn.is() ) + { + // we have to check the result columns for the tables privileges + Reference< XResultSetMetaDataSupplier > xMetaSup(xReturn,UNO_QUERY); + if ( xMetaSup.is() ) + { + Reference< XResultSetMetaData> xMeta = xMetaSup->getMetaData(); + if ( xMeta.is() && xMeta->getColumnCount() != 7 ) + { + // here we know that the count of column doesn't match + std::map<sal_Int32,sal_Int32> aColumnMatching; + static const OUStringLiteral sPrivs[] = { + "TABLE_CAT", + "TABLE_SCHEM", + "TABLE_NAME", + "GRANTOR", + "GRANTEE", + "PRIVILEGE", + "IS_GRANTABLE" + }; + + OUString sColumnName; + sal_Int32 nCount = xMeta->getColumnCount(); + for (sal_Int32 i = 1 ; i <= nCount ; ++i) + { + sColumnName = xMeta->getColumnName(i); + for (size_t j = 0 ; j < SAL_N_ELEMENTS(sPrivs); ++j) + { + if ( sPrivs[j] == sColumnName ) + { + aColumnMatching.emplace(i,j+1); + break; + } + } + + } + // fill our own resultset + ODatabaseMetaDataResultSet* pNewPrivRes = new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTablePrivileges ); + Reference< XResultSet > xTemp = xReturn; + xReturn = pNewPrivRes; + ODatabaseMetaDataResultSet::ORows aRows; + Reference< XRow > xRow(xTemp,UNO_QUERY); + OUString sValue; + + ODatabaseMetaDataResultSet::ORow aRow(8); + while ( xRow.is() && xTemp->next() ) + { + for (const auto& [nCol, nPriv] : aColumnMatching) + { + sValue = xRow->getString(nCol); + if ( xRow->wasNull() ) + aRow[nPriv] = ODatabaseMetaDataResultSet::getEmptyValue(); + else + aRow[nPriv] = new ORowSetValueDecorator(sValue); + } + + aRows.push_back(aRow); + } + pNewPrivRes->setRows(aRows); + } + } + } + return xReturn; +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getCrossReference( + const Any& primaryCatalog, const OUString& primarySchema, + const OUString& primaryTable, const Any& foreignCatalog, + const OUString& foreignSchema, const OUString& foreignTable ) +{ + static const char * const cMethodName = "getCrossReference"; + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, cMethodName ); + + jobject out(nullptr); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + static const char * const cSignature = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/sql/ResultSet;"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + jvalue args[6]; + // convert Parameter + args[0].l = primaryCatalog.hasValue() ? convertwchar_tToJavaString(t.pEnv,comphelper::getString(primaryCatalog)) : nullptr; + args[1].l = primarySchema.toChar() == '%' ? nullptr : convertwchar_tToJavaString(t.pEnv,primarySchema); + args[2].l = convertwchar_tToJavaString(t.pEnv,primaryTable); + args[3].l = foreignCatalog.hasValue() ? convertwchar_tToJavaString(t.pEnv,comphelper::getString(foreignCatalog)) : nullptr; + args[4].l = foreignSchema.toChar() == '%' ? nullptr : convertwchar_tToJavaString(t.pEnv,foreignSchema); + args[5].l = convertwchar_tToJavaString(t.pEnv,foreignTable); + out = t.pEnv->CallObjectMethod( object, mID, args[0].l,args[2].l,args[2].l,args[3].l,args[4].l,args[5].l ); + + // and clean up + if(primaryCatalog.hasValue()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + if(args[1].l) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[1].l)); + if(!primaryTable.isEmpty()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[2].l)); + if(foreignCatalog.hasValue()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[3].l)); + if(args[4].l) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[4].l)); + if(!foreignTable.isEmpty()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[5].l)); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + + if ( !out ) + return nullptr; + + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_SUCCESS, cMethodName ); + return new java_sql_ResultSet( t.pEnv, out, m_aLogger,*m_pConnection); +} + + +bool java_sql_DatabaseMetaData::impl_callBooleanMethod( const char* _pMethodName, jmethodID& _inout_MethodID ) +{ + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, _pMethodName ); + bool out( java_lang_Object::callBooleanMethod(_pMethodName,_inout_MethodID) ); + m_aLogger.log< const char*, bool>( LogLevel::FINEST, STR_LOG_META_DATA_RESULT, _pMethodName, out ); + return out; +} + + +OUString java_sql_DatabaseMetaData::impl_callStringMethod( const char* _pMethodName, jmethodID& _inout_MethodID ) +{ + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, _pMethodName ); + + const OUString sReturn( callStringMethod(_pMethodName,_inout_MethodID) ); + if ( m_aLogger.isLoggable( LogLevel::FINEST ) ) + { + OUString sLoggedResult( sReturn ); + if ( sLoggedResult.isEmpty() ) + sLoggedResult = "<empty string>"; + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_RESULT, _pMethodName, sLoggedResult ); + } + + return sReturn; +} + +sal_Int32 java_sql_DatabaseMetaData::impl_callIntMethod_ThrowSQL(const char* _pMethodName, jmethodID& _inout_MethodID) +{ + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, _pMethodName ); + sal_Int32 out( callIntMethod_ThrowSQL(_pMethodName,_inout_MethodID) ); + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_RESULT, _pMethodName, out ); + return out; +} + +sal_Int32 java_sql_DatabaseMetaData::impl_callIntMethod_ThrowRuntime(const char* _pMethodName, jmethodID& _inout_MethodID) +{ + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, _pMethodName ); + sal_Int32 out( callIntMethod_ThrowRuntime(_pMethodName,_inout_MethodID) ); + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_RESULT, _pMethodName, out ); + return out; +} + +bool java_sql_DatabaseMetaData::impl_callBooleanMethodWithIntArg( const char* _pMethodName, jmethodID& _inout_MethodID, sal_Int32 _nArgument ) +{ + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD_ARG1, _pMethodName, _nArgument ); + + bool out( callBooleanMethodWithIntArg(_pMethodName,_inout_MethodID,_nArgument) ); + + m_aLogger.log< const char*, bool >( LogLevel::FINEST, STR_LOG_META_DATA_RESULT, _pMethodName, out ); + return out; +} + + +Reference< XResultSet > java_sql_DatabaseMetaData::impl_callResultSetMethod( const char* _pMethodName, jmethodID& _inout_MethodID ) +{ + SDBThreadAttach t; + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD, _pMethodName ); + jobject out(callResultSetMethod(t.env(),_pMethodName,_inout_MethodID)); + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_SUCCESS, _pMethodName ); + return new java_sql_ResultSet( t.pEnv, out, m_aLogger,*m_pConnection); +} + + +Reference< XResultSet > java_sql_DatabaseMetaData::impl_callResultSetMethodWithStrings( const char* _pMethodName, jmethodID& _inout_MethodID, + const Any& _rCatalog, const OUString& _rSchemaPattern, const OUString& _rLeastPattern, + const OUString* _pOptionalAdditionalString ) +{ + bool bCatalog = _rCatalog.hasValue(); + OUString sCatalog; + _rCatalog >>= sCatalog; + + bool bSchema = _rSchemaPattern.toChar() != '%'; + + // log the call + if ( m_aLogger.isLoggable( LogLevel::FINEST ) ) + { + OUString sCatalogLog = bCatalog ? sCatalog : OUString( "null" ); + OUString sSchemaLog = bSchema ? _rSchemaPattern : OUString( "null" ); + if ( _pOptionalAdditionalString ) + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD_ARG4, _pMethodName, sCatalogLog, sSchemaLog, _rLeastPattern, *_pOptionalAdditionalString ); + else + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD_ARG3, _pMethodName, sCatalogLog, sSchemaLog, _rLeastPattern ); + } + + jobject out(nullptr); + + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_sql_DatabaseMetaData::impl_callResultSetMethodWithStrings: no Java environment anymore!" ); + + { + const char* pSignature = _pOptionalAdditionalString + ? "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/sql/ResultSet;" + : "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/sql/ResultSet;"; + // obtain method ID + obtainMethodId_throwSQL(t.pEnv, _pMethodName,pSignature, _inout_MethodID); + + // call method + + { + jvalue args[4]; + // convert parameters + args[0].l = bCatalog ? convertwchar_tToJavaString( t.pEnv, sCatalog ) : nullptr; + args[1].l = bSchema ? convertwchar_tToJavaString( t.pEnv, _rSchemaPattern ) : nullptr; + args[2].l = convertwchar_tToJavaString( t.pEnv, _rLeastPattern ); + args[3].l = _pOptionalAdditionalString ? convertwchar_tToJavaString( t.pEnv, *_pOptionalAdditionalString ) : nullptr; + + // actually do the call + if ( _pOptionalAdditionalString ) + out = t.pEnv->CallObjectMethod( object, _inout_MethodID, args[0].l, args[1].l, args[2].l, args[3].l ); + else + out = t.pEnv->CallObjectMethod( object, _inout_MethodID, args[0].l, args[1].l, args[2].l ); + + // clean up + if ( args[0].l ) + t.pEnv->DeleteLocalRef( static_cast<jstring>(args[0].l) ); + if ( args[1].l ) + t.pEnv->DeleteLocalRef( static_cast<jstring>(args[1].l) ); + if ( args[2].l ) + t.pEnv->DeleteLocalRef( static_cast<jstring>(args[2].l) ); + if ( args[3].l ) + t.pEnv->DeleteLocalRef( static_cast<jstring>(args[3].l) ); + + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } + + if ( !out ) + return nullptr; + + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_SUCCESS, _pMethodName ); + return new java_sql_ResultSet( t.pEnv, out, m_aLogger,*m_pConnection); +} + + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "doesMaxRowSizeIncludeBlobs", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::storesLowerCaseQuotedIdentifiers( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "storesLowerCaseQuotedIdentifiers", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::storesLowerCaseIdentifiers( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "storesLowerCaseIdentifiers", mID ); +} + +bool java_sql_DatabaseMetaData::impl_storesMixedCaseQuotedIdentifiers_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "storesMixedCaseQuotedIdentifiers", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::storesMixedCaseIdentifiers( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "storesMixedCaseIdentifiers", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::storesUpperCaseQuotedIdentifiers( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "storesUpperCaseQuotedIdentifiers", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::storesUpperCaseIdentifiers( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "storesUpperCaseIdentifiers", mID ); +} + +bool java_sql_DatabaseMetaData::impl_supportsAlterTableWithAddColumn_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsAlterTableWithAddColumn", mID ); +} + +bool java_sql_DatabaseMetaData::impl_supportsAlterTableWithDropColumn_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsAlterTableWithDropColumn", mID ); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxIndexLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxIndexLength", mID); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsNonNullableColumns( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsNonNullableColumns", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getCatalogTerm( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getCatalogTerm", mID ); +} + +OUString java_sql_DatabaseMetaData::impl_getIdentifierQuoteString_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getIdentifierQuoteString", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getExtraNameCharacters( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getExtraNameCharacters", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsDifferentTableCorrelationNames( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsDifferentTableCorrelationNames", mID ); +} + +bool java_sql_DatabaseMetaData::impl_isCatalogAtStart_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "isCatalogAtStart", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::dataDefinitionIgnoredInTransactions( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "dataDefinitionIgnoredInTransactions", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::dataDefinitionCausesTransactionCommit( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "dataDefinitionCausesTransactionCommit", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsDataManipulationTransactionsOnly( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsDataManipulationTransactionsOnly", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsDataDefinitionAndDataManipulationTransactions( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsDataDefinitionAndDataManipulationTransactions", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsPositionedDelete( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsPositionedDelete", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsPositionedUpdate( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsPositionedUpdate", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsOpenStatementsAcrossRollback( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsOpenStatementsAcrossRollback", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsOpenStatementsAcrossCommit( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsOpenStatementsAcrossCommit", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsOpenCursorsAcrossCommit( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsOpenCursorsAcrossCommit", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsOpenCursorsAcrossRollback( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsOpenCursorsAcrossRollback", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsTransactionIsolationLevel( sal_Int32 level ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "supportsTransactionIsolationLevel", mID, level ); +} + +bool java_sql_DatabaseMetaData::impl_supportsSchemasInDataManipulation_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSchemasInDataManipulation", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsANSI92FullSQL( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsANSI92FullSQL", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsANSI92EntryLevelSQL( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsANSI92EntryLevelSQL", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsIntegrityEnhancementFacility( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsIntegrityEnhancementFacility", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsSchemasInIndexDefinitions( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSchemasInIndexDefinitions", mID ); +} + +bool java_sql_DatabaseMetaData::impl_supportsSchemasInTableDefinitions_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSchemasInTableDefinitions", mID ); +} + +bool java_sql_DatabaseMetaData::impl_supportsCatalogsInTableDefinitions_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsCatalogsInTableDefinitions", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsCatalogsInIndexDefinitions( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsCatalogsInIndexDefinitions", mID ); +} + +bool java_sql_DatabaseMetaData::impl_supportsCatalogsInDataManipulation_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsCatalogsInDataManipulation", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsOuterJoins( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsOuterJoins", mID ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getTableTypes( ) +{ + static jmethodID mID(nullptr); + return impl_callResultSetMethod( "getTableTypes", mID ); +} + +sal_Int32 java_sql_DatabaseMetaData::impl_getMaxStatements_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxStatements", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxProcedureNameLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxProcedureNameLength", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxSchemaNameLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxSchemaNameLength", mID); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsTransactions( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsTransactions", mID ); +} + + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::allProceduresAreCallable( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "allProceduresAreCallable", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsStoredProcedures( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsStoredProcedures", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsSelectForUpdate( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSelectForUpdate", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::allTablesAreSelectable( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "allTablesAreSelectable", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::isReadOnly( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "isReadOnly", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::usesLocalFiles( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "usesLocalFiles", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::usesLocalFilePerTable( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "usesLocalFilePerTable", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsTypeConversion( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsTypeConversion", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::nullPlusNonNullIsNull( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "nullPlusNonNullIsNull", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsColumnAliasing( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsColumnAliasing", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsTableCorrelationNames( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsTableCorrelationNames", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsConvert( sal_Int32 fromType, sal_Int32 toType ) +{ + static const char* const pMethodName = "supportsConvert"; + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD_ARG2, pMethodName, fromType, toType ); + + bool out( false ); + SDBThreadAttach t; + + { + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, pMethodName,"(II)Z", mID); + out = t.pEnv->CallBooleanMethod( object, mID, fromType, toType ); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + + m_aLogger.log< const char*, bool >( LogLevel::FINEST, STR_LOG_META_DATA_RESULT, pMethodName, out ); + return out; +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsExpressionsInOrderBy( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsExpressionsInOrderBy", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsGroupBy( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsGroupBy", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsGroupByBeyondSelect( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsGroupByBeyondSelect", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsGroupByUnrelated( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsGroupByUnrelated", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsMultipleTransactions( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsMultipleTransactions", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsMultipleResultSets( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsMultipleResultSets", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsLikeEscapeClause( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsLikeEscapeClause", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsOrderByUnrelated( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsOrderByUnrelated", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsUnion( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsUnion", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsUnionAll( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsUnionAll", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsMixedCaseIdentifiers( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsMixedCaseIdentifiers", mID ); +} + +bool java_sql_DatabaseMetaData::impl_supportsMixedCaseQuotedIdentifiers_throw( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsMixedCaseQuotedIdentifiers", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::nullsAreSortedAtEnd( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "nullsAreSortedAtEnd", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::nullsAreSortedAtStart( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "nullsAreSortedAtStart", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::nullsAreSortedHigh( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "nullsAreSortedHigh", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::nullsAreSortedLow( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "nullsAreSortedLow", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsSchemasInProcedureCalls( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSchemasInProcedureCalls", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsSchemasInPrivilegeDefinitions( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSchemasInPrivilegeDefinitions", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsCatalogsInProcedureCalls( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsCatalogsInProcedureCalls", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsCatalogsInPrivilegeDefinitions( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsCatalogsInPrivilegeDefinitions", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsCorrelatedSubqueries( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsCorrelatedSubqueries", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsSubqueriesInComparisons( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSubqueriesInComparisons", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsSubqueriesInExists( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSubqueriesInExists", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsSubqueriesInIns( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSubqueriesInIns", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsSubqueriesInQuantifieds( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsSubqueriesInQuantifieds", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsANSI92IntermediateSQL( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsANSI92IntermediateSQL", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getURL( ) +{ + OUString sURL = m_pConnection->getURL(); + if ( sURL.isEmpty() ) + { + static jmethodID mID(nullptr); + sURL = impl_callStringMethod( "getURL", mID ); + } + return sURL; +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getUserName( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getUserName", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getDriverName( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getDriverName", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getDriverVersion( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getDriverVersion", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getDatabaseProductVersion( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getDatabaseProductVersion", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getDatabaseProductName( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getDatabaseProductName", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getProcedureTerm( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getProcedureTerm", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getSchemaTerm( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getSchemaTerm", mID ); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getDriverMajorVersion( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowRuntime("getDriverMajorVersion", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getDefaultTransactionIsolation( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getDefaultTransactionIsolation", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getDriverMinorVersion( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowRuntime("getDriverMinorVersion", mID); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getSQLKeywords( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getSQLKeywords", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getSearchStringEscape( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getSearchStringEscape", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getStringFunctions( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getStringFunctions", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getTimeDateFunctions( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getTimeDateFunctions", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getSystemFunctions( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getSystemFunctions", mID ); +} + +OUString SAL_CALL java_sql_DatabaseMetaData::getNumericFunctions( ) +{ + static jmethodID mID(nullptr); + return impl_callStringMethod( "getNumericFunctions", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsExtendedSQLGrammar( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsExtendedSQLGrammar", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsCoreSQLGrammar( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsCoreSQLGrammar", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsMinimumSQLGrammar( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsMinimumSQLGrammar", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsFullOuterJoins( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsFullOuterJoins", mID ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsLimitedOuterJoins( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsLimitedOuterJoins", mID ); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxColumnsInGroupBy( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxColumnsInGroupBy", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxColumnsInOrderBy", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxColumnsInSelect( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxColumnsInSelect", mID); +} + +sal_Int32 SAL_CALL java_sql_DatabaseMetaData::getMaxUserNameLength( ) +{ + static jmethodID mID(nullptr); + return impl_callIntMethod_ThrowSQL("getMaxUserNameLength", mID); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsResultSetType( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "supportsResultSetType", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 concurrency ) +{ + static const char* const pMethodName = "supportsResultSetConcurrency"; + m_aLogger.log( LogLevel::FINEST, STR_LOG_META_DATA_METHOD_ARG2, pMethodName, setType, concurrency ); + + bool out( false ); + SDBThreadAttach t; + + { + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, pMethodName,"(II)Z", mID); + out = t.pEnv->CallBooleanMethod( object, mID, setType, concurrency); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + + m_aLogger.log< const char*, bool >( LogLevel::FINEST, STR_LOG_META_DATA_RESULT, pMethodName, out ); + return out; +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::ownUpdatesAreVisible( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "ownUpdatesAreVisible", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::ownDeletesAreVisible( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "ownDeletesAreVisible", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::ownInsertsAreVisible( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "ownInsertsAreVisible", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::othersUpdatesAreVisible( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "othersUpdatesAreVisible", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::othersDeletesAreVisible( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "othersDeletesAreVisible", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::othersInsertsAreVisible( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "othersInsertsAreVisible", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::updatesAreDetected( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "updatesAreDetected", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::deletesAreDetected( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "deletesAreDetected", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::insertsAreDetected( sal_Int32 setType ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethodWithIntArg( "insertsAreDetected", mID, setType ); +} + +sal_Bool SAL_CALL java_sql_DatabaseMetaData::supportsBatchUpdates( ) +{ + static jmethodID mID(nullptr); + return impl_callBooleanMethod( "supportsBatchUpdates", mID ); +} + +Reference< XResultSet > SAL_CALL java_sql_DatabaseMetaData::getUDTs( + const Any& catalog, const OUString& schemaPattern, const OUString& typeNamePattern, + const Sequence< sal_Int32 >& types ) +{ + jobject out(nullptr); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + + + static const char * const cSignature = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/sql/ResultSet;"; + static const char * const cMethodName = "getUDTs"; + // dismiss Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + { + jvalue args[4]; + // initialize temporary Variable + args[0].l = catalog.hasValue() ? convertwchar_tToJavaString(t.pEnv,comphelper::getString(catalog)) : nullptr; + args[1].l = schemaPattern.toChar() == '%' ? nullptr : convertwchar_tToJavaString(t.pEnv,schemaPattern); + args[2].l = convertwchar_tToJavaString(t.pEnv,typeNamePattern); + jintArray pArray = t.pEnv->NewIntArray(types.getLength()); + jint * typesData = reinterpret_cast<jint *>( + const_cast<sal_Int32 *>(types.getConstArray())); + // 4th param of Set*ArrayRegion changed from pointer to non-const to + // pointer to const between <http://docs.oracle.com/javase/6/docs/ + // technotes/guides/jni/spec/functions.html#wp22933> and + // <http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/ + // functions.html#wp22933>; work around that difference in a way + // that doesn't trigger loplugin:redundantcast + t.pEnv->SetIntArrayRegion(pArray,0,types.getLength(),typesData); + args[3].l = pArray; + + out = t.pEnv->CallObjectMethod( object, mID, args[0].l, args[1].l,args[2].l,args[3].l); + + if(catalog.hasValue()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + if(!schemaPattern.isEmpty()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[1].l)); + if(!typeNamePattern.isEmpty()) + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[2].l)); + if(args[3].l) + t.pEnv->DeleteLocalRef(static_cast<jintArray>(args[3].l)); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } + + return out ? new java_sql_ResultSet( t.pEnv, out, m_aLogger,*m_pConnection ) : nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Date.cxx b/connectivity/source/drivers/jdbc/Date.cxx new file mode 100644 index 000000000..8adaf455a --- /dev/null +++ b/connectivity/source/drivers/jdbc/Date.cxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <java/util/Date.hxx> + +using namespace connectivity; + +//************ Class: java.util.Date + + +jclass java_util_Date::theClass = nullptr; + +java_util_Date::~java_util_Date() +{} + +jclass java_util_Date::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/util/Date"); + return theClass; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/DriverPropertyInfo.cxx b/connectivity/source/drivers/jdbc/DriverPropertyInfo.cxx new file mode 100644 index 000000000..9c33eeb60 --- /dev/null +++ b/connectivity/source/drivers/jdbc/DriverPropertyInfo.cxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/DriverPropertyInfo.hxx> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + + +//************ Class: java.sql.Driver + + +jclass java_sql_DriverPropertyInfo::theClass = nullptr; + +java_sql_DriverPropertyInfo::~java_sql_DriverPropertyInfo() +{} + +jclass java_sql_DriverPropertyInfo::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/DriverPropertyInfo"); + return theClass; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Exception.cxx b/connectivity/source/drivers/jdbc/Exception.cxx new file mode 100644 index 000000000..2fdef4ee8 --- /dev/null +++ b/connectivity/source/drivers/jdbc/Exception.cxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/lang/Exception.hxx> +using namespace connectivity; + +//************ Class: java.lang.Exception + + +jclass java_lang_Exception::theClass = nullptr; + +java_lang_Exception::~java_lang_Exception() +{} + +jclass java_lang_Exception::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/lang/Exception"); + return theClass; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/InputStream.cxx b/connectivity/source/drivers/jdbc/InputStream.cxx new file mode 100644 index 000000000..c384760b3 --- /dev/null +++ b/connectivity/source/drivers/jdbc/InputStream.cxx @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <com/sun/star/io/BufferSizeExceededException.hpp> +#include <java/io/InputStream.hxx> +#include <osl/diagnose.h> + +#include <string.h> + +using namespace connectivity; + +#if OSL_DEBUG_LEVEL > 0 +#define THROW_WHERE SAL_WHERE +#else +#define THROW_WHERE "" +#endif + + +//************ Class: java.io.InputStream + + +jclass java_io_InputStream::theClass = nullptr; +java_io_InputStream::java_io_InputStream( JNIEnv * pEnv, jobject myObj ) + : java_lang_Object( pEnv, myObj ) +{ + SDBThreadAttach::addRef(); +} +java_io_InputStream::~java_io_InputStream() +{ + SDBThreadAttach::releaseRef(); +} + +jclass java_io_InputStream::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/io/InputStream"); + return theClass; +} + + +sal_Int32 SAL_CALL java_io_InputStream::readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) +{ + return readBytes(aData,nMaxBytesToRead); +} + +void SAL_CALL java_io_InputStream::skipBytes( sal_Int32 nBytesToSkip ) +{ + static jmethodID mID(nullptr); + callIntMethodWithIntArg_ThrowRuntime("skip",mID,nBytesToSkip); +} + +sal_Int32 SAL_CALL java_io_InputStream::available( ) +{ + static jmethodID mID(nullptr); + return callIntMethod_ThrowRuntime("available", mID); +} + +void SAL_CALL java_io_InputStream::closeInput( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowRuntime("close",mID); +} + +sal_Int32 SAL_CALL java_io_InputStream::readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) +{ + if (nBytesToRead < 0) + throw css::io::BufferSizeExceededException( THROW_WHERE, *this ); + + jint out(0); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + jbyteArray pByteArray = t.pEnv->NewByteArray(nBytesToRead); + static const char * const cSignature = "([BII)I"; + static const char * const cMethodName = "read"; + // execute Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwRuntime(t.pEnv, cMethodName,cSignature, mID); + out = t.pEnv->CallIntMethod( object, mID, pByteArray, 0, nBytesToRead ); + if ( !out ) + ThrowRuntimeException(t.pEnv,*this); + if(out > 0) + { + jboolean p = false; + aData.realloc ( out ); + memcpy(aData.getArray(),t.pEnv->GetByteArrayElements(pByteArray,&p),out); + } + t.pEnv->DeleteLocalRef(pByteArray); + } //t.pEnv + return out; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/JBigDecimal.cxx b/connectivity/source/drivers/jdbc/JBigDecimal.cxx new file mode 100644 index 000000000..7cbcd6486 --- /dev/null +++ b/connectivity/source/drivers/jdbc/JBigDecimal.cxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <java/math/BigDecimal.hxx> +#include <java/tools.hxx> +using namespace connectivity; + +//************ Class: java.lang.Boolean + + +jclass java_math_BigDecimal::theClass = nullptr; + +java_math_BigDecimal::~java_math_BigDecimal() +{} + +jclass java_math_BigDecimal::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/math/BigDecimal"); + return theClass; +} + +java_math_BigDecimal::java_math_BigDecimal( const OUString& _par0 ): java_lang_Object( nullptr, nullptr ) +{ + SDBThreadAttach t; + if( !t.pEnv ) + return; + // Java-Call for the Constructor + // initialize temporary Variable + static const char * const cSignature = "(Ljava/lang/String;)V"; + jobject tempObj; + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, "<init>",cSignature, mID); + + jstring str = convertwchar_tToJavaString(t.pEnv,_par0.replace(',','.')); + tempObj = t.pEnv->NewObject( getMyClass(), mID, str ); + t.pEnv->DeleteLocalRef(str); + saveRef( t.pEnv, tempObj ); + t.pEnv->DeleteLocalRef( tempObj ); + ThrowSQLException( t.pEnv, nullptr ); + // and cleanup +} + +java_math_BigDecimal::java_math_BigDecimal( const double& _par0 ): java_lang_Object( nullptr, nullptr ) +{ + SDBThreadAttach t; + if( !t.pEnv ) + return; + // Java-Call for the Constructor + // initialize temporary Variable + static const char * const cSignature = "(D)V"; + jobject tempObj; + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, "<init>",cSignature, mID); + tempObj = t.pEnv->NewObject( getMyClass(), mID, _par0 ); + saveRef( t.pEnv, tempObj ); + t.pEnv->DeleteLocalRef( tempObj ); + ThrowSQLException( t.pEnv, nullptr ); + // and cleanup +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/JConnection.cxx b/connectivity/source/drivers/jdbc/JConnection.cxx new file mode 100644 index 000000000..1d33f2941 --- /dev/null +++ b/connectivity/source/drivers/jdbc/JConnection.cxx @@ -0,0 +1,804 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/Connection.hxx> +#include <java/lang/Class.hxx> +#include <java/tools.hxx> +#include <java/ContextClassLoader.hxx> +#include <java/sql/DatabaseMetaData.hxx> +#include <java/sql/JStatement.hxx> +#include <java/sql/Driver.hxx> +#include <java/sql/PreparedStatement.hxx> +#include <java/sql/CallableStatement.hxx> +#include <java/sql/SQLWarning.hxx> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <connectivity/dbexception.hxx> +#include <java/util/Property.hxx> +#include <java/LocalRef.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <jvmaccess/classpath.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <jni.h> +#include <strings.hrc> +#include <unotools/confignode.hxx> +#include <strings.hxx> + +#include <vector> +#include <memory> + +using namespace connectivity; +using namespace connectivity::jdbc; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +namespace { + +struct ClassMapEntry { + ClassMapEntry( + OUString const & theClassPath, OUString const & theClassName): + classPath(theClassPath), className(theClassName), classLoader(nullptr), + classObject(nullptr) {} + + OUString classPath; + OUString className; + jweak classLoader; + jweak classObject; +}; + +typedef std::vector< ClassMapEntry > ClassMap; + +struct ClassMapData { + osl::Mutex mutex; + + ClassMap map; +}; + +template < typename T > +bool getLocalFromWeakRef( jweak& _weak, LocalRef< T >& _inout_local ) +{ + _inout_local.set( static_cast< T >( _inout_local.env().NewLocalRef( _weak ) ) ); + + if ( !_inout_local.is() ) + { + if ( _inout_local.env().ExceptionCheck()) + { + return false; + } + else if ( _weak != nullptr ) + { + _inout_local.env().DeleteWeakGlobalRef( _weak ); + _weak = nullptr; + } + } + return true; +} + +// Load a class. A map from pairs of (classPath, name) to pairs of weak Java +// references to (ClassLoader, Class) is maintained, so that a class is only +// loaded once. +// +// It may happen that the weak reference to the ClassLoader becomes null while +// the reference to the Class remains non-null (in case the Class was actually +// loaded by some parent of the ClassLoader), in which case the ClassLoader is +// resurrected (which cannot cause any classes to be loaded multiple times, as +// the ClassLoader is no longer reachable, so no classes it has ever loaded are +// still reachable). +// +// Similarly, it may happen that the weak reference to the Class becomes null +// while the reference to the ClassLoader remains non-null, in which case the +// Class is simply re-loaded. +// +// This code is close to the implementation of jvmaccess::ClassPath::loadClass +// in jvmaccess/classpath.hxx, but not close enough to avoid the duplication. +// +// If false is returned, a (still pending) JNI exception occurred. +bool loadClass( + Reference< XComponentContext > const & context, JNIEnv& environment, + OUString const & classPath, OUString const & name, + LocalRef< jobject > * classLoaderPtr, LocalRef< jclass > * classPtr) +{ + OSL_ASSERT(classLoaderPtr != nullptr); + // For any jweak entries still present in the map upon destruction, + // DeleteWeakGlobalRef is not called (which is a leak): + static ClassMapData classMapData; + osl::MutexGuard g(classMapData.mutex); + ClassMap::iterator i(classMapData.map.begin()); + LocalRef< jobject > cloader(environment); + LocalRef< jclass > cl(environment); + // Prune dangling weak references from the list while searching for a match, + // so that the list cannot grow unbounded: + for (; i != classMapData.map.end();) + { + LocalRef< jobject > classLoader( environment ); + if ( !getLocalFromWeakRef( i->classLoader, classLoader ) ) + return false; + + LocalRef< jclass > classObject( environment ); + if ( !getLocalFromWeakRef( i->classObject, classObject ) ) + return false; + + if ( !classLoader.is() && !classObject.is() ) + { + i = classMapData.map.erase(i); + } + else if ( i->classPath == classPath && i->className == name ) + { + cloader.set( classLoader.release() ); + cl.set( classObject.release() ); + break; + } + else + { + ++i; + } + } + if ( !cloader.is() || !cl.is() ) + { + if ( i == classMapData.map.end() ) + { + // Push a new ClassMapEntry (which can potentially fail) before + // loading the class, so that it never happens that a class is + // loaded but not added to the map (which could have effects on the + // JVM that are not easily undone). If the pushed ClassMapEntry is + // not used after all (return false, etc.) it will be pruned on next + // call because its classLoader/classObject are null: + classMapData.map.push_back( ClassMapEntry( classPath, name ) ); + i = std::prev(classMapData.map.end()); + } + + LocalRef< jclass > clClass( environment ); + clClass.set( environment.FindClass( "java/net/URLClassLoader" ) ); + if ( !clClass.is() ) + return false; + + jweak wcloader = nullptr; + if (!cloader.is()) + { + jmethodID ctorLoader( environment.GetMethodID( clClass.get(), "<init>", "([Ljava/net/URL;)V" ) ); + if (ctorLoader == nullptr) + return false; + + LocalRef< jobjectArray > arr( environment ); + arr.set( jvmaccess::ClassPath::translateToUrls( context, &environment, classPath ) ); + if ( !arr.is() ) + return false; + + jvalue arg; + arg.l = arr.get(); + cloader.set( environment.NewObjectA( clClass.get(), ctorLoader, &arg ) ); + if ( !cloader.is() ) + return false; + + wcloader = environment.NewWeakGlobalRef( cloader.get() ); + if ( wcloader == nullptr ) + return false; + } + + jweak wcl = nullptr; + if ( !cl.is() ) + { + jmethodID methLoadClass( environment.GetMethodID( clClass.get(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;" ) ); + if ( methLoadClass == nullptr ) + return false; + + LocalRef< jstring > str( environment ); + str.set( convertwchar_tToJavaString( &environment, name ) ); + if ( !str.is() ) + return false; + + jvalue arg; + arg.l = str.get(); + cl.set( static_cast< jclass >( environment.CallObjectMethodA( cloader.get(), methLoadClass, &arg ) ) ); + if ( !cl.is() ) + return false; + + wcl = environment.NewWeakGlobalRef( cl.get() ); + if ( wcl == nullptr ) + return false; + } + + if ( wcloader != nullptr) + { + i->classLoader = wcloader; + } + if ( wcl != nullptr ) + { + i->classObject = wcl; + } + } + + classLoaderPtr->set( cloader.release() ); + classPtr->set( cl.release() ); + return true; +} + +} + + +IMPLEMENT_SERVICE_INFO(java_sql_Connection,"com.sun.star.sdbcx.JConnection","com.sun.star.sdbc.Connection"); + + +//************ Class: java.sql.Connection + +jclass java_sql_Connection::theClass = nullptr; + +java_sql_Connection::java_sql_Connection( const java_sql_Driver& _rDriver ) + :java_lang_Object() + ,m_xContext( _rDriver.getContext() ) + ,m_pDriver( &_rDriver ) + ,m_pDriverobject(nullptr) + ,m_pDriverClassLoader() + ,m_Driver_theClass(nullptr) + ,m_aLogger( _rDriver.getLogger() ) + ,m_bIgnoreDriverPrivileges(true) + ,m_bIgnoreCurrency(false) +{ +} + +java_sql_Connection::~java_sql_Connection() +{ + ::rtl::Reference< jvmaccess::VirtualMachine > xTest = java_lang_Object::getVM(); + if ( !xTest.is() ) + return; + + SDBThreadAttach t; + clearObject(*t.pEnv); + + { + if ( m_pDriverobject ) + t.pEnv->DeleteGlobalRef( m_pDriverobject ); + m_pDriverobject = nullptr; + if ( m_Driver_theClass ) + t.pEnv->DeleteGlobalRef( m_Driver_theClass ); + m_Driver_theClass = nullptr; + } + SDBThreadAttach::releaseRef(); +} + +void java_sql_Connection::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + m_aLogger.log( LogLevel::INFO, STR_LOG_SHUTDOWN_CONNECTION ); + + java_sql_Connection_BASE::disposing(); + + if ( object ) + { + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("close", mID); + } +} + +jclass java_sql_Connection::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/Connection"); + return theClass; +} + + +OUString SAL_CALL java_sql_Connection::getCatalog( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + + static jmethodID mID(nullptr); + return callStringMethod("getCatalog",mID); +} + +Reference< XDatabaseMetaData > SAL_CALL java_sql_Connection::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + + + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getMetaData","()Ljava/sql/DatabaseMetaData;", mID); + if(out) + { + xMetaData = new java_sql_DatabaseMetaData( t.pEnv, out, *this ); + m_xMetaData = xMetaData; + } + } + + return xMetaData; +} + +void SAL_CALL java_sql_Connection::close( ) +{ + dispose(); +} + +void SAL_CALL java_sql_Connection::commit( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("commit", mID); +} + +sal_Bool SAL_CALL java_sql_Connection::isClosed( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + static jmethodID mID(nullptr); + return callBooleanMethod( "isClosed", mID ) && java_sql_Connection_BASE::rBHelper.bDisposed; +} + +sal_Bool SAL_CALL java_sql_Connection::isReadOnly( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + static jmethodID mID(nullptr); + return callBooleanMethod( "isReadOnly", mID ); +} + +void SAL_CALL java_sql_Connection::setCatalog( const OUString& catalog ) +{ + static jmethodID mID(nullptr); + callVoidMethodWithStringArg("setCatalog",mID,catalog); +} + +void SAL_CALL java_sql_Connection::rollback( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("rollback", mID); +} + +sal_Bool SAL_CALL java_sql_Connection::getAutoCommit( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "getAutoCommit", mID ); +} + +void SAL_CALL java_sql_Connection::setReadOnly( sal_Bool readOnly ) +{ + static jmethodID mID(nullptr); + callVoidMethodWithBoolArg_ThrowSQL("setReadOnly", mID, readOnly); +} + +void SAL_CALL java_sql_Connection::setAutoCommit( sal_Bool autoCommit ) +{ + static jmethodID mID(nullptr); + callVoidMethodWithBoolArg_ThrowSQL("setAutoCommit", mID, autoCommit); +} + +Reference< css::container::XNameAccess > SAL_CALL java_sql_Connection::getTypeMap( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + callObjectMethod(t.pEnv,"getTypeMap","()Ljava/util/Map;", mID); + // WARNING: the caller becomes the owner of the returned pointer + return nullptr; +} + +void SAL_CALL java_sql_Connection::setTypeMap( const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this ); +} + + +sal_Int32 SAL_CALL java_sql_Connection::getTransactionIsolation( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + + static jmethodID mID(nullptr); + return callIntMethod_ThrowSQL("getTransactionIsolation", mID); +} + +void SAL_CALL java_sql_Connection::setTransactionIsolation( sal_Int32 level ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowSQL("setTransactionIsolation", mID, level); +} + +Reference< XStatement > SAL_CALL java_sql_Connection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_CREATE_STATEMENT ); + + SDBThreadAttach t; + java_sql_Statement* pStatement = new java_sql_Statement( t.pEnv, *this ); + Reference< XStatement > xStmt = pStatement; + m_aStatements.push_back( WeakReferenceHelper( xStmt ) ); + + m_aLogger.log( LogLevel::FINE, STR_LOG_CREATED_STATEMENT_ID, pStatement->getStatementObjectID() ); + return xStmt; +} + +Reference< XPreparedStatement > SAL_CALL java_sql_Connection::prepareStatement( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_PREPARE_STATEMENT, sql ); + + SDBThreadAttach t; + + java_sql_PreparedStatement* pStatement = new java_sql_PreparedStatement( t.pEnv, *this, sql ); + Reference< XPreparedStatement > xReturn( pStatement ); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + + m_aLogger.log( LogLevel::FINE, STR_LOG_PREPARED_STATEMENT_ID, pStatement->getStatementObjectID() ); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL java_sql_Connection::prepareCall( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_PREPARE_CALL, sql ); + + SDBThreadAttach t; + + java_sql_CallableStatement* pStatement = new java_sql_CallableStatement( t.pEnv, *this, sql ); + Reference< XPreparedStatement > xStmt( pStatement ); + m_aStatements.push_back(WeakReferenceHelper(xStmt)); + + m_aLogger.log( LogLevel::FINE, STR_LOG_PREPARED_CALL_ID, pStatement->getStatementObjectID() ); + return xStmt; +} + +OUString SAL_CALL java_sql_Connection::nativeSQL( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + + OUString aStr; + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + + // initialize temporary Variable + static const char * const cSignature = "(Ljava/lang/String;)Ljava/lang/String;"; + static const char * const cMethodName = "nativeSQL"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + // Convert Parameter + jdbc::LocalRef< jstring > str( t.env(),convertwchar_tToJavaString(t.pEnv,sql)); + + jobject out = t.pEnv->CallObjectMethod( object, mID, str.get() ); + aStr = JavaString2String(t.pEnv, static_cast<jstring>(out) ); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } //t.pEnv + + m_aLogger.log( LogLevel::FINER, STR_LOG_NATIVE_SQL, sql, aStr ); + + return aStr; +} + +void SAL_CALL java_sql_Connection::clearWarnings( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("clearWarnings", mID); +} + +Any SAL_CALL java_sql_Connection::getWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getWarnings","()Ljava/sql/SQLWarning;", mID); + // WARNING: the caller becomes the owner of the returned pointer + if( out ) + { + java_sql_SQLWarning_BASE warn_base(t.pEnv, out); + SQLException aAsException( java_sql_SQLWarning( warn_base, *this ) ); + + // translate to warning + SQLWarning aWarning; + aWarning.Context = aAsException.Context; + aWarning.Message = aAsException.Message; + aWarning.SQLState = aAsException.SQLState; + aWarning.ErrorCode = aAsException.ErrorCode; + aWarning.NextException = aAsException.NextException; + + return makeAny( aWarning ); + } + + return Any(); +} + + +namespace +{ + OUString lcl_getDriverLoadErrorMessage( const ::connectivity::SharedResources& _aResource,const OUString& _rDriverClass, const OUString& _rDriverClassPath ) + { + OUString sError1( _aResource.getResourceStringWithSubstitution( + STR_NO_CLASSNAME, + "$classname$", _rDriverClass + ) ); + if ( !_rDriverClassPath.isEmpty() ) + { + const OUString sError2( _aResource.getResourceStringWithSubstitution( + STR_NO_CLASSNAME_PATH, + "$classpath$", _rDriverClassPath + ) ); + sError1 += sError2; + } // if ( _rDriverClassPath.getLength() ) + return sError1; + } +} + + +namespace +{ + bool lcl_setSystemProperties_nothrow( const java::sql::ConnectionLog& _rLogger, + JNIEnv& _rEnv, const Sequence< NamedValue >& _rSystemProperties ) + { + if ( !_rSystemProperties.hasElements() ) + // nothing to do + return true; + + LocalRef< jclass > systemClass( _rEnv ); + jmethodID nSetPropertyMethodID = nullptr; + // retrieve the java.lang.System class + systemClass.set( _rEnv.FindClass( "java/lang/System" ) ); + if ( systemClass.is() ) + { + nSetPropertyMethodID = _rEnv.GetStaticMethodID( + systemClass.get(), "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" ); + } + + if ( nSetPropertyMethodID == nullptr ) + return false; + + for ( auto const & systemProp : _rSystemProperties ) + { + OUString sValue; + OSL_VERIFY( systemProp.Value >>= sValue ); + + _rLogger.log( LogLevel::FINER, STR_LOG_SETTING_SYSTEM_PROPERTY, systemProp.Name, sValue ); + + LocalRef< jstring > jName( _rEnv, convertwchar_tToJavaString( &_rEnv, systemProp.Name ) ); + LocalRef< jstring > jValue( _rEnv, convertwchar_tToJavaString( &_rEnv, sValue ) ); + + _rEnv.CallStaticObjectMethod( systemClass.get(), nSetPropertyMethodID, jName.get(), jValue.get() ); + LocalRef< jthrowable > throwable( _rEnv, _rEnv.ExceptionOccurred() ); + if ( throwable.is() ) + return false; + } + + return true; + } +} + + +void java_sql_Connection::loadDriverFromProperties( const OUString& _sDriverClass, const OUString& _sDriverClassPath, + const Sequence< NamedValue >& _rSystemProperties ) +{ + // first try if the jdbc driver is already registered at the driver manager + SDBThreadAttach t; + try + { + if ( !object ) + { + if ( !lcl_setSystemProperties_nothrow( getLogger(), *t.pEnv, _rSystemProperties ) ) + ThrowLoggedSQLException( getLogger(), t.pEnv, *this ); + + m_pDriverClassLoader.reset(); + + // here I try to find the class for jdbc driver + java_sql_SQLException_BASE::st_getMyClass(); + java_lang_Throwable::st_getMyClass(); + + if ( _sDriverClass.isEmpty() ) + { + m_aLogger.log( LogLevel::SEVERE, STR_LOG_NO_DRIVER_CLASS ); + ::dbtools::throwGenericSQLException( + lcl_getDriverLoadErrorMessage( getResources(),_sDriverClass, _sDriverClassPath ), + *this + ); + } + else + { + m_aLogger.log( LogLevel::INFO, STR_LOG_LOADING_DRIVER, _sDriverClass ); + // the driver manager holds the class of the driver for later use + std::unique_ptr< java_lang_Class > pDrvClass; + if ( _sDriverClassPath.isEmpty() ) + { + // if forName didn't find the class it will throw an exception + pDrvClass.reset(java_lang_Class::forName(_sDriverClass)); + } + else + { + LocalRef< jclass > driverClass(t.env()); + LocalRef< jobject > driverClassLoader(t.env()); + + loadClass( + m_pDriver->getContext(), + t.env(), _sDriverClassPath, _sDriverClass, &driverClassLoader, &driverClass ); + + m_pDriverClassLoader.set( driverClassLoader ); + pDrvClass.reset( new java_lang_Class( t.pEnv, driverClass.release() ) ); + + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + if (pDrvClass) + { + LocalRef< jobject > driverObject( t.env() ); + driverObject.set( pDrvClass->newInstanceObject() ); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + m_pDriverobject = driverObject.release(); + + if( m_pDriverobject ) + m_pDriverobject = t.pEnv->NewGlobalRef( m_pDriverobject ); + + { + jclass tempClass = t.pEnv->GetObjectClass(m_pDriverobject); + if ( m_pDriverobject ) + { + m_Driver_theClass = static_cast<jclass>(t.pEnv->NewGlobalRef( tempClass )); + t.pEnv->DeleteLocalRef( tempClass ); + } + } + } + m_aLogger.log( LogLevel::INFO, STR_LOG_CONN_SUCCESS ); + } + } + } + catch( const SQLException& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw SQLException( + lcl_getDriverLoadErrorMessage( getResources(),_sDriverClass, _sDriverClassPath ), + *this, + OUString(), + 1000, + anyEx); + } + catch( Exception& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + ::dbtools::throwGenericSQLException( + lcl_getDriverLoadErrorMessage( getResources(),_sDriverClass, _sDriverClassPath ), + *this, + anyEx + ); + } +} + +OUString java_sql_Connection::impl_getJavaDriverClassPath_nothrow(const OUString& _sDriverClass) +{ + static const char s_sNodeName[] = "org.openoffice.Office.DataAccess/JDBC/DriverClassPaths"; + ::utl::OConfigurationTreeRoot aNamesRoot = ::utl::OConfigurationTreeRoot::createWithComponentContext( + m_pDriver->getContext(), s_sNodeName, -1, ::utl::OConfigurationTreeRoot::CM_READONLY); + OUString sURL; + if ( aNamesRoot.isValid() && aNamesRoot.hasByName( _sDriverClass ) ) + { + ::utl::OConfigurationNode aRegisterObj = aNamesRoot.openNode( _sDriverClass ); + OSL_VERIFY( aRegisterObj.getNodeValue( "Path" ) >>= sURL ); + } + return sURL; +} + +bool java_sql_Connection::construct(const OUString& url, + const Sequence< PropertyValue >& info) +{ + { // initialize the java vm + ::rtl::Reference< jvmaccess::VirtualMachine > xTest = java_lang_Object::getVM(m_xContext); + if ( !xTest.is() ) + throwGenericSQLException(STR_NO_JAVA,*this); + } + SDBThreadAttach t; + SDBThreadAttach::addRef(); // will be released in dtor + if ( !t.pEnv ) + throwGenericSQLException(STR_NO_JAVA,*this); + + OUString sGeneratedValueStatement; // contains the statement which should be used when query for automatically generated values + bool bAutoRetrievingEnabled = false; // set to <TRUE/> when we should allow to query for generated values + OUString sDriverClassPath,sDriverClass; + Sequence< NamedValue > aSystemProperties; + + ::comphelper::NamedValueCollection aSettings( info ); + sDriverClass = aSettings.getOrDefault( "JavaDriverClass", sDriverClass ); + sDriverClassPath = aSettings.getOrDefault( "JavaDriverClassPath", sDriverClassPath); + if ( sDriverClassPath.isEmpty() ) + sDriverClassPath = impl_getJavaDriverClassPath_nothrow(sDriverClass); + bAutoRetrievingEnabled = aSettings.getOrDefault( "IsAutoRetrievingEnabled", bAutoRetrievingEnabled ); + sGeneratedValueStatement = aSettings.getOrDefault( "AutoRetrievingStatement", sGeneratedValueStatement ); + m_bIgnoreDriverPrivileges = aSettings.getOrDefault( "IgnoreDriverPrivileges", m_bIgnoreDriverPrivileges ); + m_bIgnoreCurrency = aSettings.getOrDefault( "IgnoreCurrency", m_bIgnoreCurrency ); + aSystemProperties = aSettings.getOrDefault( "SystemProperties", aSystemProperties ); + m_aCatalogRestriction = aSettings.getOrDefault( "ImplicitCatalogRestriction", Any() ); + m_aSchemaRestriction = aSettings.getOrDefault( "ImplicitSchemaRestriction", Any() ); + + loadDriverFromProperties( sDriverClass, sDriverClassPath, aSystemProperties ); + + enableAutoRetrievingEnabled(bAutoRetrievingEnabled); + setAutoRetrievingStatement(sGeneratedValueStatement); + + if ( t.pEnv && m_Driver_theClass && m_pDriverobject ) + { + // Java-Call + static const char * const cSignature = "(Ljava/lang/String;Ljava/util/Properties;)Ljava/sql/Connection;"; + static const char * const cMethodName = "connect"; + jmethodID mID = t.pEnv->GetMethodID( m_Driver_theClass, cMethodName, cSignature ); + + if ( mID ) + { + jvalue args[2]; + // convert Parameter + args[0].l = convertwchar_tToJavaString(t.pEnv,url); + std::unique_ptr<java_util_Properties> pProps = createStringPropertyArray(info); + args[1].l = pProps->getJavaObject(); + + LocalRef< jobject > ensureDelete( t.env(), args[0].l ); + + jobject out = nullptr; + // In some cases (e.g., + // connectivity/source/drivers/hsqldb/HDriver.cxx:1.24 + // l. 249) the JavaDriverClassPath contains multiple jars, + // as creating the JavaDriverClass instance requires + // (reflective) access to those other jars. Now, if the + // JavaDriverClass is actually loaded by some parent class + // loader (e.g., because its jar is also on the global + // class path), it would still not have access to the + // additional jars on the JavaDriverClassPath. Hence, the + // JavaDriverClassPath class loader is pushed as context + // class loader around the JavaDriverClass instance + // creation: + // #i82222# / 2007-10-15 + { + ContextClassLoaderScope ccl( t.env(), getDriverClassLoader(), getLogger(), *this ); + out = t.pEnv->CallObjectMethod( m_pDriverobject, mID, args[0].l,args[1].l ); + pProps.reset(); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + + if ( !out ) + m_aLogger.log( LogLevel::SEVERE, STR_LOG_NO_SYSTEM_CONNECTION ); + + if ( out ) + object = t.pEnv->NewGlobalRef( out ); + + if ( object ) + m_aLogger.log( LogLevel::INFO, STR_LOG_GOT_JDBC_CONNECTION, url ); + + m_aConnectionInfo = info; + } //mID + } //t.pEnv + return object != nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/JDriver.cxx b/connectivity/source/drivers/jdbc/JDriver.cxx new file mode 100644 index 000000000..3ec181bb3 --- /dev/null +++ b/connectivity/source/drivers/jdbc/JDriver.cxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/Driver.hxx> +#include <java/sql/Connection.hxx> +#include <sal/log.hxx> +#include <connectivity/dbexception.hxx> +#include <jvmfwk/framework.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <strings.hxx> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +java_sql_Driver::java_sql_Driver(const Reference< css::uno::XComponentContext >& _rxContext) + :m_aContext( _rxContext ) + ,m_aLogger( _rxContext, "org.openoffice.sdbc.jdbcBridge" ) +{ +} + +java_sql_Driver::~java_sql_Driver() +{ +} + +// static ServiceInfo + +OUString java_sql_Driver::getImplementationName_Static( ) +{ + return "com.sun.star.comp.sdbc.JDBCDriver"; + // this name is referenced in the configuration and in the jdbc.xml + // Please take care when changing it. +} + +Sequence< OUString > java_sql_Driver::getSupportedServiceNames_Static( ) +{ + Sequence<OUString> aSNS { "com.sun.star.sdbc.Driver" }; + return aSNS; +} + +css::uno::Reference< css::uno::XInterface > connectivity::java_sql_Driver_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory) +{ + return *(new java_sql_Driver( comphelper::getComponentContext(_rxFactory))); +} + +OUString SAL_CALL java_sql_Driver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL java_sql_Driver::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + + +Sequence< OUString > SAL_CALL java_sql_Driver::getSupportedServiceNames( ) +{ + return getSupportedServiceNames_Static(); +} + +Reference< XConnection > SAL_CALL java_sql_Driver::connect( const OUString& url, const + Sequence< PropertyValue >& info ) +{ + m_aLogger.log( LogLevel::INFO, STR_LOG_DRIVER_CONNECTING_URL, url ); + + Reference< XConnection > xOut; + if ( acceptsURL(url ) ) + { + java_sql_Connection* pConnection = new java_sql_Connection( *this ); + xOut = pConnection; + if ( !pConnection->construct(url,info) ) + xOut.clear(); // an error occurred and the java driver didn't throw an exception + else + m_aLogger.log( LogLevel::INFO, STR_LOG_DRIVER_SUCCESS ); + } + return xOut; +} + +sal_Bool SAL_CALL java_sql_Driver::acceptsURL( const OUString& url ) +{ + // don't ask the real driver for the url + // I feel responsible for all jdbc url's + bool bEnabled = false; + javaFrameworkError e = jfw_getEnabled(&bEnabled); + switch (e) { + case JFW_E_NONE: + break; + case JFW_E_DIRECT_MODE: + SAL_INFO( + "connectivity.jdbc", + "jfw_getEnabled: JFW_E_DIRECT_MODE, assuming true"); + bEnabled = true; + break; + default: + SAL_WARN("connectivity.jdbc", "jfw_getEnabled: error code " << +e); + break; + } + return bEnabled && url.startsWith("jdbc:"); +} + +Sequence< DriverPropertyInfo > SAL_CALL java_sql_Driver::getPropertyInfo( const OUString& url, + const Sequence< PropertyValue >& /*info*/ ) +{ + if ( acceptsURL(url) ) + { + std::vector< DriverPropertyInfo > aDriverInfo; + + Sequence< OUString > aBooleanValues(2); + aBooleanValues[0] = "false"; + aBooleanValues[1] = "true"; + + aDriverInfo.push_back(DriverPropertyInfo( + "JavaDriverClass" + ,"The JDBC driver class name." + ,true + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "JavaDriverClassPath" + ,"The class path where to look for the JDBC driver." + ,true + , "" + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "SystemProperties" + ,"Additional properties to set at java.lang.System before loading the driver." + ,true + , "" + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "ParameterNameSubstitution" + ,"Change named parameters with '?'." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "IgnoreDriverPrivileges" + ,"Ignore the privileges from the database driver." + ,false + , "false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "IsAutoRetrievingEnabled" + ,"Retrieve generated values." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "AutoRetrievingStatement" + ,"Auto-increment statement." + ,false + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "GenerateASBeforeCorrelationName" + ,"Generate AS before table correlation names." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "IgnoreCurrency" + ,"Ignore the currency field from the ResultsetMetaData." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "EscapeDateTime" + ,"Escape date time format." + ,false + ,"true" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "TypeInfoSettings" + ,"Defines how the type info of the database metadata should be manipulated." + ,false + ,OUString( ) + ,Sequence< OUString > ()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "ImplicitCatalogRestriction" + ,"The catalog which should be used in getTables calls, when the caller passed NULL." + ,false + ,OUString( ) + ,Sequence< OUString > ()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "ImplicitSchemaRestriction" + ,"The schema which should be used in getTables calls, when the caller passed NULL." + ,false + ,OUString( ) + ,Sequence< OUString > ()) + ); + return Sequence< DriverPropertyInfo >(aDriverInfo.data(),aDriverInfo.size()); + } + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + return Sequence< DriverPropertyInfo >(); +} + +sal_Int32 SAL_CALL java_sql_Driver::getMajorVersion( ) +{ + return 1; +} + +sal_Int32 SAL_CALL java_sql_Driver::getMinorVersion( ) +{ + return 0; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/JStatement.cxx b/connectivity/source/drivers/jdbc/JStatement.cxx new file mode 100644 index 000000000..9bfe11838 --- /dev/null +++ b/connectivity/source/drivers/jdbc/JStatement.cxx @@ -0,0 +1,823 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/JStatement.hxx> +#include <java/sql/ResultSet.hxx> +#include <java/sql/Connection.hxx> +#include <java/sql/SQLWarning.hxx> +#include <java/tools.hxx> +#include <java/ContextClassLoader.hxx> +#include <comphelper/property.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/sequence.hxx> +#include <TConnection.hxx> +#include <comphelper/types.hxx> +#include <tools/diagnose_ex.h> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> + +#include <strings.hxx> + +#include <algorithm> +#include <string.h> + +using namespace ::comphelper; +using namespace connectivity; +using namespace ::cppu; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +//************ Class: java.sql.Statement + + +jclass java_sql_Statement_Base::theClass = nullptr; + + +java_sql_Statement_Base::java_sql_Statement_Base( JNIEnv * pEnv, java_sql_Connection& _rCon ) + :java_sql_Statement_BASE(m_aMutex) + ,java_lang_Object( pEnv, nullptr ) + ,OPropertySetHelper(java_sql_Statement_BASE::rBHelper) + ,m_pConnection( &_rCon ) + ,m_aLogger( _rCon.getLogger(), java::sql::ConnectionLog::STATEMENT ) + ,m_nResultSetConcurrency(ResultSetConcurrency::READ_ONLY) + ,m_nResultSetType(ResultSetType::FORWARD_ONLY) + ,m_bEscapeProcessing(true) +{ +} + + +java_sql_Statement_Base::~java_sql_Statement_Base() +{ +} + + +void SAL_CALL OStatement_BASE2::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + if ( object ) + { + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("close", mID); + } + + ::comphelper::disposeComponent(m_xGeneratedStatement); + m_pConnection.clear(); + + java_sql_Statement_Base::disposing(); +} + +jclass java_sql_Statement_Base::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/Statement"); + return theClass; +} + +void SAL_CALL java_sql_Statement_Base::disposing() +{ + m_aLogger.log( LogLevel::FINE, STR_LOG_CLOSING_STATEMENT ); + java_sql_Statement_BASE::disposing(); + clearObject(); +} + + +Any SAL_CALL java_sql_Statement_Base::queryInterface( const Type & rType ) +{ + if ( m_pConnection.is() && !m_pConnection->isAutoRetrievingEnabled() && rType == cppu::UnoType<XGeneratedResultSet>::get()) + return Any(); + Any aRet( java_sql_Statement_BASE::queryInterface(rType) ); + return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType); +} + +Sequence< Type > SAL_CALL java_sql_Statement_Base::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<css::beans::XMultiPropertySet>::get(), + cppu::UnoType<css::beans::XFastPropertySet>::get(), + cppu::UnoType<css::beans::XPropertySet>::get()); + + Sequence< Type > aOldTypes = java_sql_Statement_BASE::getTypes(); + if ( m_pConnection.is() && !m_pConnection->isAutoRetrievingEnabled() ) + { + auto newEnd = std::remove(aOldTypes.begin(), aOldTypes.end(), + cppu::UnoType<XGeneratedResultSet>::get()); + aOldTypes.realloc(std::distance(aOldTypes.begin(), newEnd)); + } + + return ::comphelper::concatSequences(aTypes.getTypes(),aOldTypes); +} + +Reference< XResultSet > SAL_CALL java_sql_Statement_Base::getGeneratedValues( ) +{ + m_aLogger.log( LogLevel::FINE, STR_LOG_GENERATED_VALUES ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + jobject out(nullptr); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + // initialize temporary Variable + try + { + static jmethodID mID(nullptr); + out = callResultSetMethod(t.env(),"getGeneratedKeys",mID); + } + catch(const SQLException&) + { + // ignore + } + + Reference< XResultSet > xRes; + if ( !out ) + { + OSL_ENSURE( m_pConnection.is() && m_pConnection->isAutoRetrievingEnabled(),"Illegal call here. isAutoRetrievingEnabled is false!"); + if ( m_pConnection.is() ) + { + OUString sStmt = m_pConnection->getTransformedGeneratedStatement(m_sSqlStatement); + if ( !sStmt.isEmpty() ) + { + m_aLogger.log( LogLevel::FINER, STR_LOG_GENERATED_VALUES_FALLBACK, sStmt ); + ::comphelper::disposeComponent(m_xGeneratedStatement); + m_xGeneratedStatement = m_pConnection->createStatement(); + xRes = m_xGeneratedStatement->executeQuery(sStmt); + } + } + } + else + xRes = new java_sql_ResultSet( t.pEnv, out, m_aLogger,*m_pConnection, this ); + return xRes; +} + + +void SAL_CALL java_sql_Statement_Base::cancel( ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowRuntime("cancel",mID); +} + + +void SAL_CALL java_sql_Statement_Base::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (java_sql_Statement_BASE::rBHelper.bDisposed) + throw DisposedException(); + } + dispose(); +} + + +void SAL_CALL java_sql_Statement::clearBatch( ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("clearBatch", mID); + } //t.pEnv +} + + +sal_Bool SAL_CALL java_sql_Statement_Base::execute( const OUString& sql ) +{ + m_aLogger.log( LogLevel::FINE, STR_LOG_EXECUTE_STATEMENT, sql ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + bool out(false); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + createStatement(t.pEnv); + m_sSqlStatement = sql; + // initialize temporary Variable + static const char * const cSignature = "(Ljava/lang/String;)Z"; + static const char * const cMethodName = "execute"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + // convert Parameter + jdbc::LocalRef< jstring > str( t.env(), convertwchar_tToJavaString( t.pEnv, sql ) ); + { + jdbc::ContextClassLoaderScope ccl( t.env(), + m_pConnection.is() ? m_pConnection->getDriverClassLoader() : jdbc::GlobalRef< jobject >(), + m_aLogger, + *this + ); + + out = t.pEnv->CallBooleanMethod( object, mID, str.get() ); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } //t.pEnv + return out; +} + + +Reference< XResultSet > SAL_CALL java_sql_Statement_Base::executeQuery( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_EXECUTE_QUERY, sql ); + + jobject out(nullptr); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + createStatement(t.pEnv); + m_sSqlStatement = sql; + // initialize temporary variable + static const char * const cSignature = "(Ljava/lang/String;)Ljava/sql/ResultSet;"; + static const char * const cMethodName = "executeQuery"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + // convert Parameter + jdbc::LocalRef< jstring > str( t.env(), convertwchar_tToJavaString( t.pEnv, sql ) ); + { + jdbc::ContextClassLoaderScope ccl( t.env(), + m_pConnection.is() ? m_pConnection->getDriverClassLoader() : jdbc::GlobalRef< jobject >(), + m_aLogger, + *this + ); + + out = t.pEnv->CallObjectMethod( object, mID, str.get() ); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } //t.pEnv + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_ResultSet( t.pEnv, out, m_aLogger, *m_pConnection,this ); +} + +Reference< XConnection > SAL_CALL java_sql_Statement_Base::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + return Reference< XConnection >(m_pConnection.get()); +} + + +Any SAL_CALL java_sql_Statement::queryInterface( const Type & rType ) +{ + Any aRet = ::cppu::queryInterface(rType,static_cast< XBatchExecution*> (this)); + return aRet.hasValue() ? aRet : java_sql_Statement_Base::queryInterface(rType); +} + + +void SAL_CALL java_sql_Statement::addBatch( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethodWithStringArg("addBatch",mID,sql); + } //t.pEnv +} + + +Sequence< sal_Int32 > SAL_CALL java_sql_Statement::executeBatch( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + Sequence< sal_Int32 > aSeq; + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jintArray out = static_cast<jintArray>(callObjectMethod(t.pEnv,"executeBatch","()[I", mID)); + if (out) + { + jboolean p = false; + aSeq.realloc(t.pEnv->GetArrayLength(out)); + memcpy(aSeq.getArray(),t.pEnv->GetIntArrayElements(out,&p),aSeq.getLength()); + t.pEnv->DeleteLocalRef(out); + } + return aSeq; +} + + +sal_Int32 SAL_CALL java_sql_Statement_Base::executeUpdate( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_EXECUTE_UPDATE, sql ); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + m_sSqlStatement = sql; + static jmethodID mID(nullptr); + return callIntMethodWithStringArg("executeUpdate",mID,sql); +} + + +Reference< css::sdbc::XResultSet > SAL_CALL java_sql_Statement_Base::getResultSet( ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callResultSetMethod(t.env(),"getResultSet",mID); + + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_ResultSet( t.pEnv, out, m_aLogger, *m_pConnection,this ); +} + + +sal_Int32 SAL_CALL java_sql_Statement_Base::getUpdateCount( ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + sal_Int32 out = callIntMethod_ThrowSQL("getUpdateCount", mID); + m_aLogger.log( LogLevel::FINER, STR_LOG_UPDATE_COUNT, out ); + return out; +} + + +sal_Bool SAL_CALL java_sql_Statement_Base::getMoreResults( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "getMoreResults", mID ); +} + + +Any SAL_CALL java_sql_Statement_Base::getWarnings( ) +{ + SDBThreadAttach t; + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getWarnings","()Ljava/sql/SQLWarning;", mID); + // WARNING: the caller becomes the owner of the returned pointer + if( out ) + { + java_sql_SQLWarning_BASE warn_base( t.pEnv, out ); + return makeAny( + static_cast< css::sdbc::SQLException >( + java_sql_SQLWarning(warn_base,*static_cast<cppu::OWeakObject*>(this)))); + } + + return Any(); +} + +void SAL_CALL java_sql_Statement_Base::clearWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; + + { + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("clearWarnings", mID); + } +} + +sal_Int32 java_sql_Statement_Base::getQueryTimeOut() +{ + static jmethodID mID(nullptr); + return impl_getProperty("getQueryTimeOut",mID); +} + +sal_Int32 java_sql_Statement_Base::getMaxRows() +{ + static jmethodID mID(nullptr); + return impl_getProperty("getMaxRows",mID); +} + +sal_Int32 java_sql_Statement_Base::getResultSetConcurrency() +{ + static jmethodID mID(nullptr); + return impl_getProperty("getResultSetConcurrency",mID,m_nResultSetConcurrency); +} + + +sal_Int32 java_sql_Statement_Base::getResultSetType() +{ + static jmethodID mID(nullptr); + return impl_getProperty("getResultSetType",mID,m_nResultSetType); +} + +sal_Int32 java_sql_Statement_Base::impl_getProperty(const char* _pMethodName, jmethodID& _inout_MethodID,sal_Int32 _nDefault) +{ + sal_Int32 out = _nDefault; + if ( object ) + out = callIntMethod_ThrowRuntime(_pMethodName, _inout_MethodID); + return out; +} + +sal_Int32 java_sql_Statement_Base::impl_getProperty(const char* _pMethodName, jmethodID& _inout_MethodID) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + return callIntMethod_ThrowRuntime(_pMethodName, _inout_MethodID); +} + +sal_Int32 java_sql_Statement_Base::getFetchDirection() +{ + static jmethodID mID(nullptr); + return impl_getProperty("getFetchDirection",mID); +} + +sal_Int32 java_sql_Statement_Base::getFetchSize() +{ + static jmethodID mID(nullptr); + return impl_getProperty("getFetchSize",mID); +} + +sal_Int32 java_sql_Statement_Base::getMaxFieldSize() +{ + static jmethodID mID(nullptr); + return impl_getProperty("getMaxFieldSize",mID); +} + +OUString java_sql_Statement_Base::getCursorName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + try + { + return callStringMethod("getCursorName",mID); + } + catch(const SQLException&) + { + } + return OUString(); +} + +void java_sql_Statement_Base::setQueryTimeOut(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowRuntime("setQueryTimeOut", mID, _par0); +} + + +void java_sql_Statement_Base::setEscapeProcessing(bool _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_SET_ESCAPE_PROCESSING, _par0 ); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + m_bEscapeProcessing = _par0; + createStatement( t.pEnv ); + static jmethodID mID(nullptr); + callVoidMethodWithBoolArg_ThrowRuntime("setEscapeProcessing", mID, _par0); +} + +void java_sql_Statement_Base::setMaxRows(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowRuntime("setMaxRows", mID, _par0); +} + +void java_sql_Statement_Base::setResultSetConcurrency(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_RESULT_SET_CONCURRENCY, _par0 ); + m_nResultSetConcurrency = _par0; + + clearObject(); +} + +void java_sql_Statement_Base::setResultSetType(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_RESULT_SET_TYPE, _par0 ); + m_nResultSetType = _par0; + + clearObject(); +} + +void java_sql_Statement_Base::setFetchDirection(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINER, STR_LOG_FETCH_DIRECTION, _par0 ); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowRuntime("setFetchDirection", mID, _par0); +} + +void java_sql_Statement_Base::setFetchSize(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINER, STR_LOG_FETCH_SIZE, _par0 ); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowRuntime("setFetchSize", mID, _par0); +} + +void java_sql_Statement_Base::setMaxFieldSize(sal_Int32 _par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowRuntime("setMaxFieldSize", mID, _par0); +} + +void java_sql_Statement_Base::setCursorName(const OUString &_par0) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethodWithStringArg("setCursorName",mID,_par0); + } //t.pEnv +} + + +::cppu::IPropertyArrayHelper* java_sql_Statement_Base::createArrayHelper( ) const +{ + Sequence< Property > aProps(10); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING), + PROPERTY_ID_ESCAPEPROCESSING, cppu::UnoType<bool>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE), + PROPERTY_ID_MAXFIELDSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS), + PROPERTY_ID_MAXROWS, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT), + PROPERTY_ID_QUERYTIMEOUT, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_USEBOOKMARKS), + PROPERTY_ID_USEBOOKMARKS, cppu::UnoType<bool>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +::cppu::IPropertyArrayHelper & java_sql_Statement_Base::getInfoHelper() + +{ + return *getArrayHelper(); +} + +sal_Bool java_sql_Statement_Base::convertFastPropertyValue( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + try + { + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getQueryTimeOut()); + case PROPERTY_ID_MAXFIELDSIZE: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getMaxFieldSize()); + case PROPERTY_ID_MAXROWS: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getMaxRows()); + case PROPERTY_ID_CURSORNAME: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getCursorName()); + case PROPERTY_ID_RESULTSETCONCURRENCY: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getResultSetConcurrency()); + case PROPERTY_ID_RESULTSETTYPE: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getResultSetType()); + case PROPERTY_ID_FETCHDIRECTION: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection()); + case PROPERTY_ID_FETCHSIZE: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize()); + case PROPERTY_ID_ESCAPEPROCESSING: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bEscapeProcessing ); + case PROPERTY_ID_USEBOOKMARKS: + // return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAsLink); + default: + ; + } + } + catch(const css::lang::IllegalArgumentException&) + { + throw; + } + catch(const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("connectivity.jdbc"); + } + return false; +} + +void java_sql_Statement_Base::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const Any& rValue + ) +{ + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + setQueryTimeOut(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_MAXFIELDSIZE: + setMaxFieldSize(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_MAXROWS: + setMaxRows(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_CURSORNAME: + setCursorName(comphelper::getString(rValue)); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + setResultSetConcurrency(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_RESULTSETTYPE: + setResultSetType(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_FETCHDIRECTION: + setFetchDirection(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_FETCHSIZE: + setFetchSize(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_ESCAPEPROCESSING: + setEscapeProcessing( ::comphelper::getBOOL( rValue ) ); + break; + case PROPERTY_ID_USEBOOKMARKS: + // return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAsLink); + default: + ; + } +} + +void java_sql_Statement_Base::getFastPropertyValue( + Any& rValue, + sal_Int32 nHandle + ) const +{ + java_sql_Statement_Base* THIS = const_cast<java_sql_Statement_Base*>(this); + try + { + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + rValue <<= THIS->getQueryTimeOut(); + break; + case PROPERTY_ID_MAXFIELDSIZE: + rValue <<= THIS->getMaxFieldSize(); + break; + case PROPERTY_ID_MAXROWS: + rValue <<= THIS->getMaxRows(); + break; + case PROPERTY_ID_CURSORNAME: + rValue <<= THIS->getCursorName(); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= THIS->getResultSetConcurrency(); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= THIS->getResultSetType(); + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= THIS->getFetchDirection(); + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= THIS->getFetchSize(); + break; + case PROPERTY_ID_ESCAPEPROCESSING: + rValue <<= m_bEscapeProcessing; + break; + case PROPERTY_ID_USEBOOKMARKS: + default: + ; + } + } + catch(const Exception&) + { + } +} + +jclass java_sql_Statement::theClass = nullptr; + +java_sql_Statement::~java_sql_Statement() +{} + +jclass java_sql_Statement::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/Statement"); + return theClass; +} + + +void java_sql_Statement::createStatement(JNIEnv* _pEnv) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + if( !(_pEnv && !object) ) + return; + + // initialize temporary variable + static const char * const cMethodName = "createStatement"; + // Java-Call + jobject out = nullptr; + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(II)Ljava/sql/Statement;"; + mID = _pEnv->GetMethodID( m_pConnection->getMyClass(), cMethodName, cSignature ); + } + if( mID ){ + out = _pEnv->CallObjectMethod( m_pConnection->getJavaObject(), mID,m_nResultSetType,m_nResultSetConcurrency ); + } //mID + else + { + static const char * const cSignature2 = "()Ljava/sql/Statement;"; + static jmethodID mID2 = _pEnv->GetMethodID( m_pConnection->getMyClass(), cMethodName, cSignature2 );OSL_ENSURE(mID2,"Unknown method id!"); + if( mID2 ){ + out = _pEnv->CallObjectMethod( m_pConnection->getJavaObject(), mID2); + } //mID + } + ThrowLoggedSQLException( m_aLogger, _pEnv, *this ); + + if ( out ) + object = _pEnv->NewGlobalRef( out ); +} + + +IMPLEMENT_SERVICE_INFO(java_sql_Statement,"com.sun.star.sdbcx.JStatement","com.sun.star.sdbc.Statement"); + +void SAL_CALL java_sql_Statement_Base::acquire() throw() +{ + java_sql_Statement_BASE::acquire(); +} + +void SAL_CALL java_sql_Statement_Base::release() throw() +{ + java_sql_Statement_BASE::release(); +} + +void SAL_CALL java_sql_Statement::acquire() throw() +{ + OStatement_BASE2::acquire(); +} + +void SAL_CALL java_sql_Statement::release() throw() +{ + OStatement_BASE2::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL java_sql_Statement_Base::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Object.cxx b/connectivity/source/drivers/jdbc/Object.cxx new file mode 100644 index 000000000..479699750 --- /dev/null +++ b/connectivity/source/drivers/jdbc/Object.cxx @@ -0,0 +1,483 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <connectivity/CommonTools.hxx> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/logging/LogLevel.hpp> +#include <java/tools.hxx> +#include <java/sql/SQLException.hxx> +#include <osl/diagnose.h> +#include <java/LocalRef.hxx> +#include <strings.hxx> + +#include <comphelper/logging.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include <memory> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +static ::rtl::Reference< jvmaccess::VirtualMachine > const & getJavaVM2(const ::rtl::Reference< jvmaccess::VirtualMachine >& _rVM = ::rtl::Reference< jvmaccess::VirtualMachine >(), + bool _bSet = false) +{ + static ::rtl::Reference< jvmaccess::VirtualMachine > s_VM; + if ( _rVM.is() || _bSet ) + s_VM = _rVM; + return s_VM; +} + +::rtl::Reference< jvmaccess::VirtualMachine > java_lang_Object::getVM(const Reference<XComponentContext >& _rxContext) +{ + ::rtl::Reference< jvmaccess::VirtualMachine > xVM = getJavaVM2(); + if ( !xVM.is() && _rxContext.is() ) + xVM = getJavaVM2(::connectivity::getJavaVM(_rxContext)); + + return xVM; +} + +SDBThreadAttach::SDBThreadAttach() + : m_aGuard(java_lang_Object::getVM()) + , pEnv(nullptr) +{ + pEnv = m_aGuard.getEnvironment(); + OSL_ENSURE(pEnv,"Environment is nULL!"); +} + +SDBThreadAttach::~SDBThreadAttach() +{ +} + +static oslInterlockedCount& getJavaVMRefCount() +{ + static oslInterlockedCount s_nRefCount = 0; + return s_nRefCount; +} + +void SDBThreadAttach::addRef() +{ + osl_atomic_increment(&getJavaVMRefCount()); +} + +void SDBThreadAttach::releaseRef() +{ + osl_atomic_decrement(&getJavaVMRefCount()); + if ( getJavaVMRefCount() == 0 ) + { + getJavaVM2(::rtl::Reference< jvmaccess::VirtualMachine >(),true); + } +} + +// static variables of the class +jclass java_lang_Object::theClass = nullptr; + +jclass java_lang_Object::getMyClass() const +{ + if( !theClass ) + theClass = findMyClass("java/lang/Object"); + return theClass; +} +// the actual constructor +java_lang_Object::java_lang_Object() + : object( nullptr ) +{ + SDBThreadAttach::addRef(); +} + +// the protected-constructor for the derived classes +java_lang_Object::java_lang_Object( JNIEnv * pXEnv, jobject myObj ) + : object( nullptr ) +{ + SDBThreadAttach::addRef(); + if( pXEnv && myObj ) + object = pXEnv->NewGlobalRef( myObj ); +} + +java_lang_Object::~java_lang_Object() COVERITY_NOEXCEPT_FALSE +{ + if( object ) + { + SDBThreadAttach t; + clearObject(*t.pEnv); + } + SDBThreadAttach::releaseRef(); +} +void java_lang_Object::clearObject(JNIEnv& rEnv) +{ + if( object ) + { + rEnv.DeleteGlobalRef( object ); + object = nullptr; + } +} + +void java_lang_Object::clearObject() +{ + if( object ) + { + SDBThreadAttach t; + clearObject(*t.pEnv); + } +} +// the protected-constructor for the derived classes +void java_lang_Object::saveRef( JNIEnv * pXEnv, jobject myObj ) +{ + OSL_ENSURE( myObj, "object in c++ -> Java Wrapper" ); + if( myObj ) + object = pXEnv->NewGlobalRef( myObj ); +} + + +OUString java_lang_Object::toString() const +{ + static jmethodID mID(nullptr); + return callStringMethod("toString",mID); +} + + +namespace +{ + bool lcl_translateJNIExceptionToUNOException( + JNIEnv* _pEnvironment, const Reference< XInterface >& _rxContext, SQLException& _out_rException ) + { + jthrowable jThrow = _pEnvironment ? _pEnvironment->ExceptionOccurred() : nullptr; + if ( !jThrow ) + return false; + + _pEnvironment->ExceptionClear(); + // we have to clear the exception here because we want to handle it itself + + if ( _pEnvironment->IsInstanceOf( jThrow, java_sql_SQLException_BASE::st_getMyClass() ) ) + { + std::unique_ptr< java_sql_SQLException_BASE > pException( new java_sql_SQLException_BASE( _pEnvironment, jThrow ) ); + _out_rException = SQLException( pException->getMessage(), _rxContext, + pException->getSQLState(), pException->getErrorCode(), Any() ); + return true; + } + else if ( _pEnvironment->IsInstanceOf( jThrow, java_lang_Throwable::st_getMyClass() ) ) + { + std::unique_ptr< java_lang_Throwable > pThrow( new java_lang_Throwable( _pEnvironment, jThrow ) ); +#if OSL_DEBUG_LEVEL > 0 + pThrow->printStackTrace(); +#endif + OUString sMessage = pThrow->getMessage(); + if ( sMessage.isEmpty() ) + sMessage = pThrow->getLocalizedMessage(); + if( sMessage.isEmpty() ) + sMessage = pThrow->toString(); + _out_rException = SQLException( sMessage, _rxContext, OUString(), -1, Any() ); + return true; + } + else + _pEnvironment->DeleteLocalRef( jThrow ); + return false; + } +} + + +void java_lang_Object::ThrowLoggedSQLException( const ::comphelper::EventLogger& _rLogger, JNIEnv* _pEnvironment, + const Reference< XInterface >& _rxContext ) +{ + SQLException aException; + if ( lcl_translateJNIExceptionToUNOException( _pEnvironment, _rxContext, aException ) ) + { + _rLogger.log( css::logging::LogLevel::SEVERE, STR_LOG_THROWING_EXCEPTION, aException.Message, aException.SQLState, aException.ErrorCode ); + throw aException; + } +} + +void java_lang_Object::ThrowSQLException( JNIEnv* _pEnvironment, const Reference< XInterface>& _rxContext ) +{ + SQLException aException; + if ( lcl_translateJNIExceptionToUNOException( _pEnvironment, _rxContext, aException ) ) + throw aException; +} + +void java_lang_Object::ThrowRuntimeException( JNIEnv* _pEnvironment, const Reference< XInterface>& _rxContext ) +{ + try + { + ThrowSQLException(_pEnvironment, _rxContext); + } + catch (const SQLException& e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } +} + +void java_lang_Object::obtainMethodId_throwSQL(JNIEnv* _pEnv,const char* _pMethodName, const char* _pSignature,jmethodID& _inout_MethodID) const +{ + if ( !_inout_MethodID ) + { + _inout_MethodID = _pEnv->GetMethodID( getMyClass(), _pMethodName, _pSignature ); + OSL_ENSURE( _inout_MethodID, _pSignature ); + if ( !_inout_MethodID ) + throw SQLException(); + } // if ( !_inout_MethodID ) +} + +void java_lang_Object::obtainMethodId_throwRuntime(JNIEnv* _pEnv,const char* _pMethodName, const char* _pSignature,jmethodID& _inout_MethodID) const +{ + if ( !_inout_MethodID ) + { + _inout_MethodID = _pEnv->GetMethodID( getMyClass(), _pMethodName, _pSignature ); + OSL_ENSURE( _inout_MethodID, _pSignature ); + if ( !_inout_MethodID ) + throw RuntimeException(); + } // if ( !_inout_MethodID ) +} + + +bool java_lang_Object::callBooleanMethod( const char* _pMethodName, jmethodID& _inout_MethodID ) const +{ + bool out( false ); + + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callBooleanMethod: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"()Z", _inout_MethodID); + // call method + out = t.pEnv->CallBooleanMethod( object, _inout_MethodID ); + ThrowSQLException( t.pEnv, nullptr ); + + return out; +} + +bool java_lang_Object::callBooleanMethodWithIntArg( const char* _pMethodName, jmethodID& _inout_MethodID, sal_Int32 _nArgument ) const +{ + bool out( false ); + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callBooleanMethodWithIntArg: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"(I)Z", _inout_MethodID); + // call method + out = t.pEnv->CallBooleanMethod( object, _inout_MethodID, _nArgument ); + ThrowSQLException( t.pEnv, nullptr ); + + return out; +} + +jobject java_lang_Object::callResultSetMethod( JNIEnv& _rEnv,const char* _pMethodName, jmethodID& _inout_MethodID ) const +{ + // call method + jobject out = callObjectMethod(&_rEnv,_pMethodName,"()Ljava/sql/ResultSet;", _inout_MethodID); + return out; +} + +sal_Int32 java_lang_Object::callIntMethod_ThrowSQL(const char* _pMethodName, jmethodID& _inout_MethodID) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"()I", _inout_MethodID); + // call method + jint out( t.pEnv->CallIntMethod( object, _inout_MethodID ) ); + ThrowSQLException( t.pEnv, nullptr ); + return static_cast<sal_Int32>(out); +} + +sal_Int32 java_lang_Object::callIntMethod_ThrowRuntime(const char* _pMethodName, jmethodID& _inout_MethodID) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwRuntime(t.pEnv, _pMethodName,"()I", _inout_MethodID); + // call method + jint out( t.pEnv->CallIntMethod( object, _inout_MethodID ) ); + ThrowRuntimeException(t.pEnv, nullptr); + return static_cast<sal_Int32>(out); +} + +sal_Int32 java_lang_Object::callIntMethodWithIntArg_ThrowSQL( const char* _pMethodName, jmethodID& _inout_MethodID,sal_Int32 _nArgument ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"(I)I", _inout_MethodID); + // call method + jint out( t.pEnv->CallIntMethod( object, _inout_MethodID , _nArgument) ); + ThrowSQLException( t.pEnv, nullptr ); + return static_cast<sal_Int32>(out); +} + +sal_Int32 java_lang_Object::callIntMethodWithIntArg_ThrowRuntime( const char* _pMethodName, jmethodID& _inout_MethodID,sal_Int32 _nArgument ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwRuntime(t.pEnv, _pMethodName,"(I)I", _inout_MethodID); + // call method + jint out( t.pEnv->CallIntMethod( object, _inout_MethodID , _nArgument) ); + ThrowRuntimeException(t.pEnv, nullptr); + return static_cast<sal_Int32>(out); +} + +void java_lang_Object::callVoidMethod_ThrowSQL( const char* _pMethodName, jmethodID& _inout_MethodID) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"()V", _inout_MethodID); + + // call method + t.pEnv->CallVoidMethod( object, _inout_MethodID ); + ThrowSQLException( t.pEnv, nullptr ); +} + +void java_lang_Object::callVoidMethod_ThrowRuntime( const char* _pMethodName, jmethodID& _inout_MethodID) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwRuntime(t.pEnv, _pMethodName,"()V", _inout_MethodID); + + // call method + t.pEnv->CallVoidMethod( object, _inout_MethodID ); + ThrowRuntimeException(t.pEnv, nullptr); +} + +void java_lang_Object::callVoidMethodWithIntArg_ThrowSQL( const char* _pMethodName, jmethodID& _inout_MethodID, sal_Int32 _nArgument ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"(I)V", _inout_MethodID); + + // call method + t.pEnv->CallVoidMethod( object, _inout_MethodID,_nArgument ); + ThrowSQLException( t.pEnv, nullptr ); +} + +void java_lang_Object::callVoidMethodWithIntArg_ThrowRuntime( const char* _pMethodName, jmethodID& _inout_MethodID, sal_Int32 _nArgument ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwRuntime(t.pEnv, _pMethodName,"(I)V", _inout_MethodID); + + // call method + t.pEnv->CallVoidMethod( object, _inout_MethodID,_nArgument ); + ThrowRuntimeException(t.pEnv, nullptr); +} + +void java_lang_Object::callVoidMethodWithBoolArg_ThrowSQL( const char* _pMethodName, jmethodID& _inout_MethodID, bool _nArgument ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"(Z)V", _inout_MethodID); + // call method + t.pEnv->CallVoidMethod( object, _inout_MethodID,int(_nArgument) ); + ThrowSQLException( t.pEnv, nullptr ); +} + +void java_lang_Object::callVoidMethodWithBoolArg_ThrowRuntime( const char* _pMethodName, jmethodID& _inout_MethodID, bool _nArgument ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwRuntime(t.pEnv, _pMethodName,"(Z)V", _inout_MethodID); + // call method + t.pEnv->CallVoidMethod( object, _inout_MethodID,int(_nArgument) ); + ThrowRuntimeException(t.pEnv, nullptr); +} + +OUString java_lang_Object::callStringMethod( const char* _pMethodName, jmethodID& _inout_MethodID ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callStringMethod: no Java environment anymore!" ); + + // call method + jstring out = static_cast<jstring>(callObjectMethod(t.pEnv,_pMethodName,"()Ljava/lang/String;", _inout_MethodID)); + return JavaString2String( t.pEnv, out ); +} + +jobject java_lang_Object::callObjectMethod( JNIEnv * _pEnv,const char* _pMethodName,const char* _pSignature, jmethodID& _inout_MethodID ) const +{ + // obtain method ID + obtainMethodId_throwSQL(_pEnv, _pMethodName,_pSignature, _inout_MethodID); + // call method + jobject out = _pEnv->CallObjectMethod( object, _inout_MethodID); + ThrowSQLException( _pEnv, nullptr ); + return out; +} + + +jobject java_lang_Object::callObjectMethodWithIntArg( JNIEnv * _pEnv,const char* _pMethodName,const char* _pSignature, jmethodID& _inout_MethodID , sal_Int32 _nArgument) const +{ + obtainMethodId_throwSQL(_pEnv, _pMethodName,_pSignature, _inout_MethodID); + // call method + jobject out = _pEnv->CallObjectMethod( object, _inout_MethodID,_nArgument ); + ThrowSQLException( _pEnv, nullptr ); + return out; +} + +OUString java_lang_Object::callStringMethodWithIntArg( const char* _pMethodName, jmethodID& _inout_MethodID , sal_Int32 _nArgument) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callStringMethod: no Java environment anymore!" ); + jstring out = static_cast<jstring>(callObjectMethodWithIntArg(t.pEnv,_pMethodName,"(I)Ljava/lang/String;",_inout_MethodID,_nArgument)); + return JavaString2String( t.pEnv, out ); +} + +void java_lang_Object::callVoidMethodWithStringArg( const char* _pMethodName, jmethodID& _inout_MethodID,const OUString& _nArgument ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethod: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"(Ljava/lang/String;)V", _inout_MethodID); + + jdbc::LocalRef< jstring > str( t.env(),convertwchar_tToJavaString(t.pEnv,_nArgument)); + // call method + t.pEnv->CallVoidMethod( object, _inout_MethodID , str.get()); + ThrowSQLException( t.pEnv, nullptr ); +} + +sal_Int32 java_lang_Object::callIntMethodWithStringArg( const char* _pMethodName, jmethodID& _inout_MethodID,const OUString& _nArgument ) const +{ + SDBThreadAttach t; + OSL_ENSURE( t.pEnv, "java_lang_Object::callIntMethodWithStringArg: no Java environment anymore!" ); + obtainMethodId_throwSQL(t.pEnv, _pMethodName,"(Ljava/lang/String;)I", _inout_MethodID); + + //TODO: Check if the code below is needed + //jdbc::LocalRef< jstring > str( t.env(), convertwchar_tToJavaString( t.pEnv, sql ) ); + //{ + // jdbc::ContextClassLoaderScope ccl( t.env(), + // m_pConnection ? m_pConnection->getDriverClassLoader() : jdbc::GlobalRef< jobject >(), + // m_aLogger, + // *this + // ); + + jdbc::LocalRef< jstring > str( t.env(),convertwchar_tToJavaString(t.pEnv,_nArgument)); + // call method + jint out = t.pEnv->CallIntMethod( object, _inout_MethodID , str.get()); + ThrowSQLException( t.pEnv, nullptr ); + return static_cast<sal_Int32>(out); +} + +jclass java_lang_Object::findMyClass(const char* _pClassName) +{ + // the class must be fetched only once, therefore static + SDBThreadAttach t; + jclass tempClass = t.pEnv->FindClass(_pClassName); OSL_ENSURE(tempClass,"Java : FindClass not successful!"); + if(!tempClass) + { + t.pEnv->ExceptionDescribe(); + t.pEnv->ExceptionClear(); + } + jclass globClass = static_cast<jclass>(t.pEnv->NewGlobalRef( tempClass )); + t.pEnv->DeleteLocalRef( tempClass ); + return globClass; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/PreparedStatement.cxx b/connectivity/source/drivers/jdbc/PreparedStatement.cxx new file mode 100644 index 000000000..34c28f084 --- /dev/null +++ b/connectivity/source/drivers/jdbc/PreparedStatement.cxx @@ -0,0 +1,697 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/PreparedStatement.hxx> +#include <java/sql/ResultSet.hxx> +#include <java/sql/ResultSetMetaData.hxx> +#include <java/sql/Connection.hxx> +#include <java/sql/Timestamp.hxx> +#include <java/math/BigDecimal.hxx> +#include <java/tools.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/FValue.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> +#include <java/LocalRef.hxx> +#include <strings.hxx> +#include <string.h> +#include <memory> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +//************ Class: java.sql.PreparedStatement + +IMPLEMENT_SERVICE_INFO(java_sql_PreparedStatement,"com.sun.star.sdbcx.JPreparedStatement","com.sun.star.sdbc.PreparedStatement"); + +java_sql_PreparedStatement::java_sql_PreparedStatement( JNIEnv * pEnv, java_sql_Connection& _rCon, const OUString& sql ) + : OStatement_BASE2( pEnv, _rCon ) +{ + m_sSqlStatement = sql; +} + +jclass java_sql_PreparedStatement::theClass = nullptr; + +java_sql_PreparedStatement::~java_sql_PreparedStatement() +{ +} + + +jclass java_sql_PreparedStatement::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/PreparedStatement"); + return theClass; +} + + +css::uno::Any SAL_CALL java_sql_PreparedStatement::queryInterface( const css::uno::Type & rType ) +{ + css::uno::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), + static_cast< XPreparedBatchExecution*>(this)); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL java_sql_PreparedStatement::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<XPreparedStatement>::get(), + cppu::UnoType<XParameters>::get(), + cppu::UnoType<XResultSetMetaDataSupplier>::get(), + cppu::UnoType<XPreparedBatchExecution>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),OStatement_BASE2::getTypes()); +} + + +sal_Bool SAL_CALL java_sql_PreparedStatement::execute( ) +{ + m_aLogger.log( LogLevel::FINE, STR_LOG_EXECUTING_PREPARED ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + return callBooleanMethod( "execute", mID ); +} + + +sal_Int32 SAL_CALL java_sql_PreparedStatement::executeUpdate( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINE, STR_LOG_EXECUTING_PREPARED_UPDATE ); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + return callIntMethod_ThrowSQL("executeUpdate", mID); +} + + +void SAL_CALL java_sql_PreparedStatement::setString( sal_Int32 parameterIndex, const OUString& x ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + m_aLogger.log( LogLevel::FINER, STR_LOG_STRING_PARAMETER, parameterIndex, x ); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { // initialize temporary Variable + createStatement(t.pEnv); + static const char * const cSignature = "(ILjava/lang/String;)V"; + static const char * const cMethodName = "setString"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + jdbc::LocalRef< jstring > str( t.env(),convertwchar_tToJavaString(t.pEnv,x)); + t.pEnv->CallVoidMethod( object, mID, parameterIndex,str.get()); + // and clean up + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } //t.pEnv +} + + +css::uno::Reference< css::sdbc::XConnection > SAL_CALL java_sql_PreparedStatement::getConnection( ) +{ + return Reference< XConnection >(m_pConnection.get()); +} + + +css::uno::Reference< css::sdbc::XResultSet > SAL_CALL java_sql_PreparedStatement::executeQuery( ) +{ + m_aLogger.log( LogLevel::FINE, STR_LOG_EXECUTING_PREPARED_QUERY ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callResultSetMethod(t.env(),"executeQuery",mID); + + return out==nullptr ? nullptr : new java_sql_ResultSet( t.pEnv, out, m_aLogger, *m_pConnection,this); +} + + +void SAL_CALL java_sql_PreparedStatement::setBoolean( sal_Int32 parameterIndex, sal_Bool x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_BOOLEAN_PARAMETER, parameterIndex, bool(x) ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setBoolean", "(IZ)V", mID, parameterIndex, x); +} + + +void SAL_CALL java_sql_PreparedStatement::setByte( sal_Int32 parameterIndex, sal_Int8 x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_BYTE_PARAMETER, parameterIndex, static_cast<sal_Int32>(x) ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setByte", "(IB)V", mID, parameterIndex, x); +} + + +void SAL_CALL java_sql_PreparedStatement::setDate( sal_Int32 parameterIndex, const css::util::Date& x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_DATE_PARAMETER, parameterIndex, x ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + java_sql_Date aT(x); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setDate", "(ILjava/sql/Date;)V", mID, parameterIndex, aT.getJavaObject()); +} + + +void SAL_CALL java_sql_PreparedStatement::setTime( sal_Int32 parameterIndex, const css::util::Time& x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_TIME_PARAMETER, parameterIndex, x ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + java_sql_Time aT(x); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setTime", "(ILjava/sql/Time;)V", mID, parameterIndex, aT.getJavaObject()); +} + + +void SAL_CALL java_sql_PreparedStatement::setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_TIMESTAMP_PARAMETER, parameterIndex, x ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + java_sql_Timestamp aD(x); + callVoidMethod_ThrowSQL("setTimestamp", "(ILjava/sql/Timestamp;)V", mID, parameterIndex, aD.getJavaObject()); +} + +void SAL_CALL java_sql_PreparedStatement::setDouble( sal_Int32 parameterIndex, double x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_DOUBLE_PARAMETER, parameterIndex, x ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setDouble", "(ID)V", mID, parameterIndex, x); +} + + +void SAL_CALL java_sql_PreparedStatement::setFloat( sal_Int32 parameterIndex, float x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_FLOAT_PARAMETER, parameterIndex, x ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setFloat", "(IF)V", mID, parameterIndex, x); +} + + +void SAL_CALL java_sql_PreparedStatement::setInt( sal_Int32 parameterIndex, sal_Int32 x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_INT_PARAMETER, parameterIndex, x ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setInt", "(II)V", mID, parameterIndex, x); +} + + +void SAL_CALL java_sql_PreparedStatement::setLong( sal_Int32 parameterIndex, sal_Int64 x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_LONG_PARAMETER, parameterIndex, x ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setLong", "(IJ)V", mID, parameterIndex, x); +} + + +void SAL_CALL java_sql_PreparedStatement::setNull( sal_Int32 parameterIndex, sal_Int32 sqlType ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_NULL_PARAMETER, parameterIndex, sqlType ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setNull", "(II)V", mID, parameterIndex, sqlType); +} + + +void SAL_CALL java_sql_PreparedStatement::setClob( sal_Int32 /*parameterIndex*/, const css::uno::Reference< css::sdbc::XClob >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setClob", *this ); +} + + +void SAL_CALL java_sql_PreparedStatement::setBlob( sal_Int32 /*parameterIndex*/, const css::uno::Reference< css::sdbc::XBlob >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setBlob", *this ); +} + + +void SAL_CALL java_sql_PreparedStatement::setArray( sal_Int32 /*parameterIndex*/, const css::uno::Reference< css::sdbc::XArray >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setArray", *this ); +} + + +void SAL_CALL java_sql_PreparedStatement::setRef( sal_Int32 /*parameterIndex*/, const css::uno::Reference< css::sdbc::XRef >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setRef", *this ); +} + + +void SAL_CALL java_sql_PreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_OBJECT_NULL_PARAMETER, parameterIndex ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + createStatement(t.pEnv); + + // initialize temporary Variable + static const char * const cSignature = "(ILjava/lang/Object;II)V"; + static const char * const cMethodName = "setObject"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + { + jobject obj = nullptr; + switch(targetSqlType) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + { + double nTemp = 0.0; + + std::unique_ptr<java_math_BigDecimal> pBigDecimal; + if ( x >>= nTemp) + { + pBigDecimal.reset(new java_math_BigDecimal(nTemp)); + //setDouble(parameterIndex,nTemp); + //return; + } + else + { + ORowSetValue aValue; + aValue.fill(x); + const OUString sValue = aValue; + if ( !sValue.isEmpty() ) + pBigDecimal.reset(new java_math_BigDecimal(sValue)); + else + pBigDecimal.reset(new java_math_BigDecimal(0.0)); + } + //obj = convertwchar_tToJavaString(t.pEnv,::comphelper::getString(x)); + t.pEnv->CallVoidMethod( object, mID, parameterIndex,pBigDecimal->getJavaObject(),targetSqlType,scale); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + return; + } + default: + obj = convertwchar_tToJavaString(t.pEnv,::comphelper::getString(x)); + break; + } + t.pEnv->CallVoidMethod( object, mID, parameterIndex,obj,targetSqlType,scale); + t.pEnv->DeleteLocalRef(obj); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + // and clean up + } //mID + } //t.pEnv +} + + +void SAL_CALL java_sql_PreparedStatement::setObjectNull( sal_Int32 parameterIndex, sal_Int32 /*sqlType*/, const OUString& /*typeName*/ ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_OBJECT_NULL_PARAMETER, parameterIndex ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL<jobject>("setObject", "(ILjava/lang/Object;)V", mID, parameterIndex, nullptr); +} + + +void SAL_CALL java_sql_PreparedStatement::setObject( sal_Int32 parameterIndex, const css::uno::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); + } +} + + +void SAL_CALL java_sql_PreparedStatement::setShort( sal_Int32 parameterIndex, sal_Int16 x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_SHORT_PARAMETER, parameterIndex, x ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("setShort", "(IS)V", mID, parameterIndex, x); +} + + +void SAL_CALL java_sql_PreparedStatement::setBytes( sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_BYTES_PARAMETER, parameterIndex ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + createStatement(t.pEnv); + + // initialize temporary Variable + static const char * const cSignature = "(I[B)V"; + static const char * const cMethodName = "setBytes"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + jbyteArray pByteArray = t.pEnv->NewByteArray(x.getLength()); + jbyte * pData = reinterpret_cast<jbyte *>( + const_cast<sal_Int8 *>(x.getConstArray())); + // 4th param of Set*ArrayRegion changed from pointer to non-const to + // pointer to const between <http://docs.oracle.com/javase/6/docs/ + // technotes/guides/jni/spec/functions.html#wp22933> and + // <http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/ + // functions.html#wp22933>; work around that difference in a way + // that doesn't trigger loplugin:redundantcast + t.pEnv->SetByteArrayRegion(pByteArray,0,x.getLength(),pData); + t.pEnv->CallVoidMethod( object, mID, parameterIndex,pByteArray); + t.pEnv->DeleteLocalRef(pByteArray); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } //t.pEnv +} + + +void SAL_CALL java_sql_PreparedStatement::setCharacterStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_CHARSTREAM_PARAMETER, parameterIndex ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; + assert(t.pEnv && "Java environment has been deleted!"); + { + createStatement(t.pEnv); + + // initialize temporary variable + static const char * const cSignature = "(ILjava/io/InputStream;I)V"; + static const char * const cMethodName = "setCharacterStream"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + Sequence< sal_Int8 > aSeq; + if ( x.is() ) + x->readBytes( aSeq, length ); + sal_Int32 actualLength = aSeq.getLength(); + + jvalue args2[3]; + jbyteArray pByteArray = t.pEnv->NewByteArray( actualLength ); + jbyte * aSeqData = reinterpret_cast<jbyte *>( + const_cast<sal_Int8 *>(aSeq.getConstArray())); + // 4th param of Set*ArrayRegion changed from pointer to non-const to + // pointer to const between <http://docs.oracle.com/javase/6/docs/ + // technotes/guides/jni/spec/functions.html#wp22933> and + // <http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/ + // functions.html#wp22933>; work around that difference in a way + // that doesn't trigger loplugin:redundantcast + t.pEnv->SetByteArrayRegion(pByteArray,0,actualLength,aSeqData); + args2[0].l = pByteArray; + args2[1].i = 0; + args2[2].i = actualLength; + // Java-Call + jclass aClass = t.pEnv->FindClass("java/io/CharArrayInputStream"); + static jmethodID mID2 = nullptr; + if ( !mID2 ) + { + // initialize temporary variable + const char * const cSignatureStream = "([BII)V"; + mID2 = t.pEnv->GetMethodID( aClass, "<init>", cSignatureStream ); + } + jobject tempObj = nullptr; + if(mID2) + tempObj = t.pEnv->NewObjectA( aClass, mID2, args2 ); + + t.pEnv->CallVoidMethod( object, mID, parameterIndex,tempObj,actualLength); + // and clean up + t.pEnv->DeleteLocalRef(pByteArray); + t.pEnv->DeleteLocalRef(tempObj); + t.pEnv->DeleteLocalRef(aClass); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } //t.pEnv +} + + +void SAL_CALL java_sql_PreparedStatement::setBinaryStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_BINARYSTREAM_PARAMETER, parameterIndex ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + createStatement(t.pEnv); + // initialize temporary variable + static const char * const cSignature = "(ILjava/io/InputStream;I)V"; + static const char * const cMethodName = "setBinaryStream"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + { + Sequence< sal_Int8 > aSeq; + if ( x.is() ) + x->readBytes( aSeq, length ); + sal_Int32 actualLength = aSeq.getLength(); + + jvalue args2[3]; + jbyteArray pByteArray = t.pEnv->NewByteArray(actualLength); + jbyte * aSeqData = reinterpret_cast<jbyte *>( + const_cast<sal_Int8 *>(aSeq.getConstArray())); + // 4th param of Set*ArrayRegion changed from pointer to non-const to + // pointer to const between <http://docs.oracle.com/javase/6/docs/ + // technotes/guides/jni/spec/functions.html#wp22933> and + // <http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/ + // functions.html#wp22933>; work around that difference in a way + // that doesn't trigger loplugin:redundantcast + t.pEnv->SetByteArrayRegion(pByteArray,0,actualLength,aSeqData); + args2[0].l = pByteArray; + args2[1].i = 0; + args2[2].i = actualLength; + + // Java-Call + jclass aClass = t.pEnv->FindClass("java/io/ByteArrayInputStream"); + static jmethodID mID2 = nullptr; + if ( !mID2 ) + { + // initialize temporary variable + const char * const cSignatureStream = "([BII)V"; + mID2 = t.pEnv->GetMethodID( aClass, "<init>", cSignatureStream ); + } + jobject tempObj = nullptr; + if(mID2) + tempObj = t.pEnv->NewObjectA( aClass, mID2, args2 ); + t.pEnv->CallVoidMethod( object, mID, parameterIndex,tempObj,actualLength); + // and clean up + t.pEnv->DeleteLocalRef(pByteArray); + t.pEnv->DeleteLocalRef(tempObj); + t.pEnv->DeleteLocalRef(aClass); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } //t.pEnv +} + + +void SAL_CALL java_sql_PreparedStatement::clearParameters( ) +{ + m_aLogger.log( LogLevel::FINER, STR_LOG_CLEAR_PARAMETERS ); + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + SDBThreadAttach t; + { + createStatement(t.pEnv); + + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("clearParameters",mID); + } //t.pEnv +} + +void SAL_CALL java_sql_PreparedStatement::clearBatch( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("clearBatch",mID); + } //t.pEnv +} + + +void SAL_CALL java_sql_PreparedStatement::addBatch( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + createStatement(t.pEnv); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("addBatch", mID); + } //t.pEnv +} + + +css::uno::Sequence< sal_Int32 > SAL_CALL java_sql_PreparedStatement::executeBatch( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + css::uno::Sequence< sal_Int32 > aSeq; + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jintArray out = static_cast<jintArray>(callObjectMethod(t.pEnv,"executeBatch","()[I", mID)); + if(out) + { + jboolean p = false; + aSeq.realloc(t.pEnv->GetArrayLength(out)); + memcpy(aSeq.getArray(),t.pEnv->GetIntArrayElements(out,&p),aSeq.getLength()); + t.pEnv->DeleteLocalRef(out); + } + return aSeq; +} + +css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL java_sql_PreparedStatement::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + createStatement(t.pEnv); + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getMetaData","()Ljava/sql/ResultSetMetaData;", mID); + + return out==nullptr ? nullptr : new java_sql_ResultSetMetaData( t.pEnv, out, *m_pConnection ); +} + +void SAL_CALL java_sql_PreparedStatement::acquire() throw() +{ + OStatement_BASE2::acquire(); +} + +void SAL_CALL java_sql_PreparedStatement::release() throw() +{ + OStatement_BASE2::release(); +} + +void java_sql_PreparedStatement::createStatement(JNIEnv* _pEnv) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(java_sql_Statement_BASE::rBHelper.bDisposed); + + if( !(!object && _pEnv) ) + return; + + // initialize temporary variable + static const char * const cMethodName = "prepareStatement"; + + jvalue args[1]; + // convert Parameter + args[0].l = convertwchar_tToJavaString(_pEnv,m_sSqlStatement); + // Java-Call + jobject out = nullptr; + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(Ljava/lang/String;II)Ljava/sql/PreparedStatement;"; + mID = _pEnv->GetMethodID( m_pConnection->getMyClass(), cMethodName, cSignature ); + } + if( mID ) + { + out = _pEnv->CallObjectMethod( m_pConnection->getJavaObject(), mID, args[0].l ,m_nResultSetType,m_nResultSetConcurrency); + } + else + { + static jmethodID mID2 = nullptr; + if ( !mID2 ) + { + static const char * const cSignature2 = "(Ljava/lang/String;)Ljava/sql/PreparedStatement;"; + mID2 = _pEnv->GetMethodID( m_pConnection->getMyClass(), cMethodName, cSignature2 ); + } + if ( mID2 ) + out = _pEnv->CallObjectMethod( m_pConnection->getJavaObject(), mID2, args[0].l ); + } + _pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + ThrowLoggedSQLException( m_aLogger, _pEnv, *this ); + if ( out ) + object = _pEnv->NewGlobalRef( out ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Reader.cxx b/connectivity/source/drivers/jdbc/Reader.cxx new file mode 100644 index 000000000..8dd12157e --- /dev/null +++ b/connectivity/source/drivers/jdbc/Reader.cxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <java/io/Reader.hxx> +#include <string.h> +#include <osl/diagnose.h> +using namespace connectivity; +using ::com::sun::star::uno::Sequence; + + +//************ Class: java.io.Reader + + +jclass java_io_Reader::theClass = nullptr; +java_io_Reader::java_io_Reader( JNIEnv * pEnv, jobject myObj ) + : java_lang_Object( pEnv, myObj ) +{ + SDBThreadAttach::addRef(); +} +java_io_Reader::~java_io_Reader() +{ + SDBThreadAttach::releaseRef(); +} + +jclass java_io_Reader::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/io/Reader"); + return theClass; +} + +sal_Int32 SAL_CALL java_io_Reader::readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) +{ + return readBytes(aData,nMaxBytesToRead); +} + +void SAL_CALL java_io_Reader::skipBytes( sal_Int32 nBytesToSkip ) +{ + static jmethodID mID(nullptr); + if(nBytesToSkip <= 0) + return; + + if(m_buf) + { + m_buf.reset(); + --nBytesToSkip; + } + + static_assert(sizeof(jchar) == 2, "I thought Java characters were UTF16 code units?"); + sal_Int32 nCharsToSkip = nBytesToSkip / sizeof(jchar); + callIntMethodWithIntArg_ThrowRuntime("skip",mID,nCharsToSkip); + if(nBytesToSkip % sizeof(jchar) != 0) + { + assert(nBytesToSkip % sizeof(jchar) == 1); + Sequence< sal_Int8 > aData(1); + assert(m_buf); + readBytes(aData, 1); + } +} + +sal_Int32 SAL_CALL java_io_Reader::available( ) +{ + if(m_buf) + return 1; + bool out; + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + static const char * const cSignature = "()Z"; + static const char * const cMethodName = "ready"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwRuntime(t.pEnv, cMethodName,cSignature, mID); + out = t.pEnv->CallBooleanMethod( object, mID); + ThrowRuntimeException(t.pEnv,*this); + } //t.pEnv + return (m_buf ? 1 : 0) + (out ? 1 : 0); // no way to tell *how much* is ready +} + +void SAL_CALL java_io_Reader::closeInput( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowRuntime("close", mID); +} + +sal_Int32 SAL_CALL java_io_Reader::readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) +{ + OSL_ENSURE(aData.getLength() >= nBytesToRead," Sequence is smaller than BytesToRead"); + + if(nBytesToRead == 0) + return 0; + + sal_Int8 *dst(aData.getArray()); + sal_Int32 nBytesWritten(0); + + if (m_buf) + { + if(!aData.hasElements()) + { + aData.realloc(1); + dst = aData.getArray(); + } + *dst = *m_buf; + m_buf.reset(); + ++nBytesWritten; + ++dst; + --nBytesToRead; + } + + if(nBytesToRead == 0) + return nBytesWritten; + + sal_Int32 nCharsToRead = (nBytesToRead + 1)/2; + + jint outChars(0); + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + + { + jcharArray pCharArray = t.pEnv->NewCharArray(nCharsToRead); + static const char * const cSignature = "([CII)I"; + static const char * const cMethodName = "read"; + // Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwRuntime(t.pEnv, cMethodName,cSignature, mID); + outChars = t.pEnv->CallIntMethod( object, mID, pCharArray, 0, nCharsToRead ); + if ( !outChars ) + { + if(nBytesWritten==0) + ThrowRuntimeException(t.pEnv,*this); + else + return 1; + } + if(outChars > 0) + { + static_assert(sizeof(jchar) == 2, "I thought Java characters were UTF16 code units?"); + const sal_Int32 jcs = sizeof(jchar); + const sal_Int32 outBytes = std::min(nBytesToRead, outChars*jcs); + assert(outBytes == outChars*jcs || outBytes == outChars*jcs - 1); + + jboolean p = JNI_FALSE; + if(aData.getLength() < nBytesWritten + outBytes) + { + aData.realloc(nBytesWritten + outBytes); + dst = aData.getArray() + nBytesWritten; + } + jchar *outBuf(t.pEnv->GetCharArrayElements(pCharArray,&p)); + + memcpy(dst, outBuf, outBytes); + nBytesWritten += outBytes; + if(outBytes < outChars*jcs) + { + assert(outChars*jcs - outBytes == 1); + assert(!m_buf); + m_buf = reinterpret_cast<char*>(outBuf)[outBytes]; + } + } + t.pEnv->DeleteLocalRef(pCharArray); + } //t.pEnv + return nBytesWritten; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Ref.cxx b/connectivity/source/drivers/jdbc/Ref.cxx new file mode 100644 index 000000000..ca8e2e112 --- /dev/null +++ b/connectivity/source/drivers/jdbc/Ref.cxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <java/sql/Ref.hxx> + +using namespace connectivity; + +//************ Class: java.sql.Ref + + +jclass java_sql_Ref::theClass = nullptr; +java_sql_Ref::java_sql_Ref( JNIEnv * pEnv, jobject myObj ) +: java_lang_Object( pEnv, myObj ) +{ + SDBThreadAttach::addRef(); +} +java_sql_Ref::~java_sql_Ref() +{ + SDBThreadAttach::releaseRef(); +} + +jclass java_sql_Ref::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/Ref"); + return theClass; +} + +OUString SAL_CALL java_sql_Ref::getBaseTypeName( ) +{ + static jmethodID mID(nullptr); + return callStringMethod("getBaseTypeName",mID); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/ResultSet.cxx b/connectivity/source/drivers/jdbc/ResultSet.cxx new file mode 100644 index 000000000..238e78702 --- /dev/null +++ b/connectivity/source/drivers/jdbc/ResultSet.cxx @@ -0,0 +1,998 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/lang/String.hxx> +#include <java/lang/Boolean.hxx> +#include <java/sql/ResultSet.hxx> +#include <java/math/BigDecimal.hxx> +#include <java/sql/JStatement.hxx> +#include <java/sql/SQLWarning.hxx> +#include <java/sql/Timestamp.hxx> +#include <java/sql/Array.hxx> +#include <java/sql/Ref.hxx> +#include <java/sql/Clob.hxx> +#include <java/sql/Blob.hxx> +#include <java/sql/ResultSetMetaData.hxx> +#include <java/io/InputStream.hxx> +#include <java/io/Reader.hxx> +#include <java/tools.hxx> +#include <comphelper/property.hxx> +#include <connectivity/CommonTools.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <TConnection.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> +#include <java/LocalRef.hxx> + +#include <string.h> +#include <memory> + +using namespace ::comphelper; + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +IMPLEMENT_SERVICE_INFO(java_sql_ResultSet,"com.sun.star.sdbcx.JResultSet","com.sun.star.sdbc.ResultSet"); + +//************ Class: java.sql.ResultSet + + +jclass java_sql_ResultSet::theClass = nullptr; +java_sql_ResultSet::java_sql_ResultSet( JNIEnv * pEnv, jobject myObj, const java::sql::ConnectionLog& _rParentLogger,java_sql_Connection& _rConnection, java_sql_Statement_Base* pStmt) + :java_sql_ResultSet_BASE(m_aMutex) + ,java_lang_Object( pEnv, myObj ) + ,OPropertySetHelper(java_sql_ResultSet_BASE::rBHelper) + ,m_aLogger( _rParentLogger, java::sql::ConnectionLog::RESULTSET ) + ,m_pConnection(&_rConnection) +{ + SDBThreadAttach::addRef(); + osl_atomic_increment(&m_refCount); + if ( pStmt ) + m_xStatement = *pStmt; + + osl_atomic_decrement(&m_refCount); +} + +java_sql_ResultSet::~java_sql_ResultSet() +{ + if ( !java_sql_ResultSet_BASE::rBHelper.bDisposed && !java_sql_ResultSet_BASE::rBHelper.bInDispose ) + { + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } +} + +jclass java_sql_ResultSet::getMyClass() const +{ + // the class must be fetched only once, therefore static + if( !theClass ) + theClass = findMyClass("java/sql/ResultSet"); + return theClass; +} + +void java_sql_ResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + if( object ) + { + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("close", mID); + clearObject(*t.pEnv); + } + + SDBThreadAttach::releaseRef(); +} + +css::uno::Any SAL_CALL java_sql_ResultSet::queryInterface( const css::uno::Type & rType ) +{ + css::uno::Any aRet = OPropertySetHelper::queryInterface(rType); + return aRet.hasValue() ? aRet : java_sql_ResultSet_BASE::queryInterface(rType); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL java_sql_ResultSet::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(),java_sql_ResultSet_BASE::getTypes()); +} + + +sal_Int32 SAL_CALL java_sql_ResultSet::findColumn( const OUString& columnName ) +{ + static jmethodID mID(nullptr); + return callIntMethodWithStringArg("findColumn",mID,columnName); +} + +Reference< css::io::XInputStream > SAL_CALL java_sql_ResultSet::getBinaryStream( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getBinaryStream","(I)Ljava/io/InputStream;", mID, columnIndex); + + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_io_InputStream( t.pEnv, out ); +} + +Reference< css::io::XInputStream > SAL_CALL java_sql_ResultSet::getCharacterStream( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getCharacterStream","(I)Ljava/io/Reader;", mID, columnIndex); + + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_io_Reader( t.pEnv, out ); +} + + +sal_Bool SAL_CALL java_sql_ResultSet::getBoolean( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "getBoolean", mID,columnIndex ); +} + + +sal_Int8 SAL_CALL java_sql_ResultSet::getByte( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + jbyte (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallByteMethod; + return callMethodWithIntArg<jbyte>(pCallMethod,"getByte","(I)B",mID,columnIndex); +} + + +Sequence< sal_Int8 > SAL_CALL java_sql_ResultSet::getBytes( sal_Int32 columnIndex ) +{ + Sequence< sal_Int8 > aSeq; + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jbyteArray out = static_cast<jbyteArray>(callObjectMethodWithIntArg(t.pEnv,"getBytes","(I)[B", mID, columnIndex)); + if (out) + { + jboolean p = false; + aSeq.realloc(t.pEnv->GetArrayLength(out)); + memcpy(aSeq.getArray(),t.pEnv->GetByteArrayElements(out,&p),aSeq.getLength()); + t.pEnv->DeleteLocalRef(out); + } + return aSeq; +} + + +css::util::Date SAL_CALL java_sql_ResultSet::getDate( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getDate","(I)Ljava/sql/Date;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out ? static_cast <css::util::Date> (java_sql_Date( t.pEnv, out )) : css::util::Date(); +} + + +double SAL_CALL java_sql_ResultSet::getDouble( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + jdouble (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallDoubleMethod; + return callMethodWithIntArg<double>(pCallMethod,"getDouble","(I)D",mID,columnIndex); +} + + +float SAL_CALL java_sql_ResultSet::getFloat( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + jfloat (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallFloatMethod; + return callMethodWithIntArg<jfloat>(pCallMethod,"getFloat","(I)F",mID,columnIndex); +} + + +sal_Int32 SAL_CALL java_sql_ResultSet::getInt( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + return callIntMethodWithIntArg_ThrowSQL("getInt",mID,columnIndex); +} + + +sal_Int32 SAL_CALL java_sql_ResultSet::getRow( ) +{ + static jmethodID mID(nullptr); + return callIntMethod_ThrowSQL("getRow", mID); +} + + +sal_Int64 SAL_CALL java_sql_ResultSet::getLong( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + jlong (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallLongMethod; + return callMethodWithIntArg<jlong>(pCallMethod,"getLong","(I)J",mID,columnIndex); +} + + +css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL java_sql_ResultSet::getMetaData( ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getMetaData","()Ljava/sql/ResultSetMetaData;", mID); + + return out==nullptr ? nullptr : new java_sql_ResultSetMetaData( t.pEnv, out, *m_pConnection ); +} + +Reference< XArray > SAL_CALL java_sql_ResultSet::getArray( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getArray","(I)Ljava/sql/Array;", mID, columnIndex); + + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_Array( t.pEnv, out ); +} + + +Reference< XClob > SAL_CALL java_sql_ResultSet::getClob( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getClob","(I)Ljava/sql/Clob;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_Clob( t.pEnv, out ); +} + +Reference< XBlob > SAL_CALL java_sql_ResultSet::getBlob( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getBlob","(I)Ljava/sql/Blob;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_Blob( t.pEnv, out ); +} + + +Reference< XRef > SAL_CALL java_sql_ResultSet::getRef( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getRef","(I)Ljava/sql/Ref;", mID, columnIndex); + + // WARNING: the caller becomes the owner of the returned pointer + return out==nullptr ? nullptr : new java_sql_Ref( t.pEnv, out ); +} + + +Any SAL_CALL java_sql_ResultSet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& typeMap ) +{ + jobject out(nullptr); + Any aRet; + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + { + jvalue args[2]; + // convert parameter + args[0].i = columnIndex; + args[1].l = convertTypeMapToJavaMap(typeMap); + // initialize temporary Variable + // Java-Call + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(I)Ljava/lang/Object;"; + static const char * const cMethodName = "getObject"; + + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + } + + out = t.pEnv->CallObjectMethodA( object, mID, args); + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[1].l)); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + // and clean up + if ( out ) + { + if ( t.pEnv->IsInstanceOf(out,java_lang_String::st_getMyClass()) ) + { + java_lang_String aVal(t.pEnv,out); + aRet <<= OUString(aVal); + } + else if ( t.pEnv->IsInstanceOf(out,java_lang_Boolean::st_getMyClass()) ) + { + java_lang_Boolean aVal(t.pEnv,out); + static jmethodID methodID = nullptr; + aRet <<= aVal.callBooleanMethod("booleanValue",methodID); + } + else if ( t.pEnv->IsInstanceOf(out,java_sql_Date::st_getMyClass()) ) + { + java_sql_Date aVal(t.pEnv,out); + aRet <<= css::util::Date(aVal); + } + else if ( t.pEnv->IsInstanceOf(out,java_sql_Time::st_getMyClass()) ) + { + java_sql_Time aVal(t.pEnv,out); + aRet <<= css::util::Time(aVal); + } + else if ( t.pEnv->IsInstanceOf(out,java_sql_Timestamp::st_getMyClass()) ) + { + java_sql_Timestamp aVal(t.pEnv,out); + aRet <<= css::util::DateTime(aVal); + } + else + t.pEnv->DeleteLocalRef(out); + } + } //t.pEnv + return aRet; +} + + +sal_Int16 SAL_CALL java_sql_ResultSet::getShort( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + jshort (JNIEnv::* const pCallMethod)( jobject obj, jmethodID methodID, ... ) = &JNIEnv::CallShortMethod; + return callMethodWithIntArg<jshort>(pCallMethod,"getShort","(I)S",mID,columnIndex); +} + + +OUString SAL_CALL java_sql_ResultSet::getString( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getString",mID,columnIndex); +} + + +css::util::Time SAL_CALL java_sql_ResultSet::getTime( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getTime","(I)Ljava/sql/Time;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out ? static_cast <css::util::Time> (java_sql_Time( t.pEnv, out )) : css::util::Time(); +} + + +css::util::DateTime SAL_CALL java_sql_ResultSet::getTimestamp( sal_Int32 columnIndex ) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + static jmethodID mID(nullptr); + jobject out = callObjectMethodWithIntArg(t.pEnv,"getTimestamp","(I)Ljava/sql/Timestamp;", mID, columnIndex); + // WARNING: the caller becomes the owner of the returned pointer + return out ? static_cast <css::util::DateTime> (java_sql_Timestamp( t.pEnv, out )) : css::util::DateTime(); +} + + +sal_Bool SAL_CALL java_sql_ResultSet::isAfterLast( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "isAfterLast", mID ); +} + +sal_Bool SAL_CALL java_sql_ResultSet::isFirst( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "isFirst", mID ); +} + +sal_Bool SAL_CALL java_sql_ResultSet::isLast( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "isLast", mID ); +} + +void SAL_CALL java_sql_ResultSet::beforeFirst( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("beforeFirst", mID); +} + +void SAL_CALL java_sql_ResultSet::afterLast( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("afterLast", mID); +} + + +void SAL_CALL java_sql_ResultSet::close( ) +{ + dispose(); +} + + +sal_Bool SAL_CALL java_sql_ResultSet::first( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "first", mID ); +} + + +sal_Bool SAL_CALL java_sql_ResultSet::last( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "last", mID ); +} + +sal_Bool SAL_CALL java_sql_ResultSet::absolute( sal_Int32 row ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "absolute", mID,row ); +} + +sal_Bool SAL_CALL java_sql_ResultSet::relative( sal_Int32 row ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "relative", mID,row ); +} + +sal_Bool SAL_CALL java_sql_ResultSet::previous( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "previous", mID ); +} + +Reference< XInterface > SAL_CALL java_sql_ResultSet::getStatement( ) +{ + return m_xStatement; +} + + +sal_Bool SAL_CALL java_sql_ResultSet::rowDeleted( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "rowDeleted", mID ); +} + +sal_Bool SAL_CALL java_sql_ResultSet::rowInserted( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "rowInserted", mID ); +} + +sal_Bool SAL_CALL java_sql_ResultSet::rowUpdated( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "rowUpdated", mID ); +} + + +sal_Bool SAL_CALL java_sql_ResultSet::isBeforeFirst( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "isBeforeFirst", mID ); +} + + +sal_Bool SAL_CALL java_sql_ResultSet::next( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "next", mID ); +} + +sal_Bool SAL_CALL java_sql_ResultSet::wasNull( ) +{ + static jmethodID mID(nullptr); + return callBooleanMethod( "wasNull", mID ); +} + +void SAL_CALL java_sql_ResultSet::cancel( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowRuntime("cancel", mID); +} + +void SAL_CALL java_sql_ResultSet::clearWarnings( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("clearWarnings", mID); +} + +css::uno::Any SAL_CALL java_sql_ResultSet::getWarnings( ) +{ + SDBThreadAttach t; + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getWarnings","()Ljava/sql/SQLWarning;", mID); + // WARNING: the caller becomes the owner of the returned pointer + if( out ) + { + java_sql_SQLWarning_BASE warn_base( t.pEnv, out ); + return makeAny( + static_cast< css::sdbc::SQLException >( + java_sql_SQLWarning(warn_base,*this))); + } + + return css::uno::Any(); +} + + +void SAL_CALL java_sql_ResultSet::insertRow( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("insertRow", mID); +} + +void SAL_CALL java_sql_ResultSet::updateRow( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateRow", mID); +} + +void SAL_CALL java_sql_ResultSet::deleteRow( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("deleteRow", mID); +} + + +void SAL_CALL java_sql_ResultSet::cancelRowUpdates( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("cancelRowUpdates", mID); +} + + +void SAL_CALL java_sql_ResultSet::moveToInsertRow( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("moveToInsertRow", mID); +} + + +void SAL_CALL java_sql_ResultSet::moveToCurrentRow( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("moveToCurrentRow", mID); +} + + +void SAL_CALL java_sql_ResultSet::updateNull( sal_Int32 columnIndex ) +{ + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowSQL("updateNull", mID, columnIndex); +} + + +void SAL_CALL java_sql_ResultSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateBoolean", "(IZ)V", mID, columnIndex, x); +} + +void SAL_CALL java_sql_ResultSet::updateByte( sal_Int32 columnIndex, sal_Int8 x ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateByte", "(IB)V", mID, columnIndex, x); +} + + +void SAL_CALL java_sql_ResultSet::updateShort( sal_Int32 columnIndex, sal_Int16 x ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateShort", "(IS)V", mID, columnIndex, x); +} + +void SAL_CALL java_sql_ResultSet::updateInt( sal_Int32 columnIndex, sal_Int32 x ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateInt", "(II)V", mID, columnIndex, x); +} + +void SAL_CALL java_sql_ResultSet::updateLong( sal_Int32 columnIndex, sal_Int64 x ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateLong", "(IJ)V", mID, columnIndex, x); +} + + +void SAL_CALL java_sql_ResultSet::updateFloat( sal_Int32 columnIndex, float x ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateFloat", "(IF)V", mID, columnIndex, x); +} + + +void SAL_CALL java_sql_ResultSet::updateDouble( sal_Int32 columnIndex, double x ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateDouble", "(ID)V", mID, columnIndex, x); +} + + +void SAL_CALL java_sql_ResultSet::updateString( sal_Int32 columnIndex, const OUString& x ) +{ + SDBThreadAttach t; + + { + // initialize temporary variable + // Java-Call + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(ILjava/lang/String;)V"; + static const char * const cMethodName = "updateString"; + + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + } + + { + // convert parameter + jdbc::LocalRef< jstring > str( t.env(),convertwchar_tToJavaString(t.pEnv,x)); + t.pEnv->CallVoidMethod( object, mID,columnIndex,str.get()); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } +} + + +void SAL_CALL java_sql_ResultSet::updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) +{ + SDBThreadAttach t; + + { + // initialize temporary variable + // Java-Call + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(I[B)V"; + static const char * const cMethodName = "updateBytes"; + + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + } + + { + jbyteArray aArray = t.pEnv->NewByteArray(x.getLength()); + jbyte * pData = reinterpret_cast<jbyte *>( + const_cast<sal_Int8 *>(x.getConstArray())); + // 4th param of Set*ArrayRegion changed from pointer to non-const to + // pointer to const between <http://docs.oracle.com/javase/6/docs/ + // technotes/guides/jni/spec/functions.html#wp22933> and + // <http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/ + // functions.html#wp22933>; work around that difference in a way + // that doesn't trigger loplugin:redundantcast + t.pEnv->SetByteArrayRegion(aArray,0,x.getLength(),pData); + // convert parameter + t.pEnv->CallVoidMethod( object, mID,columnIndex,aArray); + t.pEnv->DeleteLocalRef(aArray); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } +} + + +void SAL_CALL java_sql_ResultSet::updateDate( sal_Int32 columnIndex, const css::util::Date& x ) +{ + java_sql_Date aD(x); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateDate", "(ILjava/sql/Date;)V", mID, columnIndex, aD.getJavaObject()); +} + + +void SAL_CALL java_sql_ResultSet::updateTime( sal_Int32 columnIndex, const css::util::Time& x ) +{ + java_sql_Time aD(x); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateTime", "(ILjava/sql/Time;)V", mID, columnIndex, aD.getJavaObject()); +} + + +void SAL_CALL java_sql_ResultSet::updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) +{ + java_sql_Timestamp aD(x); + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("updateTimestamp", "(ILjava/sql/Timestamp;)V", mID, columnIndex, aD.getJavaObject()); +} + + +void SAL_CALL java_sql_ResultSet::updateBinaryStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + try + { + SDBThreadAttach t; + { + + // initialize temporary variable + // Java-Call + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(ILjava/io/InputStream;I)V"; + static const char * const cMethodName = "updateBinaryStream"; + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + } + + { + // convert Parameter + jobject obj = createByteInputStream(x,length); + t.pEnv->CallVoidMethod( object, mID, columnIndex,obj,length); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } + } + catch(const Exception&) + { + Any anyEx = ::cppu::getCaughtException(); + ::dbtools::throwFeatureNotImplementedSQLException( "XRowUpdate::updateBinaryStream", *this, anyEx ); + } +} + +void SAL_CALL java_sql_ResultSet::updateCharacterStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + try + { + SDBThreadAttach t; + { + + // initialize temporary variable + // Java-Call + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(ILjava/io/Reader;I)V"; + static const char * const cMethodName = "updateCharacterStream"; + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + } + + { + // convert Parameter + jobject obj = createCharArrayReader(x,length); + t.pEnv->CallVoidMethod( object, mID, columnIndex,obj,length); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } + } + catch(const Exception&) + { + Any anyEx = ::cppu::getCaughtException(); + ::dbtools::throwFeatureNotImplementedSQLException( "XRowUpdate::updateCharacterStream", *this, anyEx ); + } +} + +void SAL_CALL java_sql_ResultSet::updateObject( sal_Int32 columnIndex, const css::uno::Any& x ) +{ + if(!::dbtools::implUpdateObject(this,columnIndex,x)) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceStringWithSubstitution( + STR_UNKNOWN_COLUMN_TYPE, + "$position$", OUString::number(columnIndex) + ) ); + ::dbtools::throwGenericSQLException(sError,*this); + } +} + + +void SAL_CALL java_sql_ResultSet::updateNumericObject( sal_Int32 columnIndex, const css::uno::Any& x, sal_Int32 scale ) +{ + // OSL_FAIL("java_sql_ResultSet::updateNumericObject: NYI"); + try + { + SDBThreadAttach t; + + { + + // initialize temporary variable + // Java-Call + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(ILjava/lang/Object;I)V"; + static const char * const cMethodName = "updateObject"; + + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + } + + { + // convert parameter + double nTemp = 0.0; + std::unique_ptr<java_math_BigDecimal> pBigDecimal; + if ( x >>= nTemp) + { + pBigDecimal.reset(new java_math_BigDecimal(nTemp)); + } + else + pBigDecimal.reset(new java_math_BigDecimal(::comphelper::getString(x))); + + t.pEnv->CallVoidMethod( object, mID, columnIndex,pBigDecimal->getJavaObject(),scale); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + } + } + } + catch(const Exception&) + { + updateObject( columnIndex,x); + } +} + +sal_Int32 java_sql_ResultSet::getResultSetConcurrency() const +{ + static jmethodID mID(nullptr); + return callIntMethod_ThrowRuntime("getConcurrency", mID); +} + +sal_Int32 java_sql_ResultSet::getResultSetType() const +{ + static jmethodID mID(nullptr); + return callIntMethod_ThrowRuntime("getType",mID); +} + +sal_Int32 java_sql_ResultSet::getFetchDirection() const +{ + static jmethodID mID(nullptr); + return callIntMethod_ThrowRuntime("getFetchDirection", mID); +} + +sal_Int32 java_sql_ResultSet::getFetchSize() const +{ + static jmethodID mID(nullptr); + return callIntMethod_ThrowRuntime("getFetchSize", mID); +} + +OUString java_sql_ResultSet::getCursorName() const +{ + static jmethodID mID(nullptr); + return callStringMethod("getCursorName",mID); +} + + +void java_sql_ResultSet::setFetchDirection(sal_Int32 _par0) +{ + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowRuntime("setFetchDirection", mID, _par0); +} + +void SAL_CALL java_sql_ResultSet::refreshRow( ) +{ + static jmethodID mID(nullptr); + callVoidMethod_ThrowSQL("refreshRow",mID); +} + +void java_sql_ResultSet::setFetchSize(sal_Int32 _par0) +{ + static jmethodID mID(nullptr); + callVoidMethodWithIntArg_ThrowRuntime("setFetchSize", mID, _par0); +} + +::cppu::IPropertyArrayHelper* java_sql_ResultSet::createArrayHelper( ) const +{ + Sequence< Property > aProps(5); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + +::cppu::IPropertyArrayHelper & java_sql_ResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool java_sql_ResultSet::convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) +{ + bool bRet = false; + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + case PROPERTY_ID_FETCHDIRECTION: + bRet = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection()); + break; + case PROPERTY_ID_FETCHSIZE: + bRet = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize()); + break; + default: + break; + } + return bRet; +} + + +void java_sql_ResultSet::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue + ) +{ + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::uno::Exception("cannot set prop " + OUString::number(nHandle), nullptr); + case PROPERTY_ID_FETCHDIRECTION: + setFetchDirection(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_FETCHSIZE: + setFetchSize(comphelper::getINT32(rValue)); + break; + default: + ; + } +} + +void java_sql_ResultSet::getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle + ) const +{ + try + { + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + rValue <<= getCursorName(); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= getResultSetConcurrency(); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= getResultSetType(); + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= getFetchDirection(); + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= getFetchSize(); + break; + } + } + catch(const Exception&) + { + } +} + +void SAL_CALL java_sql_ResultSet::acquire() throw() +{ + java_sql_ResultSet_BASE::acquire(); +} + +void SAL_CALL java_sql_ResultSet::release() throw() +{ + java_sql_ResultSet_BASE::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL java_sql_ResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/ResultSetMetaData.cxx b/connectivity/source/drivers/jdbc/ResultSetMetaData.cxx new file mode 100644 index 000000000..fdf5bfe69 --- /dev/null +++ b/connectivity/source/drivers/jdbc/ResultSetMetaData.cxx @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/ResultSetMetaData.hxx> +#include <java/sql/Connection.hxx> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +//************ Class: java.sql.ResultSetMetaData + + +jclass java_sql_ResultSetMetaData::theClass = nullptr; +java_sql_ResultSetMetaData::java_sql_ResultSetMetaData( JNIEnv * pEnv, jobject myObj, java_sql_Connection& _rCon ) + :java_lang_Object( pEnv, myObj ) + ,m_pConnection( &_rCon ) + ,m_nColumnCount(-1) +{ + SDBThreadAttach::addRef(); +} +java_sql_ResultSetMetaData::~java_sql_ResultSetMetaData() +{ + SDBThreadAttach::releaseRef(); +} + +jclass java_sql_ResultSetMetaData::getMyClass() const +{ + // The class needs to be fetched just once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/sql/ResultSetMetaData"); + return theClass; +} + + +sal_Int32 SAL_CALL java_sql_ResultSetMetaData::getColumnDisplaySize( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callIntMethodWithIntArg_ThrowSQL("getColumnDisplaySize",mID,column); +} + + +sal_Int32 SAL_CALL java_sql_ResultSetMetaData::getColumnType( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callIntMethodWithIntArg_ThrowSQL("getColumnType",mID,column); +} + + +sal_Int32 SAL_CALL java_sql_ResultSetMetaData::getColumnCount( ) +{ + if ( m_nColumnCount == -1 ) + { + static jmethodID mID(nullptr); + m_nColumnCount = callIntMethod_ThrowSQL("getColumnCount", mID); + } // if ( m_nColumnCount == -1 ) + return m_nColumnCount; + +} + + +sal_Bool SAL_CALL java_sql_ResultSetMetaData::isCaseSensitive( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "isCaseSensitive", mID,column ); +} + +OUString SAL_CALL java_sql_ResultSetMetaData::getSchemaName( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getSchemaName",mID,column); +} + + +OUString SAL_CALL java_sql_ResultSetMetaData::getColumnName( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getColumnName",mID,column); +} + +OUString SAL_CALL java_sql_ResultSetMetaData::getTableName( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getTableName",mID,column); +} + +OUString SAL_CALL java_sql_ResultSetMetaData::getCatalogName( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getCatalogName",mID,column); +} + +OUString SAL_CALL java_sql_ResultSetMetaData::getColumnTypeName( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getColumnTypeName",mID,column); +} + +OUString SAL_CALL java_sql_ResultSetMetaData::getColumnLabel( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getColumnLabel",mID,column); +} + +OUString SAL_CALL java_sql_ResultSetMetaData::getColumnServiceName( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callStringMethodWithIntArg("getColumnClassName",mID,column); +} + + +sal_Bool SAL_CALL java_sql_ResultSetMetaData::isCurrency( sal_Int32 column ) +{ + if ( m_pConnection->isIgnoreCurrencyEnabled() ) + return false; + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "isCurrency", mID,column ); +} + + +sal_Bool SAL_CALL java_sql_ResultSetMetaData::isAutoIncrement( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "isAutoIncrement", mID,column ); +} + + +sal_Bool SAL_CALL java_sql_ResultSetMetaData::isSigned( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "isSigned", mID,column ); +} + +sal_Int32 SAL_CALL java_sql_ResultSetMetaData::getPrecision( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callIntMethodWithIntArg_ThrowSQL("getPrecision",mID,column); +} + +sal_Int32 SAL_CALL java_sql_ResultSetMetaData::getScale( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callIntMethodWithIntArg_ThrowSQL("getScale",mID,column); +} + +sal_Int32 SAL_CALL java_sql_ResultSetMetaData::isNullable( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callIntMethodWithIntArg_ThrowSQL("isNullable",mID,column); +} + + +sal_Bool SAL_CALL java_sql_ResultSetMetaData::isSearchable( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "isSearchable", mID,column ); +} + + +sal_Bool SAL_CALL java_sql_ResultSetMetaData::isReadOnly( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "isReadOnly", mID,column ); +} + + +sal_Bool SAL_CALL java_sql_ResultSetMetaData::isDefinitelyWritable( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "isDefinitelyWritable", mID,column ); +} + +sal_Bool SAL_CALL java_sql_ResultSetMetaData::isWritable( sal_Int32 column ) +{ + static jmethodID mID(nullptr); + return callBooleanMethodWithIntArg( "isWritable", mID,column ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/SQLException.cxx b/connectivity/source/drivers/jdbc/SQLException.cxx new file mode 100644 index 000000000..55bf0996c --- /dev/null +++ b/connectivity/source/drivers/jdbc/SQLException.cxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <java/sql/SQLException.hxx> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; + +//************ Class: java.sql.SQLException + +java_sql_SQLException::java_sql_SQLException( const java_sql_SQLException_BASE& _rException,const Reference< XInterface> & _rContext) + : css::sdbc::SQLException( _rException.getMessage(), + _rContext, + _rException.getSQLState(), + _rException.getErrorCode(), + makeAny(_rException.getNextException()) + ) +{ +} + +java_sql_SQLException_BASE::java_sql_SQLException_BASE( JNIEnv * pEnv, jobject myObj ) : java_lang_Exception( pEnv, myObj ) +{ +} + +jclass java_sql_SQLException_BASE::theClass = nullptr; + +java_sql_SQLException_BASE::~java_sql_SQLException_BASE() +{} + + +jclass java_sql_SQLException_BASE::getMyClass() const +{ + return st_getMyClass(); +} +jclass java_sql_SQLException_BASE::st_getMyClass() +{ + // The class needs to be fetched just once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/sql/SQLException"); + return theClass; +} + +css::sdbc::SQLException java_sql_SQLException_BASE::getNextException() const +{ + SDBThreadAttach t; + static jmethodID mID(nullptr); + jobject out = callObjectMethod(t.pEnv,"getNextException","()Ljava/sql/SQLException;", mID); + // WARNING: the caller will become the owner of the returned pointers !!! + if( out ) + { + java_sql_SQLException_BASE warn_base(t.pEnv,out); + return css::sdbc::SQLException(java_sql_SQLException(warn_base,nullptr)); + } + + return css::sdbc::SQLException(); +} + +OUString java_sql_SQLException_BASE::getSQLState() const +{ + static jmethodID mID(nullptr); + return callStringMethod("getSQLState",mID); +} +sal_Int32 java_sql_SQLException_BASE::getErrorCode() const +{ + static jmethodID mID(nullptr); + return callIntMethod_ThrowSQL("getErrorCode", mID); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/SQLWarning.cxx b/connectivity/source/drivers/jdbc/SQLWarning.cxx new file mode 100644 index 000000000..16616d18b --- /dev/null +++ b/connectivity/source/drivers/jdbc/SQLWarning.cxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/SQLWarning.hxx> +using namespace connectivity; + +//************ Class: java.sql.SQLWarning + + +jclass java_sql_SQLWarning_BASE::theClass = nullptr; + +java_sql_SQLWarning_BASE::~java_sql_SQLWarning_BASE() +{} + +jclass java_sql_SQLWarning_BASE::getMyClass() const +{ + // the class needs to be fetched only once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/sql/SQLWarning"); + return theClass; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/String.cxx b/connectivity/source/drivers/jdbc/String.cxx new file mode 100644 index 000000000..f7e682c02 --- /dev/null +++ b/connectivity/source/drivers/jdbc/String.cxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/lang/String.hxx> +#include <java/tools.hxx> +using namespace connectivity; + +//************ Class: java.lang.String + + +jclass java_lang_String::theClass = nullptr; + +java_lang_String::~java_lang_String() +{} + +jclass java_lang_String::getMyClass() const +{ + return st_getMyClass(); +} +jclass java_lang_String::st_getMyClass() +{ + // the class needs to be fetched only once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/lang/String"); + return theClass; +} + + +java_lang_String::operator OUString() +{ + SDBThreadAttach t; + if(!t.pEnv) + return OUString(); + return JavaString2String(t.pEnv, static_cast<jstring>(object)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Throwable.cxx b/connectivity/source/drivers/jdbc/Throwable.cxx new file mode 100644 index 000000000..c96f5532f --- /dev/null +++ b/connectivity/source/drivers/jdbc/Throwable.cxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/lang/Throwable.hxx> + +using namespace connectivity; + +//************ Class: java.lang.Throwable + + +jclass java_lang_Throwable::theClass = nullptr; + +java_lang_Throwable::~java_lang_Throwable() +{} + +jclass java_lang_Throwable::getMyClass() const +{ + return st_getMyClass(); +} +jclass java_lang_Throwable::st_getMyClass() +{ + // the class needs to be fetched only once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/lang/Throwable"); + return theClass; +} + + +OUString java_lang_Throwable::getMessage() const +{ + static jmethodID mID(nullptr); + return callStringMethod("getMessage",mID); +} + + +OUString java_lang_Throwable::getLocalizedMessage() const +{ + static jmethodID mID(nullptr); + return callStringMethod("getLocalizedMessage",mID); +} + +#if OSL_DEBUG_LEVEL > 0 +void java_lang_Throwable::printStackTrace() const +{ + static jmethodID mID(nullptr); + return callVoidMethod_ThrowSQL("printStackTrace",mID); +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/Timestamp.cxx b/connectivity/source/drivers/jdbc/Timestamp.cxx new file mode 100644 index 000000000..eb7624719 --- /dev/null +++ b/connectivity/source/drivers/jdbc/Timestamp.cxx @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <java/sql/Timestamp.hxx> +#include <java/tools.hxx> +#include <connectivity/dbconversion.hxx> +#include <osl/diagnose.h> + +using namespace ::comphelper; +using namespace connectivity; + +//************ Class: java.sql.Date + + +jclass java_sql_Date::theClass = nullptr; +java_sql_Date::java_sql_Date( const css::util::Date& _rOut ) : java_util_Date( nullptr, nullptr ) +{ + SDBThreadAttach t; + if( !t.pEnv ) + return; + jvalue args[1]; + // Convert parameters + OUString sDateStr = ::dbtools::DBTypeConversion::toDateString(_rOut); + args[0].l = convertwchar_tToJavaString(t.pEnv,sDateStr); + + // Turn of Java-Call for the constructor + // initialise temporary variables + jobject tempObj; + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(Ljava/lang/String;)Ljava/sql/Date;"; + mID = t.pEnv->GetStaticMethodID( getMyClass(), "valueOf", cSignature ); + } + OSL_ENSURE(mID,"Unknown method id!"); + tempObj = t.pEnv->CallStaticObjectMethod( getMyClass(), mID, args[0].l ); + saveRef( t.pEnv, tempObj ); + t.pEnv->DeleteLocalRef( tempObj ); + // and clean +} + +java_sql_Date::~java_sql_Date() +{} + +jclass java_sql_Date::getMyClass() const +{ + return st_getMyClass(); +} +jclass java_sql_Date::st_getMyClass() +{ + // the class needs only be fetched once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/sql/Date"); + return theClass; +} + + +java_sql_Date::operator css::util::Date() +{ + return ::dbtools::DBTypeConversion::toDate(toString()); +} + + +//************ Class: java.sql.Time + + +jclass java_sql_Time::theClass = nullptr; + +java_sql_Time::~java_sql_Time() +{} + +jclass java_sql_Time::getMyClass() const +{ + return st_getMyClass(); +} +jclass java_sql_Time::st_getMyClass() +{ + // the class needs only be fetched once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/sql/Time"); + return theClass; +} +java_sql_Time::java_sql_Time( const css::util::Time& _rOut ): java_util_Date( nullptr, nullptr ) +{ + SDBThreadAttach t; + if( !t.pEnv ) + return; + jvalue args[1]; + // Convert parameters + // java.sql.Time supports only whole seconds... + OUString sDateStr = ::dbtools::DBTypeConversion::toTimeStringS(_rOut); + args[0].l = convertwchar_tToJavaString(t.pEnv,sDateStr); + + // Turn off Java-Call for the constructor + // initialise temporary variables + jobject tempObj; + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(Ljava/lang/String;)Ljava/sql/Time;"; + mID = t.pEnv->GetStaticMethodID( getMyClass(), "valueOf", cSignature ); + } + OSL_ENSURE(mID,"Unknown method id!"); + tempObj = t.pEnv->CallStaticObjectMethod( getMyClass(), mID, args[0].l ); + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + saveRef( t.pEnv, tempObj ); + t.pEnv->DeleteLocalRef( tempObj ); + // and clean +} + +java_sql_Time::operator css::util::Time() +{ + return ::dbtools::DBTypeConversion::toTime(toString()); +} + +//************ Class: java.sql.Timestamp + + +jclass java_sql_Timestamp::theClass = nullptr; + +java_sql_Timestamp::~java_sql_Timestamp() +{} + +jclass java_sql_Timestamp::getMyClass() const +{ + return st_getMyClass(); +} + +jclass java_sql_Timestamp::st_getMyClass() +{ + // the class needs only be fetched once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/sql/Timestamp"); + return theClass; +} + +java_sql_Timestamp::java_sql_Timestamp(const css::util::DateTime& _rOut) + :java_util_Date( nullptr, nullptr ) +{ + SDBThreadAttach t; + if( !t.pEnv ) + return; + jvalue args[1]; + // Convert parameters + OUString sDateStr = ::dbtools::DBTypeConversion::toDateTimeString(_rOut); + + args[0].l = convertwchar_tToJavaString(t.pEnv,sDateStr); + + // Turn off Java-Call for the constructor + // initialise temporary variables + jobject tempObj; + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "(Ljava/lang/String;)Ljava/sql/Timestamp;"; + mID = t.pEnv->GetStaticMethodID( getMyClass(), "valueOf", cSignature ); + } + OSL_ENSURE(mID,"Unknown method id!"); + tempObj = t.pEnv->CallStaticObjectMethod( getMyClass(), mID, args[0].l ); + + saveRef( t.pEnv, tempObj ); + t.pEnv->DeleteLocalRef( tempObj ); + // and clean +} + + +java_sql_Timestamp::operator css::util::DateTime() +{ + return ::dbtools::DBTypeConversion::toDateTime(toString()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/jdbc.component b/connectivity/source/drivers/jdbc/jdbc.component new file mode 100644 index 000000000..07fd03c68 --- /dev/null +++ b/connectivity/source/drivers/jdbc/jdbc.component @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<!-- Recent Java 6 VMs make calls to JNI Attach/DetachCurrentThread (which this + code does extensively) very expensive. A follow-up JVM fix reduced the + overhead significantly again for all threads but the main thread. So a + quick hack to improve performance of this component again is to confine it + in the affine apartment (where all code will run on a single, dedicated + thread that is guaranteed no to be the main thread). However, a better fix + would still be to redesign the code so that it does not call + Attach/DetachCurrentThread so frequently: +--> + +<component loader="com.sun.star.loader.SharedLibrary" + environment="@CPPU_ENV@:affine" prefix="jdbc" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.JDBCDriver"> + <service name="com.sun.star.sdbc.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/jdbc/jservices.cxx b/connectivity/source/drivers/jdbc/jservices.cxx new file mode 100644 index 000000000..63ae84c8e --- /dev/null +++ b/connectivity/source/drivers/jdbc/jservices.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <java/sql/Driver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* jdbc_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + java_sql_Driver::getImplementationName_Static(), + java_sql_Driver::getSupportedServiceNames_Static(), + java_sql_Driver_CreateInstance, + ::cppu::createSingleFactory); + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + pRet = aReq.getProvider(); + } + + return pRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/jdbc/tools.cxx b/connectivity/source/drivers/jdbc/tools.cxx new file mode 100644 index 000000000..5021d653a --- /dev/null +++ b/connectivity/source/drivers/jdbc/tools.cxx @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <string.h> +#include <java/tools.hxx> +#include <java/util/Property.hxx> +#include <com/sun/star/sdbc/DriverPropertyInfo.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <connectivity/dbexception.hxx> +#include <osl/diagnose.h> + +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +void java_util_Properties::setProperty(const OUString& key, const OUString& value) +{ + SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!"); + jobject out(nullptr); + + { + jvalue args[2]; + // Convert Parameter + args[0].l = convertwchar_tToJavaString(t.pEnv,key); + args[1].l = convertwchar_tToJavaString(t.pEnv,value); + // Initialize temporary Variables + static const char * const cSignature = "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"; + static const char * const cMethodName = "setProperty"; + // Turn off Java-Call + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID); + out = t.pEnv->CallObjectMethod(object, mID, args[0].l,args[1].l); + ThrowSQLException(t.pEnv,nullptr); + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[1].l)); + t.pEnv->DeleteLocalRef(static_cast<jstring>(args[0].l)); + ThrowSQLException(t.pEnv,nullptr); + if(out) + t.pEnv->DeleteLocalRef(out); + } //t.pEnv + // WARNING: The caller will be owner of the returned pointers!!! +} +jclass java_util_Properties::theClass = nullptr; + +java_util_Properties::~java_util_Properties() +{} + +jclass java_util_Properties::getMyClass() const +{ + // the class needs only be called once, that is why it is static + if( !theClass ) + theClass = findMyClass("java/util/Properties"); + return theClass; +} + + +java_util_Properties::java_util_Properties( ): java_lang_Object( nullptr, nullptr ) +{ + SDBThreadAttach t; + if( !t.pEnv ) + return; + // Turn off Java-Call for the constructor + // Initialize temporary Variables + static const char * const cSignature = "()V"; + jobject tempObj; + static jmethodID mID(nullptr); + obtainMethodId_throwSQL(t.pEnv, "<init>",cSignature, mID); + tempObj = t.pEnv->NewObject( getMyClass(), mID); + saveRef( t.pEnv, tempObj ); + t.pEnv->DeleteLocalRef( tempObj ); +} + + +jstring connectivity::convertwchar_tToJavaString(JNIEnv *pEnv,const OUString& _rTemp) +{ + OSL_ENSURE(pEnv,"Environment is NULL!"); + jstring pStr = pEnv->NewString( + reinterpret_cast<jchar const *>(_rTemp.getStr()), _rTemp.getLength()); + pEnv->ExceptionClear(); + OSL_ENSURE(pStr,"Could not create a jsstring object!"); + return pStr; +} + + +std::unique_ptr<java_util_Properties> connectivity::createStringPropertyArray(const Sequence< PropertyValue >& info ) +{ + std::unique_ptr<java_util_Properties> pProps(new java_util_Properties()); + const PropertyValue* pBegin = info.getConstArray(); + const PropertyValue* pEnd = pBegin + info.getLength(); + + for(;pBegin != pEnd;++pBegin) + { + // these are properties used internally by LibreOffice, + // and should not be passed to the JDBC driver + // (which probably does not know anything about them anyway). + if ( pBegin->Name != "JavaDriverClass" + && pBegin->Name != "JavaDriverClassPath" + && pBegin->Name != "SystemProperties" + && pBegin->Name != "CharSet" + && pBegin->Name != "AppendTableAliasName" + && pBegin->Name != "AppendTableAliasInSelect" + && pBegin->Name != "DisplayVersionColumns" + && pBegin->Name != "GeneratedValues" + && pBegin->Name != "UseIndexDirectionKeyword" + && pBegin->Name != "UseKeywordAsBeforeAlias" + && pBegin->Name != "AddIndexAppendix" + && pBegin->Name != "FormsCheckRequiredFields" + && pBegin->Name != "GenerateASBeforeCorrelationName" + && pBegin->Name != "EscapeDateTime" + && pBegin->Name != "ParameterNameSubstitution" + && pBegin->Name != "IsPasswordRequired" + && pBegin->Name != "IsAutoRetrievingEnabled" + && pBegin->Name != "AutoRetrievingStatement" + && pBegin->Name != "UseCatalogInSelect" + && pBegin->Name != "UseSchemaInSelect" + && pBegin->Name != "AutoIncrementCreation" + && pBegin->Name != "Extension" + && pBegin->Name != "NoNameLengthLimit" + && pBegin->Name != "EnableSQL92Check" + && pBegin->Name != "EnableOuterJoinEscape" + && pBegin->Name != "BooleanComparisonMode" + && pBegin->Name != "IgnoreCurrency" + && pBegin->Name != "TypeInfoSettings" + && pBegin->Name != "IgnoreDriverPrivileges" + && pBegin->Name != "ImplicitCatalogRestriction" + && pBegin->Name != "ImplicitSchemaRestriction" + && pBegin->Name != "SupportsTableCreation" + && pBegin->Name != "UseJava" + && pBegin->Name != "Authentication" + && pBegin->Name != "PreferDosLikeLineEnds" + && pBegin->Name != "PrimaryKeySupport" + && pBegin->Name != "RespectDriverResultSetType" + ) + { + OUString aStr; + OSL_VERIFY( pBegin->Value >>= aStr ); + pProps->setProperty(pBegin->Name,aStr); + } + } + return pProps; +} + +OUString connectivity::JavaString2String(JNIEnv *pEnv,jstring Str) +{ + OUString aStr; + if(Str) + { + jboolean bCopy(true); + const jchar* pChar = pEnv->GetStringChars(Str,&bCopy); + jsize len = pEnv->GetStringLength(Str); + aStr = OUString(reinterpret_cast<sal_Unicode const *>(pChar), len); + + if(bCopy) + pEnv->ReleaseStringChars(Str,pChar); + pEnv->DeleteLocalRef(Str); + } + return aStr; +} + +jobject connectivity::convertTypeMapToJavaMap(const Reference< css::container::XNameAccess > & _rMap) +{ + if ( _rMap.is() ) + { + css::uno::Sequence< OUString > aNames = _rMap->getElementNames(); + if ( aNames.hasElements() ) + ::dbtools::throwFeatureNotImplementedSQLException( "Type maps", nullptr ); + } + return nullptr; +} + +bool connectivity::isExceptionOccurred(JNIEnv *pEnv) +{ + if ( !pEnv ) + return false; + + jthrowable pThrowable = pEnv->ExceptionOccurred(); + bool bRet = pThrowable != nullptr; + if ( pThrowable ) + { + pEnv->ExceptionClear(); + pEnv->DeleteLocalRef(pThrowable); + } + + return bRet; +} + +jobject connectivity::createByteInputStream(const css::uno::Reference< css::io::XInputStream >& x,sal_Int32 length) +{ + SDBThreadAttach t; + if( !t.pEnv || !x.is() ) + return nullptr; + // Turn off Java-Call for the constructor + // Initialize temporary variables + jclass clazz = java_lang_Object::findMyClass("java/io/ByteArrayInputStream"); + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "([B)V"; + mID = t.pEnv->GetMethodID( clazz, "<init>", cSignature ); + OSL_ENSURE( mID, cSignature ); + if ( !mID ) + throw SQLException(); + } // if ( !_inout_MethodID ) + jbyteArray pByteArray = t.pEnv->NewByteArray(length); + Sequence< sal_Int8 > aData; + x->readBytes(aData,length); + jboolean p = false; + memcpy(t.pEnv->GetByteArrayElements(pByteArray,&p),aData.getArray(),aData.getLength()); + jobject out = t.pEnv->NewObject( clazz, mID,pByteArray); + t.pEnv->DeleteLocalRef(pByteArray); + return out; +} + +jobject connectivity::createCharArrayReader(const css::uno::Reference< css::io::XInputStream >& x,sal_Int32 length) +{ + SDBThreadAttach t; + if( !t.pEnv || !x.is() ) + return nullptr; + // Turn off Java-Call for the constructor + // Initialize temporary Variables + jclass clazz = java_lang_Object::findMyClass("java/io/CharArrayReader"); + static jmethodID mID(nullptr); + if ( !mID ) + { + static const char * const cSignature = "([C)V"; + mID = t.pEnv->GetMethodID( clazz, "<init>", cSignature ); + OSL_ENSURE( mID, cSignature ); + if ( !mID ) + throw SQLException(); + } // if ( !_inout_MethodID ) + jcharArray pCharArray = t.pEnv->NewCharArray(length); + Sequence< sal_Int8 > aData; + x->readBytes(aData,length); + jboolean p = false; + memcpy(t.pEnv->GetCharArrayElements(pCharArray,&p),aData.getArray(),aData.getLength()); + jobject out = t.pEnv->NewObject( clazz, mID,pCharArray); + t.pEnv->DeleteLocalRef(pCharArray); + return out; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabAddressBook.cxx b/connectivity/source/drivers/macab/MacabAddressBook.cxx new file mode 100644 index 000000000..09e489203 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabAddressBook.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 "MacabAddressBook.hxx" +#include "MacabRecords.hxx" +#include "MacabGroup.hxx" + +#include <vector> + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <AddressBook/ABAddressBookC.h> +#include <postmac.h> +#include <connectivity/CommonTools.hxx> + +using namespace connectivity::macab; +using namespace ::com::sun::star::uno; + +namespace { + +void manageDuplicateGroups(std::vector<MacabGroup *> _xGroups) +{ + /* If we have two cases of groups, say, family, this makes it: + * family + * family (2) + */ + std::vector<MacabGroup *>::reverse_iterator iter1, iter2; + sal_Int32 count; + + for(iter1 = _xGroups.rbegin(); iter1 != _xGroups.rend(); ++iter1) + { + /* If the name matches the default table name, there is already + * (obviously) a conflict. So, start the count of groups with this + * name at 2 instead of 1. + */ + if( (*iter1)->getName() == MacabAddressBook::getDefaultTableName() ) + count = 2; + else + count = 1; + + iter2 = iter1; + for( ++iter2; iter2 != _xGroups.rend(); ++iter2) + { + if( (*iter1)->getName() == (*iter2)->getName() ) + { + count++; + } + } + + // duplicate! + if(count != 1) + { + OUString sName = (*iter1)->getName() + " (" + + OUString::number(count) + + ")"; + (*iter1)->setName(sName); + } + } +} + +} + +MacabAddressBook::MacabAddressBook( ) + : m_aAddressBook(ABGetSharedAddressBook()), + m_xMacabRecords(nullptr), + m_bRetrievedGroups(false) +{ +} + + +MacabAddressBook::~MacabAddressBook() +{ + if(m_xMacabRecords != nullptr) + { + delete m_xMacabRecords; + m_xMacabRecords = nullptr; + } + + for(MacabGroup* pMacabGroup : m_xMacabGroups) + delete pMacabGroup; + + m_bRetrievedGroups = false; +} + + +/* Get the address book's default table name. This is the table name that + * refers to the table containing _all_ records in the address book. + */ +const OUString & MacabAddressBook::getDefaultTableName() +{ + /* This string probably needs to be localized. */ + static const OUString aDefaultTableName + (OUString("Address Book")); + + return aDefaultTableName; +} + + +MacabRecords *MacabAddressBook::getMacabRecords() +{ + /* If the MacabRecords don't exist, create them. */ + if(m_xMacabRecords == nullptr) + { + m_xMacabRecords = new MacabRecords(m_aAddressBook); + m_xMacabRecords->setName(getDefaultTableName()); + m_xMacabRecords->initialize(); + } + + return m_xMacabRecords; +} + + +/* Get the MacabRecords for a given name: either a group name or the + * default table name. + */ +MacabRecords *MacabAddressBook::getMacabRecords(const OUString& _tableName) +{ + if(_tableName == getDefaultTableName()) + { + return getMacabRecords(); + } + else + { + return getMacabGroup(_tableName); + } +} + + +MacabRecords *MacabAddressBook::getMacabRecordsMatch(const OUString& _tableName) +{ + if(match(_tableName, getDefaultTableName(), '\0')) + { + return getMacabRecords(); + } + + return getMacabGroupMatch(_tableName); +} + + +std::vector<MacabGroup *> MacabAddressBook::getMacabGroups() +{ + /* If the MacabGroups haven't been created yet, create them. */ + if(!m_bRetrievedGroups) + { + /* If the MacabRecords haven't been created yet, create them. */ + if(m_xMacabRecords == nullptr) + { + m_xMacabRecords = new MacabRecords(m_aAddressBook); + m_xMacabRecords->setName(getDefaultTableName()); + m_xMacabRecords->initialize(); + } + + CFArrayRef allGroups = ABCopyArrayOfAllGroups(m_aAddressBook); + sal_Int32 nGroups = CFArrayGetCount(allGroups); + m_xMacabGroups = std::vector<MacabGroup *>(nGroups); + + sal_Int32 i; + ABGroupRef xGroup; + + /* Go through each group and create a MacabGroup out of it. */ + for(i = 0; i < nGroups; i++) + { + xGroup = static_cast<ABGroupRef>(const_cast<void *>(CFArrayGetValueAtIndex(allGroups, i))); + m_xMacabGroups[i] = new MacabGroup(m_aAddressBook, m_xMacabRecords, xGroup); + } + + CFRelease(allGroups); + + /* Manage duplicates. */ + manageDuplicateGroups(m_xMacabGroups); + m_bRetrievedGroups = true; + } + + return m_xMacabGroups; +} + + +MacabGroup *MacabAddressBook::getMacabGroup(OUString const & _groupName) +{ + // initialize groups if not already initialized + if(!m_bRetrievedGroups) + getMacabGroups(); + + sal_Int32 nGroups = m_xMacabGroups.size(); + sal_Int32 i; + + for(i = 0; i < nGroups; i++) + { + if(m_xMacabGroups[i] != nullptr) + { + if(m_xMacabGroups[i]->getName() == _groupName) + { + return m_xMacabGroups[i]; + } + } + } + + return nullptr; +} + + +MacabGroup *MacabAddressBook::getMacabGroupMatch(OUString const & _groupName) +{ + // initialize groups if not already initialized + if(!m_bRetrievedGroups) + getMacabGroups(); + + sal_Int32 nGroups = m_xMacabGroups.size(); + sal_Int32 i; + + for(i = 0; i < nGroups; i++) + { + if(m_xMacabGroups[i] != nullptr) + { + if(match(m_xMacabGroups[i]->getName(), _groupName, '\0')) + { + return m_xMacabGroups[i]; + } + } + } + + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabAddressBook.hxx b/connectivity/source/drivers/macab/MacabAddressBook.hxx new file mode 100644 index 000000000..a74621ef2 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabAddressBook.hxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABADDRESSBOOK_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABADDRESSBOOK_HXX + +#include "MacabRecords.hxx" +#include "MacabGroup.hxx" + +#include <vector> + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <AddressBook/ABAddressBookC.h> +#include <postmac.h> + +namespace connectivity +{ + namespace macab + { + class MacabAddressBook + { + protected: + ABAddressBookRef m_aAddressBook; + MacabRecords *m_xMacabRecords; + std::vector<MacabGroup *> m_xMacabGroups; + bool m_bRetrievedGroups; + + public: + MacabAddressBook(); + ~MacabAddressBook(); + static const OUString & getDefaultTableName(); + + MacabRecords *getMacabRecords(); + std::vector<MacabGroup *> getMacabGroups(); + + MacabGroup *getMacabGroup(const OUString& _groupName); + MacabRecords *getMacabRecords(const OUString& _tableName); + + MacabGroup *getMacabGroupMatch(const OUString& _groupName); + MacabRecords *getMacabRecordsMatch(const OUString& _tableName); + }; + + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABADDRESSBOOK_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabCatalog.cxx b/connectivity/source/drivers/macab/MacabCatalog.cxx new file mode 100644 index 000000000..96ff62fd5 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabCatalog.cxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabCatalog.hxx" +#include "MacabConnection.hxx" +#include "MacabTables.hxx" +#include <com/sun/star/sdbc/XRow.hpp> + +using namespace connectivity::macab; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::cppu; + + +MacabCatalog::MacabCatalog(MacabConnection* _pCon) + : connectivity::sdbcx::OCatalog(_pCon), + m_pConnection(_pCon) +{ +} + +void MacabCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + Sequence< OUString > aTypes { "%" }; + Reference< XResultSet > xResult = m_xMetaData->getTables( + Any(), "%", "%", aTypes); + + if (xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + OUString aName; + // const OUString& sDot = MacabCatalog::getDot(); + + while (xResult->next()) + { + // aName = xRow->getString(2); + // aName += sDot; + aName = xRow->getString(3); + aVector.push_back(aName); + } + } + if (m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset( new MacabTables(m_xMetaData,*this,m_aMutex,aVector) ); +} + +void MacabCatalog::refreshViews() +{ +} + +void MacabCatalog::refreshGroups() +{ +} + +void MacabCatalog::refreshUsers() +{ +} + +const OUString& MacabCatalog::getDot() +{ + static const OUString sDot = "."; + return sDot; +} + + +// XTablesSupplier +Reference< XNameAccess > SAL_CALL MacabCatalog::getTables( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + try + { + if (!m_pTables) + refreshTables(); + } + catch( const RuntimeException& ) + { + // allowed to leave this method + throw; + } + catch( const Exception& ) + { + // allowed + } + + return m_pTables.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabCatalog.hxx b/connectivity/source/drivers/macab/MacabCatalog.hxx new file mode 100644 index 000000000..9c45ab1f1 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabCatalog.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCATALOG_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCATALOG_HXX + +#include <sdbcx/VCatalog.hxx> + +namespace connectivity +{ + namespace macab + { + class MacabConnection; + + class MacabCatalog : public connectivity::sdbcx::OCatalog + { + MacabConnection* m_pConnection; // used to get the metadata + + public: + explicit MacabCatalog(MacabConnection* _pCon); + + MacabConnection* getConnection() const { return m_pConnection; } + + static const OUString& getDot(); + + // implementation of the pure virtual methods + virtual void refreshTables() override; + virtual void refreshViews() override; + virtual void refreshGroups() override; + virtual void refreshUsers() override; + + // XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( + ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCATALOG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabColumns.cxx b/connectivity/source/drivers/macab/MacabColumns.cxx new file mode 100644 index 000000000..6eaa51aa7 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabColumns.cxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabColumns.hxx" +#include "MacabTable.hxx" +#include "MacabTables.hxx" +#include "MacabCatalog.hxx" +#include <connectivity/sdbcx/VColumn.hxx> +#include <com/sun/star/sdbc/XRow.hpp> + +using namespace connectivity::macab; +using namespace connectivity::sdbcx; +using namespace connectivity; +using namespace ::comphelper; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +sdbcx::ObjectType MacabColumns::createObject(const OUString& _rName) +{ + const Any aCatalog; + const OUString sCatalogName; + const OUString sSchemaName(m_pTable->getSchema()); + const OUString sTableName(m_pTable->getTableName()); + Reference< XResultSet > xResult = m_pTable->getConnection()->getMetaData()->getColumns( + aCatalog, sSchemaName, sTableName, _rName); + + sdbcx::ObjectType xRet; + if (xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + + while (xResult->next()) + { + if (xRow->getString(4) == _rName) + { + OColumn* pRet = new 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, + true, + sCatalogName, + sSchemaName, + sTableName); + xRet = pRet; + break; + } + } + } + + return xRet; +} + +void MacabColumns::impl_refresh() +{ + m_pTable->refreshColumns(); +} + +MacabColumns::MacabColumns( MacabTable* _pTable, + ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector) + : sdbcx::OCollection(*_pTable, true, _rMutex, _rVector), + m_pTable(_pTable) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabColumns.hxx b/connectivity/source/drivers/macab/MacabColumns.hxx new file mode 100644 index 000000000..36da039a9 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabColumns.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCOLUMNS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCOLUMNS_HXX + +#include "MacabTable.hxx" +#include <connectivity/sdbcx/VCollection.hxx> + +namespace connectivity +{ + namespace macab + { + class MacabColumns : public sdbcx::OCollection + { + protected: + MacabTable* m_pTable; + + virtual sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override; + + public: + MacabColumns( MacabTable* _pTable, + ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector); + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCOLUMNS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabConnection.cxx b/connectivity/source/drivers/macab/MacabConnection.cxx new file mode 100644 index 000000000..4e5a27354 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabConnection.cxx @@ -0,0 +1,318 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabConnection.hxx" +#include "MacabAddressBook.hxx" +#include "MacabDatabaseMetaData.hxx" +#include "MacabStatement.hxx" +#include "MacabPreparedStatement.hxx" +#include "MacabDriver.hxx" +#include "MacabCatalog.hxx" +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> + +using namespace connectivity::macab; +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; + +IMPLEMENT_SERVICE_INFO(MacabConnection, "com.sun.star.sdbc.drivers.MacabConnection", "com.sun.star.sdbc.Connection") + +MacabConnection::MacabConnection(MacabDriver* _pDriver) + : m_pAddressBook(nullptr), + m_pDriver(_pDriver) +{ + m_pDriver->acquire(); +} + +MacabConnection::~MacabConnection() +{ + if (!doIsClosed()) + doClose(); + + m_pDriver->release(); + m_pDriver = nullptr; +} + +void MacabConnection::construct(const OUString&, const Sequence< PropertyValue >&) +{ + osl_atomic_increment( &m_refCount ); + + // get the macOS shared address book + m_pAddressBook = new MacabAddressBook(); + + osl_atomic_decrement( &m_refCount ); +} +// XServiceInfo + +Reference< XStatement > SAL_CALL MacabConnection::createStatement( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // create a statement + // the statement can only be executed once + Reference< XStatement > xReturn = new MacabStatement(this); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL MacabConnection::prepareStatement( const OUString& _sSql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // create a statement + // the statement can only be executed more than once + Reference< XPreparedStatement > xReturn = new MacabPreparedStatement(this, _sSql); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL MacabConnection::prepareCall( const OUString& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // not implemented yet :-) a task to do + return nullptr; +} + +OUString SAL_CALL MacabConnection::nativeSQL( const OUString& _sSql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + // when you need to transform SQL92 to you driver specific you can do it here + + return _sSql; +} + +void SAL_CALL MacabConnection::setAutoCommit( sal_Bool ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + // here you have to set your commit mode please have a look at the jdbc documentation to get a clear explanation +} + +sal_Bool SAL_CALL MacabConnection::getAutoCommit( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + // you have to distinguish which if you are in autocommit mode or not + // at normal case true should be fine here + + return true; +} + +void SAL_CALL MacabConnection::commit( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // when you database does support transactions you should commit here +} + +void SAL_CALL MacabConnection::rollback( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // same as commit but for the other case +} + +sal_Bool SAL_CALL MacabConnection::isClosed( ) +{ + return doIsClosed(); +} + +bool MacabConnection::doIsClosed() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + // just simple -> we are closed when we are disposed, that means someone called dispose(); (XComponent) + return MacabConnection_BASE::rBHelper.bDisposed; +} + +Reference< XDatabaseMetaData > SAL_CALL MacabConnection::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // here we have to create the class with biggest interface + // The answer is 42 :-) + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if (!xMetaData.is()) + { + xMetaData = new MacabDatabaseMetaData(this); // need the connection because it can return it + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +void SAL_CALL MacabConnection::setReadOnly( sal_Bool ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // set you connection to readonly +} + +sal_Bool SAL_CALL MacabConnection::isReadOnly( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // return if your connection to readonly + return false; +} + +void SAL_CALL MacabConnection::setCatalog( const OUString& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // if your database doesn't work with catalogs you go to next method otherwise you know what to do +} + +OUString SAL_CALL MacabConnection::getCatalog( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + + // return your current catalog + return OUString(); +} + +void SAL_CALL MacabConnection::setTransactionIsolation( sal_Int32 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // set your isolation level + // please have a look at @see com.sun.star.sdbc.TransactionIsolation +} + +sal_Int32 SAL_CALL MacabConnection::getTransactionIsolation( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + + // please have a look at @see com.sun.star.sdbc.TransactionIsolation + return TransactionIsolation::NONE; +} + +Reference< css::container::XNameAccess > SAL_CALL MacabConnection::getTypeMap( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + + // if your driver has special database types you can return it here + + return nullptr; +} + +void SAL_CALL MacabConnection::setTypeMap( const Reference< css::container::XNameAccess >& ) +{ + // the other way around +} + +// XCloseable +void SAL_CALL MacabConnection::close( ) +{ + doClose(); +} + +void MacabConnection::doClose() +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabConnection_BASE::rBHelper.bDisposed); + } + dispose(); +} + +// XWarningsSupplier +Any SAL_CALL MacabConnection::getWarnings( ) +{ + // when you collected some warnings -> return it + return Any(); +} + +void SAL_CALL MacabConnection::clearWarnings( ) +{ + // you should clear your collected warnings here +} + +void MacabConnection::disposing() +{ + // we noticed that we should be destroyed in near future so we have to dispose our statements + ::osl::MutexGuard aGuard(m_aMutex); + + for (auto& rxStatement : m_aStatements) + { + Reference< XComponent > xComp(rxStatement.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + } + m_aStatements.clear(); + + if (m_pAddressBook != nullptr) + { + delete m_pAddressBook; + m_pAddressBook = nullptr; + } + + m_xMetaData.clear(); + + MacabConnection_BASE::disposing(); +} + +Reference< XTablesSupplier > MacabConnection::createCatalog() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XTablesSupplier > xTab = m_xCatalog; + if (!m_xCatalog.is()) + { + MacabCatalog *pCat = new MacabCatalog(this); + xTab = pCat; + m_xCatalog = xTab; + } + return xTab; +} + +MacabAddressBook* MacabConnection::getAddressBook() const +{ + return m_pAddressBook; +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* createMacabConnection( void* _pDriver ) +{ + MacabConnection* pConnection = new MacabConnection( static_cast< MacabDriver* >( _pDriver ) ); + // by definition, the pointer crossing library boundaries as void ptr is acquired once + pConnection->acquire(); + return pConnection; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabConnection.hxx b/connectivity/source/drivers/macab/MacabConnection.hxx new file mode 100644 index 000000000..6173b26dc --- /dev/null +++ b/connectivity/source/drivers/macab/MacabConnection.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCONNECTION_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCONNECTION_HXX + +#include <map> +#include <connectivity/CommonTools.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <cppuhelper/compbase.hxx> +#include <TConnection.hxx> + +namespace connectivity +{ + namespace macab + { + + typedef ::cppu::WeakComponentImplHelper<css::sdbc::XConnection, + css::sdbc::XWarningsSupplier, + css::lang::XServiceInfo + > OMetaConnection_BASE; + + class MacabDriver; + class MacabAddressBook; + + typedef std::vector< css::uno::WeakReferenceHelper > OWeakRefArray; + + typedef connectivity::OMetaConnection MacabConnection_BASE; + + class MacabConnection : public MacabConnection_BASE + { + protected: + + // Data attributes + + MacabAddressBook* m_pAddressBook; // the address book + MacabDriver* m_pDriver; // pointer to the owning driver object + css::uno::Reference< css::sdbcx::XTablesSupplier> + m_xCatalog; // needed for the SQL interpreter + + private: + bool doIsClosed(); + + void doClose(); + + public: + /// @throws css::sdbc::SQLException + virtual void construct( const OUString& url,const css::uno::Sequence< css::beans::PropertyValue >& info); + + explicit MacabConnection(MacabDriver* _pDriver); + virtual ~MacabConnection() override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XServiceInfo + DECLARE_SERVICE_INFO(); + + // XConnection + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual void SAL_CALL setAutoCommit( sal_Bool autoCommit ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual void SAL_CALL setReadOnly( sal_Bool readOnly ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL setCatalog( const OUString& catalog ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual void SAL_CALL setTransactionIsolation( sal_Int32 level ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + + // XCloseable + virtual void SAL_CALL close( ) override; + + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + + // needed for the SQL interpreter + css::uno::Reference< css::sdbcx::XTablesSupplier > createCatalog(); + + // accessors + MacabDriver* getDriver() const { return m_pDriver;} + MacabAddressBook* getAddressBook() const; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCONNECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabDatabaseMetaData.cxx b/connectivity/source/drivers/macab/MacabDatabaseMetaData.cxx new file mode 100644 index 000000000..809537c23 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabDatabaseMetaData.cxx @@ -0,0 +1,1076 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabDatabaseMetaData.hxx" +#include "MacabAddressBook.hxx" +#include "MacabHeader.hxx" +#include "MacabGroup.hxx" +#include "macabutilities.hxx" + +#include "MacabDriver.hxx" +#include <FDatabaseMetaDataResultSet.hxx> +#include <OTypeInfo.hxx> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> + +#include <vector> + +using namespace connectivity::macab; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + +MacabDatabaseMetaData::MacabDatabaseMetaData(MacabConnection* _pCon) + : m_xConnection(_pCon), + m_bUseCatalog(true) +{ + OSL_ENSURE(_pCon,"MacabDatabaseMetaData::MacabDatabaseMetaData: No connection set!"); + + osl_atomic_increment( &m_refCount ); + m_bUseCatalog = !(usesLocalFiles() || usesLocalFilePerTable()); + osl_atomic_decrement( &m_refCount ); +} + +MacabDatabaseMetaData::~MacabDatabaseMetaData() +{ +} + +OUString SAL_CALL MacabDatabaseMetaData::getCatalogSeparator( ) +{ + if (m_bUseCatalog) + { // do some special here for you database + } + + return OUString(); +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxRowSize( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxCatalogNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxCharLiteralLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxColumnNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxColumnsInIndex( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxCursorNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxConnections( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxColumnsInTable( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxStatementLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxTableNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxTablesInSelect( ) +{ + // MaxTablesInSelect describes how many tables can participate in the FROM part of a given SELECT statement, + // currently, the resultset/statement implementations can cope with one table only + return 1; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::storesLowerCaseQuotedIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::storesLowerCaseIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::storesMixedCaseQuotedIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::storesMixedCaseIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::storesUpperCaseQuotedIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::storesUpperCaseIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsAlterTableWithAddColumn( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsAlterTableWithDropColumn( ) +{ + return false; +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxIndexLength( ) +{ + return 0; // 0 means no limit +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsNonNullableColumns( ) +{ + return false; +} + +OUString SAL_CALL MacabDatabaseMetaData::getCatalogTerm( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getIdentifierQuoteString( ) +{ + // normally this is " + return "\""; +} + +OUString SAL_CALL MacabDatabaseMetaData::getExtraNameCharacters( ) +{ + return OUString(); +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsDifferentTableCorrelationNames( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::isCatalogAtStart( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::dataDefinitionIgnoredInTransactions( ) +{ + return true; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::dataDefinitionCausesTransactionCommit( ) +{ + return true; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsDataManipulationTransactionsOnly( ) +{ + return true; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsDataDefinitionAndDataManipulationTransactions( ) +{ + return true; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsPositionedDelete( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsPositionedUpdate( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsOpenStatementsAcrossRollback( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsOpenStatementsAcrossCommit( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsOpenCursorsAcrossCommit( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsOpenCursorsAcrossRollback( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsTransactionIsolationLevel( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSchemasInDataManipulation( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsANSI92FullSQL( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsANSI92EntryLevelSQL( ) +{ + return true; // should be supported at least +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsIntegrityEnhancementFacility( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSchemasInIndexDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSchemasInTableDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsCatalogsInTableDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsCatalogsInIndexDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsCatalogsInDataManipulation( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsOuterJoins( ) +{ + return false; +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxStatements( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxProcedureNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxSchemaNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsTransactions( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::allProceduresAreCallable( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsStoredProcedures( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSelectForUpdate( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::allTablesAreSelectable( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::isReadOnly( ) +{ + // for the moment, we have read-only addresses, but this might change in the future + return true; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::usesLocalFiles( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::usesLocalFilePerTable( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsTypeConversion( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::nullPlusNonNullIsNull( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsColumnAliasing( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsTableCorrelationNames( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsConvert( sal_Int32, sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsExpressionsInOrderBy( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsGroupBy( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsGroupByBeyondSelect( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsGroupByUnrelated( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsMultipleTransactions( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsMultipleResultSets( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsLikeEscapeClause( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsOrderByUnrelated( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsUnion( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsUnionAll( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsMixedCaseIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsMixedCaseQuotedIdentifiers( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::nullsAreSortedAtEnd( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::nullsAreSortedAtStart( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::nullsAreSortedHigh( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::nullsAreSortedLow( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSchemasInProcedureCalls( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSchemasInPrivilegeDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsCatalogsInProcedureCalls( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsCatalogsInPrivilegeDefinitions( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsCorrelatedSubqueries( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSubqueriesInComparisons( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSubqueriesInExists( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSubqueriesInIns( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsSubqueriesInQuantifieds( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsANSI92IntermediateSQL( ) +{ + return false; +} + +OUString SAL_CALL MacabDatabaseMetaData::getURL( ) +{ + // if someday we support more than the default address book, + // this method should return the URL which was used to create it + return "sdbc:address:macab:"; +} + +OUString SAL_CALL MacabDatabaseMetaData::getUserName( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getDriverName( ) +{ + return "macab"; +} + +OUString SAL_CALL MacabDatabaseMetaData::getDriverVersion() +{ + return MACAB_DRIVER_VERSION; +} + +OUString SAL_CALL MacabDatabaseMetaData::getDatabaseProductVersion( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getDatabaseProductName( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getProcedureTerm( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getSchemaTerm( ) +{ + return OUString(); +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getDriverMajorVersion( ) +{ + return MACAB_DRIVER_VERSION_MAJOR; +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getDefaultTransactionIsolation( ) +{ + return TransactionIsolation::NONE; +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getDriverMinorVersion( ) +{ + return MACAB_DRIVER_VERSION_MINOR; +} + +OUString SAL_CALL MacabDatabaseMetaData::getSQLKeywords( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getSearchStringEscape( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getStringFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getTimeDateFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getSystemFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL MacabDatabaseMetaData::getNumericFunctions( ) +{ + return OUString(); +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsExtendedSQLGrammar( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsCoreSQLGrammar( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsMinimumSQLGrammar( ) +{ + return true; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsFullOuterJoins( ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsLimitedOuterJoins( ) +{ + return false; +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxColumnsInGroupBy( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxColumnsInSelect( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL MacabDatabaseMetaData::getMaxUserNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsResultSetType( sal_Int32 setType ) +{ + switch (setType) + { + case ResultSetType::FORWARD_ONLY: + case ResultSetType::SCROLL_INSENSITIVE: + return true; + } + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 ) +{ + switch (setType) + { + case ResultSetType::FORWARD_ONLY: + case ResultSetType::SCROLL_INSENSITIVE: + return true; + } + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::ownUpdatesAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::ownDeletesAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::ownInsertsAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::othersUpdatesAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::othersDeletesAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::othersInsertsAreVisible( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::updatesAreDetected( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::deletesAreDetected( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::insertsAreDetected( sal_Int32 ) +{ + return false; +} + +sal_Bool SAL_CALL MacabDatabaseMetaData::supportsBatchUpdates( ) +{ + return false; +} + +Reference< XConnection > SAL_CALL MacabDatabaseMetaData::getConnection( ) +{ + return m_xConnection.get(); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getTableTypes( ) +{ + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTableTypes); + Reference< XResultSet > xRef = pResult; + + static ODatabaseMetaDataResultSet::ORows aRows = [&] + { + static const char aTable[] = "TABLE"; + ODatabaseMetaDataResultSet::ORows tmp; + ODatabaseMetaDataResultSet::ORow aRow(2); + aRow[0] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[1] = new ORowSetValueDecorator(OUString(aTable)); + tmp.push_back(aRow); + return tmp; + }(); + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getTypeInfo( ) +{ + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTypeInfo); + Reference< XResultSet > xRef = pResult; + + static ODatabaseMetaDataResultSet::ORows aRows = [&]() + { + ODatabaseMetaDataResultSet::ORows tmp; + ODatabaseMetaDataResultSet::ORow aRow(19); + + // We support four types: char, timestamp, integer, float + aRow[0] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[1] = new ORowSetValueDecorator(OUString("CHAR")); + aRow[2] = new ORowSetValueDecorator(DataType::CHAR); + aRow[3] = new ORowSetValueDecorator(sal_Int32(254)); + aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); + aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); + aRow[6] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[7] = new ORowSetValueDecorator(sal_Int32(ColumnValue::NULLABLE)); + aRow[8] = ODatabaseMetaDataResultSet::get1Value(); + aRow[9] = new ORowSetValueDecorator(sal_Int32(ColumnSearch::CHAR)); + aRow[10] = ODatabaseMetaDataResultSet::get1Value(); + aRow[11] = ODatabaseMetaDataResultSet::get0Value(); + aRow[12] = ODatabaseMetaDataResultSet::get0Value(); + aRow[13] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[14] = ODatabaseMetaDataResultSet::get0Value(); + aRow[15] = ODatabaseMetaDataResultSet::get0Value(); + aRow[16] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[17] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[18] = new ORowSetValueDecorator(sal_Int32(10)); + + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("TIMESTAMP")); + aRow[2] = new ORowSetValueDecorator(DataType::TIMESTAMP); + aRow[3] = new ORowSetValueDecorator(sal_Int32(19)); + aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); + aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("INTEGER")); + aRow[2] = new ORowSetValueDecorator(DataType::INTEGER); + aRow[3] = new ORowSetValueDecorator(sal_Int32(20)); + aRow[15] = new ORowSetValueDecorator(sal_Int32(20)); + tmp.push_back(aRow); + + aRow[1] = new ORowSetValueDecorator(OUString("FLOAT")); + aRow[2] = new ORowSetValueDecorator(DataType::FLOAT); + aRow[3] = new ORowSetValueDecorator(sal_Int32(20)); + aRow[15] = new ORowSetValueDecorator(sal_Int32(15)); + tmp.push_back(aRow); + + return tmp; + }(); + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getCatalogs( ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eCatalogs ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getSchemas( ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eSchemas ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getColumnPrivileges( + const Any&, const OUString&, const OUString&, + const OUString& ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eColumnPrivileges ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getColumns( + const Any&, + const OUString&, + const OUString& tableNamePattern, + const OUString& columnNamePattern) +{ + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eColumns); + Reference< XResultSet > xRef = pResult; + MacabRecords *aRecords; + OUString sTableName; + + aRecords = m_xConnection->getAddressBook()->getMacabRecordsMatch(tableNamePattern); + + ODatabaseMetaDataResultSet::ORows aRows; + if(aRecords != nullptr) + { + MacabHeader *aHeader = aRecords->getHeader(); + sTableName = aRecords->getName(); + + ODatabaseMetaDataResultSet::ORow aRow(19); + + aRow[0] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[1] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[2] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[3] = new ORowSetValueDecorator(sTableName); + aRow[8] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[9] = ODatabaseMetaDataResultSet::get0Value(); + aRow[10] = new ORowSetValueDecorator(sal_Int32(10)); + aRow[11] = ODatabaseMetaDataResultSet::get1Value(); + aRow[12] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[13] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[14] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[15] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[16] = new ORowSetValueDecorator(sal_Int32(254)); + aRow[18] = new ORowSetValueDecorator(OUString("YES")); + + sal_Int32 nPosition = 1; + OUString sName; + + MacabHeader::iterator aField; + + for ( aField = aHeader->begin(); + aField != aHeader->end(); + ++aField, ++nPosition) + { + + sName = CFStringToOUString(static_cast<CFStringRef>((*aField)->value)); + if (match(columnNamePattern, sName, '\0')) + { + aRow[4] = new ORowSetValueDecorator(sName); + aRow[17] = new ORowSetValueDecorator(nPosition); + switch((*aField)->type) + { + case kABStringProperty: + aRow[5] = new ORowSetValueDecorator(DataType::CHAR); + aRow[6] = new ORowSetValueDecorator(OUString("CHAR")); + aRow[7] = new ORowSetValueDecorator(sal_Int32(256)); + aRows.push_back(aRow); + break; + case kABDateProperty: + aRow[5] = new ORowSetValueDecorator(DataType::TIMESTAMP); + aRow[6] = new ORowSetValueDecorator(OUString("TIMESTAMP")); + aRows.push_back(aRow); + break; + case kABIntegerProperty: + aRow[5] = new ORowSetValueDecorator(DataType::INTEGER); + aRow[6] = new ORowSetValueDecorator(OUString("INTEGER")); + aRow[7] = new ORowSetValueDecorator(sal_Int32(20)); + aRows.push_back(aRow); + break; + case kABRealProperty: + aRow[5] = new ORowSetValueDecorator(DataType::FLOAT); + aRow[6] = new ORowSetValueDecorator(OUString("FLOAT")); + aRow[7] = new ORowSetValueDecorator(sal_Int32(15)); + aRows.push_back(aRow); + break; + default: + ; + // shouldn't happen -- throw an error...? + } + } + } + } + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getTables( + const Any&, + const OUString&, + const OUString&, + const Sequence< OUString >& types) +{ + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTables); + Reference< XResultSet > xRef = pResult; + + // check whether we have tables in the requested types + // for the moment, we answer only the "TABLE" table type + // when no types are given at all, we return all the tables + static const char aTable[] = "TABLE"; + bool bTableFound = false; + const OUString* p = types.getConstArray(), + * pEnd = p + types.getLength(); + + if (p == pEnd) + { + bTableFound = true; + } + else while (p < pEnd) + { + if (match(*p, aTable, '\0')) + { + bTableFound = true; + break; + } + p++; + } + if (!bTableFound) + return xRef; + + static ODatabaseMetaDataResultSet::ORows aRows = [&]() + { + ODatabaseMetaDataResultSet::ORows tmp; + ODatabaseMetaDataResultSet::ORow aRow(6); + + MacabRecords *xRecords = m_xConnection->getAddressBook()->getMacabRecords(); + std::vector<MacabGroup *> xGroups = m_xConnection->getAddressBook()->getMacabGroups(); + sal_Int32 i, nGroups; + nGroups = xGroups.size(); + + aRow[0] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[1] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[2] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[3] = new ORowSetValueDecorator(xRecords->getName()); + aRow[4] = new ORowSetValueDecorator(OUString(aTable)); + aRow[5] = ODatabaseMetaDataResultSet::getEmptyValue(); + tmp.push_back(aRow); + + for(i = 0; i < nGroups; i++) + { + aRow[3] = new ORowSetValueDecorator(xGroups[i]->getName()); + tmp.push_back(aRow); + } + return tmp; + }(); + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getProcedureColumns( + const Any&, const OUString&, + const OUString&, const OUString& ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eProcedureColumns ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getProcedures( + const Any&, const OUString&, + const OUString& ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eProcedures ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getVersionColumns( + const Any&, const OUString&, const OUString& table ) +{ + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eVersionColumns); + Reference< XResultSet > xRef = pResult; + + ODatabaseMetaDataResultSet::ORows aRows; + + if (m_xConnection->getAddressBook()->getMacabRecords(table) != nullptr) + { + ODatabaseMetaDataResultSet::ORow aRow( 9 ); + + OUString sName = CFStringToOUString(kABModificationDateProperty); + + aRow[0] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[1] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[2] = new ORowSetValueDecorator(sName); + aRow[3] = new ORowSetValueDecorator(DataType::TIMESTAMP); + aRow[4] = new ORowSetValueDecorator(OUString("TIMESTAMP")); + + aRow[5] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[6] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[7] = ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[8] = ODatabaseMetaDataResultSet::getEmptyValue(); + + aRows.push_back(aRow); + } + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getExportedKeys( + const Any&, const OUString&, const OUString& ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eExportedKeys ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getImportedKeys( + const Any&, const OUString&, const OUString& ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eImportedKeys ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getPrimaryKeys( + const Any&, const OUString&, const OUString& ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::ePrimaryKeys ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getIndexInfo( + const Any&, const OUString&, const OUString&, + sal_Bool, sal_Bool ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eIndexInfo ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getBestRowIdentifier( + const Any&, const OUString&, const OUString&, sal_Int32, + sal_Bool ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eBestRowIdentifier ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getTablePrivileges( + const Any&, const OUString&, const OUString& ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eTablePrivileges ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getCrossReference( + const Any&, const OUString&, + const OUString&, const Any&, + const OUString&, const OUString& ) +{ + return new ODatabaseMetaDataResultSet( ODatabaseMetaDataResultSet::eCrossReference ); +} + +Reference< XResultSet > SAL_CALL MacabDatabaseMetaData::getUDTs( const Any&, const OUString&, const OUString&, const Sequence< sal_Int32 >& ) +{ + OSL_FAIL("Not implemented yet!"); + throw SQLException(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabDatabaseMetaData.hxx b/connectivity/source/drivers/macab/MacabDatabaseMetaData.hxx new file mode 100644 index 000000000..c1c9feb5c --- /dev/null +++ b/connectivity/source/drivers/macab/MacabDatabaseMetaData.hxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABDATABASEMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABDATABASEMETADATA_HXX + +#include "MacabConnection.hxx" +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <cppuhelper/implbase.hxx> + +namespace connectivity +{ + namespace macab + { + + class MacabDatabaseMetaData : public ::cppu::WeakImplHelper< css::sdbc::XDatabaseMetaData> + { + rtl::Reference< MacabConnection > m_xConnection; + bool m_bUseCatalog; + + public: + + MacabConnection* getOwnConnection() const { return m_xConnection.get(); } + + explicit MacabDatabaseMetaData(MacabConnection* _pCon); + virtual ~MacabDatabaseMetaData() override; + + // this interface is really BIG + // XDatabaseMetaData + virtual sal_Bool SAL_CALL allProceduresAreCallable( ) override; + virtual sal_Bool SAL_CALL allTablesAreSelectable( ) override; + virtual OUString SAL_CALL getURL( ) override; + virtual OUString SAL_CALL getUserName( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedHigh( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedLow( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtStart( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtEnd( ) override; + virtual OUString SAL_CALL getDatabaseProductName( ) override; + virtual OUString SAL_CALL getDatabaseProductVersion( ) override; + virtual OUString SAL_CALL getDriverName( ) override; + virtual OUString SAL_CALL getDriverVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMajorVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMinorVersion( ) override; + virtual sal_Bool SAL_CALL usesLocalFiles( ) override; + virtual sal_Bool SAL_CALL usesLocalFilePerTable( ) override; + virtual sal_Bool SAL_CALL supportsMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL supportsMixedCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesMixedCaseQuotedIdentifiers( ) override; + virtual OUString SAL_CALL getIdentifierQuoteString( ) override; + virtual OUString SAL_CALL getSQLKeywords( ) override; + virtual OUString SAL_CALL getNumericFunctions( ) override; + virtual OUString SAL_CALL getStringFunctions( ) override; + virtual OUString SAL_CALL getSystemFunctions( ) override; + virtual OUString SAL_CALL getTimeDateFunctions( ) override; + virtual OUString SAL_CALL getSearchStringEscape( ) override; + virtual OUString SAL_CALL getExtraNameCharacters( ) override; + virtual sal_Bool SAL_CALL supportsAlterTableWithAddColumn( ) override; + virtual sal_Bool SAL_CALL supportsAlterTableWithDropColumn( ) override; + virtual sal_Bool SAL_CALL supportsColumnAliasing( ) override; + virtual sal_Bool SAL_CALL nullPlusNonNullIsNull( ) override; + virtual sal_Bool SAL_CALL supportsTypeConversion( ) override; + virtual sal_Bool SAL_CALL supportsConvert( sal_Int32 fromType, sal_Int32 toType ) override; + virtual sal_Bool SAL_CALL supportsTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsDifferentTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsExpressionsInOrderBy( ) override; + virtual sal_Bool SAL_CALL supportsOrderByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupBy( ) override; + virtual sal_Bool SAL_CALL supportsGroupByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupByBeyondSelect( ) override; + virtual sal_Bool SAL_CALL supportsLikeEscapeClause( ) override; + virtual sal_Bool SAL_CALL supportsMultipleResultSets( ) override; + virtual sal_Bool SAL_CALL supportsMultipleTransactions( ) override; + virtual sal_Bool SAL_CALL supportsNonNullableColumns( ) override; + virtual sal_Bool SAL_CALL supportsMinimumSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsCoreSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsExtendedSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsANSI92EntryLevelSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92IntermediateSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92FullSQL( ) override; + virtual sal_Bool SAL_CALL supportsIntegrityEnhancementFacility( ) override; + virtual sal_Bool SAL_CALL supportsOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsFullOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsLimitedOuterJoins( ) override; + virtual OUString SAL_CALL getSchemaTerm( ) override; + virtual OUString SAL_CALL getProcedureTerm( ) override; + virtual OUString SAL_CALL getCatalogTerm( ) override; + virtual sal_Bool SAL_CALL isCatalogAtStart( ) override; + virtual OUString SAL_CALL getCatalogSeparator( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInDataManipulation( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInTableDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInDataManipulation( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInTableDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsPositionedDelete( ) override; + virtual sal_Bool SAL_CALL supportsPositionedUpdate( ) override; + virtual sal_Bool SAL_CALL supportsSelectForUpdate( ) override; + virtual sal_Bool SAL_CALL supportsStoredProcedures( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInComparisons( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInExists( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInIns( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInQuantifieds( ) override; + virtual sal_Bool SAL_CALL supportsCorrelatedSubqueries( ) override; + virtual sal_Bool SAL_CALL supportsUnion( ) override; + virtual sal_Bool SAL_CALL supportsUnionAll( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossRollback( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossRollback( ) override; + virtual sal_Int32 SAL_CALL getMaxBinaryLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCharLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInGroupBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInIndex( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInOrderBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInSelect( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInTable( ) override; + virtual sal_Int32 SAL_CALL getMaxConnections( ) override; + virtual sal_Int32 SAL_CALL getMaxCursorNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxIndexLength( ) override; + virtual sal_Int32 SAL_CALL getMaxSchemaNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxProcedureNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCatalogNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxRowSize( ) override; + virtual sal_Bool SAL_CALL doesMaxRowSizeIncludeBlobs( ) override; + virtual sal_Int32 SAL_CALL getMaxStatementLength( ) override; + virtual sal_Int32 SAL_CALL getMaxStatements( ) override; + virtual sal_Int32 SAL_CALL getMaxTableNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxTablesInSelect( ) override; + virtual sal_Int32 SAL_CALL getMaxUserNameLength( ) override; + virtual sal_Int32 SAL_CALL getDefaultTransactionIsolation( ) override; + virtual sal_Bool SAL_CALL supportsTransactions( ) override; + virtual sal_Bool SAL_CALL supportsTransactionIsolationLevel( sal_Int32 level ) override; + virtual sal_Bool SAL_CALL supportsDataDefinitionAndDataManipulationTransactions( ) override; + virtual sal_Bool SAL_CALL supportsDataManipulationTransactionsOnly( ) override; + virtual sal_Bool SAL_CALL dataDefinitionCausesTransactionCommit( ) override; + virtual sal_Bool SAL_CALL dataDefinitionIgnoredInTransactions( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getProcedures( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getProcedureColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTables( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const css::uno::Sequence< OUString >& types ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getSchemas( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getCatalogs( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTableTypes( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumnPrivileges( const css::uno::Any& catalog, const OUString& schema, const OUString& table, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTablePrivileges( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getBestRowIdentifier( const css::uno::Any& catalog, const OUString& schema, const OUString& table, sal_Int32 scope, sal_Bool nullable ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getVersionColumns( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getPrimaryKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getImportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getExportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getCrossReference( const css::uno::Any& primaryCatalog, const OUString& primarySchema, const OUString& primaryTable, const css::uno::Any& foreignCatalog, const OUString& foreignSchema, const OUString& foreignTable ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTypeInfo( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getIndexInfo( const css::uno::Any& catalog, const OUString& schema, const OUString& table, sal_Bool unique, sal_Bool approximate ) override; + virtual sal_Bool SAL_CALL supportsResultSetType( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 concurrency ) override; + virtual sal_Bool SAL_CALL ownUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL updatesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL deletesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL insertsAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsBatchUpdates( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getUDTs( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& typeNamePattern, const css::uno::Sequence< sal_Int32 >& types ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABDATABASEMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabDriver.cxx b/connectivity/source/drivers/macab/MacabDriver.cxx new file mode 100644 index 000000000..7a130f364 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabDriver.cxx @@ -0,0 +1,322 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabDriver.hxx" +#include "MacabConnection.hxx" + +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <strings.hrc> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::sdb; +using namespace com::sun::star::frame; +using namespace connectivity::macab; + +namespace { + +/** throws a generic SQL exception with SQLState S1000 and error code 0 + */ +void throwGenericSQLException( const OUString& _rMessage ) +{ + SQLException aError; + aError.Message = _rMessage; + aError.SQLState = "S1000"; + aError.ErrorCode = 0; + throw aError; +} + +/** throws an SQLException saying that no Mac OS installation was found + */ +void throwNoMacOSException() +{ + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString( + STR_NO_MAC_OS_FOUND + ) ); + throwGenericSQLException( sError ); +} + + +} + +// = MacabImplModule + + +MacabImplModule::MacabImplModule() + :m_bAttemptedLoadModule(false) + ,m_hConnectorModule(nullptr) + ,m_pConnectionFactoryFunc(nullptr) +{ +} + + +bool MacabImplModule::isMacOSPresent() +{ + return impl_loadModule(); +} + + +namespace +{ + template< typename FUNCTION > + void lcl_getFunctionFromModuleOrUnload( oslModule& _rModule, const char* _pAsciiSymbolName, FUNCTION& _rFunction ) + { + _rFunction = nullptr; + if ( _rModule ) + { + + const OUString sSymbolName = OUString::createFromAscii( _pAsciiSymbolName ); + _rFunction = reinterpret_cast<FUNCTION>( osl_getSymbol( _rModule, sSymbolName.pData ) ); + + if ( !_rFunction ) + { // did not find the symbol + SAL_WARN( "connectivity.macab", "lcl_getFunctionFromModuleOrUnload: could not find the symbol " << _pAsciiSymbolName ); + osl_unloadModule( _rModule ); + _rModule = nullptr; + } + } + } +} + + +extern "C" { static void thisModule() {} } + +bool MacabImplModule::impl_loadModule() +{ + if ( m_bAttemptedLoadModule ) + return ( m_hConnectorModule != nullptr ); + m_bAttemptedLoadModule = true; + + OSL_ENSURE( !m_hConnectorModule && !m_pConnectionFactoryFunc, + "MacabImplModule::impl_loadModule: inconsistence: inconsistency (never attempted load before, but some values already set)!"); + + const OUString sModuleName( SAL_MODULENAME( "macabdrv1" ) ); + m_hConnectorModule = osl_loadModuleRelative( &thisModule, sModuleName.pData, SAL_LOADMODULE_NOW ); // LAZY! #i61335# + OSL_ENSURE( m_hConnectorModule, "MacabImplModule::impl_loadModule: could not load the implementation library!" ); + if ( !m_hConnectorModule ) + return false; + + lcl_getFunctionFromModuleOrUnload( m_hConnectorModule, "createMacabConnection", m_pConnectionFactoryFunc ); + + if ( !m_hConnectorModule ) + // one of the symbols did not exist + throw RuntimeException(); + + return true; +} + + +void MacabImplModule::impl_unloadModule() +{ + OSL_PRECOND( m_hConnectorModule != nullptr, "MacabImplModule::impl_unloadModule: no module!" ); + + osl_unloadModule( m_hConnectorModule ); + m_hConnectorModule = nullptr; + + m_pConnectionFactoryFunc = nullptr; + + m_bAttemptedLoadModule = false; +} + + +void MacabImplModule::init() +{ + if ( !impl_loadModule() ) + throwNoMacOSException(); + +} + + +MacabConnection* MacabImplModule::createConnection( MacabDriver* _pDriver ) const +{ + OSL_PRECOND( m_hConnectorModule, "MacabImplModule::createConnection: not initialized!" ); + + void* pUntypedConnection = (*m_pConnectionFactoryFunc)( _pDriver ); + if ( !pUntypedConnection ) + throw RuntimeException(); + + return static_cast< MacabConnection* >( pUntypedConnection ); +} + + +void MacabImplModule::shutdown() +{ + if ( !m_hConnectorModule ) + return; + + impl_unloadModule(); +} + + +// = MacabDriver + +MacabDriver::MacabDriver( + const Reference< css::uno::XComponentContext >& _rxContext) + : MacabDriver_BASE(m_aMutex), + m_xContext(_rxContext), + m_aImplModule() +{ + if ( !m_xContext.is() ) + throw NullPointerException(); + + osl_atomic_increment( &m_refCount ); + try + { + Reference< XDesktop2 > xDesktop = Desktop::create( m_xContext ); + xDesktop->addTerminateListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.macab"); + } + osl_atomic_decrement( &m_refCount ); +} + +void MacabDriver::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + // when driver will be destroyed so all our connections have to be destroyed as well + for (auto& rxConnection : m_xConnections) + { + Reference< XComponent > xComp(rxConnection.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + } + m_xConnections.clear(); + + WeakComponentImplHelperBase::disposing(); +} +// static ServiceInfo + +OUString MacabDriver::getImplementationName_Static( ) +{ + return "com.sun.star.comp.sdbc.macab.Driver"; +} + +Sequence< OUString > MacabDriver::getSupportedServiceNames_Static( ) +{ + // which service is supported + // for more information @see com.sun.star.sdbc.Driver + Sequence<OUString> aSNS { "com.sun.star.sdbc.Driver" }; + + return aSNS; +} + +OUString SAL_CALL MacabDriver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL MacabDriver::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > SAL_CALL MacabDriver::getSupportedServiceNames( ) +{ + return getSupportedServiceNames_Static(); +} + +Reference< XConnection > SAL_CALL MacabDriver::connect( const OUString& url, const Sequence< PropertyValue >& info ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + + m_aImplModule.init(); + + // create a new connection with the given properties and append it to our vector + MacabConnection* pConnection = m_aImplModule.createConnection( this ); + SAL_WARN_IF( !pConnection, "connectivity.macab", "MacabDriver::connect: no connection has been created by the factory!" ); + + // by definition, the factory function returned an object which was acquired once + Reference< XConnection > xConnection = pConnection; + pConnection->release(); + + // late constructor call which can throw exception and allows a correct dtor call when so + pConnection->construct( url, info ); + + // remember it + m_xConnections.push_back( WeakReferenceHelper( *pConnection ) ); + + return xConnection; +} + +sal_Bool SAL_CALL MacabDriver::acceptsURL( const OUString& url ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + + if ( !m_aImplModule.isMacOSPresent() ) + return false; + + // here we have to look whether we support this URL format + return url == "sdbc:address:macab"; +} + +Sequence< DriverPropertyInfo > SAL_CALL MacabDriver::getPropertyInfo( const OUString&, const Sequence< PropertyValue >& ) +{ + // if you have something special to say, return it here :-) + return Sequence< DriverPropertyInfo >(); +} + +sal_Int32 SAL_CALL MacabDriver::getMajorVersion( ) +{ + return MACAB_DRIVER_VERSION_MAJOR; +} + +sal_Int32 SAL_CALL MacabDriver::getMinorVersion( ) +{ + return MACAB_DRIVER_VERSION_MINOR; +} + +void SAL_CALL MacabDriver::queryTermination( const EventObject& ) +{ + // nothing to do, nothing to veto +} + +void SAL_CALL MacabDriver::notifyTermination( const EventObject& ) +{ + m_aImplModule.shutdown(); +} + +void SAL_CALL MacabDriver::disposing( const EventObject& ) +{ + // not interested in (this is the disposing of the desktop, if any) +} + +OUString MacabDriver::impl_getConfigurationSettingsPath() +{ + return "/org.openoffice.Office.DataAccess/DriverSettings/com.sun.star.comp.sdbc.macab.Driver"; +} + +Reference< XInterface > MacabDriver::Create( const Reference< XMultiServiceFactory >& _rxFactory ) +{ + return *(new MacabDriver(comphelper::getComponentContext(_rxFactory))); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabDriver.hxx b/connectivity/source/drivers/macab/MacabDriver.hxx new file mode 100644 index 000000000..0d389712f --- /dev/null +++ b/connectivity/source/drivers/macab/MacabDriver.hxx @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABDRIVER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABDRIVER_HXX + +#include <com/sun/star/sdbc/XDriver.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/compbase.hxx> +#include <osl/module.h> + +// the address book driver's version +#define MACAB_DRIVER_VERSION "0.1" +#define MACAB_DRIVER_VERSION_MAJOR 0 +#define MACAB_DRIVER_VERSION_MINOR 1 + +namespace connectivity +{ + namespace macab + { + class MacabConnection; + class MacabDriver; + + typedef void* (SAL_CALL * ConnectionFactoryFunction)( void* _pDriver ); + + typedef std::vector< css::uno::WeakReferenceHelper > OWeakRefArray; + + + // = MacabImplModule + + class MacabImplModule + { + private: + /// Did we already attempt to load the module and to retrieve the symbols? + bool m_bAttemptedLoadModule; + oslModule m_hConnectorModule; + ConnectionFactoryFunction m_pConnectionFactoryFunc; + + public: + MacabImplModule(); + + /** determines whether there is a mac OS present in the environment + */ + bool isMacOSPresent(); + + /** initializes the implementation module. + + @throws css::uno::RuntimeException + if the module could be loaded, but required symbols are missing + @throws css::sdbc::SQLException + if no Mac OS was found at all + */ + void init(); + + /** shuts down the impl module + */ + void shutdown(); + + /** creates a new connection + @precond + <member>init</member> has been called before + @throws css::uno::RuntimeException + if no connection object could be created (which is a severe error, normally impossible) + */ + MacabConnection* createConnection( MacabDriver* _pDriver ) const; + + private: + /** loads the implementation module and retrieves the needed symbols + + Save against being called multiple times. + + @return <TRUE/> if the module could be loaded successfully. + + @throws css::uno::RuntimeException + if the module could be loaded, but required symbols are missing + */ + bool impl_loadModule(); + + /** unloads the implementation module, and resets all function pointers to <NULL/> + @precond m_hConnectorModule is not <NULL/> + */ + void impl_unloadModule(); + }; + + + // = MacabDriver + + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XDriver, + css::lang::XServiceInfo, + css::frame::XTerminateListener > MacabDriver_BASE; + class MacabDriver : public MacabDriver_BASE + { + protected: + ::osl::Mutex m_aMutex; // mutex is need to control member access + OWeakRefArray m_xConnections; // vector containing a list of all the + // MacabConnection objects for this Driver + css::uno::Reference< css::uno::XComponentContext > + m_xContext; // the multi-service factory + MacabImplModule m_aImplModule; + + public: + /// @throws css::uno::Exception + static css::uno::Reference< css::uno::XInterface > Create(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory); + + // XServiceInfo - static versions + /// @throws css::uno::RuntimeException + static OUString getImplementationName_Static( ); + /// @throws css::uno::RuntimeException + static css::uno::Sequence< OUString > getSupportedServiceNames_Static( ); + + css::uno::Reference< css::uno::XComponentContext > const & + getComponentContext() const { return m_xContext; } + + /** returns the path of our configuration settings + */ + static OUString impl_getConfigurationSettingsPath(); + + protected: + explicit MacabDriver(const css::uno::Reference< css::uno::XComponentContext >& _rxContext); + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XDriver + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL connect( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + virtual sal_Bool SAL_CALL acceptsURL( const OUString& url ) override; + virtual css::uno::Sequence< css::sdbc::DriverPropertyInfo > SAL_CALL getPropertyInfo( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + virtual sal_Int32 SAL_CALL getMajorVersion() override; + virtual sal_Int32 SAL_CALL getMinorVersion() override; + + // XTerminateListener + virtual void SAL_CALL queryTermination( const css::lang::EventObject& Event ) override; + virtual void SAL_CALL notifyTermination( const css::lang::EventObject& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + /** shuts down the library which contains the real implementations + + This method is safe against being called multiple times + + @precond our mutex is locked + */ + void impl_shutdownImplementationModule(); + }; + } + +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABDRIVER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabGroup.cxx b/connectivity/source/drivers/macab/MacabGroup.cxx new file mode 100644 index 000000000..a57f1729f --- /dev/null +++ b/connectivity/source/drivers/macab/MacabGroup.cxx @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabGroup.hxx" +#include "MacabRecords.hxx" +#include "macabutilities.hxx" + +using namespace connectivity::macab; + + +/* A MacabGroup is basically a MacabRecords with a different constructor. + * It only exists as a different entity for clarification purposes (a group + * is its own entity in the macOS Address Book) and because its + * construction is so unique (it is based on an already existent + * MacabRecords of the entire address book). + */ +MacabGroup::MacabGroup(const ABAddressBookRef _addressBook, const MacabRecords *_allRecords, const ABGroupRef _xGroup) + : MacabRecords(_addressBook) +{ + sal_Int32 i, j, nAllRecordsSize; + CFArrayRef xGroupMembers = ABGroupCopyArrayOfAllMembers(_xGroup); + ABPersonRef xPerson; + CFStringRef sGroupMemberUID; + bool bFound; + macabfield *xRecordField; + + // Set the group's name (stored in MacabRecords as m_sName) + CFStringRef sGroupName; + sGroupName = static_cast<CFStringRef>(ABRecordCopyValue(_xGroup, kABGroupNameProperty)); + m_sName = CFStringToOUString(sGroupName); + CFRelease(sGroupName); + + // The _group's_ records (remember MacabGroup inherits from MacabRecords) + recordsSize = static_cast<sal_Int32>(CFArrayGetCount(xGroupMembers)); + records = new MacabRecord *[recordsSize]; + setHeader(_allRecords->getHeader()); + + /* Go through each record in the group and try to find that record's UID + * in the MacabRecords that was passed in. If it is found, add that + * record to the group. Otherwise, report an error. (All records should + * exist in the MacabRecords that was passed in.) + */ + nAllRecordsSize = _allRecords->size(); + for(i = 0; i < recordsSize; i++) + { + xPerson = static_cast<ABPersonRef>(const_cast<void *>(CFArrayGetValueAtIndex(xGroupMembers,i))); + if(xPerson != nullptr) + { + sGroupMemberUID = static_cast<CFStringRef>(ABRecordCopyValue(xPerson, kABUIDProperty)); + if(sGroupMemberUID != nullptr) + { + bFound = false; + for(j = 0; j < nAllRecordsSize; j++) + { + xRecordField = _allRecords->getField(j,CFStringToOUString(kABUIDProperty)); + if(xRecordField != nullptr && xRecordField->value != nullptr) + { + if(CFEqual(xRecordField->value, sGroupMemberUID)) + { + /* Found the matching UID! Insert into the group... */ + insertRecord(_allRecords->getRecord(j)); + bFound = true; + break; + } + } + } + OSL_ENSURE(bFound, "MacabGroup::MacabGroup : Could not find group member based on UID!"); + CFRelease(sGroupMemberUID); + } + } + } + + CFRelease(xGroupMembers); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabGroup.hxx b/connectivity/source/drivers/macab/MacabGroup.hxx new file mode 100644 index 000000000..4d9313282 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabGroup.hxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABGROUP_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABGROUP_HXX + +#include "MacabRecords.hxx" + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <AddressBook/ABAddressBookC.h> +#include <postmac.h> + + +namespace connectivity +{ + namespace macab + { + class MacabGroup: public MacabRecords { + public: + MacabGroup(const ABAddressBookRef _addressBook, const MacabRecords *_allRecords, const ABGroupRef _xGroup); + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABGROUP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabHeader.cxx b/connectivity/source/drivers/macab/MacabHeader.cxx new file mode 100644 index 000000000..dc83d9c3d --- /dev/null +++ b/connectivity/source/drivers/macab/MacabHeader.cxx @@ -0,0 +1,334 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabHeader.hxx" +#include "MacabRecord.hxx" +#include "macabutilities.hxx" + +#include <math.h> +#include <com/sun/star/sdbc/DataType.hpp> +#include <connectivity/dbconversion.hxx> + +using namespace connectivity::macab; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::util; +using namespace ::dbtools; + + +MacabHeader::MacabHeader(const sal_Int32 _size, macabfield **_fields) +{ + sal_Int32 i; + size = _size; + fields = std::make_unique<macabfield *[]>(size); + for(i = 0; i < size; i++) + { + if(_fields[i] == nullptr) + { + fields[i] = nullptr; + } + else + { + /* The constructor duplicates the macabfields it gets because they + * are either deleted later or used for other purposes. + */ + fields[i] = new macabfield; + fields[i]->type = _fields[i]->type; + fields[i]->value = _fields[i]->value; + if (fields[i]->value) + CFRetain(fields[i]->value); + } + } + +} + + +MacabHeader::MacabHeader() +{ + size = 0; + fields = nullptr; +} + + +MacabHeader::~MacabHeader() +{ +} + + +void MacabHeader::operator+= (const MacabHeader *r) +{ + /* Add one MacabHeader to another. Anything not already in the header is + * added to the end of it. + */ + sal_Int32 rSize = r->getSize(); + if(rSize != 0) // If the new header does actually have fields + { + /* If our header is currently empty, just copy all of the fields from + * the new header to this one. + */ + if(size == 0) + { + sal_Int32 i; + size = rSize; + fields = std::make_unique<macabfield *[]>(size); + for(i = 0; i < size; i++) + { + fields[i] = r->copy(i); + } + } + + /* Otherwise, only add the duplicates. We do this with a two-pass + * approach. First, find out how many fields to add, then reallocate + * the size of the fields array and add the old ones at the end. + * (More precisely, we create a _new_ fields array with the new length + * allocated to it, then get all of the fields from the current + * fields array to it, then copy the non-duplicates from the new + * header to the end.) + */ + else + { + sal_Int32 i; + sal_Int32 numToAdd = 0, numAdded = 0; + macabfield **newFields; + for( i = 0; i < rSize; i++) + { + if(!contains(r->get(i))) + { + numToAdd++; + } + } + + newFields = new macabfield *[size+numToAdd]; + for(i = 0; i < size; i++) + { + newFields[i] = copy(i); + } + + for( i = 0; i < rSize; i++) + { + if(!contains(r->get(i))) + { + newFields[size+numAdded] = r->copy(i); + numAdded++; + if(numAdded == numToAdd) + break; + } + } + + releaseFields(); + size += numAdded; + fields.reset(newFields); + } + } +} + + +OUString MacabHeader::getString(const sal_Int32 i) const +{ + OUString nRet; + + if(i < size) + { + if(fields[i] == nullptr || fields[i]->value == nullptr || CFGetTypeID(fields[i]->value) != CFStringGetTypeID()) + return OUString(); + try + { + nRet = CFStringToOUString(static_cast<CFStringRef>(fields[i]->value)); + } + catch(...){ } + } + + return nRet; +} + + +void MacabHeader::sortRecord() +{ + sortRecord(0,size); +} + + +macabfield **MacabHeader::sortRecord(const sal_Int32 _start, const sal_Int32 _length) +{ + /* Sort using mergesort. Because it uses mergesort, it is recursive and + * not in place (so it creates a new array at every step of the + * recursion), so if you prefer to use a different sort, please feel + * free to implement it. + */ + macabfield** sorted = new macabfield *[_length]; + if(_length <= 2) + { + if(_length == 2) + { + if(compareFields(fields[_start], fields[_start+1]) > 0) + { + sorted[0] = get(_start+1); + sorted[1] = get(_start); + } + else + { + sorted[0] = get(_start); + sorted[1] = get(_start+1); + } + } + else if(_length == 1) + { + sorted[0] = get(_start); + } + } + else + { + sal_Int32 halfLength = floor(_length/2); + sal_Int32 fp = 0, lp = 0; + sal_Int32 i; + macabfield **firstHalf = sortRecord(_start, halfLength); + macabfield **lastHalf = sortRecord(_start+halfLength, _length-halfLength); + + for(i = 0; i < _length; i++) + { + if(compareFields(firstHalf[fp],lastHalf[lp]) < 0) + { + sorted[i] = firstHalf[fp++]; + if(fp == halfLength) + { + for( i++; i < _length; i++) + { + sorted[i] = lastHalf[lp++]; + } + break; + } + } + else + { + sorted[i] = lastHalf[lp++]; + if(lp == _length - halfLength) + { + for( i++; i < _length; i++) + { + sorted[i] = firstHalf[fp++]; + } + break; + } + } + } + if(_length == size) + { + fields.reset(sorted); + } + delete firstHalf; + delete lastHalf; + } + return sorted; +} + +sal_Int32 MacabHeader::compareFields(const macabfield *_field1, const macabfield *_field2) +{ + /* Comparing two fields in a MacabHeader is different than comparing two + * fields in a MacabRecord. It starts in the same way (if one of the two + * fields is NULL, it belongs after the other, so it is considered + * "greater"). But, then, all headers are CFStrings, no matter what + * type they claim to be (since they actually hold the expected type for + * the records with that header). That being said, all we have to do is + * the built-in CFStringCompare. + */ + if(_field1 == _field2) + return 0; + if(_field1 == nullptr) + return 1; + if(_field2 == nullptr) + return -1; + + CFComparisonResult result = CFStringCompare( + static_cast<CFStringRef>(_field1->value), + static_cast<CFStringRef>(_field2->value), + 0); // 0 = no options (like ignore case) + + return static_cast<sal_Int32>(result); +} + + +sal_Int32 MacabHeader::getColumnNumber(const OUString& s) const +{ + sal_Int32 i; + for(i = 0; i < size; i++) + { + if(getString(i) == s) + break; + } + + if(i == size) + i = -1; + + return i; +} + + +MacabHeader *MacabHeader::begin() +{ + return this; +} + + +MacabHeader::iterator::iterator () +{ +} + + +MacabHeader::iterator::~iterator () +{ +} + +MacabHeader::iterator& MacabHeader::iterator::operator= (MacabHeader *_record) +{ + id = 0; + record = _record; + return *this; +} + + +void MacabHeader::iterator::operator++ () +{ + id++; +} + + +bool MacabHeader::iterator::operator!= (const sal_Int32 i) const +{ + return(id != i); +} + + +bool MacabHeader::iterator::operator== (const sal_Int32 i) const +{ + return(id == i); +} + + +macabfield *MacabHeader::iterator::operator* () const +{ + return record->get(id); +} + + +sal_Int32 MacabHeader::end() const +{ + return size; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabHeader.hxx b/connectivity/source/drivers/macab/MacabHeader.hxx new file mode 100644 index 000000000..c77ae2da0 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabHeader.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABHEADER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABHEADER_HXX + +#include "MacabRecord.hxx" + +namespace connectivity +{ + namespace macab + { + class MacabHeader: public MacabRecord{ + protected: + macabfield **sortRecord(sal_Int32 _start, sal_Int32 _length); + public: + MacabHeader(); + MacabHeader(const sal_Int32 _size, macabfield **_fields); + virtual ~MacabHeader() override; + void operator+= (const MacabHeader *r); + OUString getString(const sal_Int32 i) const; + void sortRecord(); + sal_Int32 getColumnNumber(const OUString& s) const; + + static sal_Int32 compareFields(const macabfield *_field1, const macabfield *_field2); + + MacabHeader *begin(); + sal_Int32 end() const; + class iterator{ + protected: + sal_Int32 id; + MacabHeader *record; + public: + iterator& operator= (MacabHeader *_record); + iterator(); + ~iterator(); + void operator++ (); + bool operator!= (const sal_Int32 i) const; + bool operator== (const sal_Int32 i) const; + macabfield *operator* () const; + }; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABHEADER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabPreparedStatement.cxx b/connectivity/source/drivers/macab/MacabPreparedStatement.cxx new file mode 100644 index 000000000..cbb3dc756 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabPreparedStatement.cxx @@ -0,0 +1,343 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabPreparedStatement.hxx" +#include "MacabAddressBook.hxx" +#include <propertyids.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + +using namespace connectivity::macab; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::util; + +IMPLEMENT_SERVICE_INFO(MacabPreparedStatement, "com.sun.star.sdbc.drivers.MacabPreparedStatement", "com.sun.star.sdbc.PreparedStatement"); + +void MacabPreparedStatement::checkAndResizeParameters(sal_Int32 nParams) +{ + if ( !m_aParameterRow.is() ) + m_aParameterRow = new OValueVector(); + + if (nParams < 1) + ::dbtools::throwInvalidIndexException(*this); + + if (nParams >= static_cast<sal_Int32>(m_aParameterRow->size())) + m_aParameterRow->resize(nParams); +} + +void MacabPreparedStatement::setMacabFields() const +{ + ::rtl::Reference<connectivity::OSQLColumns> xColumns; // selected columns + + xColumns = m_aSQLIterator.getSelectColumns(); + if (!xColumns.is()) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString( + STR_INVALID_COLUMN_SELECTION + ) ); + ::dbtools::throwGenericSQLException(sError,nullptr); + } + m_xMetaData->setMacabFields(xColumns); +} + +void MacabPreparedStatement::resetParameters() const +{ + m_nParameterIndex = 0; +} + +void MacabPreparedStatement::getNextParameter(OUString &rParameter) const +{ + if (m_nParameterIndex >= static_cast<sal_Int32>(m_aParameterRow->size())) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString( + STR_INVALID_PARA_COUNT + ) ); + ::dbtools::throwGenericSQLException(sError,*const_cast<MacabPreparedStatement *>(this)); + } + + rParameter = (*m_aParameterRow)[m_nParameterIndex]; + + m_nParameterIndex++; +} + +MacabPreparedStatement::MacabPreparedStatement( + MacabConnection* _pConnection, + const OUString& sql) + : MacabPreparedStatement_BASE(_pConnection), + m_sSqlStatement(sql), + m_bPrepared(false), + m_nParameterIndex(0), + m_aParameterRow() +{ + +} + +MacabPreparedStatement::~MacabPreparedStatement() +{ +} + +void MacabPreparedStatement::disposing() +{ + MacabPreparedStatement_BASE::disposing(); + + if (m_aParameterRow.is()) + { + m_aParameterRow->clear(); + m_aParameterRow = nullptr; + } +} + +Reference< XResultSetMetaData > SAL_CALL MacabPreparedStatement::getMetaData() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabCommonStatement_BASE::rBHelper.bDisposed); + + if (!m_xMetaData.is()) + { + const OSQLTables& xTabs = m_aSQLIterator.getTables(); + OUString sTableName = MacabAddressBook::getDefaultTableName(); + + if(! xTabs.empty() ) + { + + // can only deal with one table at a time + if(xTabs.size() == 1 && !m_aSQLIterator.hasErrors() ) + sTableName = xTabs.begin()->first; + + } + m_xMetaData = new MacabResultSetMetaData(getOwnConnection(),sTableName); + setMacabFields(); + } + Reference< XResultSetMetaData > xMetaData = m_xMetaData.get(); + return xMetaData; +} + +void SAL_CALL MacabPreparedStatement::close() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabCommonStatement_BASE::rBHelper.bDisposed); + + // Reset last warning message + try { + clearWarnings (); + MacabCommonStatement::close(); + } + catch (SQLException &) { + // If we get an error, ignore + } + + // Remove this Statement object from the Connection object's + // list +} + +sal_Bool SAL_CALL MacabPreparedStatement::execute() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabCommonStatement_BASE::rBHelper.bDisposed); + + Reference< XResultSet> xRS = MacabCommonStatement::executeQuery(m_sSqlStatement); + + return xRS.is(); +} + +sal_Int32 SAL_CALL MacabPreparedStatement::executeUpdate() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabCommonStatement_BASE::rBHelper.bDisposed); + + // same as in statement with the difference that this statement also can contain parameter + return 0; +} + +Reference< XConnection > SAL_CALL MacabPreparedStatement::getConnection() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabCommonStatement_BASE::rBHelper.bDisposed); + + return m_pConnection; +} + +Reference< XResultSet > SAL_CALL MacabPreparedStatement::executeQuery() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabCommonStatement_BASE::rBHelper.bDisposed); + + Reference< XResultSet > rs = MacabCommonStatement::executeQuery(m_sSqlStatement); + + return rs; +} + +void SAL_CALL MacabPreparedStatement::setNull(sal_Int32 parameterIndex, sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabCommonStatement_BASE::rBHelper.bDisposed); + + checkAndResizeParameters(parameterIndex); + + (*m_aParameterRow)[parameterIndex - 1].setNull(); +} + +void SAL_CALL MacabPreparedStatement::setObjectNull(sal_Int32, sal_Int32, const OUString&) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setObjectNull", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setBoolean(sal_Int32, sal_Bool) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setBoolean", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setByte(sal_Int32, sal_Int8) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setByte", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setShort(sal_Int32, sal_Int16) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setShort", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setInt(sal_Int32, sal_Int32) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setInt", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setLong(sal_Int32, sal_Int64) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setLong", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setFloat(sal_Int32, float) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setFloat", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setDouble(sal_Int32, double) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setDouble", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setString(sal_Int32 parameterIndex, const OUString &x) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabCommonStatement_BASE::rBHelper.bDisposed); + + checkAndResizeParameters(parameterIndex); + + (*m_aParameterRow)[parameterIndex - 1] = x; +} + +void SAL_CALL MacabPreparedStatement::setBytes(sal_Int32, const Sequence< sal_Int8 >&) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setBytes", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setDate(sal_Int32, const Date&) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setDate", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setTime(sal_Int32, const css::util::Time&) +{ + + ::dbtools::throwFunctionNotSupportedSQLException("setTime", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setTimestamp(sal_Int32, const DateTime&) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setTimestamp", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setBinaryStream(sal_Int32, const Reference< css::io::XInputStream >&, sal_Int32) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setBinaryStream", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setCharacterStream(sal_Int32, const Reference< css::io::XInputStream >&, sal_Int32) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setCharacterStream", nullptr); +} + +void SAL_CALL MacabPreparedStatement::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); + } +} + +void SAL_CALL MacabPreparedStatement::setObjectWithInfo(sal_Int32, const Any&, sal_Int32, sal_Int32) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setObjectWithInfo", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setRef(sal_Int32, const Reference< XRef >&) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setRef", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setBlob(sal_Int32, const Reference< XBlob >&) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setBlob", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setClob(sal_Int32, const Reference< XClob >&) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setClob", nullptr); +} + +void SAL_CALL MacabPreparedStatement::setArray(sal_Int32, const Reference< XArray >&) +{ + ::dbtools::throwFunctionNotSupportedSQLException("setArray", nullptr); +} + +void SAL_CALL MacabPreparedStatement::clearParameters() +{ + ::dbtools::throwFunctionNotSupportedSQLException("clearParameters", nullptr); +} + +void MacabPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + switch (nHandle) + { + case PROPERTY_ID_RESULTSETCONCURRENCY: + break; + case PROPERTY_ID_RESULTSETTYPE: + break; + case PROPERTY_ID_FETCHDIRECTION: + break; + case PROPERTY_ID_USEBOOKMARKS: + break; + default: + MacabCommonStatement::setFastPropertyValue_NoBroadcast(nHandle,rValue); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabPreparedStatement.hxx b/connectivity/source/drivers/macab/MacabPreparedStatement.hxx new file mode 100644 index 000000000..a37c1b29a --- /dev/null +++ b/connectivity/source/drivers/macab/MacabPreparedStatement.hxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABPREPAREDSTATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABPREPAREDSTATEMENT_HXX + +#include "MacabStatement.hxx" +#include "MacabResultSetMetaData.hxx" +#include <connectivity/FValue.hxx> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <cppuhelper/implbase.hxx> + +namespace connectivity +{ + namespace macab + { + typedef ::cppu::ImplInheritanceHelper< MacabCommonStatement, + css::sdbc::XPreparedStatement, + css::sdbc::XParameters, + css::sdbc::XResultSetMetaDataSupplier, + css::lang::XServiceInfo> MacabPreparedStatement_BASE; + + class MacabPreparedStatement : public MacabPreparedStatement_BASE + { + protected: + OUString m_sSqlStatement; + ::rtl::Reference< MacabResultSetMetaData > + m_xMetaData; + bool m_bPrepared; + mutable sal_Int32 m_nParameterIndex; + OValueRow m_aParameterRow; + + /// @throws css::sdbc::SQLException + void checkAndResizeParameters(sal_Int32 nParams); + /// @throws css::sdbc::SQLException + void setMacabFields() const; + + protected: + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + + virtual void resetParameters() const override; + virtual void getNextParameter(OUString &rParameter) const override; + virtual ~MacabPreparedStatement() override; + + public: + DECLARE_SERVICE_INFO(); + MacabPreparedStatement(MacabConnection* _pConnection, const OUString& sql); + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XPreparedStatement + using MacabCommonStatement::executeQuery; + using MacabCommonStatement::executeUpdate; + using MacabCommonStatement::execute; + + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( ) override; + virtual sal_Int32 SAL_CALL executeUpdate( ) override; + virtual sal_Bool SAL_CALL execute( ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; + + // XParameters + virtual void SAL_CALL setNull( sal_Int32 parameterIndex, sal_Int32 sqlType ) override; + virtual void SAL_CALL setObjectNull( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) override; + virtual void SAL_CALL setBoolean( sal_Int32 parameterIndex, sal_Bool x ) override; + virtual void SAL_CALL setByte( sal_Int32 parameterIndex, sal_Int8 x ) override; + virtual void SAL_CALL setShort( sal_Int32 parameterIndex, sal_Int16 x ) override; + virtual void SAL_CALL setInt( sal_Int32 parameterIndex, sal_Int32 x ) override; + virtual void SAL_CALL setLong( sal_Int32 parameterIndex, sal_Int64 x ) override; + virtual void SAL_CALL setFloat( sal_Int32 parameterIndex, float x ) override; + virtual void SAL_CALL setDouble( sal_Int32 parameterIndex, double x ) override; + virtual void SAL_CALL setString( sal_Int32 parameterIndex, const OUString& x ) override; + virtual void SAL_CALL setBytes( sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL setDate( sal_Int32 parameterIndex, const css::util::Date& x ) override; + virtual void SAL_CALL setTime( sal_Int32 parameterIndex, const css::util::Time& x ) override; + virtual void SAL_CALL setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL setBinaryStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setCharacterStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setObject( sal_Int32 parameterIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL setObjectWithInfo( sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) override; + virtual void SAL_CALL setRef( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef >& x ) override; + virtual void SAL_CALL setBlob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob >& x ) override; + virtual void SAL_CALL setClob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XClob >& x ) override; + virtual void SAL_CALL setArray( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray >& x ) override; + virtual void SAL_CALL clearParameters( ) override; + + // XCloseable + virtual void SAL_CALL close( ) override; + + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABPREPAREDSTATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabRecord.cxx b/connectivity/source/drivers/macab/MacabRecord.cxx new file mode 100644 index 000000000..3072e1eaa --- /dev/null +++ b/connectivity/source/drivers/macab/MacabRecord.cxx @@ -0,0 +1,336 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabRecord.hxx" +#include "macabutilities.hxx" +#include <com/sun/star/util/DateTime.hpp> + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <AddressBook/ABAddressBookC.h> +#include <postmac.h> +#include <connectivity/dbconversion.hxx> + +using namespace connectivity::macab; +using namespace com::sun::star::util; +using namespace ::dbtools; + + +MacabRecord::MacabRecord() : size(0) +{ +} + + +MacabRecord::MacabRecord(const sal_Int32 _size) + : size(_size), + fields(std::make_unique<macabfield *[]>(size)) +{ + sal_Int32 i; + for(i = 0; i < size; i++) + fields[i] = nullptr; +} + + +MacabRecord::~MacabRecord() +{ + if(size > 0) + { + releaseFields(); + int i; + for(i = 0; i < size; i++) + { + delete fields[i]; + fields[i] = nullptr; + } + } +} + + +void MacabRecord::insertAtColumn (CFTypeRef _value, ABPropertyType _type, const sal_Int32 _column) +{ + if(_column < size) + { + if(fields[_column] == nullptr) + fields[_column] = new macabfield; + + fields[_column]->value = _value; + if (fields[_column]->value) + CFRetain(fields[_column]->value); + fields[_column]->type = _type; + } +} + + +bool MacabRecord::contains (const macabfield *_field) const +{ + if(_field == nullptr) + return false; + else + return contains(_field->value); +} + + +bool MacabRecord::contains (const CFTypeRef _value) const +{ + sal_Int32 i; + for(i = 0; i < size; i++) + { + if(fields[i] != nullptr) + { + if(CFEqual(fields[i]->value, _value)) + { + return true; + } + } + } + + return false; +} + + +sal_Int32 MacabRecord::getSize() const +{ + return size; +} + + +macabfield *MacabRecord::copy(const sal_Int32 i) const +{ + /* Note: copy(i) creates a new macabfield identical to that at + * location i, whereas get(i) returns a pointer to the macabfield + * at location i. + */ + if(i < size) + { + macabfield *_copy = new macabfield; + _copy->type = fields[i]->type; + _copy->value = fields[i]->value; + if (_copy->value) + CFRetain(_copy->value); + return _copy; + } + + return nullptr; +} + + +macabfield *MacabRecord::get(const sal_Int32 i) const +{ + /* Note: copy(i) creates a new macabfield identical to that at + * location i, whereas get(i) returns a pointer to the macabfield + * at location i. + */ + if(i < size) + { + return fields[i]; + } + + return nullptr; +} + + +void MacabRecord::releaseFields() +{ + /* This method is, at the moment, only used in MacabHeader.cxx, but + * the idea is simple: if you are not destroying this object but want + * to clear it of its macabfields, you should release each field's + * value. + */ + sal_Int32 i; + for(i = 0; i < size; i++) + CFRelease(fields[i]->value); +} + + +sal_Int32 MacabRecord::compareFields(const macabfield *_field1, const macabfield *_field2) +{ + + /* When comparing records, if either field is NULL (and the other is + * not), that field is considered "greater than" the other, so that it + * shows up later in the list when fields are ordered. + */ + if(_field1 == _field2) + return 0; + if(_field1 == nullptr) + return 1; + if(_field2 == nullptr) + return -1; + + /* If they aren't the same type, for now, return the one with + * the smaller type ID... I don't know of a better way to compare + * two different data types. + */ + if(_field1->type != _field2->type) + return(_field1->type - _field2->type); + + CFComparisonResult result; + + /* Carbon has a unique compare function for each data type: */ + switch(_field1->type) + { + case kABStringProperty: + result = CFStringCompare( + static_cast<CFStringRef>(_field1->value), + static_cast<CFStringRef>(_field2->value), + kCFCompareLocalized); // Specifies that the comparison should take into account differences related to locale, such as the thousands separator character. + break; + + case kABDateProperty: + result = CFDateCompare( + static_cast<CFDateRef>(_field1->value), + static_cast<CFDateRef>(_field2->value), + nullptr); // NULL = unused variable + break; + + case kABIntegerProperty: + case kABRealProperty: + result = CFNumberCompare( + static_cast<CFNumberRef>(_field1->value), + static_cast<CFNumberRef>(_field2->value), + nullptr); // NULL = unused variable + break; + + default: + result = kCFCompareEqualTo; // can't compare + } + + return static_cast<sal_Int32>(result); +} + + +/* Create a macabfield out of an OUString and type. Together with the + * method fieldToString() (below), it is possible to switch conveniently + * between an OUString and a macabfield (for use when creating and handling + * SQL statement). + */ +macabfield *MacabRecord::createMacabField(const OUString& _newFieldString, const ABPropertyType _abType) +{ + macabfield *newField = nullptr; + switch(_abType) + { + case kABStringProperty: + newField = new macabfield; + newField->value = OUStringToCFString(_newFieldString); + newField->type = _abType; + break; + case kABDateProperty: + { + DateTime aDateTime = DBTypeConversion::toDateTime(_newFieldString); + + // bad format... + if(aDateTime.Year == 0 && aDateTime.Month == 0 && aDateTime.Day == 0) + { + } + else + { + double nTime = DBTypeConversion::toDouble(aDateTime, DBTypeConversion::getStandardDate()); + nTime -= kCFAbsoluteTimeIntervalSince1970; + newField = new macabfield; + newField->value = CFDateCreate(nullptr, static_cast<CFAbsoluteTime>(nTime)); + newField->type = _abType; + } + } + break; + case kABIntegerProperty: + try + { + sal_Int64 nVal = _newFieldString.toInt64(); + + newField = new macabfield; + newField->value = CFNumberCreate(nullptr,kCFNumberLongType, &nVal); + newField->type = _abType; + } + // bad format... + catch(...) + { + } + break; + case kABRealProperty: + try + { + double nVal = _newFieldString.toDouble(); + + newField = new macabfield; + newField->value = CFNumberCreate(nullptr,kCFNumberDoubleType, &nVal); + newField->type = _abType; + } + // bad format... + catch(...) + { + } + break; + default: + ; + } + return newField; +} + + +/* Create an OUString out of a macabfield. Together with the method + * createMacabField() (above), it is possible to switch conveniently + * between an OUString and a macabfield (for use when creating and handling + * SQL statement). + */ +OUString MacabRecord::fieldToString(const macabfield *_aField) +{ + if(_aField == nullptr) + return OUString(); + + OUString fieldString; + + switch(_aField->type) + { + case kABStringProperty: + fieldString = CFStringToOUString(static_cast<CFStringRef>(_aField->value)); + break; + case kABDateProperty: + { + DateTime aTime = CFDateToDateTime(static_cast<CFDateRef>(_aField->value)); + fieldString = DBTypeConversion::toDateTimeString(aTime); + } + break; + case kABIntegerProperty: + { + CFNumberType numberType = CFNumberGetType( static_cast<CFNumberRef>(_aField->value) ); + sal_Int64 nVal; + // Should we check for the wrong type here, e.g., a float? + bool m_bSuccess = !CFNumberGetValue(static_cast<CFNumberRef>(_aField->value), numberType, &nVal); + if(m_bSuccess) + fieldString = OUString::number(nVal); + } + break; + case kABRealProperty: + { + CFNumberType numberType = CFNumberGetType( static_cast<CFNumberRef>(_aField->value) ); + double nVal; + // Should we check for the wrong type here, e.g., an int? + bool m_bSuccess = !CFNumberGetValue(static_cast<CFNumberRef>(_aField->value), numberType, &nVal); + if(m_bSuccess) + fieldString = OUString::number(nVal); + } + break; + default: + ; + } + return fieldString; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabRecord.hxx b/connectivity/source/drivers/macab/MacabRecord.hxx new file mode 100644 index 000000000..e677611bd --- /dev/null +++ b/connectivity/source/drivers/macab/MacabRecord.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRECORD_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRECORD_HXX + +#include <sal/config.h> + +#include <memory> + +#include <sal/types.h> +#include <osl/diagnose.h> +#include <rtl/ustring.hxx> + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <AddressBook/ABAddressBookC.h> +#include <postmac.h> + +namespace connectivity +{ + namespace macab + { + /* a MacabRecord is at root a list of macabfields (which is just + * something to hold both a CFTypeRef (a CoreFoundation object) and + * its Address Book type. + */ + struct macabfield + { + CFTypeRef value; + ABPropertyType type; + }; + + class MacabRecord{ + protected: + sal_Int32 size; + std::unique_ptr<macabfield *[]> fields; + protected: + void releaseFields(); + public: + MacabRecord(); + explicit MacabRecord(const sal_Int32 _size); + virtual ~MacabRecord(); + void insertAtColumn (CFTypeRef _value, ABPropertyType _type, const sal_Int32 _column); + bool contains(const macabfield *_field) const; + bool contains(const CFTypeRef _value) const; + sal_Int32 getSize() const; + macabfield *copy(const sal_Int32 i) const; + macabfield *get(const sal_Int32 i) const; + + static sal_Int32 compareFields(const macabfield *_field1, const macabfield *_field2); + static macabfield *createMacabField(const OUString& _newFieldString, const ABPropertyType _abtype); + static OUString fieldToString(const macabfield *_aField); + + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRECORD_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabRecords.cxx b/connectivity/source/drivers/macab/MacabRecords.cxx new file mode 100644 index 000000000..0bc27ea0e --- /dev/null +++ b/connectivity/source/drivers/macab/MacabRecords.cxx @@ -0,0 +1,1164 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <memory> +#include <utility> +#include <vector> + +#include "MacabRecords.hxx" +#include "MacabRecord.hxx" +#include "MacabHeader.hxx" +#include "macabutilities.hxx" + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <AddressBook/ABAddressBookC.h> +#include <postmac.h> +#include <com/sun/star/util/DateTime.hpp> + +using namespace connectivity::macab; +using namespace com::sun::star::util; + +namespace { + +void manageDuplicateHeaders(macabfield **_headerNames, const sal_Int32 _length) +{ + /* If we have two cases of, say, phone: home, this makes it: + * phone: home (1) + * phone: home (2) + */ + sal_Int32 i, j; + sal_Int32 count; + for(i = _length-1; i >= 0; i--) + { + count = 1; + for( j = i-1; j >= 0; j--) + { + if(CFEqual(_headerNames[i]->value, _headerNames[j]->value)) + { + count++; + } + } + + // duplicate! + if(count != 1) + { + // There is probably a better way to do this... + OUString newName = CFStringToOUString(static_cast<CFStringRef>(_headerNames[i]->value)); + CFRelease(_headerNames[i]->value); + newName += " (" + OUString::number(count) + ")"; + _headerNames[i]->value = OUStringToCFString(newName); + } + } +} + +} + +MacabRecords::MacabRecords(const ABAddressBookRef _addressBook, MacabHeader *_header, MacabRecord **_records, sal_Int32 _numRecords) + : recordsSize(_numRecords), currentRecord(_numRecords), recordType(kABPersonRecordType), + header(_header), records(_records), addressBook(_addressBook) +{ + /* Variables constructed... */ + bootstrap_CF_types(); + bootstrap_requiredProperties(); +} + + +/* Creates a MacabRecords from another: copies the length, name, and + * address book of the original, but the header or the records themselves. + * The idea is that the only reason to copy a MacabRecords is to create + * a filtered version of it, which can have the same length (to avoid + * resizing) and will work from the same base addressbook, but might have + * entirely different values and even (possibly in the future) a different + * header. + */ +MacabRecords::MacabRecords(const MacabRecords *_copy) + : recordsSize(_copy->recordsSize), currentRecord(0), recordType(kABPersonRecordType), + header(nullptr), records(new MacabRecord *[recordsSize]), addressBook(_copy->addressBook), + m_sName(_copy->m_sName) +{ + /* Variables constructed... */ + bootstrap_CF_types(); + bootstrap_requiredProperties(); +} + + +MacabRecords::MacabRecords(const ABAddressBookRef _addressBook) + : recordsSize(0), currentRecord(0), recordType(kABPersonRecordType), + header(nullptr), records(nullptr), addressBook(_addressBook) +{ + /* Variables constructed... */ + bootstrap_CF_types(); + bootstrap_requiredProperties(); +} + + +void MacabRecords::initialize() +{ + + /* Make sure everything is NULL before initializing. (We usually just + * initialize after we use the constructor that takes only a + * MacabAddressBook, so these variables will most likely already be + * NULL. + */ + if(records != nullptr) + { + sal_Int32 i; + + for(i = 0; i < recordsSize; i++) + delete records[i]; + + delete [] records; + } + + if(header != nullptr) + delete header; + + /* We can handle both default record Address Book record types in + * this method, though only kABPersonRecordType is ever used. + */ + CFArrayRef allRecords; + if(CFStringCompare(recordType, kABPersonRecordType, 0) == kCFCompareEqualTo) + allRecords = ABCopyArrayOfAllPeople(addressBook); + else + allRecords = ABCopyArrayOfAllGroups(addressBook); + + ABRecordRef record; + sal_Int32 i; + recordsSize = static_cast<sal_Int32>(CFArrayGetCount(allRecords)); + records = new MacabRecord *[recordsSize]; + + /* First, we create the header... */ + header = createHeaderForRecordType(allRecords, recordType); + + /* Then, we create each of the records... */ + for(i = 0; i < recordsSize; i++) + { + record = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(allRecords, i)); + records[i] = createMacabRecord(record, header, recordType); + } + currentRecord = recordsSize; + + CFRelease(allRecords); +} + + +MacabRecords::~MacabRecords() +{ +} + + +void MacabRecords::setHeader(MacabHeader *_header) +{ + if(header != nullptr) + delete header; + header = _header; +} + + +MacabHeader *MacabRecords::getHeader() const +{ + return header; +} + + +/* Inserts a MacabRecord at a given location. If there is already a + * MacabRecord at that location, return it. + */ +MacabRecord *MacabRecords::insertRecord(MacabRecord *_newRecord, const sal_Int32 _location) +{ + MacabRecord *oldRecord; + + /* If the location is greater than the current allocated size of this + * MacabRecords, allocate more space. + */ + if(_location >= recordsSize) + { + sal_Int32 i; + MacabRecord **newRecordsArray = new MacabRecord *[_location+1]; + for(i = 0; i < recordsSize; i++) + { + newRecordsArray[i] = records[i]; + } + delete [] records; + records = newRecordsArray; + } + + /* Remember: currentRecord refers to one above the highest existing + * record (i.e., it refers to where to place the next record if a + * location is not given). + */ + if(_location >= currentRecord) + currentRecord = _location+1; + + oldRecord = records[_location]; + records[_location] = _newRecord; + return oldRecord; +} + + +/* Insert a record at the next available place. */ +void MacabRecords::insertRecord(MacabRecord *_newRecord) +{ + insertRecord(_newRecord, currentRecord); +} + + +MacabRecord *MacabRecords::getRecord(const sal_Int32 _location) const +{ + if(_location >= recordsSize) + return nullptr; + return records[_location]; +} + + +macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const sal_Int32 _columnNumber) const +{ + if(_recordNumber >= recordsSize) + return nullptr; + + MacabRecord *record = records[_recordNumber]; + + if(_columnNumber < 0 || _columnNumber >= record->getSize()) + return nullptr; + + return record->get(_columnNumber); +} + + +macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const OUString& _columnName) const +{ + if(header != nullptr) + { + sal_Int32 columnNumber = header->getColumnNumber(_columnName); + if(columnNumber == -1) + return nullptr; + + return getField(_recordNumber, columnNumber); + } + else + { + // error: shouldn't access field with null header! + return nullptr; + } +} + + +sal_Int32 MacabRecords::getFieldNumber(const OUString& _columnName) const +{ + if(header != nullptr) + return header->getColumnNumber(_columnName); + else + // error: shouldn't access field with null header! + return -1; +} + + +/* Create the lcl_CFTypes array -- we need this because there is no + * way to get the ABType of an object from the object itself, and the + * function ABTypeOfProperty can't handle multiple levels of data + * (e.g., it can tell us that "address" is of type + * kABDictionaryProperty, but it cannot tell us that all of the keys + * and values in the dictionary have type kABStringProperty. On the + * other hand, we _can_ get the CFType out of any object. + * Unfortunately, all information about CFTypeIDs comes with the + * warning that they change between releases, so we build them + * ourselves here. (The one that we can't build is for multivalues, + * e.g., kABMultiStringProperty. All of these appear to have the + * same type: 1, but there is no function that I've found to give + * us that dynamically in case that number ever changes. + */ +void MacabRecords::bootstrap_CF_types() +{ + lcl_CFTypes = { + {CFNumberGetTypeID(), kABIntegerProperty}, + {CFStringGetTypeID(), kABStringProperty}, + {CFDateGetTypeID(), kABDateProperty}, + {CFArrayGetTypeID(), kABArrayProperty}, + {CFDictionaryGetTypeID(), kABDictionaryProperty}, + {CFDataGetTypeID(), kABDataProperty}}; +} + + +/* This is based on the possible fields required in the mail merge template + * in sw. If the fields possible there change, it would be optimal to + * change these fields as well. + */ +void MacabRecords::bootstrap_requiredProperties() +{ + requiredProperties = { + kABTitleProperty, kABFirstNameProperty, kABLastNameProperty, kABOrganizationProperty, + kABAddressProperty, kABPhoneProperty, kABEmailProperty}; +} + + +/* Create the header for a given record type and a given array of records. + * Because the array of records and the record type are given, if you want + * to, you can run this method on the members of a group, or on any other + * filtered list of people and get a header relevant to them (e.g., if + * they only have home addresses, the work address fields won't show up). + */ +MacabHeader *MacabRecords::createHeaderForRecordType(const CFArrayRef _records, const CFStringRef _recordType) const +{ + /* We have two types of properties for a given record type, nonrequired + * and required. Required properties are ones that will show up whether + * or not they are empty. Nonrequired properties will only show up if + * at least one record in the set has that property filled. The reason + * is that some properties, like the kABTitleProperty are required by + * the mail merge wizard (in module sw) but are by default not shown in + * the macOS address book, so they would be weeded out at this stage + * and not shown if they were not required. + * + * Note: with the addition of required properties, I am not sure that + * this method still works for kABGroupRecordType (since the required + * properties are all for kABPersonRecordType). + * + * Note: required properties are constructed in the method + * bootstrap_requiredProperties() (above). + */ + CFArrayRef allProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType); + CFStringRef *nonRequiredProperties; + sal_Int32 numRecords = static_cast<sal_Int32>(CFArrayGetCount(_records)); + sal_Int32 numProperties = static_cast<sal_Int32>(CFArrayGetCount(allProperties)); + sal_Int32 numNonRequiredProperties = numProperties - requiredProperties.size(); + + /* While searching through the properties for required properties, these + * sal_Bools will keep track of what we have found. + */ + bool bFoundRequiredProperties[requiredProperties.size()]; + + + /* We have three MacabHeaders: headerDataForProperty is where we + * store the result of createHeaderForProperty(), which return a + * MacabHeader for a single property. lcl_header is where we store + * the MacabHeader that we are constructing. And, nonRequiredHeader + * is where we construct the MacabHeader for non-required properties, + * so that we can sort them before adding them to lcl_header. + */ + MacabHeader *headerDataForProperty; + MacabHeader *lcl_header = new MacabHeader(); + MacabHeader *nonRequiredHeader = new MacabHeader(); + + /* Other variables... */ + sal_Int32 k; + ABRecordRef record; + CFStringRef property; + + + /* Allocate and initialize... */ + nonRequiredProperties = new CFStringRef[numNonRequiredProperties]; + k = 0; + for(std::vector<CFStringRef>::size_type i = 0; i < requiredProperties.size(); i++) + bFoundRequiredProperties[i] = false; + + /* Determine the non-required properties... */ + for(sal_Int32 i = 0; i < numProperties; i++) + { + bool bFoundProperty = false; + property = static_cast<CFStringRef>(CFArrayGetValueAtIndex(allProperties, i)); + for(std::vector<CFStringRef>::size_type j = 0; j < requiredProperties.size(); j++) + { + if(CFEqual(property, requiredProperties[j])) + { + bFoundProperty = true; + bFoundRequiredProperties[j] = true; + break; + } + } + + if(!bFoundProperty) + { + /* If we have found too many non-required properties */ + if(k == numNonRequiredProperties) + { + k++; // so that the OSL_ENSURE below fails + break; + } + nonRequiredProperties[k] = property; + k++; + } + } + + // Somehow, we got too many or too few non-required properties... + // Most likely, one of the required properties no longer exists, which + // we also test later. + OSL_ENSURE(k == numNonRequiredProperties, "MacabRecords::createHeaderForRecordType: Found an unexpected number of non-required properties"); + + /* Fill the header with required properties first... */ + for(std::vector<CFStringRef>::size_type i = 0; i < requiredProperties.size(); i++) + { + if(bFoundRequiredProperties[i]) + { + /* The order of these matters (we want all address properties + * before any phone properties, or else things will look weird), + * so we get all possibilities for each property, going through + * each record, and then go onto the next property. + * (Note: the reason that we have to go through all records + * in the first place is that properties like address, phone, and + * e-mail are multi-value properties with an unknown number of + * values. A user could specify thirteen different kinds of + * e-mail addresses for one of her or his contacts, and we need to + * get all of them. + */ + for(sal_Int32 j = 0; j < numRecords; j++) + { + record = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(_records, j)); + headerDataForProperty = createHeaderForProperty(record,requiredProperties[i],_recordType,true); + if(headerDataForProperty != nullptr) + { + (*lcl_header) += headerDataForProperty; + delete headerDataForProperty; + } + } + } + else + { + // Couldn't find a required property... + OSL_FAIL(OString("MacabRecords::createHeaderForRecordType: could not find required property: " + + OUStringToOString(CFStringToOUString(requiredProperties[i]), RTL_TEXTENCODING_ASCII_US)).getStr()); + } + } + + /* And now, non-required properties... */ + for(sal_Int32 i = 0; i < numRecords; i++) + { + record = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(_records, i)); + + for(sal_Int32 j = 0; j < numNonRequiredProperties; j++) + { + property = nonRequiredProperties[j]; + headerDataForProperty = createHeaderForProperty(record,property,_recordType,false); + if(headerDataForProperty != nullptr) + { + (*nonRequiredHeader) += headerDataForProperty; + delete headerDataForProperty; + } + } + + } + nonRequiredHeader->sortRecord(); + + (*lcl_header) += nonRequiredHeader; + delete nonRequiredHeader; + + CFRelease(allProperties); + delete [] nonRequiredProperties; + + return lcl_header; +} + + +/* Create a header for a single property. Basically, this method gets + * the property's value and type and then calls another method of + * the same name to do the dirty work. + */ +MacabHeader *MacabRecords::createHeaderForProperty(const ABRecordRef _record, const CFStringRef _propertyName, const CFStringRef _recordType, const bool _isPropertyRequired) const +{ + // local variables + CFStringRef localizedPropertyName; + CFTypeRef propertyValue; + ABPropertyType propertyType; + MacabHeader *result; + + /* Get the property's value */ + propertyValue = ABRecordCopyValue(_record,_propertyName); + if(propertyValue == nullptr && !_isPropertyRequired) + return nullptr; + + propertyType = ABTypeOfProperty(addressBook, _recordType, _propertyName); + localizedPropertyName = ABCopyLocalizedPropertyOrLabel(_propertyName); + + result = createHeaderForProperty(propertyType, propertyValue, localizedPropertyName); + + if(propertyValue != nullptr) + CFRelease(propertyValue); + + return result; +} + + +/* Create a header for a single property. This method is recursive + * because a single property might contain several sub-properties that + * we also want to treat singly. + */ +MacabHeader *MacabRecords::createHeaderForProperty(const ABPropertyType _propertyType, const CFTypeRef _propertyValue, const CFStringRef _propertyName) const +{ + macabfield **headerNames = nullptr; + sal_Int32 length = 0; + + switch(_propertyType) + { + /* Scalars */ + case kABStringProperty: + case kABRealProperty: + case kABIntegerProperty: + case kABDateProperty: + length = 1; + headerNames = new macabfield *[1]; + headerNames[0] = new macabfield; + headerNames[0]->value = _propertyName; + headerNames[0]->type = _propertyType; + break; + + /* Multi-scalars */ + case kABMultiIntegerProperty: + case kABMultiDateProperty: + case kABMultiStringProperty: + case kABMultiRealProperty: + case kABMultiDataProperty: + /* For non-scalars, we can only get more information if the property + * actually exists. + */ + if(_propertyValue != nullptr) + { + sal_Int32 i; + + sal_Int32 multiLength = ABMultiValueCount(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))); + CFStringRef multiLabel, localizedMultiLabel; + OUString multiLabelString; + OUString multiPropertyString; + OUString headerNameString; + ABPropertyType multiType = static_cast<ABPropertyType>(ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))) - 0x100); + + length = multiLength; + headerNames = new macabfield *[multiLength]; + multiPropertyString = CFStringToOUString(_propertyName); + + /* Go through each element, and - since each element is a scalar - + * just create a new macabfield for it. + */ + for(i = 0; i < multiLength; i++) + { + multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i); + localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); + multiLabelString = CFStringToOUString(localizedMultiLabel); + CFRelease(multiLabel); + CFRelease(localizedMultiLabel); + headerNameString = multiPropertyString + ": " + fixLabel(multiLabelString); + headerNames[i] = new macabfield; + headerNames[i]->value = OUStringToCFString(headerNameString); + headerNames[i]->type = multiType; + } + } + break; + + /* Multi-array or dictionary */ + case kABMultiArrayProperty: + case kABMultiDictionaryProperty: + /* For non-scalars, we can only get more information if the property + * actually exists. + */ + if(_propertyValue != nullptr) + { + sal_Int32 i,j,k; + + // Total number of multi-array or multi-dictionary elements. + sal_Int32 multiLengthFirstLevel = ABMultiValueCount(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))); + + /* Total length, including the length of each element (e.g., if + * this multi-dictionary contains three dictionaries, and each + * dictionary has four elements, this variable will be twelve, + * whereas multiLengthFirstLevel will be three. + */ + sal_Int32 multiLengthSecondLevel = 0; + + CFStringRef multiLabel, localizedMultiLabel; + CFTypeRef multiValue; + OUString multiLabelString; + OUString multiPropertyString; + std::vector<std::unique_ptr<MacabHeader>> multiHeaders; + ABPropertyType multiType = static_cast<ABPropertyType>(ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))) - 0x100); + + multiPropertyString = CFStringToOUString(_propertyName); + + /* Go through each element - since each element can really + * contain anything, we run this method again on each element + * and store the resulting MacabHeader (in the multiHeaders + * array). Then, all we'll have to do is combine the MacabHeaders + * into a single one. + */ + for(i = 0; i < multiLengthFirstLevel; i++) + { + /* label */ + multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i); + multiValue = ABMultiValueCopyValueAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i); + std::unique_ptr<MacabHeader> hdr; + if(multiValue && multiLabel) + { + localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); + multiLabelString = multiPropertyString + ": " + fixLabel(CFStringToOUString(localizedMultiLabel)); + CFRelease(multiLabel); + CFRelease(localizedMultiLabel); + multiLabel = OUStringToCFString(multiLabelString); + hdr.reset(createHeaderForProperty(multiType, multiValue, multiLabel)); + if (!hdr) + hdr = std::make_unique<MacabHeader>(); + multiLengthSecondLevel += hdr->getSize(); + } + else + { + hdr = std::make_unique<MacabHeader>(); + } + if(multiValue) + CFRelease(multiValue); + if(multiLabel) + CFRelease(multiLabel); + multiHeaders.push_back(std::move(hdr)); + } + + /* We now have enough information to create our final MacabHeader. + * We go through each field of each header and add it to the + * headerNames array (which is what is used below to construct + * the MacabHeader we return). + */ + length = multiLengthSecondLevel; + headerNames = new macabfield *[multiLengthSecondLevel]; + + for(i = 0, j = 0, k = 0; i < multiLengthSecondLevel; i++,k++) + { + while(multiHeaders[j]->getSize() == k) + { + j++; + k = 0; + } + + headerNames[i] = multiHeaders[j]->copy(k); + } + } + break; + + /* Dictionary */ + case kABDictionaryProperty: + /* For non-scalars, we can only get more information if the property + * actually exists. + */ + if(_propertyValue != nullptr) + { + /* Assume all keys are strings */ + sal_Int32 numRecords = static_cast<sal_Int32>(CFDictionaryGetCount(static_cast<CFDictionaryRef>(_propertyValue))); + + /* The only method for getting info out of a CFDictionary, of both + * keys and values, is to all of them all at once, so these + * variables will hold them. + */ + CFStringRef *dictKeys; + CFTypeRef *dictValues; + + sal_Int32 i,j,k; + OUString dictKeyString, propertyNameString; + ABPropertyType dictType; + MacabHeader **dictHeaders = new MacabHeader *[numRecords]; + OUString dictLabelString; + CFStringRef dictLabel, localizedDictKey; + + /* Get the keys and values */ + dictKeys = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef)*numRecords)); + dictValues = static_cast<CFTypeRef *>(malloc(sizeof(CFTypeRef)*numRecords)); + CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef>(_propertyValue), reinterpret_cast<const void **>(dictKeys), dictValues); + + propertyNameString = CFStringToOUString(_propertyName); + + length = 0; + /* Go through each element - assuming that the key is a string but + * that the value could be anything. Since the value could be + * anything, we can't assume that it is scalar (it could even be + * another dictionary), so we attempt to get its type using + * the method getABTypeFromCFType and then run this method + * recursively on that element, storing the MacabHeader that + * results. Then, we just combine all of the MacabHeaders into + * one. + */ + for(i = 0; i < numRecords; i++) + { + dictType = getABTypeFromCFType( CFGetTypeID(dictValues[i]) ); + localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]); + dictKeyString = CFStringToOUString(localizedDictKey); + dictLabelString = propertyNameString + ": " + fixLabel(dictKeyString); + dictLabel = OUStringToCFString(dictLabelString); + dictHeaders[i] = createHeaderForProperty(dictType, dictValues[i], dictLabel); + if (!dictHeaders[i]) + dictHeaders[i] = new MacabHeader(); + length += dictHeaders[i]->getSize(); + CFRelease(dictLabel); + CFRelease(localizedDictKey); + } + + /* Combine all of the macabfields in each MacabHeader into the + * headerNames array, which (at the end of this method) is used + * to create the MacabHeader that is returned. + */ + headerNames = new macabfield *[length]; + for(i = 0, j = 0, k = 0; i < length; i++,k++) + { + while(dictHeaders[j]->getSize() == k) + { + j++; + k = 0; + } + + headerNames[i] = dictHeaders[j]->copy(k); + } + + for(i = 0; i < numRecords; i++) + delete dictHeaders[i]; + + delete [] dictHeaders; + free(dictKeys); + free(dictValues); + } + break; + + /* Array */ + case kABArrayProperty: + /* For non-scalars, we can only get more information if the property + * actually exists. + */ + if(_propertyValue != nullptr) + { + sal_Int32 arrLength = static_cast<sal_Int32>(CFArrayGetCount(static_cast<CFArrayRef>(_propertyValue))); + sal_Int32 i,j,k; + CFTypeRef arrValue; + ABPropertyType arrType; + std::vector<std::unique_ptr<MacabHeader>> arrHeaders; + OUString propertyNameString = CFStringToOUString(_propertyName); + OUString arrLabelString; + CFStringRef arrLabel; + + length = 0; + /* Go through each element - since the elements here do not have + * unique keys like the ones in dictionaries, we create a unique + * key out of the id of the element in the array (the first + * element gets a 0 plopped onto the end of it, the second a 1... + * As with dictionaries, the elements could be anything, including + * another array, so we have to run this method recursively on + * each element, storing the resulting MacabHeader into an array, + * which we then combine into one MacabHeader that is returned. + */ + for(i = 0; i < arrLength; i++) + { + arrValue = CFArrayGetValueAtIndex(static_cast<CFArrayRef>(_propertyValue), i); + arrType = getABTypeFromCFType( CFGetTypeID(arrValue) ); + arrLabelString = propertyNameString + OUString::number(i); + arrLabel = OUStringToCFString(arrLabelString); + auto hdr = std::unique_ptr<MacabHeader>(createHeaderForProperty(arrType, arrValue, arrLabel)); + if (!hdr) + hdr = std::make_unique<MacabHeader>(); + length += hdr->getSize(); + CFRelease(arrLabel); + arrHeaders.push_back(std::move(hdr)); + } + + headerNames = new macabfield *[length]; + for(i = 0, j = 0, k = 0; i < length; i++,k++) + { + while(arrHeaders[j]->getSize() == k) + { + j++; + k = 0; + } + + headerNames[i] = arrHeaders[j]->copy(k); + } + } + break; + + default: + break; + + } + + /* If we succeeded at adding elements to the headerNames array, then + * length will no longer be 0. If it is, create a new MacabHeader + * out of the headerNames (after weeding out duplicate headers), and + * then return the result. If the length is still 0, return NULL: we + * failed to create a MacabHeader out of this property. + */ + if(length != 0) + { + manageDuplicateHeaders(headerNames, length); + MacabHeader *headerResult = new MacabHeader(length, headerNames); + for(sal_Int32 i = 0; i < length; ++i) + delete headerNames[i]; + delete [] headerNames; + return headerResult; + } + else + return nullptr; +} + + +/* Create a MacabRecord out of an ABRecord, using a given MacabHeader and + * the record's type. We go through each property for this record type + * then process it much like we processed the header (above), with two + * exceptions: if we come upon something not in the header, we ignore it + * (it's something we don't want to add), and once we find a corresponding + * location in the header, we store the property and the property type in + * a macabfield. (For the header, we stored the property type and the name + * of the property as a CFString.) + */ +MacabRecord *MacabRecords::createMacabRecord(const ABRecordRef _abrecord, const MacabHeader *_header, const CFStringRef _recordType) const +{ + /* The new record that we will create... */ + MacabRecord *macabRecord = new MacabRecord(_header->getSize()); + + CFArrayRef recordProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType); + sal_Int32 numProperties = static_cast<sal_Int32>(CFArrayGetCount(recordProperties)); + + sal_Int32 i; + + CFTypeRef propertyValue; + ABPropertyType propertyType; + + CFStringRef propertyName, localizedPropertyName; + OUString propertyNameString; + for(i = 0; i < numProperties; i++) + { + propertyName = static_cast<CFStringRef>(CFArrayGetValueAtIndex(recordProperties, i)); + localizedPropertyName = ABCopyLocalizedPropertyOrLabel(propertyName); + propertyNameString = CFStringToOUString(localizedPropertyName); + CFRelease(localizedPropertyName); + + /* Get the property's value */ + propertyValue = ABRecordCopyValue(_abrecord,propertyName); + if(propertyValue != nullptr) + { + propertyType = ABTypeOfProperty(addressBook, _recordType, propertyName); + if(propertyType != kABErrorInProperty) + insertPropertyIntoMacabRecord(propertyType, macabRecord, _header, propertyNameString, propertyValue); + + CFRelease(propertyValue); + } + } + CFRelease(recordProperties); + return macabRecord; +} + + +/* Inserts a given property into a MacabRecord. This method calls another + * method by the same name after getting the property type (it only + * receives the property value). It is called when we aren't given the + * property's type already. + */ +void MacabRecords::insertPropertyIntoMacabRecord(MacabRecord *_abrecord, const MacabHeader *_header, const OUString& _propertyName, const CFTypeRef _propertyValue) const +{ + CFTypeID cf_type = CFGetTypeID(_propertyValue); + ABPropertyType ab_type = getABTypeFromCFType( cf_type ); + + if(ab_type != kABErrorInProperty) + insertPropertyIntoMacabRecord(ab_type, _abrecord, _header, _propertyName, _propertyValue); +} + + +/* Inserts a given property into a MacabRecord. This method is recursive + * because properties can contain many sub-properties. + */ +void MacabRecords::insertPropertyIntoMacabRecord(const ABPropertyType _propertyType, MacabRecord *_abrecord, const MacabHeader *_header, const OUString& _propertyName, const CFTypeRef _propertyValue) const +{ + /* If there is no value, return */ + if(_propertyValue == nullptr) + return; + + /* The main switch statement */ + switch(_propertyType) + { + /* Scalars */ + case kABStringProperty: + case kABRealProperty: + case kABIntegerProperty: + case kABDateProperty: + { + /* Only scalars actually insert a property into the MacabRecord. + * In all other cases, this method is called recursively until a + * scalar type, an error, or an unknown type are found. + * Because of that, the following checks only occur for this type. + * We store whether we have successfully placed this property + * into the MacabRecord (or whether an unrecoverable error occurred). + * Then, we try over and over again to place the property into the + * record. There are three possible results: + * 1) Success! + * 2) There is already a property stored at the column of this name, + * in which case we have a duplicate header (see the method + * manageDuplicateHeaders()). If that is the case, we add an ID + * to the end of the column name in the same format as we do in + * manageDuplicateHeaders() and try again. + * 3) No column of this name exists in the header. In this case, + * there is nothing we can do: we have failed to place this + * property into the record. + */ + bool bPlaced = false; + OUString columnName = _propertyName; + sal_Int32 i = 1; + + // A big safeguard to prevent two fields from having the same name. + while(!bPlaced) + { + sal_Int32 columnNumber = _header->getColumnNumber(columnName); + bPlaced = true; + if(columnNumber != -1) + { + // collision! A property already exists here! + if(_abrecord->get(columnNumber) != nullptr) + { + bPlaced = false; + i++; + columnName = _propertyName + " (" + OUString::number(i) + ")"; + } + + // success! + else + { + _abrecord->insertAtColumn(_propertyValue, _propertyType, columnNumber); + } + } + } + } + break; + + /* Array */ + case kABArrayProperty: + { + /* An array is basically just a list of anything, so all we do + * is go through the array, and rerun this method recursively + * on each element. + */ + sal_Int32 arrLength = static_cast<sal_Int32>(CFArrayGetCount(static_cast<CFArrayRef>(_propertyValue))); + sal_Int32 i; + OUString newPropertyName; + + /* Going through each element... */ + for(i = 0; i < arrLength; i++) + { + const void *arrValue = CFArrayGetValueAtIndex(static_cast<CFArrayRef>(_propertyValue), i); + newPropertyName = _propertyName + OUString::number(i); + insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, arrValue); + CFRelease(arrValue); + } + + } + break; + + /* Dictionary */ + case kABDictionaryProperty: + { + /* A dictionary is basically a hashmap. Technically, it can + * hold any object as a key and any object as a value. + * For our case, we assume that the key is a string (so that + * we can use the key to get the column name and match it against + * the header), but we don't assume anything about the value, so + * we run this method recursively (or, rather, we run the version + * of this method for when we don't know the object's type) until + * we hit a scalar value. + */ + + sal_Int32 numRecords = static_cast<sal_Int32>(CFDictionaryGetCount(static_cast<CFDictionaryRef>(_propertyValue))); + OUString dictKeyString; + sal_Int32 i; + OUString newPropertyName; + + /* Unfortunately, the only way to get both keys and values out + * of a dictionary in Carbon is to get them all at once, so we + * do that. + */ + CFStringRef *dictKeys; + CFStringRef localizedDictKey; + CFTypeRef *dictValues; + dictKeys = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef)*numRecords)); + dictValues = static_cast<CFTypeRef *>(malloc(sizeof(CFTypeRef)*numRecords)); + CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef>(_propertyValue), reinterpret_cast<const void **>(dictKeys), dictValues); + + /* Going through each element... */ + for(i = 0; i < numRecords; i++) + { + localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]); + dictKeyString = CFStringToOUString(localizedDictKey); + CFRelease(localizedDictKey); + newPropertyName = _propertyName + ": " + fixLabel(dictKeyString); + insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, dictValues[i]); + } + + free(dictKeys); + free(dictValues); + } + break; + + /* Multivalue */ + case kABMultiIntegerProperty: + case kABMultiDateProperty: + case kABMultiStringProperty: + case kABMultiRealProperty: + case kABMultiDataProperty: + case kABMultiDictionaryProperty: + case kABMultiArrayProperty: + { + /* All scalar multivalues are handled in the same way. Each element + * is a label and a value. All labels are strings + * (kABStringProperty), and all values have the same type + * (which is the type of the multivalue minus 255, or as + * Carbon's list of property types has it, minus 0x100. + * We just get the correct type, then go through each element + * and get the label and value and print them in a list. + */ + + sal_Int32 i; + sal_Int32 multiLength = ABMultiValueCount(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))); + CFStringRef multiLabel, localizedMultiLabel; + CFTypeRef multiValue; + OUString multiLabelString, newPropertyName; + ABPropertyType multiType = static_cast<ABPropertyType>(ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))) - 0x100); + + /* Go through each element... */ + for(i = 0; i < multiLength; i++) + { + /* Label and value */ + multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i); + multiValue = ABMultiValueCopyValueAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i); + + localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); + multiLabelString = CFStringToOUString(localizedMultiLabel); + newPropertyName = _propertyName + ": " + fixLabel(multiLabelString); + insertPropertyIntoMacabRecord(multiType, _abrecord, _header, newPropertyName, multiValue); + + /* free our variables */ + CFRelease(multiLabel); + CFRelease(localizedMultiLabel); + CFRelease(multiValue); + } + } + break; + + /* Unhandled types */ + case kABErrorInProperty: + case kABDataProperty: + default: + /* An error, as far as I have seen, only shows up as a type + * returned by a function for dictionaries when the dictionary + * holds many types of values. Since we do not use that function, + * it shouldn't come up. I have yet to see the kABDataProperty, + * and I am not sure how to represent it as a string anyway, + * since it appears to just be a bunch of bytes. Assumably, if + * these bytes made up a string, the type would be + * kABStringProperty. I think that this is used when we are not + * sure what the type is (e.g., it could be a string or a number). + * That being the case, I still don't know how to represent it. + * And, default should never come up, since we've exhausted all + * of the possible types for ABPropertyType, but... just in case. + */ + break; + } + +} + + +ABPropertyType MacabRecords::getABTypeFromCFType(const CFTypeID cf_type ) const +{ + for(auto const & i: lcl_CFTypes) + { + /* A match! */ + if(i.cf == cf_type) + { + return static_cast<ABPropertyType>(i.ab); + } + } + return kABErrorInProperty; +} + + +sal_Int32 MacabRecords::size() const +{ + return currentRecord; +} + + +MacabRecords *MacabRecords::begin() +{ + return this; +} + + +MacabRecords::iterator::iterator () +{ +} + + +MacabRecords::iterator::~iterator () +{ +} + + +MacabRecords::iterator& MacabRecords::iterator::operator= (MacabRecords *_records) +{ + id = 0; + records = _records; + return *this; +} + + +void MacabRecords::iterator::operator++ () +{ + id++; +} + + +bool MacabRecords::iterator::operator!= (const sal_Int32 i) const +{ + return(id != i); +} + + +bool MacabRecords::iterator::operator== (const sal_Int32 i) const +{ + return(id == i); +} + + +MacabRecord *MacabRecords::iterator::operator* () const +{ + return records->getRecord(id); +} + + +sal_Int32 MacabRecords::end() const +{ + return currentRecord; +} + + +void MacabRecords::swap(const sal_Int32 _id1, const sal_Int32 _id2) +{ + MacabRecord *swapRecord = records[_id1]; + + records[_id1] = records[_id2]; + records[_id2] = swapRecord; +} + + +void MacabRecords::setName(const OUString& _sName) +{ + m_sName = _sName; +} + + +OUString const & MacabRecords::getName() const +{ + return m_sName; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabRecords.hxx b/connectivity/source/drivers/macab/MacabRecords.hxx new file mode 100644 index 000000000..1e7d9975d --- /dev/null +++ b/connectivity/source/drivers/macab/MacabRecords.hxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRECORDS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRECORDS_HXX + +#include <sal/config.h> + +#include <vector> + +#include "MacabRecord.hxx" +#include "MacabHeader.hxx" + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <AddressBook/ABAddressBookC.h> +#include <postmac.h> +#include <com/sun/star/util/DateTime.hpp> + +namespace connectivity +{ + namespace macab + { + /* This struct is for converting CF types to AB types (Core Foundation + * types to Address Book types). + */ + struct lcl_CFType { + CFTypeID cf; + sal_Int32 ab; + }; + + class MacabRecords{ + protected: + /* MacabRecords is, at its core, a table of macabfields, in the + * form of a header and a list of objects of type MacabRecord. + * It also has a unique name that refers to the name of the table. + */ + sal_Int32 recordsSize; + sal_Int32 currentRecord; + CFStringRef recordType; + MacabHeader *header; + MacabRecord **records; + ABAddressBookRef addressBook; + OUString m_sName; + + /* For converting CF types to AB types */ + std::vector<lcl_CFType> lcl_CFTypes; + + /* For required properties */ + std::vector<CFStringRef> requiredProperties; + + private: + /* All of the private methods are for creating a MacabHeader or a + * MacabRecord. They are used by the initialize method that goes + * about filling a MacabRecords using all of the records in the + * macOS Address Book. + */ + void bootstrap_CF_types(); + void bootstrap_requiredProperties(); + MacabHeader *createHeaderForProperty(const ABRecordRef _record, const CFStringRef _propertyName, const CFStringRef _recordType, const bool _isPropertyRequired) const; + MacabHeader *createHeaderForProperty(const ABPropertyType _propertyType, const CFTypeRef _propertyValue, const CFStringRef _propertyName) const; + ABPropertyType getABTypeFromCFType(const CFTypeID cf_type ) const; + void insertPropertyIntoMacabRecord(MacabRecord *_abrecord, const MacabHeader *_header, const OUString& _propertyName, const CFTypeRef _propertyValue) const; + void insertPropertyIntoMacabRecord(const ABPropertyType _propertyType, MacabRecord *_abrecord, const MacabHeader *_header, const OUString& _propertyName, const CFTypeRef _propertyValue) const; + public: + MacabRecords(const ABAddressBookRef _addressBook, MacabHeader *_header, MacabRecord **_records, sal_Int32 _numRecords); + explicit MacabRecords(const MacabRecords *_copy); + explicit MacabRecords(const ABAddressBookRef _addressBook); + ~MacabRecords(); + + void initialize(); + + void setHeader(MacabHeader *_header); + MacabHeader *getHeader() const; + + void setName(const OUString& _sName); + OUString const & getName() const; + + MacabRecord *insertRecord(MacabRecord *_newRecord, const sal_Int32 _location); + void insertRecord(MacabRecord *_newRecord); + MacabRecord *getRecord(const sal_Int32 _location) const; + void swap(const sal_Int32 _id1, const sal_Int32 _id2); + + macabfield *getField(const sal_Int32 _recordNumber, const sal_Int32 _columnNumber) const; + macabfield *getField(const sal_Int32 _recordNumber, const OUString& _columnName) const; + sal_Int32 getFieldNumber(const OUString& _columnName) const; + + sal_Int32 size() const; + + MacabHeader *createHeaderForRecordType(const CFArrayRef _records, const CFStringRef _recordType) const; + MacabRecord *createMacabRecord(const ABRecordRef _abrecord, const MacabHeader *_header, const CFStringRef _recordType) const; + + MacabRecords *begin(); + sal_Int32 end() const; + class iterator{ + protected: + MacabRecords *records; + public: + sal_Int32 id; + iterator& operator= (MacabRecords *_records); + iterator(); + ~iterator(); + void operator++ (); + bool operator!= (const sal_Int32 i) const; + bool operator== (const sal_Int32 i) const; + MacabRecord *operator* () const; + }; + + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRECORDS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabResultSet.cxx b/connectivity/source/drivers/macab/MacabResultSet.cxx new file mode 100644 index 000000000..7b0911223 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabResultSet.cxx @@ -0,0 +1,1074 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabResultSet.hxx" +#include "MacabAddressBook.hxx" +#include "MacabRecords.hxx" +#include "macabutilities.hxx" +#include "MacabResultSetMetaData.hxx" +#include "MacabConnection.hxx" +#include "macabcondition.hxx" +#include "macaborder.hxx" +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <TConnection.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <resource/sharedresources.hxx> +#include <strings.hrc> + +using namespace connectivity::macab; +using namespace cppu; +using namespace css::uno; +using namespace css::lang; +using namespace css::beans; +using namespace css::sdbc; +using namespace css::sdbcx; +using namespace css::io; +using namespace css::util; + +IMPLEMENT_SERVICE_INFO(MacabResultSet, "com.sun.star.sdbc.drivers.MacabResultSet", "com.sun.star.sdbc.ResultSet"); + +MacabResultSet::MacabResultSet(MacabCommonStatement* pStmt) + : MacabResultSet_BASE(m_aMutex), + OPropertySetHelper(MacabResultSet_BASE::rBHelper), + m_xStatement(pStmt), + m_aMacabRecords(), + m_nRowPos(-1), + m_bWasNull(true), + m_sTableName(MacabAddressBook::getDefaultTableName()) +{ +} + +MacabResultSet::~MacabResultSet() +{ +} + +void MacabResultSet::allMacabRecords() +{ + MacabConnection* pConnection = static_cast< MacabConnection *>(m_xStatement->getConnection().get()); + + m_aMacabRecords = pConnection->getAddressBook()->getMacabRecords(m_sTableName); +} + +void MacabResultSet::someMacabRecords(const MacabCondition *pCondition) +{ + MacabConnection* pConnection = static_cast< MacabConnection *>(m_xStatement->getConnection().get()); + MacabRecords* allRecords; + + allRecords = pConnection->getAddressBook()->getMacabRecords(m_sTableName); + + // Bad table!! Throw exception? + if(allRecords == nullptr) + return; + + if(m_aMacabRecords != nullptr && m_aMacabRecords != allRecords) + delete m_aMacabRecords; + + // The copy constructor copies everything but records (including the + // maximum allocated size, which means that we'll never have to resize) + m_aMacabRecords = new MacabRecords(allRecords); + + if(pCondition->isAlwaysFalse()) + { + return; + } + + MacabRecords::iterator iterator; + + for (iterator = allRecords->begin(); + iterator != allRecords->end(); + ++iterator) + { + if (pCondition->eval(*iterator)) + m_aMacabRecords->insertRecord(*iterator); + } +} + +void MacabResultSet::sortMacabRecords(const MacabOrder *pOrder) +{ + // I do this with ints rather than an iterator because the ids will + // be changing when we change the order and ints are easier to deal + // with (for me). + sal_Int32 i, j, size, smallest; + size = m_aMacabRecords->size(); + + for(i = 0; i < size; i++) + { + smallest = i; + for( j = i + 1; j < size; j++) + { + // if smallest > j + if(pOrder->compare(m_aMacabRecords->getRecord(smallest), + m_aMacabRecords->getRecord(j) ) > 0) + { + smallest = j; + } + + } + + if(smallest != i) + { + m_aMacabRecords->swap(i,smallest); + } + } + +} + +void MacabResultSet::setTableName(OUString const & _sTableName) +{ + m_sTableName = _sTableName; +} + +void MacabResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + + m_xStatement.clear(); + m_xMetaData.clear(); +} + +Any SAL_CALL MacabResultSet::queryInterface(const Type & rType) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + if (!aRet.hasValue()) + aRet = MacabResultSet_BASE::queryInterface(rType); + return aRet; +} + +void SAL_CALL MacabResultSet::acquire() throw() +{ + MacabResultSet_BASE::acquire(); +} + +void SAL_CALL MacabResultSet::release() throw() +{ + MacabResultSet_BASE::release(); +} + +Sequence< Type > SAL_CALL MacabResultSet::getTypes() +{ + 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(), MacabResultSet_BASE::getTypes()); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL MacabResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +sal_Int32 SAL_CALL MacabResultSet::findColumn(const OUString& columnName) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + // find the first column with the name columnName + Reference< XResultSetMetaData > xMeta = getMetaData(); + sal_Int32 nLen = xMeta->getColumnCount(); + + for (sal_Int32 i = 1; 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 +} + +OUString SAL_CALL MacabResultSet::getString(sal_Int32 columnIndex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + OUString aRet; + sal_Int32 nRecords = m_aMacabRecords->size(); + m_bWasNull = true; + + if (m_nRowPos != -1 && m_nRowPos != nRecords && m_xMetaData.is()) + { + sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(columnIndex); + macabfield *aField = m_aMacabRecords->getField(m_nRowPos,nFieldNumber); + if(aField != nullptr) + { + if(aField->type == kABStringProperty) + { + aRet = CFStringToOUString(static_cast<CFStringRef>(aField->value)); + m_bWasNull = false; + } + } + } + +// Trigger an exception if m_bWasNull is true? + return aRet; +} + +sal_Bool SAL_CALL MacabResultSet::getBoolean(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getBoolean", nullptr); + + return false; +} + +sal_Int8 SAL_CALL MacabResultSet::getByte(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getByte", nullptr); + + return 0; +} + +sal_Int16 SAL_CALL MacabResultSet::getShort(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getShort", nullptr); + + return 0; +} + +sal_Int32 SAL_CALL MacabResultSet::getInt(sal_Int32 columnIndex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nRet = 0; + sal_Int32 nRecords = m_aMacabRecords->size(); + m_bWasNull = true; + + if (m_nRowPos != -1 && m_nRowPos != nRecords && m_xMetaData.is()) + { + sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(columnIndex); + macabfield *aField = m_aMacabRecords->getField(m_nRowPos,nFieldNumber); + if(aField != nullptr) + { + if(aField->type == kABIntegerProperty) + { + CFNumberType numberType = CFNumberGetType( static_cast<CFNumberRef>(aField->value) ); + // m_bWasNull now becomes whether getting the value was successful + // Should we check for the wrong type here, e.g., a float or a 64 bit int? + m_bWasNull = !CFNumberGetValue(static_cast<CFNumberRef>(aField->value), numberType, &nRet); + } + } + } + +// Trigger an exception if m_bWasNull is true? + return nRet; +} + +sal_Int64 SAL_CALL MacabResultSet::getLong(sal_Int32 columnIndex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int64 nRet = 0; + sal_Int32 nRecords = m_aMacabRecords->size(); + m_bWasNull = true; + + if (m_nRowPos != -1 && m_nRowPos != nRecords && m_xMetaData.is()) + { + sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(columnIndex); + macabfield *aField = m_aMacabRecords->getField(m_nRowPos,nFieldNumber); + if(aField != nullptr) + { + if(aField->type == kABIntegerProperty) + { + CFNumberType numberType = CFNumberGetType( static_cast<CFNumberRef>(aField->value) ); + // m_bWasNull now becomes whether getting the value was successful + // Should we check for the wrong type here, e.g., a float or a 32 bit int? + m_bWasNull = !CFNumberGetValue(static_cast<CFNumberRef>(aField->value), numberType, &nRet); + } + } + } + +// Trigger an exception if m_bWasNull is true? + return nRet; +} + +float SAL_CALL MacabResultSet::getFloat(sal_Int32 columnIndex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + float nVal = 0; + sal_Int32 nRecords = m_aMacabRecords->size(); + m_bWasNull = true; + + if (m_nRowPos != -1 && m_nRowPos != nRecords && m_xMetaData.is()) + { + sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(columnIndex); + macabfield *aField = m_aMacabRecords->getField(m_nRowPos,nFieldNumber); + if(aField != nullptr) + { + if(aField->type == kABRealProperty) + { + CFNumberType numberType = CFNumberGetType( static_cast<CFNumberRef>(aField->value) ); + // m_bWasNull now becomes whether getting the value was successful + // Should we check for the wrong type here, e.g., an int or a double? + m_bWasNull = !CFNumberGetValue(static_cast<CFNumberRef>(aField->value), numberType, &nVal); + } + } + } + +// Trigger an exception if m_bWasNull is true? + return nVal; +} + +double SAL_CALL MacabResultSet::getDouble(sal_Int32 columnIndex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + double nVal = 0; + sal_Int32 nRecords = m_aMacabRecords->size(); + m_bWasNull = true; + + if (m_nRowPos != -1 && m_nRowPos != nRecords && m_xMetaData.is()) + { + sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(columnIndex); + macabfield *aField = m_aMacabRecords->getField(m_nRowPos,nFieldNumber); + if(aField != nullptr) + { + if(aField->type == kABRealProperty) + { + CFNumberType numberType = CFNumberGetType( static_cast<CFNumberRef>(aField->value) ); + // m_bWasNull now becomes whether getting the value was successful + // Should we check for the wrong type here, e.g., an int or a float? + m_bWasNull = !CFNumberGetValue(static_cast<CFNumberRef>(aField->value), numberType, &nVal); + } + } + } + +// Trigger an exception if m_bWasNull is true? + return nVal; +} + +Sequence< sal_Int8 > SAL_CALL MacabResultSet::getBytes(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getBytes", nullptr); + + return Sequence< sal_Int8 >(); +} + +Date SAL_CALL MacabResultSet::getDate(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getDate", nullptr); + + Date aRet; + return aRet; +} + +Time SAL_CALL MacabResultSet::getTime(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getTime", nullptr); + + css::util::Time nRet; + return nRet; +} + +DateTime SAL_CALL MacabResultSet::getTimestamp(sal_Int32 columnIndex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + DateTime nRet; + sal_Int32 nRecords = m_aMacabRecords->size(); + m_bWasNull = true; + + if (m_nRowPos != -1 && m_nRowPos != nRecords && m_xMetaData.is()) + { + sal_Int32 nFieldNumber = m_xMetaData->fieldAtColumn(columnIndex); + macabfield *aField = m_aMacabRecords->getField(m_nRowPos,nFieldNumber); + if(aField != nullptr) + { + if(aField->type == kABDateProperty) + { + nRet = CFDateToDateTime(static_cast<CFDateRef>(aField->value)); + m_bWasNull = false; + } + } + } + +// Trigger an exception if m_bWasNull is true? + return nRet; +} + +Reference< XInputStream > SAL_CALL MacabResultSet::getBinaryStream(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getBinaryStream", nullptr); + + return nullptr; +} + +Reference< XInputStream > SAL_CALL MacabResultSet::getCharacterStream(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getCharacterStream", nullptr); + + return nullptr; +} + +Any SAL_CALL MacabResultSet::getObject(sal_Int32, const Reference< css::container::XNameAccess >&) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getObject", nullptr); + + return Any(); +} + +Reference< XRef > SAL_CALL MacabResultSet::getRef(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getRef", nullptr); + + return nullptr; +} + +Reference< XBlob > SAL_CALL MacabResultSet::getBlob(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getBlob", nullptr); + + return nullptr; +} + +Reference< XClob > SAL_CALL MacabResultSet::getClob(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getClob", nullptr); + + return nullptr; +} + +Reference< XArray > SAL_CALL MacabResultSet::getArray(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException("getArray", nullptr); + + return nullptr; +} + +Reference< XResultSetMetaData > SAL_CALL MacabResultSet::getMetaData() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + if (!m_xMetaData.is()) + m_xMetaData = new MacabResultSetMetaData(m_xStatement->getOwnConnection(), m_sTableName); + + Reference< XResultSetMetaData > xMetaData = m_xMetaData.get(); + return xMetaData; +} + +sal_Bool SAL_CALL MacabResultSet::isBeforeFirst() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + if (m_nRowPos == -1) + return true; + + return false; +} + +sal_Bool SAL_CALL MacabResultSet::isAfterLast() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nRecords = m_aMacabRecords->size(); + if (m_nRowPos == nRecords) + return true; + + return false; +} + +sal_Bool SAL_CALL MacabResultSet::isFirst() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + if (m_nRowPos == 0) + return true; + + return false; +} + +sal_Bool SAL_CALL MacabResultSet::isLast() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nRecords = m_aMacabRecords->size(); + if (m_nRowPos == nRecords - 1) + return true; + + return false; +} + +void SAL_CALL MacabResultSet::beforeFirst() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + // move before the first row + m_nRowPos = -1; +} + +void SAL_CALL MacabResultSet::afterLast() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + // move after the last row + sal_Int32 nRecords = m_aMacabRecords->size(); + m_nRowPos = nRecords; +} + +void SAL_CALL MacabResultSet::close() +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + } + dispose(); +} + +sal_Bool SAL_CALL MacabResultSet::first() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nRecords = m_aMacabRecords->size(); + if (nRecords == 0) + return false; + + m_nRowPos = 0; + return true; +} + +sal_Bool SAL_CALL MacabResultSet::last() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nRecords = m_aMacabRecords->size(); + if (nRecords == 0) + return false; + + m_nRowPos = nRecords - 1; + return true; +} + +sal_Int32 SAL_CALL MacabResultSet::getRow() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return m_nRowPos; +} + +sal_Bool SAL_CALL MacabResultSet::absolute(sal_Int32 row) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nRecords = m_aMacabRecords->size(); + if (row <= -1 || + row >= nRecords) + return false; + + m_nRowPos = row; + return true; +} + +sal_Bool SAL_CALL MacabResultSet::relative(sal_Int32 row) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return absolute(m_nRowPos + row); +} + +sal_Bool SAL_CALL MacabResultSet::next() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return absolute(m_nRowPos + 1); +} + +sal_Bool SAL_CALL MacabResultSet::previous() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return absolute(m_nRowPos - 1); +} + +Reference< XInterface > SAL_CALL MacabResultSet::getStatement() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + Reference< XStatement > xStatement = m_xStatement.get(); + return xStatement; +} + +sal_Bool SAL_CALL MacabResultSet::rowDeleted() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL MacabResultSet::rowInserted() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL MacabResultSet::rowUpdated() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL MacabResultSet::wasNull() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return m_bWasNull; +} + +void SAL_CALL MacabResultSet::cancel() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::clearWarnings() +{ +} + +Any SAL_CALL MacabResultSet::getWarnings() +{ + return Any(); +} + +void SAL_CALL MacabResultSet::insertRow() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + // you only have to implement this if you want to insert new rows +} + +void SAL_CALL MacabResultSet::updateRow() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + // only when you allow updates +} + +void SAL_CALL MacabResultSet::deleteRow() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::cancelRowUpdates() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::moveToInsertRow() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + // only when you allow inserts +} + +void SAL_CALL MacabResultSet::moveToCurrentRow() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateNull(sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateBoolean(sal_Int32, sal_Bool) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateByte(sal_Int32, sal_Int8) +{ + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); +} + +void SAL_CALL MacabResultSet::updateShort(sal_Int32, sal_Int16) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateInt(sal_Int32, sal_Int32) +{ + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); +} + +void SAL_CALL MacabResultSet::updateLong(sal_Int32, sal_Int64) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateFloat(sal_Int32, float) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateDouble(sal_Int32, double) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateString(sal_Int32, const OUString&) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateBytes(sal_Int32, const Sequence< sal_Int8 >&) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateDate(sal_Int32, const Date&) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateTime(sal_Int32, const css::util::Time&) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateTimestamp(sal_Int32, const DateTime&) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateBinaryStream(sal_Int32, const Reference< XInputStream >&, sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateCharacterStream(sal_Int32, const Reference< XInputStream >&, sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::refreshRow() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateObject(sal_Int32, const Any&) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL MacabResultSet::updateNumericObject(sal_Int32, const Any&, sal_Int32) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); +} + +// XRowLocate +Any SAL_CALL MacabResultSet::getBookmark() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nRecords = m_aMacabRecords->size(); + + if (m_nRowPos != -1 && m_nRowPos != nRecords) + { + macabfield *uidField = m_aMacabRecords->getField(m_nRowPos,OUString("UID")); + if(uidField != nullptr) + { + if(uidField->type == kABStringProperty) + { + return makeAny(CFStringToOUString( static_cast<CFStringRef>(uidField->value) )); + } + } + } + return Any(); +} + +sal_Bool SAL_CALL MacabResultSet::moveToBookmark(const Any& bookmark) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + OUString sBookmark = comphelper::getString(bookmark); + sal_Int32 nRecords = m_aMacabRecords->size(); + + for (sal_Int32 nRow = 0; nRow < nRecords; nRow++) + { + macabfield *uidField = m_aMacabRecords->getField(m_nRowPos,OUString("UID")); + if(uidField != nullptr) + { + if(uidField->type == kABStringProperty) + { + OUString sUniqueIdentifier = CFStringToOUString( static_cast<CFStringRef>(uidField->value) ); + if (sUniqueIdentifier == sBookmark) + { + m_nRowPos = nRow; + return true; + } + } + } + } + return false; +} + +sal_Bool SAL_CALL MacabResultSet::moveRelativeToBookmark(const Any& bookmark, sal_Int32 rows) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nRowSave = m_nRowPos; + + if (moveToBookmark(bookmark)) + { + sal_Int32 nRecords = m_aMacabRecords->size(); + + m_nRowPos += rows; + + if (-1 < m_nRowPos && m_nRowPos < nRecords) + return true; + } + + m_nRowPos = nRowSave; + return false; +} + +sal_Int32 SAL_CALL MacabResultSet::compareBookmarks(const Any& firstItem, const Any& secondItem) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + OUString sFirst = comphelper::getString(firstItem); + OUString sSecond = comphelper::getString(secondItem); + + if (sFirst < sSecond) + return CompareBookmark::LESS; + if (sFirst > sSecond) + return CompareBookmark::GREATER; + return CompareBookmark::EQUAL; +} + +sal_Bool SAL_CALL MacabResultSet::hasOrderedBookmarks() +{ + return false; +} + +sal_Int32 SAL_CALL MacabResultSet::hashBookmark(const Any& bookmark) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + OUString sBookmark = comphelper::getString(bookmark); + + return sBookmark.hashCode(); +} + +// XDeleteRows +Sequence< sal_Int32 > SAL_CALL MacabResultSet::deleteRows(const Sequence< Any >&) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(MacabResultSet_BASE::rBHelper.bDisposed); + + return Sequence< sal_Int32 >(); +} + +IPropertyArrayHelper* MacabResultSet::createArrayHelper() const +{ + Sequence< Property > aProps(6); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), + PROPERTY_ID_ISBOOKMARKABLE, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + return new OPropertyArrayHelper(aProps); +} + +IPropertyArrayHelper & MacabResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool MacabResultSet::convertFastPropertyValue( + Any &, + Any &, + sal_Int32 nHandle, + const Any& ) +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + break; + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + default: + ; + } + return false; +} + +void MacabResultSet::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const Any& ) +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw Exception("cannot set prop " + OUString::number(nHandle), nullptr); + break; + case PROPERTY_ID_FETCHDIRECTION: + break; + case PROPERTY_ID_FETCHSIZE: + break; + default: + ; + } +} + +void MacabResultSet::getFastPropertyValue( + Any& _rValue, + sal_Int32 nHandle) const +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + _rValue <<= false; + break; + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + ; + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabResultSet.hxx b/connectivity/source/drivers/macab/MacabResultSet.hxx new file mode 100644 index 000000000..418a66c1f --- /dev/null +++ b/connectivity/source/drivers/macab/MacabResultSet.hxx @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRESULTSET_HXX + +#include "MacabStatement.hxx" +#include "MacabResultSetMetaData.hxx" +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdbcx/XDeleteRows.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> + +namespace connectivity +{ + namespace macab + { + /* + ** MacabResultSet + */ + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XResultSet, + css::sdbc::XRow, + css::sdbc::XResultSetMetaDataSupplier, + css::util::XCancellable, + css::sdbc::XWarningsSupplier, + css::sdbc::XResultSetUpdate, + css::sdbc::XRowUpdate, + css::sdbcx::XRowLocate, + css::sdbcx::XDeleteRows, + css::sdbc::XCloseable, + css::sdbc::XColumnLocate, + css::lang::XServiceInfo> MacabResultSet_BASE; + class MacabRecords; + + class MacabResultSet : public cppu::BaseMutex, + public MacabResultSet_BASE, + public ::cppu::OPropertySetHelper, + public comphelper::OPropertyArrayUsageHelper<MacabResultSet> + { + protected: + ::rtl::Reference< MacabCommonStatement > m_xStatement; // the statement that has created this result set + ::rtl::Reference< MacabResultSetMetaData > m_xMetaData; // the description of the columns in this result set + MacabRecords * m_aMacabRecords; // address book entries matching the query + sal_Int32 m_nRowPos; // the current row within the result set + bool m_bWasNull; // last entry retrieved from this result set was NULL + OUString m_sTableName; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle) const override; + + // you can't delete objects of this type + virtual ~MacabResultSet() override; + + public: + DECLARE_SERVICE_INFO(); + + explicit MacabResultSet(MacabCommonStatement *pStmt); + + css::uno::Reference< css::uno::XInterface > operator *() + { + return css::uno::Reference< css::uno::XInterface >(*static_cast<MacabResultSet_BASE*>(this)); + } + + void allMacabRecords(); + void someMacabRecords(const class MacabCondition *pCondition); + void sortMacabRecords(const class MacabOrder *pOrder); + void setTableName(const OUString& _sTableName); + + // ::cppu::OComponentHelper + virtual void SAL_CALL disposing() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XResultSet + virtual sal_Bool SAL_CALL isBeforeFirst( ) override; + virtual sal_Bool SAL_CALL isAfterLast( ) override; + virtual sal_Bool SAL_CALL isFirst( ) override; + virtual sal_Bool SAL_CALL isLast( ) override; + virtual void SAL_CALL beforeFirst( ) override; + virtual void SAL_CALL afterLast( ) override; + virtual sal_Bool SAL_CALL first( ) override; + virtual sal_Bool SAL_CALL last( ) override; + virtual sal_Int32 SAL_CALL getRow( ) override; + virtual sal_Bool SAL_CALL absolute( sal_Int32 row ) override; + virtual sal_Bool SAL_CALL relative( sal_Int32 rows ) override; + virtual sal_Bool SAL_CALL next( ) override; + virtual sal_Bool SAL_CALL previous( ) override; + virtual void SAL_CALL refreshRow( ) override; + virtual sal_Bool SAL_CALL rowUpdated( ) override; + virtual sal_Bool SAL_CALL rowInserted( ) override; + virtual sal_Bool SAL_CALL rowDeleted( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getStatement( ) override; + + // XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + + // XCancellable + virtual void SAL_CALL cancel( ) override; + + // XCloseable + virtual void SAL_CALL close( ) override; + + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + + // XResultSetUpdate + virtual void SAL_CALL insertRow( ) override; + virtual void SAL_CALL updateRow( ) override; + virtual void SAL_CALL deleteRow( ) override; + virtual void SAL_CALL cancelRowUpdates( ) override; + virtual void SAL_CALL moveToInsertRow( ) override; + virtual void SAL_CALL moveToCurrentRow( ) override; + // XRowUpdate + virtual void SAL_CALL updateNull( sal_Int32 columnIndex ) override; + virtual void SAL_CALL updateBoolean( sal_Int32 columnIndex, sal_Bool x ) override; + virtual void SAL_CALL updateByte( sal_Int32 columnIndex, sal_Int8 x ) override; + virtual void SAL_CALL updateShort( sal_Int32 columnIndex, sal_Int16 x ) override; + virtual void SAL_CALL updateInt( sal_Int32 columnIndex, sal_Int32 x ) override; + virtual void SAL_CALL updateLong( sal_Int32 columnIndex, sal_Int64 x ) override; + virtual void SAL_CALL updateFloat( sal_Int32 columnIndex, float x ) override; + virtual void SAL_CALL updateDouble( sal_Int32 columnIndex, double x ) override; + virtual void SAL_CALL updateString( sal_Int32 columnIndex, const OUString& x ) override; + virtual void SAL_CALL updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL updateDate( sal_Int32 columnIndex, const css::util::Date& x ) override; + virtual void SAL_CALL updateTime( sal_Int32 columnIndex, const css::util::Time& x ) override; + virtual void SAL_CALL updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL updateBinaryStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateCharacterStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateObject( sal_Int32 columnIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL updateNumericObject( sal_Int32 columnIndex, const css::uno::Any& x, sal_Int32 scale ) override; + + // XColumnLocate + virtual sal_Int32 SAL_CALL findColumn( const OUString& columnName ) override; + + // XRowLocate + virtual css::uno::Any SAL_CALL getBookmark( ) override; + virtual sal_Bool SAL_CALL moveToBookmark( const css::uno::Any& bookmark ) override; + virtual sal_Bool SAL_CALL moveRelativeToBookmark( const css::uno::Any& bookmark, sal_Int32 rows ) override; + virtual sal_Int32 SAL_CALL compareBookmarks( const css::uno::Any& firstItem, const css::uno::Any& secondItem ) override; + virtual sal_Bool SAL_CALL hasOrderedBookmarks( ) override; + virtual sal_Int32 SAL_CALL hashBookmark( const css::uno::Any& bookmark ) override; + + // XDeleteRows + virtual css::uno::Sequence< sal_Int32 > SAL_CALL deleteRows( const css::uno::Sequence< css::uno::Any >& rows ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRESULTSET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabResultSetMetaData.cxx b/connectivity/source/drivers/macab/MacabResultSetMetaData.cxx new file mode 100644 index 000000000..38e7dca05 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabResultSetMetaData.cxx @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabResultSetMetaData.hxx" +#include "MacabHeader.hxx" +#include "MacabRecords.hxx" +#include "MacabAddressBook.hxx" +#include "macabutilities.hxx" +#include <strings.hrc> + +using namespace connectivity::macab; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; + +MacabResultSetMetaData::MacabResultSetMetaData(MacabConnection* _pConnection, OUString const & _sTableName) + : m_pConnection(_pConnection), + m_sTableName(_sTableName), + m_aMacabFields() +{ +} + +MacabResultSetMetaData::~MacabResultSetMetaData() +{ +} + +void MacabResultSetMetaData::setMacabFields(const ::rtl::Reference<connectivity::OSQLColumns> &xColumns) +{ + static const char aName[] = "Name"; + MacabRecords *aRecords; + MacabHeader *aHeader; + + aRecords = m_pConnection->getAddressBook()->getMacabRecords(m_sTableName); + + // In case, somehow, we don't have anything with the name m_sTableName + if(aRecords == nullptr) + { + impl_throwError(STR_NO_TABLE); + } + + aHeader = aRecords->getHeader(); + + for (const auto& rxColumn : *xColumns) + { + OUString aFieldName; + sal_uInt32 nFieldNumber; + + rxColumn->getPropertyValue(aName) >>= aFieldName; + nFieldNumber = aHeader->getColumnNumber(aFieldName); + m_aMacabFields.push_back(nFieldNumber); + } + +} + +sal_Int32 SAL_CALL MacabResultSetMetaData::getColumnDisplaySize(sal_Int32 /* column */) +{ + // For now, all columns are the same size. + return 50; +} + +sal_Int32 SAL_CALL MacabResultSetMetaData::getColumnType(sal_Int32 column) +{ + MacabRecords *aRecords; + MacabHeader *aHeader; + macabfield *aField; + + aRecords = m_pConnection->getAddressBook()->getMacabRecords(m_sTableName); + + // In case, somehow, we don't have anything with the name m_sTableName + if(aRecords == nullptr) + { + impl_throwError(STR_NO_TABLE); + } + + aHeader = aRecords->getHeader(); + aField = aHeader->get(column-1); + + if(aField == nullptr) + { + ::dbtools::throwInvalidIndexException(*this); + return -1; + } + + return ABTypeToDataType(aField->type); +} + +sal_Int32 SAL_CALL MacabResultSetMetaData::getColumnCount() +{ + return m_aMacabFields.size(); +} + +sal_Bool SAL_CALL MacabResultSetMetaData::isCaseSensitive(sal_Int32) +{ + return true; +} + +OUString SAL_CALL MacabResultSetMetaData::getSchemaName(sal_Int32) +{ + return OUString(); +} + +OUString SAL_CALL MacabResultSetMetaData::getColumnName(sal_Int32 column) +{ + sal_uInt32 nFieldNumber = m_aMacabFields[column - 1]; + MacabRecords *aRecords; + MacabHeader *aHeader; + + aRecords = m_pConnection->getAddressBook()->getMacabRecords(m_sTableName); + + // In case, somehow, we don't have anything with the name m_sTableName + if(aRecords == nullptr) + { + impl_throwError(STR_NO_TABLE); + } + + aHeader = aRecords->getHeader(); + OUString aName = aHeader->getString(nFieldNumber); + + return aName; +} + +OUString SAL_CALL MacabResultSetMetaData::getTableName(sal_Int32) +{ + return m_sTableName; +} + +OUString SAL_CALL MacabResultSetMetaData::getCatalogName(sal_Int32) +{ + return OUString(); +} + +OUString SAL_CALL MacabResultSetMetaData::getColumnTypeName(sal_Int32) +{ + return OUString(); +} + +OUString SAL_CALL MacabResultSetMetaData::getColumnLabel(sal_Int32) +{ + return OUString(); +} + +OUString SAL_CALL MacabResultSetMetaData::getColumnServiceName(sal_Int32) +{ + return OUString(); +} + +sal_Bool SAL_CALL MacabResultSetMetaData::isCurrency(sal_Int32) +{ + return false; +} + +sal_Bool SAL_CALL MacabResultSetMetaData::isAutoIncrement(sal_Int32) +{ + return false; +} + +sal_Bool SAL_CALL MacabResultSetMetaData::isSigned(sal_Int32) +{ + return false; +} + +sal_Int32 SAL_CALL MacabResultSetMetaData::getPrecision(sal_Int32) +{ + return 0; +} + +sal_Int32 SAL_CALL MacabResultSetMetaData::getScale(sal_Int32) +{ + return 0; +} + +sal_Int32 SAL_CALL MacabResultSetMetaData::isNullable(sal_Int32) +{ + return sal_Int32(true); +} + +sal_Bool SAL_CALL MacabResultSetMetaData::isSearchable(sal_Int32) +{ + return true; +} + +sal_Bool SAL_CALL MacabResultSetMetaData::isReadOnly(sal_Int32) +{ + return true; +} + +sal_Bool SAL_CALL MacabResultSetMetaData::isDefinitelyWritable(sal_Int32) +{ + return false; +} + +sal_Bool SAL_CALL MacabResultSetMetaData::isWritable(sal_Int32) +{ + return false; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabResultSetMetaData.hxx b/connectivity/source/drivers/macab/MacabResultSetMetaData.hxx new file mode 100644 index 000000000..ec7c2c651 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabResultSetMetaData.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRESULTSETMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRESULTSETMETADATA_HXX + +#include "MacabConnection.hxx" +#include <connectivity/CommonTools.hxx> +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <cppuhelper/implbase.hxx> +#include <connectivity/dbexception.hxx> +#include <rtl/ref.hxx> + +namespace connectivity +{ + namespace macab + { + /* + ** MacabResultSetMetaData + */ + class MacabResultSetMetaData : public ::cppu::WeakImplHelper< css::sdbc::XResultSetMetaData> + { + MacabConnection* m_pConnection; + OUString m_sTableName; + std::vector<sal_Int32> m_aMacabFields; // for each selected column, contains the number + // of the corresponding AddressBook field + + protected: + virtual ~MacabResultSetMetaData() override; + + public: + MacabResultSetMetaData(MacabConnection* _pConnection, OUString const & _sTableName); + + // avoid ambiguous cast error from the compiler + operator css::uno::Reference< css::sdbc::XResultSetMetaData > () throw() + { return this; } + + /// @throws css::sdbc::SQLException + void setMacabFields( + const ::rtl::Reference<connectivity::OSQLColumns> &xColumns); + sal_uInt32 fieldAtColumn(sal_Int32 columnIndex) const + { return m_aMacabFields[columnIndex - 1]; } + + virtual sal_Int32 SAL_CALL getColumnCount( ) override; + virtual sal_Bool SAL_CALL isAutoIncrement( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCaseSensitive( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSearchable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCurrency( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL isNullable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSigned( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnDisplaySize( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnLabel( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnName( sal_Int32 column ) override; + virtual OUString SAL_CALL getSchemaName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getPrecision( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getScale( sal_Int32 column ) override; + virtual OUString SAL_CALL getTableName( sal_Int32 column ) override; + virtual OUString SAL_CALL getCatalogName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnType( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnTypeName( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isReadOnly( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isWritable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isDefinitelyWritable( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnServiceName( sal_Int32 column ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABRESULTSETMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabServices.cxx b/connectivity/source/drivers/macab/MacabServices.cxx new file mode 100644 index 000000000..a1cfc340c --- /dev/null +++ b/connectivity/source/drivers/macab/MacabServices.cxx @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "MacabDriver.hxx" +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::macab; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* macab_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void*) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + MacabDriver::getImplementationName_Static(), + MacabDriver::getSupportedServiceNames_Static(), + &MacabDriver::Create, + ::cppu::createSingleFactory) + ; + + if (aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabStatement.cxx b/connectivity/source/drivers/macab/MacabStatement.cxx new file mode 100644 index 000000000..db6e3876b --- /dev/null +++ b/connectivity/source/drivers/macab/MacabStatement.cxx @@ -0,0 +1,594 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabStatement.hxx" +#include <sqlbison.hxx> +#include "MacabConnection.hxx" +#include "MacabAddressBook.hxx" +#include "MacabDriver.hxx" +#include "MacabResultSet.hxx" +#include "MacabResultSetMetaData.hxx" +#include "macabcondition.hxx" +#include "macaborder.hxx" +#include "macabutilities.hxx" +#include <TConnection.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <connectivity/dbexception.hxx> +#include <resource/sharedresources.hxx> +#include <strings.hrc> + +using namespace connectivity::macab; +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::io; +using namespace com::sun::star::util; + +namespace connectivity +{ + namespace macab + { + void impl_throwError(const char* pErrorId) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(pErrorId) ); + ::dbtools::throwGenericSQLException(sError,nullptr); + } + } +} + +IMPLEMENT_SERVICE_INFO(MacabStatement, "com.sun.star.sdbc.drivers.MacabStatement", "com.sun.star.sdbc.Statement"); + +MacabCommonStatement::MacabCommonStatement(MacabConnection* _pConnection ) + : MacabCommonStatement_BASE(m_aMutex), + OPropertySetHelper(rBHelper), + m_aParser(_pConnection->getDriver()->getComponentContext()), + m_aSQLIterator(_pConnection, _pConnection->createCatalog()->getTables(), m_aParser ), + m_pParseTree(nullptr), + m_pConnection(_pConnection) +{ + m_pConnection->acquire(); +} + +MacabCommonStatement::~MacabCommonStatement() +{ +} + +void MacabCommonStatement::resetParameters() const +{ +} + +void MacabCommonStatement::getNextParameter(OUString &) const +{ + impl_throwError(STR_PARA_ONLY_PREPARED); +} + +MacabCondition *MacabCommonStatement::analyseWhereClause(const OSQLParseNode *pParseNode) const +{ + if (pParseNode->count() == 3) + { + const OSQLParseNode *pLeft = pParseNode->getChild(0), + *pMiddle = pParseNode->getChild(1), + *pRight = pParseNode->getChild(2); + + // WHERE ( ... ) ? + if (SQL_ISPUNCTUATION(pLeft, "(") && SQL_ISPUNCTUATION(pRight, ")")) + { + return analyseWhereClause(pMiddle); + } + else if (SQL_ISRULE(pParseNode, comparison_predicate)) + { + if (pLeft->isToken() && pRight->isToken()) + { + switch (pMiddle->getNodeType()) + { + case SQLNodeType::Equal: + // WHERE 0 = 1 + return new MacabConditionConstant(pLeft->getTokenValue() == pRight->getTokenValue()); + + case SQLNodeType::NotEqual: + // WHERE 0 <> 1 + // (might not be correct SQL... don't care, handling anyway) + return new MacabConditionConstant(pLeft->getTokenValue() != pRight->getTokenValue()); + + default: + break; + } + } + else if (SQL_ISRULE(pLeft, column_ref)) + { + OUString sColumnName, + sTableRange; + + m_aSQLIterator.getColumnRange(pLeft, sColumnName, sTableRange); + + if (pRight->isToken() || SQL_ISRULE(pRight, parameter)) + { + OUString sMatchString; + + if (pRight->isToken()) // WHERE Name = 'Doe' + sMatchString = pRight->getTokenValue(); + else if (SQL_ISRULE(pRight, parameter)) // WHERE Name = ? + getNextParameter(sMatchString); + + switch (pMiddle->getNodeType()) + { + case SQLNodeType::Equal: + // WHERE Name = 'Smith' + return new MacabConditionEqual(m_pHeader, sColumnName, sMatchString); + + case SQLNodeType::NotEqual: + // WHERE Name <> 'Jones' + return new MacabConditionDifferent(m_pHeader, sColumnName, sMatchString); + + default: + break; + } + } + } + } + else if (SQL_ISRULE(pParseNode, search_condition)) + { + if (SQL_ISTOKEN(pMiddle, OR)) + { + // WHERE Name = 'Smith' OR Name = 'Jones' + return new MacabConditionOr( + analyseWhereClause(pLeft), + analyseWhereClause(pRight)); + } + } + else if (SQL_ISRULE(pParseNode, boolean_term)) + { + if (SQL_ISTOKEN(pMiddle, AND)) + { + // WHERE Name = 'Smith' AND "Given Name" = 'Peter' + return new MacabConditionAnd( + analyseWhereClause(pLeft), + analyseWhereClause(pRight)); + } + } + } + else if (SQL_ISRULE(pParseNode, test_for_null) || SQL_ISRULE(pParseNode, like_predicate)) + { + const OSQLParseNode *pLeft = pParseNode->getChild(0); + const OSQLParseNode* pPart2 = pParseNode->getChild(1); + const OSQLParseNode *pMiddleLeft = pPart2->getChild(0), + *pMiddleRight = pPart2->getChild(1), + *pRight = pPart2->getChild(2); + + if (SQL_ISRULE(pParseNode, test_for_null)) + { + if (SQL_ISRULE(pLeft, column_ref) && + SQL_ISTOKEN(pMiddleLeft, IS) && + SQL_ISTOKEN(pRight, NULL)) + { + OUString sColumnName, + sTableRange; + + m_aSQLIterator.getColumnRange(pLeft, sColumnName, sTableRange); + + if (SQL_ISTOKEN(pMiddleRight, NOT)) + { + // WHERE "Mobile Phone" IS NOT NULL + return new MacabConditionNotNull(m_pHeader, sColumnName); + } + else + { + // WHERE "Mobile Phone" IS NULL + return new MacabConditionNull(m_pHeader, sColumnName); + } + } + } + else if (SQL_ISRULE(pParseNode, like_predicate)) + { + if (SQL_ISRULE(pLeft, column_ref)) + { + OUString sColumnName, + sTableRange; + + m_aSQLIterator.getColumnRange(pLeft, sColumnName, sTableRange); + + if (pMiddleRight->isToken() || SQL_ISRULE(pMiddleRight, parameter)) + { + OUString sMatchString; + + if (pMiddleRight->isToken()) // WHERE Name LIKE 'Sm%' + sMatchString = pMiddleRight->getTokenValue(); + else if (SQL_ISRULE(pMiddleRight, parameter)) // WHERE Name LIKE ? + getNextParameter(sMatchString); + + return new MacabConditionSimilar(m_pHeader, sColumnName, sMatchString); + } + } + } + } + impl_throwError(STR_QUERY_TOO_COMPLEX); + // Unreachable: + OSL_ASSERT(false); + return nullptr; +} + +MacabOrder *MacabCommonStatement::analyseOrderByClause(const OSQLParseNode *pParseNode) const +{ + if (SQL_ISRULE(pParseNode, ordering_spec_commalist)) + { + MacabComplexOrder *list = new MacabComplexOrder(); + sal_uInt32 n = pParseNode->count(); + + // Iterate through the ordering columns + for (sal_uInt32 i = 0; i < n; i++) + { + list->addOrder + (analyseOrderByClause(pParseNode->getChild(i))); + } + + return list; + } + else if (SQL_ISRULE(pParseNode, ordering_spec)) + { + if (pParseNode->count() == 2) + { + OSQLParseNode* pColumnRef = pParseNode->getChild(0); + OSQLParseNode* pAscendingDescending = pParseNode->getChild(1); + + if (SQL_ISRULE(pColumnRef, column_ref)) + { + if (pColumnRef->count() == 3) + pColumnRef = pColumnRef->getChild(2); + + if (pColumnRef->count() == 1) + { + OUString sColumnName = + pColumnRef->getChild(0)->getTokenValue(); + bool bAscending = + !SQL_ISTOKEN(pAscendingDescending, DESC); + + return new MacabSimpleOrder(m_pHeader, sColumnName, bAscending); + } + } + } + } + impl_throwError(STR_QUERY_TOO_COMPLEX); + // Unreachable: + OSL_ASSERT(false); + return nullptr; +} + +OUString MacabCommonStatement::getTableName() const +{ + const OSQLTables& xTabs = m_aSQLIterator.getTables(); + + if( xTabs.empty() ) + return OUString(); + + // can only deal with one table at a time + if(xTabs.size() > 1 || m_aSQLIterator.hasErrors() ) + return OUString(); + + return xTabs.begin()->first; +} + +void MacabCommonStatement::setMacabFields(MacabResultSet *pResult) const +{ + ::rtl::Reference<connectivity::OSQLColumns> xColumns; // selected columns + MacabResultSetMetaData *pMeta; // meta information - holds the list of AddressBook fields + + xColumns = m_aSQLIterator.getSelectColumns(); + if (!xColumns.is()) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString( + STR_INVALID_COLUMN_SELECTION + ) ); + ::dbtools::throwGenericSQLException(sError,nullptr); + } + pMeta = static_cast<MacabResultSetMetaData *>(pResult->getMetaData().get()); + pMeta->setMacabFields(xColumns); +} + +void MacabCommonStatement::selectRecords(MacabResultSet *pResult) const +{ + const OSQLParseNode *pParseNode; + + pParseNode = m_aSQLIterator.getWhereTree(); + if (pParseNode != nullptr) + { + if (SQL_ISRULE(pParseNode, where_clause)) + { + resetParameters(); + pParseNode = pParseNode->getChild(1); + MacabCondition *pCondition = analyseWhereClause(pParseNode); + if (pCondition->isAlwaysTrue()) + pResult->allMacabRecords(); + else + pResult->someMacabRecords(pCondition); + delete pCondition; + return; + } + } + + // no WHERE clause: get all rows + pResult->allMacabRecords(); +} + +void MacabCommonStatement::sortRecords(MacabResultSet *pResult) const +{ + const OSQLParseNode *pParseNode; + + pParseNode = m_aSQLIterator.getOrderTree(); + if (pParseNode != nullptr) + { + if (SQL_ISRULE(pParseNode, opt_order_by_clause)) + { + pParseNode = pParseNode->getChild(2); + MacabOrder *pOrder = analyseOrderByClause(pParseNode); + pResult->sortMacabRecords(pOrder); + delete pOrder; + } + } +} + +Any SAL_CALL MacabCommonStatement::queryInterface( const Type & rType ) +{ + Any aRet = MacabCommonStatement_BASE::queryInterface(rType); + if (!aRet.hasValue()) + aRet = OPropertySetHelper::queryInterface(rType); + return aRet; +} + +Sequence< Type > SAL_CALL MacabCommonStatement::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get()); + + return comphelper::concatSequences(aTypes.getTypes(),MacabCommonStatement_BASE::getTypes()); +} + +void SAL_CALL MacabCommonStatement::cancel( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + checkDisposed(rBHelper.bDisposed); + // cancel the current sql statement +} + +void SAL_CALL MacabCommonStatement::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(rBHelper.bDisposed); + + } + dispose(); +} + +sal_Bool SAL_CALL MacabCommonStatement::execute( + const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(rBHelper.bDisposed); + + Reference< XResultSet > xRS = executeQuery(sql); + + return xRS.is(); +} + +Reference< XResultSet > SAL_CALL MacabCommonStatement::executeQuery( + const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(rBHelper.bDisposed); + + MacabResultSet* pResult = new MacabResultSet(this); + Reference< XResultSet > xRS = pResult; + OUString aErr; + + m_pParseTree = m_aParser.parseTree(aErr, sql).release(); + if (m_pParseTree == nullptr) + throw SQLException(aErr, *this, aErr, 0, Any()); + + m_aSQLIterator.setParseTree(m_pParseTree); + m_aSQLIterator.traverseAll(); + switch (m_aSQLIterator.getStatementType()) + { + case OSQLStatementType::Select: + { + OUString sTableName = getTableName(); // FROM which table ? + if (sTableName.getLength() != 0) // a match + { + MacabRecords *aRecords; + aRecords = m_pConnection->getAddressBook()->getMacabRecords(sTableName); + + // In case, somehow, we don't have anything with the name m_sTableName + if(aRecords == nullptr) + { + impl_throwError(STR_NO_TABLE); + } + else + { + m_pHeader = aRecords->getHeader(); + + pResult->setTableName(sTableName); + + setMacabFields(pResult); // SELECT which columns ? + selectRecords(pResult); // WHERE which condition ? + sortRecords(pResult); // ORDER BY which columns ? + } +// To be continued: DISTINCT +// etc... + } + } + break; + + default: +// To be continued: UPDATE +// DELETE +// etc... + impl_throwError(STR_QUERY_TOO_COMPLEX); + } + + m_xResultSet = Reference<XResultSet>(pResult); + return xRS; +} + +Reference< XConnection > SAL_CALL MacabCommonStatement::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(rBHelper.bDisposed); + + // just return our connection here + return m_pConnection; +} + +sal_Int32 SAL_CALL MacabCommonStatement::executeUpdate( const OUString& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(rBHelper.bDisposed); + + // the return values gives information about how many rows are affected by executing the sql statement + return 0; +} + +Any SAL_CALL MacabCommonStatement::getWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(rBHelper.bDisposed); + + return makeAny(m_aLastWarning); +} + +void SAL_CALL MacabCommonStatement::clearWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(rBHelper.bDisposed); + + m_aLastWarning = SQLWarning(); +} + +::cppu::IPropertyArrayHelper* MacabCommonStatement::createArrayHelper( ) const +{ + // this properties are defined by the service statement + // they must be in alphabetic order + Sequence< Property > aProps(10); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING), + PROPERTY_ID_ESCAPEPROCESSING, cppu::UnoType<bool>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE), + PROPERTY_ID_MAXFIELDSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS), + PROPERTY_ID_MAXROWS, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT), + PROPERTY_ID_QUERYTIMEOUT, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_USEBOOKMARKS), + PROPERTY_ID_USEBOOKMARKS, cppu::UnoType<bool>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + +::cppu::IPropertyArrayHelper & MacabCommonStatement::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool MacabCommonStatement::convertFastPropertyValue( + Any &, + Any &, + sal_Int32, + const Any&) +{ + // here we have to try to convert + return false; +} + +void MacabCommonStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any&) +{ + // set the value to whatever is necessary + switch (nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + case PROPERTY_ID_MAXFIELDSIZE: + case PROPERTY_ID_MAXROWS: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + case PROPERTY_ID_ESCAPEPROCESSING: + case PROPERTY_ID_USEBOOKMARKS: + default: + ; + } +} + +void MacabCommonStatement::getFastPropertyValue(Any&,sal_Int32 nHandle) const +{ + switch (nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + case PROPERTY_ID_MAXFIELDSIZE: + case PROPERTY_ID_MAXROWS: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + case PROPERTY_ID_ESCAPEPROCESSING: + case PROPERTY_ID_USEBOOKMARKS: + default: + ; + } +} + +void SAL_CALL MacabCommonStatement::acquire() throw() +{ + MacabCommonStatement_BASE::acquire(); +} + +void SAL_CALL MacabCommonStatement::release() throw() +{ + MacabCommonStatement_BASE::release(); +} + +Reference< css::beans::XPropertySetInfo > SAL_CALL MacabCommonStatement::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +MacabStatement::MacabStatement(MacabConnection* _pConnection) + : MacabStatement_BASE(_pConnection) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabStatement.hxx b/connectivity/source/drivers/macab/MacabStatement.hxx new file mode 100644 index 000000000..3c8187016 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabStatement.hxx @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABSTATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABSTATEMENT_HXX + +#include "MacabConnection.hxx" +#include "MacabHeader.hxx" +#include <connectivity/sqliterator.hxx> +#include <connectivity/sqlparse.hxx> +#include <com/sun/star/sdbc/XStatement.hpp> +#include <com/sun/star/util/XCancellable.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <comphelper/proparrhlp.hxx> + +namespace connectivity +{ + namespace macab + { + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XStatement, + css::sdbc::XWarningsSupplier, + css::util::XCancellable, + css::sdbc::XCloseable> MacabCommonStatement_BASE; + + + // Class MacabCommonStatement + // is a base class for the normal statement and for the prepared statement + + class MacabCommonStatement : public cppu::BaseMutex, + public MacabCommonStatement_BASE, + public ::cppu::OPropertySetHelper, + public comphelper::OPropertyArrayUsageHelper<MacabCommonStatement> + + { + css::sdbc::SQLWarning m_aLastWarning; + + protected: + connectivity::OSQLParser m_aParser; + connectivity::OSQLParseTreeIterator m_aSQLIterator; + connectivity::OSQLParseNode* m_pParseTree; + MacabConnection* m_pConnection; // The owning Connection object + MacabHeader* m_pHeader; // The header of the address book on which to run queries (provided by m_pConnection) + css::uno::WeakReference< css::sdbc::XResultSet> m_xResultSet; // The last ResultSet created + + + protected: + /// @throws css::sdbc::SQLException + class MacabCondition *analyseWhereClause( + const OSQLParseNode *pParseNode) const; + /// @throws css::sdbc::SQLException + class MacabOrder *analyseOrderByClause( + const OSQLParseNode *pParseNode) const; + OUString getTableName( ) const; + /// @throws css::sdbc::SQLException + void setMacabFields(class MacabResultSet *pResult) const; + /// @throws css::sdbc::SQLException + void selectRecords(MacabResultSet *pResult) const; + /// @throws css::sdbc::SQLException + void sortRecords(MacabResultSet *pResult) const; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle) const override; + + /// @throws css::sdbc::SQLException + virtual void resetParameters() const; + /// @throws css::sdbc::SQLException + virtual void getNextParameter(OUString &rParameter) const; + virtual ~MacabCommonStatement() override; + + public: + using MacabCommonStatement_BASE::rBHelper; + + explicit MacabCommonStatement(MacabConnection *_pConnection); + using MacabCommonStatement_BASE::operator css::uno::Reference< css::uno::XInterface >; + + // OComponentHelper + using MacabCommonStatement_BASE::disposing; + + // XInterface + virtual void SAL_CALL release() throw() override; + virtual void SAL_CALL acquire() throw() override; + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & rType + ) override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( + ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( + ) override; + + // XStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( + const OUString& sql ) override; + virtual sal_Int32 SAL_CALL executeUpdate( + const OUString& sql ) override; + virtual sal_Bool SAL_CALL execute( + const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( + ) override; + + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( + ) override; + virtual void SAL_CALL clearWarnings( + ) override; + + // XCancellable + virtual void SAL_CALL cancel( + ) override; + + // XCloseable + virtual void SAL_CALL close( + ) override; + + // other methods + MacabConnection* getOwnConnection() const { return m_pConnection; } + }; + + + // Class MacabStatement + + typedef ::cppu::ImplInheritanceHelper< + MacabCommonStatement, css::lang::XServiceInfo > MacabStatement_BASE; + + class MacabStatement : public MacabStatement_BASE + { + protected: + virtual ~MacabStatement() override { } + + public: + explicit MacabStatement(MacabConnection* _pConnection); + DECLARE_SERVICE_INFO(); + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABSTATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabTable.cxx b/connectivity/source/drivers/macab/MacabTable.cxx new file mode 100644 index 000000000..75da75bc1 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabTable.cxx @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabTable.hxx" +#include "MacabTables.hxx" +#include "MacabColumns.hxx" +#include "MacabCatalog.hxx" +#include <com/sun/star/sdbc/XRow.hpp> + +using namespace connectivity::macab; +using namespace connectivity; +using namespace ::comphelper; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + + +MacabTable::MacabTable( sdbcx::OCollection* _pTables, MacabConnection* _pConnection) + : MacabTable_TYPEDEF(_pTables, true), + m_pConnection(_pConnection) +{ + construct(); +} + +MacabTable::MacabTable( sdbcx::OCollection* _pTables, + MacabConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description , + const OUString& SchemaName, + const OUString& CatalogName + ) : MacabTable_TYPEDEF(_pTables,true, + Name, + Type, + Description, + SchemaName, + CatalogName), + m_pConnection(_pConnection) +{ + construct(); +} + +void MacabTable::refreshColumns() +{ + ::std::vector< OUString> aVector; + + if (!isNew()) + { + 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 = new MacabColumns(this,m_aMutex,aVector); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabTable.hxx b/connectivity/source/drivers/macab/MacabTable.hxx new file mode 100644 index 000000000..58e43175d --- /dev/null +++ b/connectivity/source/drivers/macab/MacabTable.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABTABLE_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABTABLE_HXX + +#include "MacabConnection.hxx" +#include <connectivity/sdbcx/VTable.hxx> + +namespace connectivity +{ + namespace macab + { + typedef connectivity::sdbcx::OTable MacabTable_TYPEDEF; + + class MacabTable : public MacabTable_TYPEDEF + { + css::uno::Reference< css::sdbc::XDatabaseMetaData > m_xMetaData; + MacabConnection* m_pConnection; + + public: + MacabTable( sdbcx::OCollection* _pTables, MacabConnection* _pConnection); + MacabTable( sdbcx::OCollection* _pTables, + MacabConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description = OUString(), + const OUString& SchemaName = OUString(), + const OUString& CatalogName = OUString() + ); + + MacabConnection* getConnection() { return m_pConnection;} + + virtual void refreshColumns() override; + + OUString const & getTableName() const { return m_Name; } + OUString const & getSchema() const { return m_SchemaName; } + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABTABLE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabTables.cxx b/connectivity/source/drivers/macab/MacabTables.cxx new file mode 100644 index 000000000..c70aa87f8 --- /dev/null +++ b/connectivity/source/drivers/macab/MacabTables.cxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MacabTables.hxx" +#include "MacabTable.hxx" +#include "MacabCatalog.hxx" +#include "MacabConnection.hxx" +#include <comphelper/types.hxx> +#include <com/sun/star/sdbc/XRow.hpp> + +using namespace connectivity::macab; +using namespace connectivity; +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +sdbcx::ObjectType MacabTables::createObject(const OUString& _rName) +{ + OUString aName,aSchema; + aSchema = "%"; + aName = _rName; + + Sequence< OUString > aTypes { "%" }; + + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(), aSchema, aName, aTypes); + + sdbcx::ObjectType xRet; + if (xResult.is()) + { + Reference< XRow > xRow(xResult, UNO_QUERY); + if (xResult->next()) // there can be only one table with this name + { + MacabTable* pRet = new MacabTable( + this, + static_cast<MacabCatalog&>(m_rParent).getConnection(), + aName, + xRow->getString(4), + xRow->getString(5), + ""); + xRet = pRet; + } + } + ::comphelper::disposeComponent(xResult); + + return xRet; +} + +void MacabTables::impl_refresh( ) +{ + static_cast<MacabCatalog&>(m_rParent).refreshTables(); +} + +void MacabTables::disposing() +{ + m_xMetaData.clear(); + OCollection::disposing(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/MacabTables.hxx b/connectivity/source/drivers/macab/MacabTables.hxx new file mode 100644 index 000000000..7e87a473a --- /dev/null +++ b/connectivity/source/drivers/macab/MacabTables.hxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABTABLES_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABTABLES_HXX + +#include <connectivity/sdbcx/VCollection.hxx> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> + +namespace connectivity +{ + namespace macab + { + class MacabTables : public sdbcx::OCollection + { + css::uno::Reference< css::sdbc::XDatabaseMetaData > m_xMetaData; + + protected: + virtual sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override; + + public: + MacabTables( + const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _rMetaData, + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector) + : sdbcx::OCollection(_rParent,true,_rMutex,_rVector), + m_xMetaData(_rMetaData) + { } + + virtual void disposing() override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABTABLES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/macab1.component b/connectivity/source/drivers/macab/macab1.component new file mode 100644 index 000000000..84d17a9cb --- /dev/null +++ b/connectivity/source/drivers/macab/macab1.component @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="macab" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.macab.Driver"> + <service name="com.sun.star.sdbc.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/macab/macabcondition.cxx b/connectivity/source/drivers/macab/macabcondition.cxx new file mode 100644 index 000000000..0d1d20227 --- /dev/null +++ b/connectivity/source/drivers/macab/macabcondition.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 "macabcondition.hxx" +#include "MacabHeader.hxx" +#include "MacabRecord.hxx" +#include <connectivity/CommonTools.hxx> + +using namespace ::connectivity::macab; +using namespace ::com::sun::star::sdbc; + +MacabCondition::~MacabCondition() +{ +} + +MacabConditionConstant::MacabConditionConstant(const bool bValue) + : MacabCondition(), + m_bValue(bValue) +{ +} + +bool MacabConditionConstant::isAlwaysTrue() const +{ + return m_bValue; +} + +bool MacabConditionConstant::isAlwaysFalse() const +{ + return !m_bValue; +} + +bool MacabConditionConstant::eval(const MacabRecord *) const +{ + return m_bValue; +} + +MacabConditionColumn::MacabConditionColumn(const MacabHeader *header, const OUString &sColumnName) + : MacabCondition(), + m_nFieldNumber(header->getColumnNumber(sColumnName)) +{ +} + +bool MacabConditionColumn::isAlwaysTrue() const +{ + // Sometimes true, sometimes false + return false; +} + +bool MacabConditionColumn::isAlwaysFalse() const +{ + // Sometimes true, sometimes false + return false; +} + +MacabConditionNull::MacabConditionNull(const MacabHeader *header, const OUString &sColumnName) + : MacabConditionColumn(header, sColumnName) +{ +} + +bool MacabConditionNull::eval(const MacabRecord *aRecord) const +{ + macabfield *aValue = aRecord->get(m_nFieldNumber); + + if(aValue == nullptr) + return true; + else if(aValue->value == nullptr) + return true; + else + return false; +} + +MacabConditionNotNull::MacabConditionNotNull(const MacabHeader *header, const OUString &sColumnName) + : MacabConditionColumn(header, sColumnName) +{ +} + +bool MacabConditionNotNull::eval(const MacabRecord *aRecord) const +{ + macabfield *aValue = aRecord->get(m_nFieldNumber); + + if(aValue == nullptr) + return false; + else if(aValue->value == nullptr) + return false; + else + return true; +} + +MacabConditionCompare::MacabConditionCompare(const MacabHeader *header, const OUString &sColumnName, const OUString &sMatchString) + : MacabConditionColumn(header, sColumnName), + m_sMatchString(sMatchString) +{ +} + +MacabConditionEqual::MacabConditionEqual(const MacabHeader *header, const OUString &sColumnName, const OUString &sMatchString) + : MacabConditionCompare(header, sColumnName, sMatchString) +{ +} + +bool MacabConditionEqual::eval(const MacabRecord *aRecord) const +{ + macabfield *aValue = aRecord->get(m_nFieldNumber); + + if(aValue == nullptr) + return false; + + macabfield *aValue2 = MacabRecord::createMacabField(m_sMatchString,aValue->type); + + if(aValue2 == nullptr) + return false; + + sal_Int32 nReturn = MacabRecord::compareFields(aValue, aValue2); + + delete aValue2; + return nReturn == 0; +} + +MacabConditionDifferent::MacabConditionDifferent(const MacabHeader *header, const OUString &sColumnName, const OUString &sMatchString) + : MacabConditionCompare(header, sColumnName, sMatchString) +{ +} + +bool MacabConditionDifferent::eval(const MacabRecord *aRecord) const +{ + macabfield *aValue = aRecord->get(m_nFieldNumber); + + if(aValue == nullptr) + return false; + + macabfield *aValue2 = MacabRecord::createMacabField(m_sMatchString,aValue->type); + + if(aValue2 == nullptr) + return false; + + sal_Int32 nReturn = MacabRecord::compareFields(aValue, aValue2); + + delete aValue2; + return nReturn != 0; +} + +MacabConditionSimilar::MacabConditionSimilar(const MacabHeader *header, const OUString &sColumnName, const OUString &sMatchString) + : MacabConditionCompare(header, sColumnName, sMatchString) +{ +} + +bool MacabConditionSimilar::eval(const MacabRecord *aRecord) const +{ + macabfield *aValue = aRecord->get(m_nFieldNumber); + + if(aValue == nullptr) + return false; + + OUString sName = MacabRecord::fieldToString(aValue); + + return match(m_sMatchString, sName, '\0'); +} + +MacabConditionBoolean::MacabConditionBoolean(MacabCondition *pLeft, MacabCondition *pRight) + : MacabCondition(), + m_pLeft(pLeft), + m_pRight(pRight) +{ +} + +MacabConditionBoolean::~MacabConditionBoolean() +{ + delete m_pLeft; + delete m_pRight; +} + +MacabConditionOr::MacabConditionOr(MacabCondition *pLeft, MacabCondition *pRight) + : MacabConditionBoolean(pLeft, pRight) +{ +} + +bool MacabConditionOr::isAlwaysTrue() const +{ + return m_pLeft->isAlwaysTrue() || m_pRight->isAlwaysTrue(); +} + +bool MacabConditionOr::isAlwaysFalse() const +{ + return m_pLeft->isAlwaysFalse() && m_pRight->isAlwaysFalse(); +} + +bool MacabConditionOr::eval(const MacabRecord *aRecord) const +{ + // We avoid evaluating terms as much as we can + if (m_pLeft->isAlwaysTrue() || m_pRight->isAlwaysTrue()) return true; + if (m_pLeft->isAlwaysFalse() && m_pRight->isAlwaysFalse()) return false; + + if (m_pLeft->eval(aRecord)) return true; + if (m_pRight->eval(aRecord)) return true; + + return false; +} + +MacabConditionAnd::MacabConditionAnd(MacabCondition *pLeft, MacabCondition *pRight) + : MacabConditionBoolean(pLeft, pRight) +{ +} + +bool MacabConditionAnd::isAlwaysTrue() const +{ + return m_pLeft->isAlwaysTrue() && m_pRight->isAlwaysTrue(); +} + +bool MacabConditionAnd::isAlwaysFalse() const +{ + return m_pLeft->isAlwaysFalse() || m_pRight->isAlwaysFalse(); +} + +bool MacabConditionAnd::eval(const MacabRecord *aRecord) const +{ + // We avoid evaluating terms as much as we can + if (m_pLeft->isAlwaysFalse() || m_pRight->isAlwaysFalse()) return false; + if (m_pLeft->isAlwaysTrue() && m_pRight->isAlwaysTrue()) return true; + + if (!m_pLeft->eval(aRecord)) return false; + if (!m_pRight->eval(aRecord)) return false; + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/macabcondition.hxx b/connectivity/source/drivers/macab/macabcondition.hxx new file mode 100644 index 000000000..15eb853b1 --- /dev/null +++ b/connectivity/source/drivers/macab/macabcondition.hxx @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCONDITION_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCONDITION_HXX + +#include "MacabHeader.hxx" +#include "MacabRecord.hxx" + +#include <connectivity/dbexception.hxx> + +namespace connectivity +{ + namespace macab + { + +class MacabCondition +{ + public: + virtual ~MacabCondition(); + virtual bool isAlwaysTrue() const = 0; + virtual bool isAlwaysFalse() const = 0; + virtual bool eval(const MacabRecord *aRecord) const = 0; +}; + +class MacabConditionConstant : public MacabCondition +{ + protected: + bool m_bValue; + + public: + explicit MacabConditionConstant(const bool bValue); + virtual bool isAlwaysTrue() const override; + virtual bool isAlwaysFalse() const override; + virtual bool eval(const MacabRecord *aRecord) const override; +}; + +class MacabConditionColumn : public MacabCondition +{ + protected: + sal_Int32 m_nFieldNumber; + + public: + /// @throws css::sdbc::SQLException + MacabConditionColumn( + const MacabHeader *header, + const OUString &sColumnName); + virtual bool isAlwaysTrue() const override; + virtual bool isAlwaysFalse() const override; +}; + +class MacabConditionNull : public MacabConditionColumn +{ + public: + /// @throws css::sdbc::SQLException + MacabConditionNull( + const MacabHeader *header, + const OUString &sColumnName); + virtual bool eval(const MacabRecord *aRecord) const override; +}; + +class MacabConditionNotNull : public MacabConditionColumn +{ + public: + /// @throws css::sdbc::SQLException + MacabConditionNotNull( + const MacabHeader *header, + const OUString &sColumnName); + virtual bool eval(const MacabRecord *aRecord) const override; +}; + +class MacabConditionCompare : public MacabConditionColumn +{ + protected: + const OUString m_sMatchString; + + public: + /// @throws css::sdbc::SQLException + MacabConditionCompare( + const MacabHeader *header, + const OUString &sColumnName, + const OUString &sMatchString); +}; + +class MacabConditionEqual : public MacabConditionCompare +{ + public: + /// @throws css::sdbc::SQLException + MacabConditionEqual( + const MacabHeader *header, + const OUString &sColumnName, + const OUString &sMatchString); + virtual bool eval(const MacabRecord *aRecord) const override; +}; + +class MacabConditionDifferent : public MacabConditionCompare +{ + public: + /// @throws css::sdbc::SQLException + MacabConditionDifferent( + const MacabHeader *header, + const OUString &sColumnName, + const OUString &sMatchString); + virtual bool eval(const MacabRecord *aRecord) const override; +}; + +class MacabConditionSimilar : public MacabConditionCompare +{ + public: + /// @throws css::sdbc::SQLException + MacabConditionSimilar( + const MacabHeader *header, + const OUString &sColumnName, + const OUString &sMatchString); + virtual bool eval(const MacabRecord *aRecord) const override; +}; + +class MacabConditionBoolean : public MacabCondition +{ + protected: + MacabCondition *m_pLeft, *m_pRight; + + public: + MacabConditionBoolean(MacabCondition *pLeft, MacabCondition *pRight); + virtual ~MacabConditionBoolean() override; +}; + +class MacabConditionOr : public MacabConditionBoolean +{ + public: + MacabConditionOr(MacabCondition *pLeft, MacabCondition *pRight); + virtual bool isAlwaysTrue() const override; + virtual bool isAlwaysFalse() const override; + virtual bool eval(const MacabRecord *aRecord) const override; +}; + +class MacabConditionAnd : public MacabConditionBoolean +{ + public: + MacabConditionAnd(MacabCondition *pLeft, MacabCondition *pRight); + virtual bool isAlwaysTrue() const override; + virtual bool isAlwaysFalse() const override; + virtual bool eval(const MacabRecord *aRecord) const override; +}; + + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABCONDITION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/macaborder.cxx b/connectivity/source/drivers/macab/macaborder.cxx new file mode 100644 index 000000000..aaef39ef6 --- /dev/null +++ b/connectivity/source/drivers/macab/macaborder.cxx @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "macaborder.hxx" +#include "MacabHeader.hxx" +#include "MacabRecord.hxx" + +using namespace ::connectivity::macab; + +MacabOrder::~MacabOrder() +{ +} + +MacabSimpleOrder::MacabSimpleOrder(MacabHeader const *header, OUString const &sColumnName, bool bAscending) + : MacabOrder(), + m_nFieldNumber(header->getColumnNumber(sColumnName)), + m_bAscending(bAscending) +{ +} + +sal_Int32 MacabSimpleOrder::compare(const MacabRecord *record1, const MacabRecord *record2) const +{ + sal_Int32 result; + + result = MacabRecord::compareFields(record1->get(m_nFieldNumber), record2->get(m_nFieldNumber)); + + if (!m_bAscending) result = -result; + + return result; +} + +MacabComplexOrder::MacabComplexOrder() + : MacabOrder(), + m_aOrders() +{ +} + +MacabComplexOrder::~MacabComplexOrder() +{ +} + +void MacabComplexOrder::addOrder(MacabOrder *pOrder) +{ + m_aOrders.emplace_back(pOrder); +} + +sal_Int32 MacabComplexOrder::compare(const MacabRecord *record1, const MacabRecord *record2) const +{ + for (auto const & p: m_aOrders) + { + sal_Int32 result = p->compare(record1, record2); + + if (result) return result; + } + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/macaborder.hxx b/connectivity/source/drivers/macab/macaborder.hxx new file mode 100644 index 000000000..de7a2ed7c --- /dev/null +++ b/connectivity/source/drivers/macab/macaborder.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABORDER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABORDER_HXX + +#include <rtl/ustring.hxx> +#include "MacabHeader.hxx" +#include "MacabRecord.hxx" + +#include <memory> +#include <vector> + +namespace connectivity +{ + namespace macab + { + class MacabOrder + { + public: + virtual ~MacabOrder(); + + virtual sal_Int32 compare(const MacabRecord *record1, const MacabRecord *record2) const = 0; + }; + + class MacabSimpleOrder : public MacabOrder + { + sal_Int32 m_nFieldNumber; + bool m_bAscending; + + public: + MacabSimpleOrder(MacabHeader const *header, OUString const &sColumnName, bool bAscending); + + virtual sal_Int32 compare(const MacabRecord *record1, const MacabRecord *record2) const override; + }; + + class MacabComplexOrder : public MacabOrder + { + std::vector<std::unique_ptr<MacabOrder>> m_aOrders; + + public: + MacabComplexOrder(); + virtual ~MacabComplexOrder() override; + + void addOrder(MacabOrder *pOrder); + virtual sal_Int32 compare(const MacabRecord *record1, const MacabRecord *record2) const override; + }; + } +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/macab/macabutilities.hxx b/connectivity/source/drivers/macab/macabutilities.hxx new file mode 100644 index 000000000..c384604e3 --- /dev/null +++ b/connectivity/source/drivers/macab/macabutilities.hxx @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABUTILITIES_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MACAB_MACABUTILITIES_HXX + +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/sdbc/DataType.hpp> + +#include <time.h> +#include <premac.h> +#include <Carbon/Carbon.h> +#include <AddressBook/ABAddressBookC.h> +#include <postmac.h> + +namespace connectivity +{ + namespace macab + { + + inline OUString CFStringToOUString(const CFStringRef sOrig) + { + /* Copied all-but directly from code by Florian Heckl in + * cws_src680_aquafilepicker01 + * File was: fpicker/source/aqua/CFStringUtilities + * I only removed commented debugging lines and changed variable + * names. + */ + if (nullptr == sOrig) { + return OUString(); + } + + CFRetain(sOrig); + CFIndex nStringLength = CFStringGetLength(sOrig); + + UniChar unichars[nStringLength+1]; + + //'close' the string buffer correctly + unichars[nStringLength] = '\0'; + + CFStringGetCharacters (sOrig, CFRangeMake(0,nStringLength), unichars); + CFRelease(sOrig); + + return OUString(reinterpret_cast<sal_Unicode *>(unichars)); + } + + + inline CFStringRef OUStringToCFString(const OUString& aString) + { + /* Copied directly from code by Florian Heckl in + * cws_src680_aquafilepicker01 + * File was: fpicker/source/aqua/CFStringUtilities + */ + + CFStringRef ref = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<UniChar const *>(aString.getStr()), aString.getLength()); + + return ref; + } + + + inline css::util::DateTime CFDateToDateTime(const CFDateRef _cfDate) + { + /* Carbon can give us the time since 2001 of any CFDateRef, + * and it also stores the time since 1970 as a constant, + * basically allowing us to get the unixtime of any + * CFDateRef. From there, it is just a matter of choosing what + * we want to do with it. + */ + css::util::DateTime nRet; + double timeSince2001 = CFDateGetAbsoluteTime(_cfDate); + time_t unixtime = timeSince2001+kCFAbsoluteTimeIntervalSince1970; + struct tm *ptm = localtime(&unixtime); + nRet.Year = ptm->tm_year+1900; + nRet.Month = ptm->tm_mon+1; + nRet.Day = ptm->tm_mday; + nRet.Hours = ptm->tm_hour; + nRet.Minutes = ptm->tm_min; + nRet.Seconds = ptm->tm_sec; + nRet.NanoSeconds = 0; + return nRet; + } + + + inline OUString fixLabel(const OUString& _originalLabel) + { + /* Get the length, and make sure that there is actually a string + * here. + */ + if(_originalLabel.startsWith("_$!<")) + { + return _originalLabel.copy(4,_originalLabel.getLength()-8); + } + + return _originalLabel; + } + + + inline sal_Int32 ABTypeToDataType(const ABPropertyType _abType) + { + sal_Int32 dataType; + switch(_abType) + { + case kABStringProperty: + dataType = css::sdbc::DataType::CHAR; + break; + case kABDateProperty: + dataType = css::sdbc::DataType::TIMESTAMP; + break; + case kABIntegerProperty: + dataType = css::sdbc::DataType::INTEGER; + break; + case kABRealProperty: + dataType = css::sdbc::DataType::FLOAT; + break; + default: + dataType = -1; + } + return dataType; + } + + void impl_throwError(const char* pErrorId); + } +} + +#endif // _ CONNECTIVITY_MACAB_UTILITIES_HXX_ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MCatalog.cxx b/connectivity/source/drivers/mork/MCatalog.cxx new file mode 100644 index 000000000..743d1e5ab --- /dev/null +++ b/connectivity/source/drivers/mork/MCatalog.cxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "MCatalog.hxx" +#include "MConnection.hxx" +#include "MTables.hxx" + +#include <com/sun/star/sdbc/XRow.hpp> + + +using namespace connectivity::mork; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; + + +OCatalog::OCatalog(OConnection* _pCon) : connectivity::sdbcx::OCatalog(_pCon) + ,m_pConnection(_pCon) +{ +// osl_atomic_increment( &m_refCount ); +// refreshTables(); +// refreshViews(); +// refreshGroups(); +// refreshUsers(); +// osl_atomic_decrement( &m_refCount ); +} + +void OCatalog::refreshTables() +{ + ::std::vector< OUString> aVector; + Sequence< OUString > aTypes { "%" }; + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(), + "%", "%", aTypes); + + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + OUString aName; + while(xResult->next()) + { + aName = xRow->getString(3); + aVector.push_back(aName); + } + } + if(m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset( new OTables(m_xMetaData,*this,m_aMutex,aVector) ); +} + +void OCatalog::refreshViews() +{ +} + +void OCatalog::refreshGroups() +{ +} + +void OCatalog::refreshUsers() +{ +} + + +// XTablesSupplier +Reference< XNameAccess > SAL_CALL OCatalog::getTables( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + try + { + if(!m_pTables || OConnection::getForceLoadTables()) + refreshTables(); + } + catch( const RuntimeException& ) + { + // allowed to leave this method + throw; + } + catch( const Exception& ) + { + // allowed + } + + return m_pTables.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MCatalog.hxx b/connectivity/source/drivers/mork/MCatalog.hxx new file mode 100644 index 000000000..170809f7c --- /dev/null +++ b/connectivity/source/drivers/mork/MCatalog.hxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCATALOG_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCATALOG_HXX + +#include <sdbcx/VCatalog.hxx> + +namespace connectivity +{ + namespace mork + { + // please don't name the class the same name as in another namespaces + // some compilers have problems with this task as I noticed on windows + class OConnection; + class OCatalog : public connectivity::sdbcx::OCatalog + { + OConnection* m_pConnection; // used to get the metadata + + public: + // implementation of the pure virtual methods + virtual void refreshTables() override; + virtual void refreshViews() override ; + virtual void refreshGroups() override; + virtual void refreshUsers() override ; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( ) override; + public: + explicit OCatalog(OConnection* _pCon); + + OConnection* getConnection() const { return m_pConnection; } + + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCATALOG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MColumnAlias.cxx b/connectivity/source/drivers/mork/MColumnAlias.cxx new file mode 100644 index 000000000..b320d8c45 --- /dev/null +++ b/connectivity/source/drivers/mork/MColumnAlias.cxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "MColumnAlias.hxx" + +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <officecfg/Office/DataAccess.hxx> + +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <algorithm> + +using namespace ::connectivity::mork; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; + + +OColumnAlias::OColumnAlias( const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxORB ) +{ + static const char* s_pProgrammaticNames[] = + { + "FirstName", + "LastName", + "DisplayName", + "NickName", + "PrimaryEmail", + "SecondEmail", + "PreferMailFormat", + "WorkPhone", + "HomePhone", + "FaxNumber", + "PagerNumber", + "CellularNumber", + "HomeAddress", + "HomeAddress2", + "HomeCity", + "HomeState", + "HomeZipCode", + "HomeCountry", + "WorkAddress", + "WorkAddress2", + "WorkCity", + "WorkState", + "WorkZipCode", + "WorkCountry", + "JobTitle", + "Department", + "Company", + "WebPage1", + "WebPage2", + "BirthYear", + "BirthMonth", + "BirthDay", + "Custom1", + "Custom2", + "Custom3", + "Custom4", + "Notes", + }; + + for ( size_t i = 0; i < SAL_N_ELEMENTS( s_pProgrammaticNames ); ++i ) + m_aAliasMap[ OUString::createFromAscii( s_pProgrammaticNames[i] ) ] = AliasEntry( s_pProgrammaticNames[i], i ); + + initialize( _rxORB ); +} + + +void OColumnAlias::initialize( const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxORB ) +{ + Reference< XNameAccess > xAliasesNode( + officecfg::Office::DataAccess::DriverSettings:: + com_sun_star_comp_sdbc_MozabDriver::ColumnAliases::get( + comphelper::getComponentContext(_rxORB)), + UNO_QUERY_THROW); + const Sequence< OUString > aProgrammaticNames(xAliasesNode->getElementNames()); + for (const auto& rProgrammaticName : aProgrammaticNames) { + OString sAsciiProgrammaticName( + OUStringToOString( + rProgrammaticName, RTL_TEXTENCODING_ASCII_US)); + auto j = std::find_if(m_aAliasMap.begin(), m_aAliasMap.end(), + [&sAsciiProgrammaticName](const AliasMap::value_type& rEntry) { + return rEntry.second.programmaticAsciiName == sAsciiProgrammaticName; }); + if (j != m_aAliasMap.end()) { + OUString sAssignedAlias; + xAliasesNode->getByName(rProgrammaticName) >>= + sAssignedAlias; + if (sAssignedAlias.isEmpty()) { + sAssignedAlias = rProgrammaticName; + } + AliasEntry entry(j->second); + m_aAliasMap.erase(j); + m_aAliasMap[sAssignedAlias] = entry; + } + else { + SAL_WARN( + "connectivity.mork", + "unknown programmatic name " << rProgrammaticName + <<" from configuration"); + } + } +} + + +OString OColumnAlias::getProgrammaticNameOrFallbackToUTF8Alias( const OUString& _rAlias ) const +{ + AliasMap::const_iterator pos = m_aAliasMap.find( _rAlias ); + if ( pos == m_aAliasMap.end() ) + { + OSL_FAIL( "OColumnAlias::getProgrammaticNameOrFallbackToUTF8Alias: no programmatic name for this alias!" ); + return OUStringToOString( _rAlias, RTL_TEXTENCODING_UTF8 ); + } + return pos->second.programmaticAsciiName; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MColumnAlias.hxx b/connectivity/source/drivers/mork/MColumnAlias.hxx new file mode 100644 index 000000000..3900b324b --- /dev/null +++ b/connectivity/source/drivers/mork/MColumnAlias.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCOLUMNALIAS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCOLUMNALIAS_HXX + +#include <unotools/confignode.hxx> + +#include <unordered_map> + +namespace connectivity +{ + namespace mork + { + class OColumnAlias + { + public: + struct AliasEntry + { + OString programmaticAsciiName; + size_t columnPosition; + + AliasEntry() + :programmaticAsciiName() + ,columnPosition( 0 ) + { + } + AliasEntry( const char* _programmaticAsciiName, size_t _columnPosition ) + :programmaticAsciiName( _programmaticAsciiName ) + ,columnPosition( _columnPosition ) + { + } + }; + typedef std::unordered_map< OUString, AliasEntry > AliasMap; + + private: + AliasMap m_aAliasMap; + + public: + explicit OColumnAlias( const css::uno::Reference< css::lang::XMultiServiceFactory > & ); + + OString getProgrammaticNameOrFallbackToUTF8Alias( const OUString& _rAlias ) const; + + AliasMap::const_iterator begin() const { return m_aAliasMap.begin(); } + AliasMap::const_iterator end() const { return m_aAliasMap.end(); } + + + private: + void initialize( const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxORB ); + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCOLUMNALIAS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MColumns.cxx b/connectivity/source/drivers/mork/MColumns.cxx new file mode 100644 index 000000000..c9163a6ab --- /dev/null +++ b/connectivity/source/drivers/mork/MColumns.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 "MColumns.hxx" +#include <com/sun/star/sdbc/XRow.hpp> +#include <connectivity/sdbcx/VColumn.hxx> + +using namespace connectivity::mork; +using namespace connectivity::sdbcx; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; + + +sdbcx::ObjectType OColumns::createObject(const OUString& _rName) +{ + const Any aCatalog; + const OUString sCatalogName; + const OUString sSchemaName(m_pTable->getSchema()); + const OUString sTableName(m_pTable->getTableName()); + Reference< XResultSet > xResult = m_pTable->getConnection()->getMetaData()->getColumns( + aCatalog, sSchemaName, sTableName, _rName); + + sdbcx::ObjectType xRet; + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + while(xResult->next()) + { + if(xRow->getString(4) == _rName) + { + sal_Int32 nType = xRow->getInt(5); + OUString sTypeName = xRow->getString(6); + sal_Int32 nPrec = xRow->getInt(7); + + OColumn* pRet = new OColumn(_rName, + sTypeName, + xRow->getString(13), + xRow->getString(12), + xRow->getInt(11), + nPrec, + xRow->getInt(9), + nType, + false,false,false,true, + sCatalogName, + sSchemaName, + sTableName); + xRet = pRet; + break; + } + } + } + + return xRet; +} + + +void OColumns::impl_refresh() +{ + m_pTable->refreshColumns(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MColumns.hxx b/connectivity/source/drivers/mork/MColumns.hxx new file mode 100644 index 000000000..b0774af27 --- /dev/null +++ b/connectivity/source/drivers/mork/MColumns.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCOLUMNS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCOLUMNS_HXX + +#include <connectivity/sdbcx/VCollection.hxx> +#include "MTable.hxx" + +namespace connectivity +{ + namespace mork + { + class OColumns final : public sdbcx::OCollection + { + OTable* m_pTable; + + virtual sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override; + public: + OColumns( OTable* _pTable, + ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector + ) : sdbcx::OCollection(*_pTable, true, _rMutex, _rVector) + ,m_pTable(_pTable) + {} + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCOLUMNS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MConnection.cxx b/connectivity/source/drivers/mork/MConnection.cxx new file mode 100644 index 000000000..e6658b6dc --- /dev/null +++ b/connectivity/source/drivers/mork/MConnection.cxx @@ -0,0 +1,377 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "MConnection.hxx" +#include "MDriver.hxx" +#include "MDatabaseMetaData.hxx" +#include "MCatalog.hxx" +#include "MPreparedStatement.hxx" +#include "MorkParser.hxx" + +#include <connectivity/dbexception.hxx> +#include <sal/log.hxx> + +#include <strings.hrc> + +#include <com/sun/star/sdbc/TransactionIsolation.hpp> + +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; + + +namespace connectivity::mork { + +static const int defaultScope = 0x80; + + +OConnection::OConnection(MorkDriver* _pDriver) + :m_xDriver(_pDriver) + ,m_aColumnAlias( _pDriver->getFactory() ) +{ + m_pBook.reset( new MorkParser() ); + m_pHistory.reset( new MorkParser() ); +} + +OConnection::~OConnection() +{ + if(!isClosed()) + close(); + m_pBook.reset(); + m_pHistory.reset(); +} + +void OConnection::construct(const OUString& url) +{ + SAL_INFO("connectivity.mork", "=> OConnection::construct()" ); + // open file + setURL(url); + + // Skip 'sdbc:mozab: part of URL + + sal_Int32 nLen = url.indexOf(':'); + nLen = url.indexOf(':',nLen+1); + OSL_ENSURE( url.startsWith("sdbc:address:"), "OConnection::construct: invalid start of the URI - should never have survived XDriver::acceptsURL!" ); + + OUString aAddrbookURI(url.copy(nLen+1)); + // Get Scheme + nLen = aAddrbookURI.indexOf(':'); + OUString aAddrbookScheme; + if ( nLen == -1 ) + { + // There isn't any subschema: - but could be just subschema + if ( !aAddrbookURI.isEmpty() ) + { + aAddrbookScheme= aAddrbookURI; + } + else + { + SAL_WARN("connectivity.mork", "No subschema given!!!"); + throwGenericSQLException( STR_URI_SYNTAX_ERROR, *this ); + } + } + else + { + aAddrbookScheme = aAddrbookURI.copy(0, nLen); + } + + SAL_INFO("connectivity.mork", "URI = " << aAddrbookURI ); + SAL_INFO("connectivity.mork", "Scheme = " << aAddrbookScheme ); + + OUString abook; + OUString history; + const OUString UNITTEST_URL = "thunderbird:unittest:"; + sal_Int32 unittestIndex = url.indexOf(UNITTEST_URL); + + // production? + if (unittestIndex == -1) + { + OUString path = m_xDriver->getProfilePath(); + SAL_INFO("connectivity.mork", "ProfilePath: " << path); + abook = path + "/abook.mab"; + history = path + "/history.mab"; + SAL_INFO("connectivity.mork", "AdressbookPath (abook): " << abook); + SAL_INFO("connectivity.mork", "AdressbookPath (history): " << history); + } + else + { + abook = aAddrbookURI.replaceFirst(UNITTEST_URL, ""); + SAL_INFO("connectivity.mork", "unit test: " << abook); + } + + OString strPath = OUStringToOString(abook, RTL_TEXTENCODING_UTF8); + + // Open and parse mork file + if (!m_pBook->open(strPath.getStr())) + { + SAL_WARN("connectivity.mork", "Can not parse abook mork file: " << strPath); + const OUString sError( getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, "$filename$", abook)); + ::dbtools::throwGenericSQLException( sError, *this ); + } + + // read history only in production + if (unittestIndex == -1) + { + strPath = OUStringToOString(history, RTL_TEXTENCODING_UTF8); + if (!m_pHistory->open(strPath.getStr())) + { + SAL_WARN("connectivity.mork", "Can not parse history mork file: " << strPath); + const OUString sError( getResources().getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, "$filename$", history)); + ::dbtools::throwGenericSQLException( sError, *this ); + } + } + + // check that we can retrieve the tables: + MorkTableMap *Tables = m_pBook->getTables( defaultScope ); + if (Tables) + { + // Iterate all tables + for ( const auto& rEntry : Tables->map ) + { + if ( 0 == rEntry.first ) continue; + SAL_INFO("connectivity.mork", "table->first : " << rEntry.first); + } + } + // check that we can retrieve the history tables: + MorkTableMap *Tables_hist = m_pHistory->getTables( defaultScope ); + if (Tables_hist) + { + // Iterate all tables + for ( const auto& rEntry : Tables_hist->map ) + { + if ( 0 == rEntry.first ) continue; + SAL_INFO("connectivity.mork", "table->first : " << rEntry.first); + } + } +} + +// XServiceInfo + +IMPLEMENT_SERVICE_INFO(OConnection, "com.sun.star.sdbc.drivers.mork.OConnection", "com.sun.star.sdbc.Connection") + + +Reference< XStatement > SAL_CALL OConnection::createStatement( ) +{ + SAL_INFO("connectivity.mork", "=> OConnection::createStatement()" ); + + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + // create a statement + // the statement can only be executed once + Reference< XStatement > xReturn = new OStatement(this); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL OConnection::prepareStatement( const OUString& _sSql ) +{ + SAL_INFO("connectivity.mork", "=> OConnection::prepareStatement()" ); + SAL_INFO("connectivity.mork", "OConnection::prepareStatement( " << _sSql << " )"); + + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + // the pre + // create a statement + // the statement can only be executed more than once + OPreparedStatement* pPrepared = new OPreparedStatement(this,_sSql); + Reference< XPreparedStatement > xReturn = pPrepared; + pPrepared->lateInit(); + + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL OConnection::prepareCall( const OUString& _sSql ) +{ + SAL_INFO("connectivity.mork", "=> OConnection::prepareCall()" ); + SAL_INFO("connectivity.mork", "sql: " << _sSql); + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this ); + SAL_INFO("connectivity.mork", "OConnection::prepareCall( " << _sSql << " )"); + return nullptr; +} + +OUString SAL_CALL OConnection::nativeSQL( const OUString& _sSql ) +{ + SAL_INFO("connectivity.mork", "=> OConnection::nativeSQL()" ); + SAL_INFO("connectivity.mork", "sql: " << _sSql); + + ::osl::MutexGuard aGuard( m_aMutex ); + // when you need to transform SQL92 to you driver specific you can do it here + SAL_INFO("connectivity.mork", "OConnection::nativeSQL(" << _sSql << " )" ); + + return _sSql; +} + +void SAL_CALL OConnection::setAutoCommit( sal_Bool /*autoCommit*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setAutoCommit", *this ); +} + +sal_Bool SAL_CALL OConnection::getAutoCommit( ) +{ + // you have to distinguish which if you are in autocommit mode or not + // at normal case true should be fine here + + return true; +} + +void SAL_CALL OConnection::commit( ) +{ + // when you database does support transactions you should commit here +} + +void SAL_CALL OConnection::rollback( ) +{ + // same as commit but for the other case +} + +sal_Bool SAL_CALL OConnection::isClosed( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + // just simple -> we are close when we are disposed that means someone called dispose(); (XComponent) + return OConnection_BASE::rBHelper.bDisposed; +} + +Reference< XDatabaseMetaData > SAL_CALL OConnection::getMetaData( ) +{ + SAL_INFO("connectivity.mork", "=> OConnection::getMetaData()" ); + + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + // here we have to create the class with biggest interface + // The answer is 42 :-) + Reference< XDatabaseMetaData > xMetaData = m_xMetaData; + if(!xMetaData.is()) + { + xMetaData = new ODatabaseMetaData(this); // need the connection because it can return it + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +void SAL_CALL OConnection::setReadOnly( sal_Bool /*readOnly*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setReadOnly", *this ); +} + +sal_Bool SAL_CALL OConnection::isReadOnly( ) +{ + // return if your connection to readonly + return false; +} + +void SAL_CALL OConnection::setCatalog( const OUString& /*catalog*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setCatalog", *this ); +} + +OUString SAL_CALL OConnection::getCatalog( ) +{ + return OUString(); +} + +void SAL_CALL OConnection::setTransactionIsolation( sal_Int32 /*level*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTransactionIsolation", *this ); +} + +sal_Int32 SAL_CALL OConnection::getTransactionIsolation( ) +{ + // please have a look at @see com.sun.star.sdbc.TransactionIsolation + return TransactionIsolation::NONE; +} + +Reference< css::container::XNameAccess > SAL_CALL OConnection::getTypeMap( ) +{ + // if your driver has special database types you can return it here + return nullptr; +} + +void SAL_CALL OConnection::setTypeMap( const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this ); +} + +// XCloseable +void SAL_CALL OConnection::close( ) +{ + // we just dispose us + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + } + dispose(); +} + +// XWarningsSupplier +Any SAL_CALL OConnection::getWarnings( ) +{ + // when you collected some warnings -> return it + return Any(); +} + +void SAL_CALL OConnection::clearWarnings( ) +{ + // you should clear your collected warnings here +} + +void OConnection::disposing() +{ + // we noticed that we should be destroyed in near future so we have to dispose our statements + ::osl::MutexGuard aGuard(m_aMutex); + m_xCatalog.clear(); +} + +Reference< XTablesSupplier > OConnection::createCatalog() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XTablesSupplier > xTab = m_xCatalog; + if(!m_xCatalog.is()) + { + OCatalog *pCat = new OCatalog(this); + xTab = pCat; + m_xCatalog = xTab; + } + return xTab; +} + +void OConnection::throwSQLException( const ErrorDescriptor& _rError, const Reference< XInterface >& _rxContext ) +{ + if (_rError.getResId() != nullptr) + { + throwGenericSQLException( _rError.getResId(), _rxContext ); + OSL_FAIL( "OConnection::throwSQLException: unreachable (2)!" ); + } + + throwGenericSQLException( STR_UNSPECIFIED_ERROR, _rxContext ); +} + +void OConnection::throwSQLException( const char* pErrorResourceId, const Reference< XInterface >& _rxContext ) +{ + ErrorDescriptor aError; + aError.setResId(pErrorResourceId); + throwSQLException(aError, _rxContext); +} + +} // namespace connectivity::mork + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MConnection.hxx b/connectivity/source/drivers/mork/MConnection.hxx new file mode 100644 index 000000000..0bcb936bd --- /dev/null +++ b/connectivity/source/drivers/mork/MConnection.hxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCONNECTION_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCONNECTION_HXX + +#include <TConnection.hxx> +#include "MColumnAlias.hxx" + +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> + +// do we want here namespace too? +class MorkParser; + +namespace connectivity +{ + namespace mork + { + class MorkDriver; + class ErrorDescriptor; + + typedef connectivity::OMetaConnection OConnection_BASE; // implements basics and text encoding + + class OConnection final : public OConnection_BASE + { + // Data attributes + + rtl::Reference<MorkDriver> m_xDriver; // Pointer to the owning + // driver object + OColumnAlias m_aColumnAlias; + // Mork Parser (abook) + std::unique_ptr<MorkParser> m_pBook; + // Mork Parser (history) + std::unique_ptr<MorkParser> m_pHistory; + // Store Catalog + css::uno::Reference< css::sdbcx::XTablesSupplier> m_xCatalog; + + public: + /// @throws css::sdbc::SQLException + void construct( const OUString& url); + explicit OConnection(MorkDriver* const driver); + virtual ~OConnection() override; + + const rtl::Reference<MorkDriver>& getDriver() const {return m_xDriver;}; + MorkParser* getMorkParser(const OString& t) {return t == "CollectedAddressBook" ? m_pHistory.get() : m_pBook.get();}; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XServiceInfo + DECLARE_SERVICE_INFO(); + // XConnection + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual void SAL_CALL setAutoCommit( sal_Bool autoCommit ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual void SAL_CALL setReadOnly( sal_Bool readOnly ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL setCatalog( const OUString& catalog ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual void SAL_CALL setTransactionIsolation( sal_Int32 level ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + // XCloseable + virtual void SAL_CALL close( ) override; + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings() override; + + const OColumnAlias & getColumnAlias() const { return m_aColumnAlias; } + + static bool getForceLoadTables() {return true;} + + // Added to enable me to use SQLInterpreter which requires an + // XNameAccess i/f to access tables. + css::uno::Reference< css::sdbcx::XTablesSupplier > createCatalog(); + + void throwSQLException( const ErrorDescriptor& _rError, const css::uno::Reference< css::uno::XInterface >& _rxContext ); + void throwSQLException( const char* pErrorResourceId, const css::uno::Reference< css::uno::XInterface >& _rxContext ); + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MCONNECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MDatabaseMetaData.cxx b/connectivity/source/drivers/mork/MDatabaseMetaData.cxx new file mode 100644 index 000000000..7858230b0 --- /dev/null +++ b/connectivity/source/drivers/mork/MDatabaseMetaData.cxx @@ -0,0 +1,957 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "MConnection.hxx" +#include "MDatabaseMetaData.hxx" + +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <sal/log.hxx> +#include <vector> + +#include "MDatabaseMetaDataHelper.hxx" + +using namespace connectivity::mork; +using namespace connectivity; + +using namespace com::sun::star::uno; +using namespace com::sun::star::sdbc; + + +namespace connectivity::mork +{ + static sal_Int32 const s_nCOLUMN_SIZE = 256; + static sal_Int32 const s_nDECIMAL_DIGITS = 0; + static sal_Int32 const s_nNULLABLE = 1; + static sal_Int32 const s_nCHAR_OCTET_LENGTH = 65535; +} + +ODatabaseMetaData::ODatabaseMetaData(OConnection* _pCon) + : ::connectivity::ODatabaseMetaDataBase(_pCon,_pCon->getConnectionInfo()) + ,m_pConnection(_pCon) + ,m_pMetaDataHelper(new MDatabaseMetaDataHelper) +{ + OSL_ENSURE(m_pConnection,"ODatabaseMetaData::ODatabaseMetaData: No connection set!"); +} + +ODatabaseMetaData::~ODatabaseMetaData() +{ +} + + +ODatabaseMetaDataResultSet::ORows ODatabaseMetaData::getColumnRows( + const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + SAL_INFO("connectivity.mork", "=> ODatabaseMetaData::getColumnRows()" ); + SAL_INFO("connectivity.mork", "tableNamePattern: " << tableNamePattern); + SAL_INFO("connectivity.mork", "columnNamePattern: " << columnNamePattern); + + ODatabaseMetaDataResultSet::ORows aRows; + ODatabaseMetaDataResultSet::ORow aRow(19); + + ::osl::MutexGuard aGuard( m_aMutex ); + std::vector< OUString > tables; + connectivity::mork::MDatabaseMetaDataHelper::getTableStrings(m_pConnection, tables); + + // **************************************************** + // Some entries in a row never change, so set them now + // **************************************************** + + // Catalog + aRow[1] = new ORowSetValueDecorator(OUString()); + // Schema + aRow[2] = new ORowSetValueDecorator(OUString()); + // DATA_TYPE + aRow[5] = new ORowSetValueDecorator(static_cast<sal_Int16>(DataType::VARCHAR)); + // TYPE_NAME, not used + aRow[6] = new ORowSetValueDecorator(OUString("VARCHAR")); + // COLUMN_SIZE + aRow[7] = new ORowSetValueDecorator(s_nCOLUMN_SIZE); + // BUFFER_LENGTH, not used + aRow[8] = ODatabaseMetaDataResultSet::getEmptyValue(); + // DECIMAL_DIGITS. + aRow[9] = new ORowSetValueDecorator(s_nDECIMAL_DIGITS); + // NUM_PREC_RADIX + aRow[10] = new ORowSetValueDecorator(sal_Int32(10)); + // NULLABLE + aRow[11] = new ORowSetValueDecorator(s_nNULLABLE); + // REMARKS + aRow[12] = ODatabaseMetaDataResultSet::getEmptyValue(); + // COULUMN_DEF, not used + aRow[13] = ODatabaseMetaDataResultSet::getEmptyValue(); + // SQL_DATA_TYPE, not used + aRow[14] = ODatabaseMetaDataResultSet::getEmptyValue(); + // SQL_DATETIME_SUB, not used + aRow[15] = ODatabaseMetaDataResultSet::getEmptyValue(); + // CHAR_OCTET_LENGTH, refer to [5] + aRow[16] = new ORowSetValueDecorator(s_nCHAR_OCTET_LENGTH); + // IS_NULLABLE + aRow[18] = new ORowSetValueDecorator(OUString("YES")); + + // Iterate over all tables + for(const OUString & table : tables) { + if(match(tableNamePattern, table,'\0')) { + // TABLE_NAME + aRow[3] = new ORowSetValueDecorator( table ); + + const OColumnAlias& colNames = m_pConnection->getColumnAlias(); + + SAL_INFO("connectivity.mork", "\tTableName = : " << table); + // Iterate over all columns in the table. + for (const auto& [rName, rAlias] : colNames) + { + if ( match( columnNamePattern, rName, '\0' ) ) + { + SAL_INFO("connectivity.mork", "\t\tColumnNam : " << rName); + + // COLUMN_NAME + aRow[4] = new ORowSetValueDecorator( rName ); + // ORDINAL_POSITION + aRow[17] = new ORowSetValueDecorator( static_cast< sal_Int32 >( rAlias.columnPosition ) + 1 ); + aRows.push_back(aRow); + } + } + } + } + return aRows; +} + +OUString ODatabaseMetaData::impl_getCatalogSeparator_throw( ) +{ + return OUString(); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + return 65535; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxRowSize( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCatalogNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCharLiteralLength( ) +{ + return 254; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnNameLength( ) +{ + return 20; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInIndex( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCursorNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxConnections( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInTable( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 ODatabaseMetaData::impl_getMaxStatements_throw( ) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTableNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 ODatabaseMetaData::impl_getMaxTablesInSelect_throw( ) +{ + // We only support a single table + return 1; +} + + +sal_Bool SAL_CALL ODatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + return false; +} + +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; // 0 means no limit +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsNonNullableColumns( ) +{ + return false; +} + +OUString SAL_CALL ODatabaseMetaData::getCatalogTerm( ) +{ + return OUString(); +} + +OUString ODatabaseMetaData::impl_getIdentifierQuoteString_throw( ) +{ + // normally this is " + 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( ) +{ + //We support create table + 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 true; // should be supported at least +} + +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; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatementLength( ) +{ + return 0;// 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxProcedureNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxSchemaNameLength( ) +{ + return 0; // 0 means no limit +} + +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( ) +{ + // We allow you to select from any table. + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::isReadOnly( ) +{ + //we support insert/update/delete now + //But we have to set this to return sal_True otherwise the UI will add create "table/edit table" + //entry to the popup menu. We should avoid them. + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFiles( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFilePerTable( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTypeConversion( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullPlusNonNullIsNull( ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsColumnAliasing( ) +{ + // Support added for this. + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTableCorrelationNames( ) +{ + return false; +} + +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 false; +} + +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( ) +{ + // Any case may be used + return true; +} + +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( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return m_pConnection->getURL(); +} + +OUString SAL_CALL ODatabaseMetaData::getUserName( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getDriverName( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getDriverVersion() +{ + OUString aValue = OUString::number(1); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductVersion( ) +{ + OUString aValue = OUString::number(0); + return aValue; +} + +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 1; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDefaultTransactionIsolation( ) +{ + return TransactionIsolation::NONE; +} + +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 OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getTimeDateFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getSystemFunctions( ) +{ + return OUString(); +} + +OUString SAL_CALL ODatabaseMetaData::getNumericFunctions( ) +{ + return OUString(); +} + +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; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInSelect( ) +{ + return 0; // 0 means no limit +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxUserNameLength( ) +{ + return 0; // 0 means no limit +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetType( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetConcurrency( sal_Int32 /*setType*/, sal_Int32 /*concurrency*/ ) +{ + 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 false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersDeletesAreVisible( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersInsertsAreVisible( sal_Int32 /*setType*/ ) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::updatesAreDetected( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::deletesAreDetected( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::insertsAreDetected( sal_Int32 /*setType*/ ) +{ + return true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsBatchUpdates( ) +{ + return false; +} + +// here follow all methods which return a resultset +// the first methods is an example implementation how to use this resultset +// of course you could implement it on your and you should do this because +// the general way is more memory expensive + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTableTypes( ) +{ + // there exists no possibility to get table types so we have to check + static const OUStringLiteral sTableTypes[] = + { + "TABLE", + "VIEW" + // Currently we only support a 'TABLE' and 'VIEW' nothing more complex + + // OUString("SYSTEM TABLE"), + // OUString("GLOBAL TEMPORARY"), + // OUString("LOCAL TEMPORARY"), + // OUString("ALIAS"), + // OUString("SYNONYM") + }; + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTableTypes); + Reference< XResultSet > xRef = pResult; + + // here we fill the rows which should be visible when ask for data from the resultset returned here + ODatabaseMetaDataResultSet::ORows aRows; + for(const auto & sTableType : sTableTypes) + { + ODatabaseMetaDataResultSet::ORow aRow; + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(OUString(sTableType))); + // bound row + aRows.push_back(aRow); + } + // here we set the rows at the resultset + pResult->setRows(aRows); + return xRef; +} + +Reference< XResultSet > ODatabaseMetaData::impl_getTypeInfo_throw( ) +{ + // this returns an empty resultset where the column-names are already set + // in special the metadata of the resultset already returns the right columns + ODatabaseMetaDataResultSet* pResultSet = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTypeInfo); + Reference< XResultSet > xResultSet = pResultSet; + static ODatabaseMetaDataResultSet::ORows aRows = [&]() + { + ODatabaseMetaDataResultSet::ORows tmp; + ODatabaseMetaDataResultSet::ORow aRow; + aRow.reserve(19); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(OUString("VARCHAR"))); + aRow.push_back(new ORowSetValueDecorator(DataType::VARCHAR)); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(s_nCHAR_OCTET_LENGTH))); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getQuoteValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + // aRow.push_back(new ORowSetValueDecorator((sal_Int32)ColumnValue::NULLABLE)); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(ColumnSearch::CHAR))); + aRow.push_back(ODatabaseMetaDataResultSet::get1Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::get0Value()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRow.push_back(new ORowSetValueDecorator(sal_Int32(10))); + + tmp.push_back(aRow); + return tmp; + }(); + pResultSet->setRows(aRows); + return xResultSet; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumns( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + // this returns an empty resultset where the column-names are already set + // in special the metadata of the resultset already returns the right columns + ODatabaseMetaDataResultSet* pResultSet = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eColumns); + Reference< XResultSet > xResultSet = pResultSet; + pResultSet->setRows( getColumnRows( tableNamePattern, columnNamePattern )); + return xResultSet; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTables( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& tableNamePattern, const Sequence< OUString >& /*types*/ ) +{ + SAL_INFO("connectivity.mork", "=> ODatabaseMetaData::getTables()" ); + // this returns an empty resultset where the column-names are already set + // in special the metadata of the resultset already returns the right columns + ODatabaseMetaDataResultSet* pResultSet = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTables); + Reference< XResultSet > xResultSet = pResultSet; + + // ODatabaseMetaDataResultSet::ORows aRows; + // aRows = m_pDbMetaDataHelper->getTables( m_pConnection, tableNamePattern ); + // pResultSet->setRows( aRows ); + ODatabaseMetaDataResultSet::ORows _rRows; + connectivity::mork::MDatabaseMetaDataHelper::getTables( m_pConnection, tableNamePattern, _rRows ); + pResultSet->setRows( _rRows ); + + return xResultSet; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTablePrivileges( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& tableNamePattern ) +{ + SAL_INFO("connectivity.mork", "=> ODatabaseMetaData::getTablePrivileges()" ); + ::connectivity::ODatabaseMetaDataResultSet* pResult = new ::connectivity::ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTablePrivileges); + Reference< XResultSet > xRef = pResult; + + std::vector< OUString > tables; + connectivity::mork::MDatabaseMetaDataHelper::getTableStrings( m_pConnection, tables); + + ::connectivity::ODatabaseMetaDataResultSet::ORows aRows; + ::connectivity::ODatabaseMetaDataResultSet::ORow aRow(8); + aRows.reserve(8); + aRow[0] = ::connectivity::ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[1] = ::connectivity::ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[3] = ::connectivity::ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[4] = ::connectivity::ODatabaseMetaDataResultSet::getEmptyValue(); + aRow[5] = new ::connectivity::ORowSetValueDecorator(getUserName()); + aRow[7] = new ::connectivity::ORowSetValueDecorator(OUString("NO")); + + + // Iterate over all tables + for(const OUString & table : tables) { + if(match(tableNamePattern, table,'\0')) + { + // TABLE_NAME + aRow[2] = new ORowSetValueDecorator( table ); + + SAL_INFO("connectivity.mork", "\tTableName = : " << table); + + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getSelectValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getInsertValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getDeleteValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getUpdateValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getCreateValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getReadValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getAlterValue(); + aRows.push_back(aRow); + aRow[6] = ::connectivity::ODatabaseMetaDataResultSet::getDropValue(); + aRows.push_back(aRow); + } + } + pResult->setRows(aRows); + return xRef; +} + +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/mork/MDatabaseMetaData.hxx b/connectivity/source/drivers/mork/MDatabaseMetaData.hxx new file mode 100644 index 000000000..8677fc7aa --- /dev/null +++ b/connectivity/source/drivers/mork/MDatabaseMetaData.hxx @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MDATABASEMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MDATABASEMETADATA_HXX + +#include <memory> +#include <TDatabaseMetaDataBase.hxx> +#include "MConnection.hxx" + +namespace connectivity +{ + namespace mork + { + class MDatabaseMetaDataHelper; + + //************ Class: ODatabaseMetaData + + + class ODatabaseMetaData : public ODatabaseMetaDataBase + { + OConnection* m_pConnection; + std::unique_ptr<MDatabaseMetaDataHelper> + m_pMetaDataHelper; + + /// @throws css::sdbc::SQLException + ODatabaseMetaDataResultSet::ORows getColumnRows( const OUString& tableNamePattern, const OUString& columnNamePattern ); + + protected: + virtual ~ODatabaseMetaData() override; + + public: + explicit ODatabaseMetaData(OConnection* _pCon); + + private: + virtual css::uno::Reference< css::sdbc::XResultSet > impl_getTypeInfo_throw() override; + // cached database information + virtual OUString impl_getIdentifierQuoteString_throw( ) override; + virtual bool impl_isCatalogAtStart_throw( ) override; + virtual OUString impl_getCatalogSeparator_throw( ) override; + virtual bool impl_supportsCatalogsInTableDefinitions_throw( ) override; + virtual bool impl_supportsSchemasInTableDefinitions_throw( ) override ; + virtual bool impl_supportsCatalogsInDataManipulation_throw( ) override; + virtual bool impl_supportsSchemasInDataManipulation_throw( ) override ; + virtual bool impl_supportsMixedCaseQuotedIdentifiers_throw( ) override ; + virtual bool impl_supportsAlterTableWithAddColumn_throw( ) override; + virtual bool impl_supportsAlterTableWithDropColumn_throw( ) override; + virtual sal_Int32 impl_getMaxStatements_throw( ) override; + virtual sal_Int32 impl_getMaxTablesInSelect_throw( ) override; + virtual bool impl_storesMixedCaseQuotedIdentifiers_throw( ) override; + + // as I mentioned before this interface is really BIG + // XDatabaseMetaData + virtual sal_Bool SAL_CALL allProceduresAreCallable( ) override; + virtual sal_Bool SAL_CALL allTablesAreSelectable( ) override; + virtual OUString SAL_CALL getURL( ) override; + virtual OUString SAL_CALL getUserName( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedHigh( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedLow( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtStart( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtEnd( ) override; + virtual OUString SAL_CALL getDatabaseProductName( ) override; + virtual OUString SAL_CALL getDatabaseProductVersion( ) override; + virtual OUString SAL_CALL getDriverName( ) override; + virtual OUString SAL_CALL getDriverVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMajorVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMinorVersion( ) override; + virtual sal_Bool SAL_CALL usesLocalFiles( ) override; + virtual sal_Bool SAL_CALL usesLocalFilePerTable( ) override; + virtual sal_Bool SAL_CALL supportsMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseQuotedIdentifiers( ) override; + virtual OUString SAL_CALL getSQLKeywords( ) override; + virtual OUString SAL_CALL getNumericFunctions( ) override; + virtual OUString SAL_CALL getStringFunctions( ) override; + virtual OUString SAL_CALL getSystemFunctions( ) override; + virtual OUString SAL_CALL getTimeDateFunctions( ) override; + virtual OUString SAL_CALL getSearchStringEscape( ) override; + virtual OUString SAL_CALL getExtraNameCharacters( ) override; + virtual sal_Bool SAL_CALL supportsColumnAliasing( ) override; + virtual sal_Bool SAL_CALL nullPlusNonNullIsNull( ) override; + virtual sal_Bool SAL_CALL supportsTypeConversion( ) override; + virtual sal_Bool SAL_CALL supportsConvert( sal_Int32 fromType, sal_Int32 toType ) override; + virtual sal_Bool SAL_CALL supportsTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsDifferentTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsExpressionsInOrderBy( ) override; + virtual sal_Bool SAL_CALL supportsOrderByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupBy( ) override; + virtual sal_Bool SAL_CALL supportsGroupByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupByBeyondSelect( ) override; + virtual sal_Bool SAL_CALL supportsLikeEscapeClause( ) override; + virtual sal_Bool SAL_CALL supportsMultipleResultSets( ) override; + virtual sal_Bool SAL_CALL supportsMultipleTransactions( ) override; + virtual sal_Bool SAL_CALL supportsNonNullableColumns( ) override; + virtual sal_Bool SAL_CALL supportsMinimumSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsCoreSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsExtendedSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsANSI92EntryLevelSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92IntermediateSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92FullSQL( ) override; + virtual sal_Bool SAL_CALL supportsIntegrityEnhancementFacility( ) override; + virtual sal_Bool SAL_CALL supportsOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsFullOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsLimitedOuterJoins( ) override; + virtual OUString SAL_CALL getSchemaTerm( ) override; + virtual OUString SAL_CALL getProcedureTerm( ) override; + virtual OUString SAL_CALL getCatalogTerm( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsPositionedDelete( ) override; + virtual sal_Bool SAL_CALL supportsPositionedUpdate( ) override; + virtual sal_Bool SAL_CALL supportsSelectForUpdate( ) override; + virtual sal_Bool SAL_CALL supportsStoredProcedures( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInComparisons( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInExists( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInIns( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInQuantifieds( ) override; + virtual sal_Bool SAL_CALL supportsCorrelatedSubqueries( ) override; + virtual sal_Bool SAL_CALL supportsUnion( ) override; + virtual sal_Bool SAL_CALL supportsUnionAll( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossRollback( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossRollback( ) override; + virtual sal_Int32 SAL_CALL getMaxBinaryLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCharLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInGroupBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInIndex( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInOrderBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInSelect( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInTable( ) override; + virtual sal_Int32 SAL_CALL getMaxConnections( ) override; + virtual sal_Int32 SAL_CALL getMaxCursorNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxIndexLength( ) override; + virtual sal_Int32 SAL_CALL getMaxSchemaNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxProcedureNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCatalogNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxRowSize( ) override; + virtual sal_Bool SAL_CALL doesMaxRowSizeIncludeBlobs( ) override; + virtual sal_Int32 SAL_CALL getMaxStatementLength( ) override; + virtual sal_Int32 SAL_CALL getMaxTableNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxUserNameLength( ) override; + virtual sal_Int32 SAL_CALL getDefaultTransactionIsolation( ) override; + virtual sal_Bool SAL_CALL supportsTransactions( ) override; + virtual sal_Bool SAL_CALL supportsTransactionIsolationLevel( sal_Int32 level ) override; + virtual sal_Bool SAL_CALL supportsDataDefinitionAndDataManipulationTransactions( ) override; + virtual sal_Bool SAL_CALL supportsDataManipulationTransactionsOnly( ) override; + virtual sal_Bool SAL_CALL dataDefinitionCausesTransactionCommit( ) override; + virtual sal_Bool SAL_CALL dataDefinitionIgnoredInTransactions( ) override; + + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTables( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const css::uno::Sequence< OUString >& types ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTableTypes( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTablePrivileges( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const OUString& columnNamePattern ) override; + + virtual sal_Bool SAL_CALL supportsResultSetType( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 concurrency ) override; + virtual sal_Bool SAL_CALL ownUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL updatesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL deletesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL insertsAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsBatchUpdates( ) override; + + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getUDTs( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& typeNamePattern, const css::uno::Sequence< sal_Int32 >& types ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MDATABASEMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MDatabaseMetaDataHelper.cxx b/connectivity/source/drivers/mork/MDatabaseMetaDataHelper.cxx new file mode 100644 index 000000000..ca699bafa --- /dev/null +++ b/connectivity/source/drivers/mork/MDatabaseMetaDataHelper.cxx @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "MConnection.hxx" +#include "MDatabaseMetaDataHelper.hxx" + +// do we need it? +static ::osl::Mutex m_aMetaMutex; + +#include <sal/log.hxx> + +#include "MorkParser.hxx" + +using namespace connectivity; +using namespace connectivity::mork; + + +MDatabaseMetaDataHelper::MDatabaseMetaDataHelper() +{ + SAL_INFO("connectivity.mork", "=> MDatabaseMetaDataHelper::MDatabaseMetaDataHelper()" ); +} + + +MDatabaseMetaDataHelper::~MDatabaseMetaDataHelper() +{ +} + +void MDatabaseMetaDataHelper::getTableStrings( OConnection* _pCon, + std::vector< OUString >& _rStrings) +{ + SAL_INFO("connectivity.mork", "=> MDatabaseMetaDataHelper::getTableStrings()"); + + /* add default tables */ + _rStrings.push_back("AddressBook"); + _rStrings.push_back("CollectedAddressBook"); + + /* retrieve list table names (not from collected ab) */ + std::set<std::string> lists; + MorkParser* pMork = _pCon->getMorkParser("AddressBook"); + pMork->retrieveLists(lists); + for (auto const& elem : lists) + { + OUString groupTableName = OStringToOUString(elem.c_str(), RTL_TEXTENCODING_UTF8); + SAL_INFO("connectivity.mork", "add Table " << groupTableName); + + _rStrings.push_back(groupTableName); + // remember the list in the mork parser, we'll use it later + pMork->lists_.push_back(groupTableName); + } + + std::set<std::string> lists_history; + pMork = _pCon->getMorkParser("CollectedAddressBook"); + pMork->retrieveLists(lists_history); + for (auto const& elem : lists_history) + { + OUString groupTableName = OStringToOUString(elem.c_str(), RTL_TEXTENCODING_UTF8); + SAL_INFO("connectivity.mork", "add Table " << groupTableName); + + _rStrings.push_back(groupTableName); + // remember the list in the mork parser, we'll use it later + pMork->lists_.push_back(groupTableName); + } +} + +void MDatabaseMetaDataHelper::getTables( OConnection* _pCon, + const OUString& tableNamePattern, + ODatabaseMetaDataResultSet::ORows& _rRows) +{ + + SAL_INFO("connectivity.mork", "=> MDatabaseMetaDataHelper::getTables()"); + + static ODatabaseMetaDataResultSet::ORows aRows; + + SAL_INFO("connectivity.mork", "=> MDatabaseMetaDataHelper::getTables()" ); + SAL_INFO("connectivity.mork", "tableNamePattern : " << tableNamePattern); + ::osl::MutexGuard aGuard( m_aMetaMutex ); + + ODatabaseMetaDataResultSet::ORows().swap(aRows); // this makes real clear where memory is freed as well + aRows.clear(); + + std::vector< OUString > tables; + + getTableStrings( _pCon, tables ); + + for (OUString& aTableName : tables) { + ODatabaseMetaDataResultSet::ORow aRow { nullptr, nullptr, nullptr }; + + SAL_INFO("connectivity.mork", "TableName: " << aTableName ); + + + // return tables to caller + if (match( tableNamePattern, aTableName, '\0' )) + { + if ( aTableName.isEmpty() ) { + aTableName = "AddressBook"; + } + + SAL_INFO("connectivity.mork", "TableName: " << aTableName); + + aRow.push_back( new ORowSetValueDecorator( aTableName ) ); // Table/View name + if ((aTableName == "AddressBook") || (aTableName == "CollectedAddressBook")) + { + aRow.push_back( new ORowSetValueDecorator( OUString("TABLE") ) ); // Table type + } + else + { + aRow.push_back( new ORowSetValueDecorator( OUString("VIEW") ) ); // View type + } + aRow.push_back( ODatabaseMetaDataResultSet::getEmptyValue() ); // Remarks + aRows.push_back(aRow); + } + } + + _rRows = aRows; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MDatabaseMetaDataHelper.hxx b/connectivity/source/drivers/mork/MDatabaseMetaDataHelper.hxx new file mode 100644 index 000000000..0ae96dac9 --- /dev/null +++ b/connectivity/source/drivers/mork/MDatabaseMetaDataHelper.hxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MDATABASEMETADATAHELPER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MDATABASEMETADATAHELPER_HXX + +#include <FDatabaseMetaDataResultSet.hxx> +#include "MConnection.hxx" + +namespace connectivity +{ + namespace mork + { + class MDatabaseMetaDataHelper + { + public: + MDatabaseMetaDataHelper(); + ~MDatabaseMetaDataHelper(); + + + static void getTableStrings( OConnection* _pCon, + std::vector< OUString >& _rStrings); + + static void getTables( OConnection* _pCon, + const OUString& tableNamePattern, + ODatabaseMetaDataResultSet::ORows& _rRows); + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MDATABASEMETADATAHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MDriver.cxx b/connectivity/source/drivers/mork/MDriver.cxx new file mode 100644 index 000000000..0351b6c3e --- /dev/null +++ b/connectivity/source/drivers/mork/MDriver.cxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "MDriver.hxx" +#include "MConnection.hxx" + +#include <com/sun/star/mozilla/XMozillaBootstrap.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <sal/log.hxx> + +using namespace connectivity::mork; + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_sdbc_MorkDriver_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new MorkDriver(context)); +} + +MorkDriver::MorkDriver(const css::uno::Reference< css::uno::XComponentContext >& context): + context_(context), + m_xFactory(context_->getServiceManager(), css::uno::UNO_QUERY) +{ + SAL_INFO("connectivity.mork", "=> MorkDriver::MorkDriver()" ); +} + +OUString SAL_CALL MorkDriver::getImplementationName() +{ + return MORK_DRIVER_IMPL_NAME; +} + +sal_Bool SAL_CALL MorkDriver::supportsService(const OUString& serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > MorkDriver::getSupportedServiceNames() +{ + return { "com.sun.star.sdbc.Driver" }; +} + +css::uno::Reference< css::sdbc::XConnection > MorkDriver::connect( + OUString const & url, + css::uno::Sequence< css::beans::PropertyValue > const &) +{ + SAL_INFO("connectivity.mork", "=> MorkDriver::connect()" ); + + // Profile discovery + css::uno::Reference<css::uno::XInterface> xInstance = context_->getServiceManager()->createInstanceWithContext("com.sun.star.mozilla.MozillaBootstrap", context_); + OSL_ENSURE( xInstance.is(), "failed to create instance" ); + + css::uno::Reference<css::mozilla::XMozillaBootstrap> xMozillaBootstrap(xInstance, css::uno::UNO_QUERY); + OSL_ENSURE( xMozillaBootstrap.is(), "failed to create instance" ); + + if (xMozillaBootstrap.is()) + { + OUString defaultProfile = xMozillaBootstrap->getDefaultProfile(css::mozilla::MozillaProductType_Thunderbird); + + if (!defaultProfile.isEmpty()) + { + m_sProfilePath = xMozillaBootstrap->getProfilePath(css::mozilla::MozillaProductType_Thunderbird, defaultProfile); + SAL_INFO("connectivity.mork", "Using Thunderbird profile " << m_sProfilePath); + } + } + + css::uno::Reference< css::sdbc::XConnection > xCon; + OConnection* pCon = new OConnection(this); + xCon = pCon; // important here because otherwise the connection could be deleted inside (refcount goes -> 0) + pCon->construct(url); + return xCon; +} + +sal_Bool MorkDriver::acceptsURL(OUString const & url) +{ + SAL_INFO("connectivity.mork", "=> MorkDriver::acceptsURL()" ); + // Skip 'sdbc:mozab: part of URL + + sal_Int32 nLen = url.indexOf(':'); + nLen = url.indexOf(':',nLen+1); + OUString aAddrbookURI(url.copy(nLen+1)); + // Get Scheme + nLen = aAddrbookURI.indexOf(':'); + OUString aAddrbookScheme; + if ( nLen == -1 ) + { + // There isn't any subschema: - but could be just subschema + if ( !aAddrbookURI.isEmpty() ) + { + aAddrbookScheme= aAddrbookURI; + } + else if( url == "sdbc:address:" ) + { + return false; + } + else + { + return false; + } + } + else + { + aAddrbookScheme = aAddrbookURI.copy(0, nLen); + } + + return aAddrbookScheme == "thunderbird" || aAddrbookScheme == "mozilla"; +} + +css::uno::Sequence< css::sdbc::DriverPropertyInfo > MorkDriver::getPropertyInfo( + OUString const &, + css::uno::Sequence< css::beans::PropertyValue > const &) +{ + //... TODO + return css::uno::Sequence< css::sdbc::DriverPropertyInfo >(); +} + +sal_Int32 MorkDriver::getMajorVersion() { + //... TODO + return 0; +} + +sal_Int32 MorkDriver::getMinorVersion() { + //... TODO + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MDriver.hxx b/connectivity/source/drivers/mork/MDriver.hxx new file mode 100644 index 000000000..6f6946af6 --- /dev/null +++ b/connectivity/source/drivers/mork/MDriver.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MDRIVER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MDRIVER_HXX + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/DriverPropertyInfo.hpp> + +#include <com/sun/star/sdbc/XDriver.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <cppuhelper/implbase.hxx> + +#define MORK_DRIVER_IMPL_NAME "com.sun.star.comp.sdbc.MorkDriver" + +namespace com::sun::star { + namespace uno { + class XComponentContext; + class XInterface; + } +} + +namespace connectivity::mork { +class ProfileAccess; + +class MorkDriver: + public cppu::WeakImplHelper< css::lang::XServiceInfo, css::sdbc::XDriver > +{ +public: + explicit MorkDriver(const css::uno::Reference< css::uno::XComponentContext >& context); + + const css::uno::Reference< com::sun::star::lang::XMultiServiceFactory >& getFactory() const {return m_xFactory;} + const OUString& getProfilePath() const {return m_sProfilePath;} +private: + + MorkDriver(const MorkDriver&) = delete; + MorkDriver& operator=(const MorkDriver&) = delete; + + virtual ~MorkDriver() override {} + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL connect( + OUString const & url, + css::uno::Sequence< css::beans::PropertyValue > const & info) override; + + virtual sal_Bool SAL_CALL acceptsURL( + OUString const & url) override; + + virtual css::uno::Sequence< css::sdbc::DriverPropertyInfo > SAL_CALL + getPropertyInfo( + OUString const & url, + css::uno::Sequence< css::beans::PropertyValue > const & info) override; + + virtual sal_Int32 SAL_CALL getMajorVersion() override; + + virtual sal_Int32 SAL_CALL getMinorVersion() override; + + css::uno::Reference< css::uno::XComponentContext > context_; + css::uno::Reference< css::lang::XMultiServiceFactory > m_xFactory; + OUString m_sProfilePath; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MErrorResource.hxx b/connectivity/source/drivers/mork/MErrorResource.hxx new file mode 100644 index 000000000..0691b1813 --- /dev/null +++ b/connectivity/source/drivers/mork/MErrorResource.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MERRORRESOURCE_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MERRORRESOURCE_HXX + +namespace connectivity +{ + namespace mork + { + class ErrorDescriptor + { + private: + const char* m_pErrorResourceId; + + public: + ErrorDescriptor() + :m_pErrorResourceId(nullptr) + { + } + + void setResId(const char* pErrorResourceId) + { + m_pErrorResourceId = pErrorResourceId; + } + void reset() + { + m_pErrorResourceId = nullptr; + } + + const char* getResId() const { return m_pErrorResourceId; } + + bool is() const { return m_pErrorResourceId != nullptr; } + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MERRORRESOURCE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MPreparedStatement.cxx b/connectivity/source/drivers/mork/MPreparedStatement.cxx new file mode 100644 index 000000000..f2cd8fdc7 --- /dev/null +++ b/connectivity/source/drivers/mork/MPreparedStatement.cxx @@ -0,0 +1,486 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <connectivity/sdbcx/VColumn.hxx> +#include "MPreparedStatement.hxx" +#include "MResultSetMetaData.hxx" +#include <connectivity/dbtools.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <sal/log.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::mork; +using namespace com::sun::star::uno; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::container; +using namespace com::sun::star::util; + +IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.mork.PreparedStatement","com.sun.star.sdbc.PreparedStatement"); + + +OPreparedStatement::OPreparedStatement( OConnection* _pConnection,const OUString& sql) + :OCommonStatement(_pConnection) + ,m_sSqlStatement(sql) + ,m_pResultSet() +{ +} + +OPreparedStatement::~OPreparedStatement() +{ +} + +void OPreparedStatement::lateInit() +{ + if ( eSelect != parseSql( m_sSqlStatement ) ) + throw SQLException(); +} + +void SAL_CALL OPreparedStatement::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + OCommonStatement::disposing(); + + m_xMetaData.clear(); + if(m_aParameterRow.is()) + { + m_aParameterRow->clear(); + m_aParameterRow = nullptr; + } + m_xParamColumns = nullptr; +} + +OCommonStatement::StatementType OPreparedStatement::parseSql( const OUString& sql , bool bAdjusted ) +{ + StatementType eStatementType = OCommonStatement::parseSql( sql, bAdjusted ); + if ( eStatementType != eSelect ) + return eStatementType; + + m_xParamColumns = new OSQLColumns(); + + // describe all parameters need for the resultset + describeParameter(); + + Reference<XIndexAccess> xNames(m_xColNames,UNO_QUERY); + OResultSet::setBoundedColumns( m_aRow, m_xParamColumns, xNames, false, m_xDBMetaData, m_aColMapping ); + + return eStatementType; +} + +void OPreparedStatement::initializeResultSet( OResultSet* _pResult ) +{ + OCommonStatement::initializeResultSet( _pResult ); + _pResult->setParameterRow( m_aParameterRow ); +} + + +void OPreparedStatement::clearCachedResultSet() +{ + OCommonStatement::clearCachedResultSet(); + m_pResultSet.clear(); + m_xMetaData.clear(); +} + +void OPreparedStatement::cacheResultSet( const ::rtl::Reference< OResultSet >& _pResult ) +{ + OCommonStatement::cacheResultSet( _pResult ); + OSL_PRECOND( m_pResultSet == nullptr, "OPreparedStatement::parseSql: you should call clearCachedResultSet before!" ); + m_pResultSet = _pResult; +} + + +void SAL_CALL OPreparedStatement::acquire() throw() +{ + OCommonStatement::acquire(); +} + +void SAL_CALL OPreparedStatement::release() throw() +{ + OCommonStatement::release(); +} + +Any SAL_CALL OPreparedStatement::queryInterface( const Type & rType ) +{ + Any aRet = OCommonStatement::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OPreparedStatement_BASE::queryInterface(rType); + return aRet; +} + +css::uno::Sequence< css::uno::Type > SAL_CALL OPreparedStatement::getTypes( ) +{ + return ::comphelper::concatSequences(OPreparedStatement_BASE::getTypes(),OCommonStatement::getTypes()); +} + + +Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + bool bReadOnly = true; + if ( m_pResultSet.is() ) + bReadOnly = m_pResultSet->determineReadOnly(); + // if we do not have a result set, then we have not been executed, yet. In this case, assuming readonly=true is + // okay, /me thinks. + + if ( !m_xMetaData.is() ) + m_xMetaData = new OResultSetMetaData( m_pSQLIterator->getSelectColumns(), m_pSQLIterator->getTables().begin()->first ,m_pTable,bReadOnly ); + + return m_xMetaData; +} + + +sal_Bool SAL_CALL OPreparedStatement::execute( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + Reference< XResultSet> xResult = executeQuery(); + return xResult.is(); +} + + +sal_Int32 SAL_CALL OPreparedStatement::executeUpdate( ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XStatement::executeUpdate", *this ); + return 0; +} + + +void SAL_CALL OPreparedStatement::setString( sal_Int32 parameterIndex, const OUString& x ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + setParameter( parameterIndex, x ); +} + + +Reference< XConnection > SAL_CALL OPreparedStatement::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + return Reference< XConnection >(m_pConnection.get()); +} + + +Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + // our statement has already been parsed in lateInit, no need to do all this (potentially expensive) + // stuff again. Just execute. + return impl_executeCurrentQuery(); +} + + +void SAL_CALL OPreparedStatement::setBoolean( sal_Int32 /*parameterIndex*/, sal_Bool /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setBoolean", *this ); +} + +void SAL_CALL OPreparedStatement::setByte( sal_Int32 /*parameterIndex*/, sal_Int8 /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setByte", *this ); +} + + +void SAL_CALL OPreparedStatement::setDate( sal_Int32 /*parameterIndex*/, const Date& /*aData*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setDate", *this ); +} + + +void SAL_CALL OPreparedStatement::setTime( sal_Int32 /*parameterIndex*/, const css::util::Time& /*aVal*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setTime", *this ); +} + + +void SAL_CALL OPreparedStatement::setTimestamp( sal_Int32 /*parameterIndex*/, const DateTime& /*aVal*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setTimestamp", *this ); +} + + +void SAL_CALL OPreparedStatement::setDouble( sal_Int32 /*parameterIndex*/, double /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setDouble", *this ); +} + + +void SAL_CALL OPreparedStatement::setFloat( sal_Int32 /*parameterIndex*/, float /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setFloat", *this ); +} + + +void SAL_CALL OPreparedStatement::setInt( sal_Int32 /*parameterIndex*/, sal_Int32 /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setInt", *this ); +} + + +void SAL_CALL OPreparedStatement::setLong( sal_Int32 /*parameterIndex*/, sal_Int64 /*aVal*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setLong", *this ); +} + + +void SAL_CALL OPreparedStatement::setNull( sal_Int32 parameterIndex, sal_Int32 /*sqlType*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + checkAndResizeParameters(parameterIndex); + + (*m_aParameterRow)[parameterIndex].setNull(); +} + + +void SAL_CALL OPreparedStatement::setClob( sal_Int32 /*parameterIndex*/, const Reference< XClob >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setClob", *this ); +} + + +void SAL_CALL OPreparedStatement::setBlob( sal_Int32 /*parameterIndex*/, const Reference< XBlob >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setBlob", *this ); +} + + +void SAL_CALL OPreparedStatement::setArray( sal_Int32 /*parameterIndex*/, const Reference< XArray >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setArray", *this ); +} + + +void SAL_CALL OPreparedStatement::setRef( sal_Int32 /*parameterIndex*/, const Reference< XRef >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setRef", *this ); +} + + +void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 /*parameterIndex*/, const Any& /*x*/, sal_Int32 /*sqlType*/, sal_Int32 /*scale*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setObjectWithInfo", *this ); +} + + +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 ) +{ + ::dbtools::implSetObject(this,parameterIndex,x); +} + + +void SAL_CALL OPreparedStatement::setShort( sal_Int32 /*parameterIndex*/, sal_Int16 /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setShort", *this ); +} + + +void SAL_CALL OPreparedStatement::setBytes( sal_Int32 /*parameterIndex*/, const Sequence< sal_Int8 >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setBytes", *this ); +} + + +void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 /*parameterIndex*/, const Reference< css::io::XInputStream >& /*x*/, sal_Int32 /*length*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setCharacterStream", *this ); +} + + +void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 /*parameterIndex*/, const Reference< css::io::XInputStream >& /*x*/, sal_Int32 /*length*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setBinaryStream", *this ); +} + + +void SAL_CALL OPreparedStatement::clearParameters( ) +{ +} + +void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + switch(nHandle) + { + case PROPERTY_ID_RESULTSETCONCURRENCY: + break; + case PROPERTY_ID_RESULTSETTYPE: + break; + case PROPERTY_ID_FETCHDIRECTION: + break; + case PROPERTY_ID_USEBOOKMARKS: + break; + default: + OCommonStatement::setFastPropertyValue_NoBroadcast(nHandle,rValue); + } +} + + +void OPreparedStatement::checkAndResizeParameters(sal_Int32 parameterIndex) +{ + ::connectivity::checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + if ( !m_aParameterRow.is() ) { + m_aParameterRow = new OValueVector(); + m_aParameterRow->push_back(sal_Int32(0)); + } + + if (static_cast<sal_Int32>(m_aParameterRow->size()) <= parameterIndex) + m_aParameterRow->resize(parameterIndex+1); +} + +void OPreparedStatement::setParameter(sal_Int32 parameterIndex, const +ORowSetValue& x) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkAndResizeParameters(parameterIndex); + + SAL_INFO( + "connectivity.mork", + "setParameter(" << parameterIndex << ", '" << x.getString() << "')"); + (*m_aParameterRow)[parameterIndex] = x; +} + + +void 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()) + { + // Type, Precision, Scale ... utilize the selected Columns, + // then this Column will get the value assigned or with this + // Column will the value be compared. + eType = getINT32(_xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))); + nPrecision = getINT32(_xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))); + nScale = getINT32(_xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))); + nNullable = getINT32(_xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE))); + _xCol->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= sParameterName; + } + + Reference<XPropertySet> xParaColumn = new connectivity::sdbcx::OColumn(sParameterName + ,OUString() + ,OUString() + ,OUString() + ,nNullable + ,nPrecision + ,nScale + ,eType + ,false + ,false + ,false + ,m_pSQLIterator->isCaseSensitive() + ,OUString() + ,OUString() + ,OUString()); + m_xParamColumns->push_back(xParaColumn); +} + +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_pSQLIterator->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.get(), aParseNodes); + if(aParseNodes.empty()) + return; + + m_xParamColumns = new OSQLColumns(); + const OSQLTables& rTabs = m_pSQLIterator->getTables(); + if(!rTabs.empty()) + { + OSQLTable xTable = rTabs.begin()->second; + for (auto const& parseNode : aParseNodes) + { + describeColumn(parseNode,parseNode->getParent()->getChild(0),xTable); + } + } +} + + +void OPreparedStatement::scanParameter(OSQLParseNode* pParseNode,std::vector< OSQLParseNode*>& _rParaNodes) +{ + OSL_ENSURE(pParseNode != nullptr,"OResultSet: internal error: invalid ParseNode"); + + // Parameter Name-Row found? + if (SQL_ISRULE(pParseNode,parameter)) + { + OSL_ENSURE(pParseNode->count() >= 1,"OResultSet: Faulty Parse Tree"); + OSL_ENSURE(pParseNode->getChild(0)->getNodeType() == SQLNodeType::Punctuation,"OResultSet: Faulty Parse Tree"); + + _rParaNodes.push_back(pParseNode); + // further search isn't necessary + return; + } + + // Search on in Parse Tree + for (size_t i = 0; i < pParseNode->count(); i++) + scanParameter(pParseNode->getChild(i),_rParaNodes); +} + +css::uno::Reference< css::sdbc::XResultSet > SAL_CALL OPreparedStatement::getResultSet( ) +{ + return nullptr; +} + +sal_Int32 SAL_CALL OPreparedStatement::getUpdateCount( ) +{ + return 0; +} + +sal_Bool SAL_CALL OPreparedStatement::getMoreResults( ) +{ + return false; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MPreparedStatement.hxx b/connectivity/source/drivers/mork/MPreparedStatement.hxx new file mode 100644 index 000000000..c40ffc909 --- /dev/null +++ b/connectivity/source/drivers/mork/MPreparedStatement.hxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MPREPAREDSTATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MPREPAREDSTATEMENT_HXX + +#include "MResultSet.hxx" +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XMultipleResults.hpp> +#include <cppuhelper/implbase5.hxx> + +namespace connectivity +{ + namespace mork + { + + typedef ::cppu::ImplHelper5< css::sdbc::XPreparedStatement, + css::sdbc::XParameters, + css::sdbc::XResultSetMetaDataSupplier, + css::sdbc::XMultipleResults, + css::lang::XServiceInfo> OPreparedStatement_BASE; + + class OPreparedStatement final : public OCommonStatement, + public OPreparedStatement_BASE + { + // Data attributes + + OUString m_sSqlStatement; + css::uno::Reference< css::sdbc::XResultSetMetaData > m_xMetaData; + ::rtl::Reference< OResultSet > m_pResultSet; + ::rtl::Reference<connectivity::OSQLColumns> m_xParamColumns; // the parameter columns + OValueRow m_aParameterRow; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, + const css::uno::Any& rValue) override; + virtual ~OPreparedStatement() override; + + virtual void SAL_CALL disposing() override; + + // OCommonStatement overridables + virtual StatementType + parseSql( const OUString& sql , bool bAdjusted = false) override; + virtual void initializeResultSet( OResultSet* _pResult ) override; + virtual void clearCachedResultSet() override; + virtual void cacheResultSet( const ::rtl::Reference< OResultSet >& _pResult ) override; + + + void checkAndResizeParameters(sal_Int32 parameterIndex); + void setParameter(sal_Int32 parameterIndex, const ORowSetValue& x); + + void AddParameter(connectivity::OSQLParseNode const * pParameter, + const css::uno::Reference< css::beans::XPropertySet>& _xCol); + void scanParameter(OSQLParseNode* pParseNode,std::vector< OSQLParseNode*>& _rParaNodes); + void describeColumn(OSQLParseNode const * _pParameter, OSQLParseNode const * _pNode, const OSQLTable& _xTable); + void describeParameter(); + + public: + DECLARE_SERVICE_INFO(); + // A ctor need for returning the object + OPreparedStatement( OConnection* _pConnection,const OUString& sql); + void lateInit(); + + //XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XPreparedStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( ) override; + virtual sal_Int32 SAL_CALL executeUpdate( ) override; + virtual sal_Bool SAL_CALL execute( ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; + // XParameters + virtual void SAL_CALL setNull( sal_Int32 parameterIndex, sal_Int32 sqlType ) override; + virtual void SAL_CALL setObjectNull( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) override; + virtual void SAL_CALL setBoolean( sal_Int32 parameterIndex, sal_Bool x ) override; + virtual void SAL_CALL setByte( sal_Int32 parameterIndex, sal_Int8 x ) override; + virtual void SAL_CALL setShort( sal_Int32 parameterIndex, sal_Int16 x ) override; + virtual void SAL_CALL setInt( sal_Int32 parameterIndex, sal_Int32 x ) override; + virtual void SAL_CALL setLong( sal_Int32 parameterIndex, sal_Int64 x ) override; + virtual void SAL_CALL setFloat( sal_Int32 parameterIndex, float x ) override; + virtual void SAL_CALL setDouble( sal_Int32 parameterIndex, double x ) override; + virtual void SAL_CALL setString( sal_Int32 parameterIndex, const OUString& x ) override; + virtual void SAL_CALL setBytes( sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL setDate( sal_Int32 parameterIndex, const css::util::Date& x ) override; + virtual void SAL_CALL setTime( sal_Int32 parameterIndex, const css::util::Time& x ) override; + virtual void SAL_CALL setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL setBinaryStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setCharacterStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setObject( sal_Int32 parameterIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL setObjectWithInfo( sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) override; + virtual void SAL_CALL setRef( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef >& x ) override; + virtual void SAL_CALL setBlob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob >& x ) override; + virtual void SAL_CALL setClob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XClob >& x ) override; + virtual void SAL_CALL setArray( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray >& x ) override; + virtual void SAL_CALL clearParameters( ) override; + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + // XMultipleResults + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getResultSet( ) override; + virtual sal_Int32 SAL_CALL getUpdateCount( ) override; + virtual sal_Bool SAL_CALL getMoreResults( ) override; + + public: + using OCommonStatement::executeQuery; + using OCommonStatement::executeUpdate; + using OCommonStatement::execute; + private: + using OPropertySetHelper::getFastPropertyValue; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MPREPAREDSTATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MQueryHelper.cxx b/connectivity/source/drivers/mork/MQueryHelper.cxx new file mode 100644 index 000000000..aaf8cac6a --- /dev/null +++ b/connectivity/source/drivers/mork/MQueryHelper.cxx @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MColumnAlias.hxx" +#include "MQueryHelper.hxx" +#include "MConnection.hxx" + +#include "MorkParser.hxx" +#include <string> +#include <vector> +#include <algorithm> + +#include <strings.hrc> + +#include <unotools/textsearch.hxx> +#include <sal/log.hxx> + +using namespace connectivity::mork; +using namespace connectivity; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; + + +static +std::vector<bool> entryMatchedByExpression(MQueryHelper* _aQuery, MQueryExpression const * _aExpr, MQueryHelperResultEntry* entry); + +MQueryHelperResultEntry::MQueryHelperResultEntry() +{ +} + +MQueryHelperResultEntry::~MQueryHelperResultEntry() +{ +} + +OUString MQueryHelperResultEntry::getValue( const OString &key ) const +{ + FieldMap::const_iterator iter = m_Fields.find( key ); + if ( iter == m_Fields.end() ) + { + return OUString(); + } + else + { + return iter->second; + } +} + +void MQueryHelperResultEntry::setValue( const OString &key, const OUString & rValue) +{ + m_Fields[ key ] = rValue; +} + +MQueryHelper::MQueryHelper(const OColumnAlias& _ca) + :m_rColumnAlias( _ca ) + ,m_aError() +{ + m_aResults.clear(); +} + +MQueryHelper::~MQueryHelper() +{ + clear_results(); +} + + +void MQueryHelper::setAddressbook(OUString const &ab) +{ + ::osl::MutexGuard aGuard(m_aMutex); + m_aAddressbook = ab; +} + +void MQueryHelper::append(std::unique_ptr<MQueryHelperResultEntry> resEnt) +{ + assert(resEnt); + m_aResults.push_back( std::move(resEnt) ); +} + +void MQueryHelper::clear_results() +{ + m_aResults.clear(); +} + +void MQueryHelper::reset() +{ + clear_results(); + m_aError.reset(); +} + +MQueryHelperResultEntry* +MQueryHelper::getByIndex(sal_uInt32 nRow) +{ + // Row numbers are from 1 to N, need to ensure this, and then + // subtract 1 + if ( nRow < 1 ) { + return nullptr; + } + return m_aResults[nRow -1].get(); +} + +sal_Int32 MQueryHelper::getResultCount() const +{ + sal_Int32 result = static_cast<sal_Int32>(m_aResults.size()); + + return result; +} + +bool MQueryHelper::getRowValue( ORowSetValue& rValue, sal_Int32 nDBRow,const OUString& aDBColumnName, sal_Int32 nType ) +{ + MQueryHelperResultEntry* pResEntry = getByIndex( nDBRow ); + + OSL_ENSURE( pResEntry != nullptr, "xResEntry == NULL"); + if (pResEntry == nullptr ) + { + rValue.setNull(); + return false; + } + switch ( nType ) + { + case DataType::VARCHAR: + rValue = pResEntry->getValue( m_rColumnAlias.getProgrammaticNameOrFallbackToUTF8Alias( aDBColumnName ) ); + break; + + default: + rValue.setNull(); + break; + } + + return true; +} + +sal_Int32 MQueryHelper::executeQuery(OConnection* xConnection, MQueryExpression & expr) +{ + reset(); + + OString oStringTable = OUStringToOString( m_aAddressbook, RTL_TEXTENCODING_UTF8 ); + std::set<int> listRecords; + bool handleListTable = false; + MorkParser* pMork; + + // check if we are retrieving the default table + if (oStringTable == "AddressBook" || oStringTable == "CollectedAddressBook") + { + pMork = xConnection->getMorkParser(oStringTable); + } + else + { + // Let's try to retrieve the list in Collected Addresses book + pMork = xConnection->getMorkParser("CollectedAddressBook"); + if (std::find(pMork->lists_.begin(), pMork->lists_.end(), m_aAddressbook) == pMork->lists_.end()) + { + // so the list is in Address book + // TODO : manage case where an address book has been created + pMork = xConnection->getMorkParser("AddressBook"); + } + handleListTable = true; + // retrieve row ids for that list table + std::string listTable = oStringTable.getStr(); + pMork->getRecordKeysForListTable(listTable, listRecords); + } + + MorkTableMap *Tables = pMork->getTables( 0x80 ); + if (!Tables) + return -1; + + MorkRowMap *Rows = nullptr; + + // Iterate all tables + for (auto & table : Tables->map) + { + if (table.first != 1) break; + Rows = MorkParser::getRows( 0x80, &table.second ); + if ( Rows ) + { + // Iterate all rows + for (auto const& row : Rows->map) + { + // list specific table + // only retrieve rowIds that belong to that list table. + if (handleListTable) + { + int rowId = row.first; + // belongs this row id to the list table? + if (listRecords.end() == listRecords.find(rowId)) + { + // no, skip it + continue; + } + } + + std::unique_ptr<MQueryHelperResultEntry> entry(new MQueryHelperResultEntry()); + for (auto const& cell : row.second) + { + std::string column = pMork->getColumn(cell.first); + std::string value = pMork->getValue(cell.second); + OString key(column.c_str(), static_cast<sal_Int32>(column.size())); + OString valueOString(value.c_str(), static_cast<sal_Int32>(value.size())); + OUString valueOUString = OStringToOUString( valueOString, RTL_TEXTENCODING_UTF8 ); + entry->setValue(key, valueOUString); + } + bool result = true; + for (bool elem : entryMatchedByExpression(this, &expr, entry.get())) + { + result = result && elem; + } + if (result) + { + append(std::move(entry)); + } + } + } + } + return 0; +} + +std::vector<bool> entryMatchedByExpression(MQueryHelper* _aQuery, MQueryExpression const * _aExpr, MQueryHelperResultEntry* entry) +{ + std::vector<bool> resultVector; + for (auto const& expr : _aExpr->getExpressions()) + { + if ( expr->isStringExpr() ) { + MQueryExpressionString* evStr = static_cast<MQueryExpressionString*> (expr); + // Set the 'name' property of the boolString. + OString attrName = _aQuery->getColumnAlias().getProgrammaticNameOrFallbackToUTF8Alias( evStr->getName() ); + SAL_INFO("connectivity.mork", "Name = " << attrName); + bool bRequiresValue = true; + OUString currentValue = entry->getValue(attrName); + if (evStr->getCond() == MQueryOp::Exists || evStr->getCond() == MQueryOp::DoesNotExist) + { + bRequiresValue = false; + } + if (bRequiresValue) + { + SAL_INFO("connectivity.mork", "Value = " << evStr->getValue() ); + const OUString& searchedValue = evStr->getValue(); + if (evStr->getCond() == MQueryOp::Is) { + SAL_INFO("connectivity.mork", "MQueryOp::Is; done"); + resultVector.push_back(currentValue == searchedValue); + } else if (evStr->getCond() == MQueryOp::IsNot) { + SAL_INFO("connectivity.mork", "MQueryOp::IsNot; done"); + resultVector.push_back(currentValue != searchedValue); + } else if (evStr->getCond() == MQueryOp::EndsWith) { + SAL_INFO("connectivity.mork", "MQueryOp::EndsWith; done"); + resultVector.push_back(currentValue.endsWith(searchedValue)); + } else if (evStr->getCond() == MQueryOp::BeginsWith) { + SAL_INFO("connectivity.mork", "MQueryOp::BeginsWith; done"); + resultVector.push_back(currentValue.startsWith(searchedValue)); + } else if (evStr->getCond() == MQueryOp::Contains) { + SAL_INFO("connectivity.mork", "MQueryOp::Contains; done"); + resultVector.push_back(currentValue.indexOf(searchedValue) != -1); + } else if (evStr->getCond() == MQueryOp::DoesNotContain) { + SAL_INFO("connectivity.mork", "MQueryOp::DoesNotContain; done"); + resultVector.push_back(currentValue.indexOf(searchedValue) == -1); + } else if (evStr->getCond() == MQueryOp::RegExp) { + SAL_INFO("connectivity.mork", "MQueryOp::RegExp; done"); + utl::SearchParam param( + searchedValue, utl::SearchParam::SearchType::Regexp); + utl::TextSearch ts(param, LANGUAGE_DONTKNOW); + sal_Int32 start = 0; + sal_Int32 end = currentValue.getLength(); + resultVector.push_back( + ts.SearchForward(currentValue, &start, &end)); + } + } else if (evStr->getCond() == MQueryOp::Exists) { + SAL_INFO("connectivity.mork", "MQueryOp::Exists; done"); + resultVector.push_back(!currentValue.isEmpty()); + } else if (evStr->getCond() == MQueryOp::DoesNotExist) { + SAL_INFO("connectivity.mork", "MQueryOp::DoesNotExist; done"); + resultVector.push_back(currentValue.isEmpty()); + } + } + else if ( expr->isExpr() ) { + SAL_INFO("connectivity.mork", "Appending Subquery Expression"); + MQueryExpression* queryExpression = static_cast<MQueryExpression*> (expr); + // recursive call + std::vector<bool> subquery_result = entryMatchedByExpression(_aQuery, queryExpression, entry); + MQueryExpression::bool_cond condition = queryExpression->getExpressionCondition(); + if (condition == MQueryExpression::OR) { + bool result = false; + for (bool elem : subquery_result) + { + result = result || elem; + } + resultVector.push_back(result); + } else { + assert(condition == MQueryExpression::AND && "only OR or AND should exist"); + bool result = true; + for (bool elem : subquery_result) + { + result = result && elem; + } + resultVector.push_back(result); + } + } + else { + // Should never see this... + SAL_WARN("connectivity.mork", "Unknown Expression Type!"); + _aQuery->getError().setResId(STR_ERROR_GET_ROW); + return resultVector; + } + } + return resultVector; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MQueryHelper.hxx b/connectivity/source/drivers/mork/MQueryHelper.hxx new file mode 100644 index 000000000..67d2b081d --- /dev/null +++ b/connectivity/source/drivers/mork/MQueryHelper.hxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MQUERYHELPER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MQUERYHELPER_HXX + +#include <connectivity/FValue.hxx> +#include "MErrorResource.hxx" +#include "MColumnAlias.hxx" + +namespace connectivity +{ + namespace mork + { + class OConnection; + class MQueryHelper; + class ErrorDescriptor; + + namespace MQueryOp { + typedef enum { + Exists = 0, + DoesNotExist = 1, + Contains = 2, + DoesNotContain = 3, + Is = 4, + IsNot = 5, + BeginsWith = 6, + EndsWith = 7, + RegExp = 8 + } cond_type; + } + + class MQueryExpressionBase { + public: + enum class node_type { + StringExpr, + Expr + }; + + protected: + node_type m_eNodeType; + + explicit MQueryExpressionBase( node_type _eNodeType ) : m_eNodeType( _eNodeType ) {} + + public: + virtual ~MQueryExpressionBase() {} + + bool isStringExpr( ) const { return m_eNodeType == node_type::StringExpr; } + bool isExpr( ) const { return m_eNodeType == node_type::Expr; } + }; + + class MQueryExpressionString final : public MQueryExpressionBase { + OUString m_aName; // LHS + MQueryOp::cond_type m_aBooleanCondition; + OUString m_aValue; // RHS + + public: + + MQueryExpressionString( const OUString& lhs, + MQueryOp::cond_type cond, + const OUString& rhs ) + : MQueryExpressionBase( MQueryExpressionBase::node_type::StringExpr ) + , m_aName( lhs ) + , m_aBooleanCondition( cond ) + , m_aValue( rhs ) + { + } + + MQueryExpressionString( const OUString& lhs, + MQueryOp::cond_type cond ) + : MQueryExpressionBase( MQueryExpressionBase::node_type::StringExpr ) + , m_aName( lhs ) + , m_aBooleanCondition( cond ) + , m_aValue( OUString() ) + { + } + + const OUString& getName() const { return m_aName; } + MQueryOp::cond_type getCond() const { return m_aBooleanCondition; } + const OUString& getValue() const { return m_aValue; } + }; + + class MQueryExpression final : public MQueryExpressionBase + { + friend class MQueryHelper; + + public: + typedef std::vector< MQueryExpressionBase* > ExprVector; + + typedef enum { + AND, + OR + } bool_cond; + + // All expressions on a peer level use same condition operator + void setExpressionCondition( bool_cond _cond ) + { m_aExprCondType = _cond; } + + void addExpression(MQueryExpressionBase * expr) + { m_aExprVector.push_back(expr); } + + ExprVector const & getExpressions( ) const + { return m_aExprVector; } + + // All expressions on a peer level use same condition operator + bool_cond getExpressionCondition( ) const + { return m_aExprCondType; } + + MQueryExpression() : MQueryExpressionBase( MQueryExpressionBase::node_type::Expr ), + m_aExprCondType( OR ) + {} + + private: + ExprVector m_aExprVector; + bool_cond m_aExprCondType; + + MQueryExpression(const MQueryExpression&) = delete; + MQueryExpression& operator=(const MQueryExpression&) = delete; + }; + + class MQueryHelperResultEntry + { + private: + typedef std::unordered_map< OString, OUString > FieldMap; + + FieldMap m_Fields; + + public: + MQueryHelperResultEntry(); + ~MQueryHelperResultEntry(); + + OUString getValue( const OString &key ) const; + void setValue( const OString &key, const OUString & rValue); + }; + + class MQueryHelper final + { + private: + + mutable ::osl::Mutex m_aMutex; + std::vector< std::unique_ptr<MQueryHelperResultEntry> > m_aResults; + void append(std::unique_ptr<MQueryHelperResultEntry> resEnt ); + void clear_results(); + OColumnAlias m_rColumnAlias; + ErrorDescriptor m_aError; + OUString m_aAddressbook; + + public: + explicit MQueryHelper(const OColumnAlias& _ca); + ~MQueryHelper(); + + void reset(); + MQueryHelperResultEntry* getByIndex( sal_uInt32 nRow ); + sal_Int32 getResultCount() const; + bool getRowValue( ORowSetValue& rValue, sal_Int32 nDBRow,const OUString& aDBColumnName, sal_Int32 nType ); + sal_Int32 executeQuery(OConnection* xConnection, MQueryExpression & expr); + const OColumnAlias& getColumnAlias() const { return m_rColumnAlias; } + bool hadError() const { return m_aError.is(); } + ErrorDescriptor& getError() { return m_aError; } + + void setAddressbook( OUString const &); + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MQUERYHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MResultSet.cxx b/connectivity/source/drivers/mork/MResultSet.cxx new file mode 100644 index 000000000..ab6b11a9d --- /dev/null +++ b/connectivity/source/drivers/mork/MResultSet.cxx @@ -0,0 +1,1710 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <connectivity/dbtools.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> + +#include <vector> +#include <algorithm> +#include "MResultSet.hxx" +#include <sqlbison.hxx> +#include "MResultSetMetaData.hxx" +#include <FDatabaseMetaDataResultSet.hxx> + +#include <strings.hrc> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::mork; +using namespace ::cppu; +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::io; +using namespace com::sun::star::util; + + +// IMPLEMENT_SERVICE_INFO(OResultSet,"com.sun.star.sdbcx.OResultSet","com.sun.star.sdbc.ResultSet"); +OUString SAL_CALL OResultSet::getImplementationName( ) +{ + return "com.sun.star.sdbcx.mork.ResultSet"; +} + + Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames( ) +{ + return {"com.sun.star.sdbc.ResultSet","com.sun.star.sdbcx.ResultSet"}; +} + +sal_Bool SAL_CALL OResultSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + + +OResultSet::OResultSet(OCommonStatement* pStmt, const std::shared_ptr< connectivity::OSQLParseTreeIterator >& _pSQLIterator ) + : OResultSet_BASE(m_aMutex) + ,OPropertySetHelper(OResultSet_BASE::rBHelper) + ,m_pStatement(pStmt) + ,m_xStatement(*pStmt) + ,m_nRowPos(0) + ,m_bWasNull(false) + ,m_nResultSetType(ResultSetType::SCROLL_INSENSITIVE) + ,m_nFetchDirection(FetchDirection::FORWARD) + ,m_pSQLIterator( _pSQLIterator ) + ,m_pParseTree( _pSQLIterator->getParseTree() ) + ,m_aQueryHelper(pStmt->getOwnConnection()->getColumnAlias()) + ,m_CurrentRowCount(0) + ,m_nParamIndex(0) + ,m_bIsAlwaysFalseQuery(false) + ,m_bIsReadOnly(TRISTATE_INDET) +{ + //m_aQuery.setMaxNrOfReturns(pStmt->getOwnConnection()->getMaxResultRecords()); +} + +OResultSet::~OResultSet() +{ +} + + +void OResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + + m_xStatement.clear(); + m_xMetaData.clear(); + m_pParseTree = nullptr; + m_xColumns = nullptr; + m_pKeySet = nullptr; + m_xTable.clear(); +} + +Any SAL_CALL OResultSet::queryInterface( const Type & rType ) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OResultSet_BASE::queryInterface(rType); + return aRet; +} + + Sequence< Type > SAL_CALL OResultSet::getTypes( ) +{ + 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(),OResultSet_BASE::getTypes()); +} + +void OResultSet::methodEntry() +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + if ( !m_xTable.is() ) + { + OSL_FAIL( "OResultSet::methodEntry: looks like we're disposed, but how is this possible?" ); + throw DisposedException( OUString(), *this ); + } +} + + +sal_Int32 SAL_CALL OResultSet::findColumn( const OUString& columnName ) +{ + ResultSetEntryGuard aGuard( *this ); + + // find the first column with the name columnName + 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 +} + +Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + +Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ ) +{ + return nullptr; +} + + +sal_Bool SAL_CALL OResultSet::getBoolean( sal_Int32 /*columnIndex*/ ) +{ + ResultSetEntryGuard aGuard( *this ); + m_bWasNull = true; + return false; +} + + +sal_Int8 SAL_CALL OResultSet::getByte( sal_Int32 /*columnIndex*/ ) +{ + return 0; +} + + +Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes( sal_Int32 /*columnIndex*/ ) +{ + return Sequence< sal_Int8 >(); +} + + +Date SAL_CALL OResultSet::getDate( sal_Int32 /*columnIndex*/ ) +{ + return Date(); +} + + +double SAL_CALL OResultSet::getDouble( sal_Int32 /*columnIndex*/ ) +{ + return 0.0; +} + + +float SAL_CALL OResultSet::getFloat( sal_Int32 /*columnIndex*/ ) +{ + return 0; +} + + +sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 /*columnIndex*/ ) +{ + return 0; +} + + +sal_Int32 SAL_CALL OResultSet::getRow( ) +{ + ResultSetEntryGuard aGuard( *this ); + + SAL_INFO("connectivity.mork", "return = " << m_nRowPos); + return m_nRowPos; +} + + +sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 /*columnIndex*/ ) +{ + return sal_Int64(); +} + + +Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData( ) +{ + ResultSetEntryGuard aGuard( *this ); + + if(!m_xMetaData.is()) + m_xMetaData = new OResultSetMetaData( + m_pSQLIterator->getSelectColumns(), m_pSQLIterator->getTables().begin()->first, m_xTable.get(), determineReadOnly()); + 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 Any(); +} + + +sal_Int16 SAL_CALL OResultSet::getShort( sal_Int32 /*columnIndex*/ ) +{ + return 0; +} + + +void OResultSet::checkIndex(sal_Int32 columnIndex ) +{ + if(columnIndex <= 0 || columnIndex > static_cast<sal_Int32>(m_xColumns->size())) + ::dbtools::throwInvalidIndexException(*this); +} + +sal_uInt32 OResultSet::currentRowCount() +{ + if ( m_bIsAlwaysFalseQuery ) + return 0; + //return 0;//m_aQuery.getRealRowCount() - deletedCount(); + // new implementation + return m_aQueryHelper.getResultCount(); +} + + +bool OResultSet::fetchCurrentRow( ) +{ + SAL_INFO("connectivity.mork", "m_nRowPos = " << m_nRowPos); + return fetchRow(getCurrentCardNumber()); +} + + +bool OResultSet::fetchRow(sal_Int32 cardNumber,bool bForceReload) +{ + SAL_INFO("connectivity.mork", "cardNumber = " << cardNumber); + if (!bForceReload) + { + // Check whether we've already fetched the row... + if ( !(*m_aRow)[0].isNull() && static_cast<sal_Int32>((*m_aRow)[0]) == cardNumber ) + return true; + } +// else +// m_aQuery.resyncRow(cardNumber); + + if ( !validRow( cardNumber ) ) + return false; + + (*m_aRow)[0] = cardNumber; + sal_Int32 nCount = m_aColumnNames.getLength(); + //m_RowStates = m_aQuery.getRowStates(cardNumber); + for( sal_Int32 i = 1; i <= nCount; i++ ) + { + if ( (*m_aRow)[i].isBound() ) + { + + // Everything in the addressbook is a string! + + if ( !m_aQueryHelper.getRowValue( (*m_aRow)[i], cardNumber, m_aColumnNames[i-1], DataType::VARCHAR )) + { + m_pStatement->getOwnConnection()->throwSQLException( m_aQueryHelper.getError(), *this ); + } + } + } + return true; + +} + + +const ORowSetValue& OResultSet::getValue(sal_Int32 cardNumber, sal_Int32 columnIndex ) +{ + if ( !fetchRow( cardNumber ) ) + { + OSL_FAIL("fetchRow() returned False" ); + m_bWasNull = true; + return *ODatabaseMetaDataResultSet::getEmptyValue(); + } + + m_bWasNull = (*m_aRow)[columnIndex].isNull(); + return (*m_aRow)[columnIndex]; + +} + + +OUString SAL_CALL OResultSet::getString( sal_Int32 columnIndex ) +{ + ResultSetEntryGuard aGuard( *this ); + + OSL_ENSURE(m_xColumns.is(), "Need the Columns!!"); + OSL_ENSURE(columnIndex <= static_cast<sal_Int32>(m_xColumns->size()), "Trying to access invalid columns number"); + checkIndex( columnIndex ); + + // If this query was sorted then we should have a valid KeySet, so use it + return getValue(getCurrentCardNumber(), mapColumn( columnIndex ) ); + +} + + +Time SAL_CALL OResultSet::getTime( sal_Int32 /*columnIndex*/ ) +{ + ResultSetEntryGuard aGuard( *this ); + return Time(); +} + + +DateTime SAL_CALL OResultSet::getTimestamp( sal_Int32 /*columnIndex*/ ) +{ + ResultSetEntryGuard aGuard( *this ); + return DateTime(); +} + + +sal_Bool SAL_CALL OResultSet::isBeforeFirst( ) +{ + ResultSetEntryGuard aGuard( *this ); + + // here you have to implement your movements + // return true means there is no data + return( m_nRowPos < 1 ); +} + +sal_Bool SAL_CALL OResultSet::isAfterLast( ) +{ + SAL_WARN("connectivity.mork", "OResultSet::isAfterLast() NOT IMPLEMENTED!"); + ResultSetEntryGuard aGuard( *this ); + + return m_nRowPos > currentRowCount(); +} + +sal_Bool SAL_CALL OResultSet::isFirst( ) +{ + ResultSetEntryGuard aGuard( *this ); + + return m_nRowPos == 1; +} + +sal_Bool SAL_CALL OResultSet::isLast( ) +{ + SAL_WARN("connectivity.mork", "OResultSet::isLast() NOT IMPLEMENTED!"); + ResultSetEntryGuard aGuard( *this ); + +// return sal_True; + return m_nRowPos == currentRowCount(); +} + +void SAL_CALL OResultSet::beforeFirst( ) +{ + ResultSetEntryGuard aGuard( *this ); + + // move before the first row so that isBeforeFirst returns false + if ( first() ) + previous(); +} + +void SAL_CALL OResultSet::afterLast( ) +{ + ResultSetEntryGuard aGuard( *this ); + + if(last()) + next(); +} + + +void SAL_CALL OResultSet::close() +{ + dispose(); +} + + +sal_Bool SAL_CALL OResultSet::first( ) +{ + return seekRow( FIRST_POS ); +} + + +sal_Bool SAL_CALL OResultSet::last( ) +{ + return seekRow( LAST_POS ); +} + +sal_Bool SAL_CALL OResultSet::absolute( sal_Int32 row ) +{ + return seekRow( ABSOLUTE_POS, row ); +} + +sal_Bool SAL_CALL OResultSet::relative( sal_Int32 row ) +{ + return seekRow( RELATIVE_POS, row ); +} + +sal_Bool SAL_CALL OResultSet::previous( ) +{ + ResultSetEntryGuard aGuard( *this ); + return seekRow( PRIOR_POS ); +} + +Reference< XInterface > SAL_CALL OResultSet::getStatement( ) +{ + ResultSetEntryGuard aGuard( *this ); + return m_xStatement; +} + + +sal_Bool SAL_CALL OResultSet::rowDeleted( ) +{ + SAL_WARN("connectivity.mork", "OResultSet::rowDeleted() NOT IMPLEMENTED!"); + ResultSetEntryGuard aGuard( *this ); + return true;//return ((m_RowStates & RowStates_Deleted) == RowStates_Deleted) ; +} + +sal_Bool SAL_CALL OResultSet::rowInserted( ) +{ + SAL_WARN("connectivity.mork", "OResultSet::rowInserted() NOT IMPLEMENTED!"); + ResultSetEntryGuard aGuard( *this ); + return true;//return ((m_RowStates & RowStates_Inserted) == RowStates_Inserted); +} + +sal_Bool SAL_CALL OResultSet::rowUpdated( ) +{ + SAL_WARN("connectivity.mork", "OResultSet::rowUpdated() NOT IMPLEMENTED!"); + ResultSetEntryGuard aGuard( *this ); + return true;// return ((m_RowStates & RowStates_Updated) == RowStates_Updated) ; +} + + +sal_Bool SAL_CALL OResultSet::next( ) +{ + return seekRow( NEXT_POS ); +} + + +sal_Bool SAL_CALL OResultSet::wasNull( ) +{ + ResultSetEntryGuard aGuard( *this ); + + return m_bWasNull; +} + + +void SAL_CALL OResultSet::cancel( ) +{ +} + +void SAL_CALL OResultSet::clearWarnings( ) +{ +} + +Any SAL_CALL OResultSet::getWarnings( ) +{ + return Any(); +} + +void SAL_CALL OResultSet::refreshRow( ) +{ + if (fetchRow(getCurrentCardNumber(),true)) { + //force fetch current row will cause we lose all change to the current row + m_pStatement->getOwnConnection()->throwSQLException( STR_ERROR_REFRESH_ROW, *this ); + } +} + +IPropertyArrayHelper* OResultSet::createArrayHelper( ) const +{ + Sequence< Property > aProps(5); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), + PROPERTY_ID_ISBOOKMARKABLE, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + return new OPropertyArrayHelper(aProps); +} + +IPropertyArrayHelper & OResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OResultSet::convertFastPropertyValue( + Any & /*rConvertedValue*/, + Any & /*rOldValue*/, + sal_Int32 nHandle, + const Any& /*rValue*/ ) +{ + OSL_FAIL( "OResultSet::convertFastPropertyValue: not implemented!" ); + switch(nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + default: + ; + } + return false; +} + +void OResultSet::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const Any& /*rValue*/ + ) +{ + OSL_FAIL( "OResultSet::setFastPropertyValue_NoBroadcast: not implemented!" ); + switch(nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw Exception("cannot set prop " + OUString::number(nHandle), nullptr); + case PROPERTY_ID_FETCHDIRECTION: + break; + case PROPERTY_ID_FETCHSIZE: + break; + default: + ; + } +} + +void OResultSet::getFastPropertyValue( + Any& rValue, + sal_Int32 nHandle + ) const +{ + switch(nHandle) + { + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= sal_Int32(ResultSetConcurrency::UPDATABLE); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= m_nResultSetType; + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= m_nFetchDirection; + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= sal_Int32(0); + break; + case PROPERTY_ID_ISBOOKMARKABLE: + const_cast< OResultSet* >( this )->determineReadOnly(); + rValue <<= (m_bIsReadOnly == TRISTATE_FALSE); + break; + } +} + +void SAL_CALL OResultSet::acquire() throw() +{ + OResultSet_BASE::acquire(); +} + +void SAL_CALL OResultSet::release() throw() +{ + OResultSet_BASE::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + + +void OResultSet::parseParameter( const OSQLParseNode* pNode, OUString& rMatchString ) +{ + OSL_ENSURE(pNode->count() > 0,"Error parsing parameter in Parse Tree"); + OSQLParseNode *pMark = pNode->getChild(0); + + // Initialize to empty string + rMatchString.clear(); + + OUString aParameterName; + if (SQL_ISPUNCTUATION(pMark,"?")) { + aParameterName = "?"; + } + else if (SQL_ISPUNCTUATION(pMark,":")) { + aParameterName = pNode->getChild(1)->getTokenValue(); + } + // XXX - Now we know name, what's value???? + m_nParamIndex ++; + SAL_INFO( + "connectivity.mork", + "Parameter name [" << m_nParamIndex << "]: " << aParameterName); + + if ( m_aParameterRow.is() ) { + OSL_ENSURE( m_nParamIndex < static_cast<sal_Int32>(m_aParameterRow->size()) + 1, "More parameters than values found" ); + rMatchString = (*m_aParameterRow)[static_cast<sal_uInt16>(m_nParamIndex)]; + SAL_INFO("connectivity.mork", "Prop Value: " << rMatchString); + } + else { + SAL_INFO("connectivity.mork", "Prop Value: Invalid ParameterRow!"); + } +} + +#define WILDCARD "%" +#define ALT_WILDCARD "*" +static const sal_Unicode MATCHCHAR = '_'; + +void OResultSet::analyseWhereClause( const OSQLParseNode* parseTree, + MQueryExpression &queryExpression) +{ + OUString columnName; + MQueryOp::cond_type op( MQueryOp::Is ); + OUString matchString; + + if ( parseTree == nullptr ) + return; + + if ( m_pSQLIterator->getParseTree() != nullptr ) { + ::rtl::Reference<OSQLColumns> xColumns = m_pSQLIterator->getParameters(); + if(xColumns.is()) + { + OUString aColName, aParameterValue; + sal_Int32 i = 1; + for (auto const& column : *xColumns) + { + column->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aColName; + SAL_INFO("connectivity.mork", "Prop Column Name: " << aColName); + if ( m_aParameterRow.is() ) { + aParameterValue = (*m_aParameterRow)[static_cast<sal_uInt16>(i)]; + SAL_INFO("connectivity.mork", "Prop Value: " << aParameterValue); + } + else { + SAL_INFO("connectivity.mork", "Prop Value: Invalid ParameterRow!"); + } + i++; + } + } + + } + + if ( SQL_ISRULE(parseTree,where_clause) ) + { + // Reset Parameter Counter + resetParameters(); + analyseWhereClause( parseTree->getChild( 1 ), queryExpression ); + } + else if ( parseTree->count() == 3 && // Handle ()'s + SQL_ISPUNCTUATION(parseTree->getChild(0),"(") && + SQL_ISPUNCTUATION(parseTree->getChild(2),")")) + { + MQueryExpression *subExpression = new MQueryExpression(); + analyseWhereClause( parseTree->getChild( 1 ), *subExpression ); + queryExpression.addExpression( subExpression ); + } + else if ((SQL_ISRULE(parseTree,search_condition) || SQL_ISRULE(parseTree,boolean_term)) + && parseTree->count() == 3) // Handle AND/OR + { + // TODO - Need to take care or AND, for now match is always OR + analyseWhereClause( parseTree->getChild( 0 ), queryExpression ); + analyseWhereClause( parseTree->getChild( 2 ), queryExpression ); + + if (SQL_ISTOKEN(parseTree->getChild(1),OR)) { // OR-Operator + queryExpression.setExpressionCondition( MQueryExpression::OR ); + } + else if (SQL_ISTOKEN(parseTree->getChild(1),AND)) { // AND-Operator + queryExpression.setExpressionCondition( MQueryExpression::AND ); + } + else { + OSL_FAIL("analyseSQL: Error in Parse Tree"); + } + } + else if (SQL_ISRULE(parseTree,comparison_predicate)) + { + OSL_ENSURE(parseTree->count() == 3, "Error parsing COMPARE predicate"); + if (!(SQL_ISRULE(parseTree->getChild(0),column_ref) || + parseTree->getChild(2)->getNodeType() == SQLNodeType::String || + parseTree->getChild(2)->getNodeType() == SQLNodeType::IntNum || + parseTree->getChild(2)->getNodeType() == SQLNodeType::ApproxNum || + SQL_ISTOKEN(parseTree->getChild(2),TRUE) || + SQL_ISTOKEN(parseTree->getChild(2),FALSE) || + SQL_ISRULE(parseTree->getChild(2),parameter) || + // odbc date + (SQL_ISRULE(parseTree->getChild(2),set_fct_spec) && SQL_ISPUNCTUATION(parseTree->getChild(2)->getChild(0),"{")))) + { + m_pStatement->getOwnConnection()->throwSQLException( STR_QUERY_TOO_COMPLEX, *this ); + } + + OSQLParseNode *pPrec = parseTree->getChild(1); + if (pPrec->getNodeType() == SQLNodeType::Equal) + op = MQueryOp::Is; + else if (pPrec->getNodeType() == SQLNodeType::NotEqual) + op = MQueryOp::IsNot; + + OUString sTableRange; + if(SQL_ISRULE(parseTree->getChild(0),column_ref)) + m_pSQLIterator->getColumnRange(parseTree->getChild(0),columnName,sTableRange); + else if(parseTree->getChild(0)->isToken()) + columnName = parseTree->getChild(0)->getTokenValue(); + + if ( SQL_ISRULE(parseTree->getChild(2),parameter) ) { + parseParameter( parseTree->getChild(2), matchString ); + } + else { + matchString = parseTree->getChild(2)->getTokenValue(); + } + + if ( columnName == "0" && op == MQueryOp::Is && matchString == "1" ) { + m_bIsAlwaysFalseQuery = true; + } + queryExpression.addExpression( new MQueryExpressionString( columnName, op, matchString )); + } + else if (SQL_ISRULE(parseTree,like_predicate)) + { + OSL_ENSURE(parseTree->count() == 2, "Error parsing LIKE predicate"); + + if ( !SQL_ISRULE(parseTree->getChild(0), column_ref) ) + { + m_pStatement->getOwnConnection()->throwSQLException( STR_QUERY_INVALID_LIKE_COLUMN, *this ); + } + + + OSQLParseNode *pColumn; + OSQLParseNode *pAtom; + OSQLParseNode *pOptEscape; + const OSQLParseNode* pPart2 = parseTree->getChild(1); + pColumn = parseTree->getChild(0); // Match Item + pAtom = pPart2->getChild(static_cast<sal_uInt32>(pPart2->count()-2)); // Match String + pOptEscape = pPart2->getChild(static_cast<sal_uInt32>(pPart2->count()-1)); // Opt Escape Rule + (void)pOptEscape; + const bool bNot = SQL_ISTOKEN(pPart2->getChild(0), NOT); + + if (!(pAtom->getNodeType() == SQLNodeType::String || + pAtom->getNodeType() == SQLNodeType::Name || + SQL_ISRULE(pAtom,parameter) || + ( pAtom->getChild(0) && pAtom->getChild(0)->getNodeType() == SQLNodeType::Name ) || + ( pAtom->getChild(0) && pAtom->getChild(0)->getNodeType() == SQLNodeType::String ) + ) ) + { + m_pStatement->getOwnConnection()->throwSQLException( STR_QUERY_INVALID_LIKE_STRING, *this ); + } + + OUString sTableRange; + if(SQL_ISRULE(pColumn,column_ref)) + m_pSQLIterator->getColumnRange(pColumn,columnName,sTableRange); + + SAL_INFO("connectivity.mork", "ColumnName = " << columnName); + + if ( SQL_ISRULE(pAtom,parameter) ) { + parseParameter( pAtom, matchString ); + // Replace all '*' with '%' : UI Usually does this but not with + // Parameters for some reason. + matchString = matchString.replaceAll( ALT_WILDCARD, WILDCARD ); + } + else + { + matchString = pAtom->getTokenValue(); + } + + // Determine where '%' character is... + + if ( matchString == WILDCARD ) + { + // String containing only a '%' and nothing else + op = MQueryOp::Exists; + // Will be ignored for Exists case, but clear anyway. + matchString.clear(); + } + else if ( matchString.indexOf ( WILDCARD ) == -1 && + matchString.indexOf ( MATCHCHAR ) == -1 ) + { + // Simple string , eg. "to match" + if ( bNot ) + op = MQueryOp::DoesNotContain; + else + op = MQueryOp::Contains; + } + else if ( matchString.startsWith( WILDCARD ) + && matchString.endsWith( WILDCARD ) + && matchString.indexOf ( WILDCARD, 1 ) == matchString.lastIndexOf ( WILDCARD ) + && matchString.indexOf( MATCHCHAR ) == -1 + ) + { + // Relatively simple "%string%" - ie, contains... + // Cut '%' from front and rear + matchString = matchString.replaceAt( 0, 1, OUString() ); + matchString = matchString.replaceAt( matchString.getLength() -1 , 1, OUString() ); + + if (bNot) + op = MQueryOp::DoesNotContain; + else + op = MQueryOp::Contains; + } + else if ( bNot ) + { + // We currently can't handle a 'NOT LIKE' when there are '%' or + // '_' dispersed throughout + m_pStatement->getOwnConnection()->throwSQLException( STR_QUERY_NOT_LIKE_TOO_COMPLEX, *this ); + } + else + { + if ( (matchString.indexOf ( WILDCARD ) == matchString.lastIndexOf ( WILDCARD )) + && matchString.indexOf( MATCHCHAR ) == -1 + ) + { + // One occurrence of '%' - no '_' matches... + if ( matchString.startsWith( WILDCARD ) ) + { + op = MQueryOp::EndsWith; + matchString = matchString.replaceAt( 0, 1, OUString()); + } + else if ( matchString.indexOf ( WILDCARD ) == matchString.getLength() -1 ) + { + op = MQueryOp::BeginsWith; + matchString = matchString.replaceAt( matchString.getLength() -1 , 1, OUString() ); + } + else + { + sal_Int32 pos = matchString.indexOf ( WILDCARD ); + matchString = matchString.replaceAt( pos, 1, ".*" ); + op = MQueryOp::RegExp; + } + + } + else + { + // Most Complex, need to use a RE + sal_Int32 pos; + while ( (pos = matchString.indexOf ( WILDCARD )) != -1 ) + { + matchString = matchString.replaceAt( pos, 1, ".*" ); + } + + while ( (pos = matchString.indexOf( MATCHCHAR )) != -1 ) + { + matchString = matchString.replaceAt( pos, 1, "." ); + } + + op = MQueryOp::RegExp; + } + } + + queryExpression.addExpression( new MQueryExpressionString( columnName, op, matchString )); + } + else if (SQL_ISRULE(parseTree,test_for_null)) + { + OSL_ENSURE(parseTree->count() == 2,"Error in ParseTree"); + const OSQLParseNode* pPart2 = parseTree->getChild(1); + OSL_ENSURE(SQL_ISTOKEN(pPart2->getChild(0),IS),"Error in ParseTree"); + + if (!SQL_ISRULE(parseTree->getChild(0),column_ref)) + { + m_pStatement->getOwnConnection()->throwSQLException( STR_QUERY_INVALID_IS_NULL_COLUMN, *this ); + } + + if (SQL_ISTOKEN(pPart2->getChild(1),NOT)) + { + op = MQueryOp::Exists; + } + else + { + op = MQueryOp::DoesNotExist; + } + + OUString sTableRange; + m_pSQLIterator->getColumnRange(parseTree->getChild(0),columnName,sTableRange); + + queryExpression.addExpression( new MQueryExpressionString( columnName, op )); + } + else + { + SAL_WARN("connectivity.mork", "Unexpected statement!!!" ); + m_pStatement->getOwnConnection()->throwSQLException( STR_QUERY_TOO_COMPLEX, *this ); + } +} + +void OResultSet::fillRowData() +{ + OSL_ENSURE( m_pStatement, "Require a statement" ); + + MQueryExpression queryExpression; + + OConnection* pConnection = static_cast<OConnection*>(m_pStatement->getConnection().get()); + m_xColumns = m_pSQLIterator->getSelectColumns(); + + OSL_ENSURE(m_xColumns.is(), "Need the Columns!!"); + + const OUString sPropertyName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME); + OUString sName; + sal_Int32 i = 1; + for (const auto& rxColumn : *m_xColumns) + { + rxColumn->getPropertyValue(sPropertyName) >>= sName; + SAL_INFO( + "connectivity.mork", "Query Columns : (" << i << ") " << sName); + i++; + } + + // Generate Match Conditions for Query + const OSQLParseNode* pParseTree = m_pSQLIterator->getWhereTree(); + + m_bIsAlwaysFalseQuery = false; + if ( pParseTree != nullptr ) + { + // Extract required info + + analyseWhereClause( pParseTree, queryExpression ); + } + // If the query is a 0=1 then set Row count to 0 and return + if ( m_bIsAlwaysFalseQuery ) + { + m_bIsReadOnly = TRISTATE_TRUE; + return; + } + + OUString aStr( m_xTable->getName() ); + m_aQueryHelper.setAddressbook( aStr ); + + sal_Int32 rv = m_aQueryHelper.executeQuery(pConnection, queryExpression); + if ( rv == -1 ) { + m_pStatement->getOwnConnection()->throwSQLException( STR_ERR_EXECUTING_QUERY, *this ); + } + + if (m_aQueryHelper.hadError()) + { + m_pStatement->getOwnConnection()->throwSQLException( m_aQueryHelper.getError(), *this ); + } + + //determine whether the address book is readonly + determineReadOnly(); + + SAL_INFO("connectivity.mork", "executeQuery returned " << rv); +} + + +static bool matchRow( OValueRow const & row1, OValueRow const & row2 ) +{ + // the first column is the bookmark column + return std::equal(std::next(row1->begin()), row1->end(), std::next(row2->begin()), + [](const ORowSetValue& a, const ORowSetValue& b) { return !a.isBound() || a == b; }); +} + +sal_Int32 OResultSet::getRowForCardNumber(sal_Int32 nCardNum) +{ + SAL_INFO("connectivity.mork", "nCardNum = " << nCardNum); + + if ( m_pKeySet.is() ) + { + sal_Int32 nPos; + for(nPos=0;nPos < static_cast<sal_Int32>(m_pKeySet->size());nPos++) + { + if (nCardNum == (*m_pKeySet)[nPos]) + { + SAL_INFO("connectivity.mork", "return = " << nPos+1); + return nPos+1; + } + } + } + + m_pStatement->getOwnConnection()->throwSQLException( STR_INVALID_BOOKMARK, *this ); + + return 0; +} + +void OResultSet::executeQuery() +{ + ResultSetEntryGuard aGuard( *this ); + + OSL_ENSURE( m_xTable.is(), "Need a Table object"); + if(!m_xTable.is()) + { + const OSQLTables& rTabs = m_pSQLIterator->getTables(); + if (rTabs.empty() || !rTabs.begin()->second.is()) + m_pStatement->getOwnConnection()->throwSQLException( STR_QUERY_TOO_COMPLEX, *this ); + + m_xTable = static_cast< OTable* > (rTabs.begin()->second.get()); + } + + m_nRowPos = 0; + + fillRowData(); + + OSL_ENSURE(m_xColumns.is(), "Need the Columns!!"); + + switch( m_pSQLIterator->getStatementType() ) + { + case OSQLStatementType::Select: + { + if(m_bIsAlwaysFalseQuery) { + break; + } + else if(isCount()) + { + m_pStatement->getOwnConnection()->throwSQLException( STR_NO_COUNT_SUPPORT, *this ); + } + else + { + bool bDistinct = false; + OSQLParseNode *pDistinct = m_pParseTree->getChild(1); + if (pDistinct && pDistinct->getTokenID() == SQL_TOKEN_DISTINCT) + { + if(!IsSorted()) + { + m_aOrderbyColumnNumber.push_back(m_aColMapping[1]); + m_aOrderbyAscending.push_back(TAscendingOrder::DESC); + } + bDistinct = true; + } + + OSortIndex::TKeyTypeVector eKeyType(m_aOrderbyColumnNumber.size()); + std::vector<sal_Int16>::size_type index = 0; + for (const auto& rColIndex : m_aOrderbyColumnNumber) + { + OSL_ENSURE(static_cast<sal_Int32>(m_aRow->size()) > rColIndex,"Invalid Index"); + switch ((m_aRow->begin()+rColIndex)->getTypeKind()) + { + case DataType::CHAR: + case DataType::VARCHAR: + eKeyType[index] = 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[index] = OKeyType::Double; + break; + + // Other types aren't implemented (so they are always FALSE) + default: + eKeyType[index] = OKeyType::NONE; + OSL_FAIL("MResultSet::executeQuery: Order By Data Type not implemented"); + break; + } + ++index; + } + + if (IsSorted()) + { + // Implement Sorting + + // So that we can sort we need to wait until the executed + // query to the mozilla addressbooks has returned all + // values. + + OSortIndex aSortIndex(eKeyType,m_aOrderbyAscending); + +#if OSL_DEBUG_LEVEL > 0 + for ( std::vector<sal_Int32>::size_type i = 0; i < m_aColMapping.size(); i++ ) + SAL_INFO( + "connectivity.mork", + "Mapped: " << i << " -> " << m_aColMapping[i]); +#endif + for ( sal_Int32 nRow = 1; nRow <= m_aQueryHelper.getResultCount(); nRow++ ) { + + std::unique_ptr<OKeyValue> pKeyValue = OKeyValue::createKeyValue(nRow); + + for (const auto& rColIndex : m_aOrderbyColumnNumber) + { + const ORowSetValue& value = getValue(nRow, rColIndex); + + SAL_INFO( + "connectivity.mork", + "Adding Value: (" << nRow << "," << rColIndex + << ") : " << value.getString()); + + pKeyValue->pushKey(new ORowSetValueDecorator(value)); + } + + aSortIndex.AddKeyValue( std::move(pKeyValue) ); + } + + m_pKeySet = aSortIndex.CreateKeySet(); + m_CurrentRowCount = static_cast<sal_Int32>(m_pKeySet->size()); +#if OSL_DEBUG_LEVEL > 0 + for( OKeySet::size_type i = 0; i < m_pKeySet->size(); i++ ) + SAL_INFO( + "connectivity.mork", + "Sorted: " << i << " -> " << (*m_pKeySet)[i]); +#endif + + beforeFirst(); // Go back to start + } + else //we always need m_pKeySet now + m_pKeySet = new OKeySet(); + + // Handle the DISTINCT case + if ( bDistinct && m_pKeySet.is() ) + { + OValueRow aSearchRow = new OValueVector( m_aRow->size() ); + + for(sal_Int32 & i : *m_pKeySet) + { + fetchRow( i ); // Fills m_aRow + if ( matchRow( m_aRow, aSearchRow ) ) + { + i = 0; // Marker for later to be removed + } + else + { + // They don't match, so it's not a duplicate. + // Use the current Row as the next one to match against + *aSearchRow = *m_aRow; + } + } + // Now remove any keys marked with a 0 + m_pKeySet->erase(std::remove_if(m_pKeySet->begin(),m_pKeySet->end() + ,[](sal_Int32 n) { return n == 0; }) + ,m_pKeySet->end()); + + } + } + } break; + + case OSQLStatementType::Update: + case OSQLStatementType::Delete: + case OSQLStatementType::Insert: + break; + default: + m_pStatement->getOwnConnection()->throwSQLException( STR_STMT_TYPE_NOT_SUPPORTED, *this ); + break; + } +} + +void OResultSet::setBoundedColumns(const OValueRow& _rRow, + 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); + + std::vector< OUString> aColumnNames; + aColumnNames.reserve(_rxColumns->size()); + OValueVector::iterator aRowIter = _rRow->begin()+1; + for (sal_Int32 i=0; // the first column is the bookmark column + aRowIter != _rRow->end(); + ++i, ++aRowIter + ) + { + 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 ... + sal_Int32 nColumnPos = 0; + for (const auto& rxColumn : *_rxColumns) + { + if ( nColumnPos < static_cast<sal_Int32>(aColumnNames.size()) ) + sSelectColumnRealName = aColumnNames[nColumnPos]; + else + { + if(rxColumn->getPropertySetInfo()->hasPropertyByName(sRealName)) + rxColumn->getPropertyValue(sRealName) >>= sSelectColumnRealName; + else + rxColumn->getPropertyValue(sName) >>= sSelectColumnRealName; + aColumnNames.push_back(sSelectColumnRealName); + } + + if (aCase(sTableColumnName, sSelectColumnRealName)) + { + if(_bSetColumnMapping) + { + sal_Int32 nSelectColumnPos = nColumnPos + 1; + // the getXXX methods are 1-based ... + sal_Int32 nTableColumnPos = i + 1; + // get first table column is the bookmark column + + SAL_INFO( + "connectivity.mork", + "Set Col Mapping: " << nSelectColumnPos << " -> " + << nTableColumnPos); + _rColMapping[nSelectColumnPos] = nTableColumnPos; + } + + aRowIter->setBound(true); + aRowIter->setTypeKind(DataType::VARCHAR); + } + + ++nColumnPos; + } + } + catch (Exception&) + { + OSL_FAIL("OResultSet::setBoundedColumns: caught an Exception!"); + } + } +} + + +bool OResultSet::isCount() const +{ + return (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 + ); +} + + +// Check for valid row in m_aQuery + +bool OResultSet::validRow( sal_uInt32 nRow) +{ + sal_Int32 nNumberOfRecords = m_aQueryHelper.getResultCount(); + + if (( nRow == 0 ) || + ( nRow > o3tl::make_unsigned(nNumberOfRecords)) ){ + SAL_INFO("connectivity.mork", "validRow(" << nRow << "): return False"); + return false; + } + SAL_INFO("connectivity.mork", "validRow(" << nRow << "): return True"); + + return true; +} + +void OResultSet::fillKeySet(sal_Int32 nMaxCardNumber) +{ + impl_ensureKeySet(); + if (m_CurrentRowCount < nMaxCardNumber) + { + sal_Int32 nKeyValue; + if ( static_cast<sal_Int32>(m_pKeySet->capacity()) < nMaxCardNumber ) + m_pKeySet->reserve(nMaxCardNumber + 20 ); + + for (nKeyValue = m_CurrentRowCount+1; nKeyValue <= nMaxCardNumber; nKeyValue ++) + m_pKeySet->push_back( nKeyValue ); + m_CurrentRowCount = nMaxCardNumber; + } +} + +sal_Int32 OResultSet::deletedCount() +{ + impl_ensureKeySet(); + return m_CurrentRowCount - static_cast<sal_Int32>(m_pKeySet->size()); + +} + +bool OResultSet::seekRow( eRowPosition pos, sal_Int32 nOffset ) +{ + ResultSetEntryGuard aGuard( *this ); + if ( !m_pKeySet.is() ) + m_pStatement->getOwnConnection()->throwSQLException( STR_ILLEGAL_MOVEMENT, *this ); + + sal_Int32 nNumberOfRecords = m_aQueryHelper.getResultCount(); + sal_Int32 nRetrievedRows = currentRowCount(); + sal_Int32 nCurPos = m_nRowPos; + + SAL_INFO("connectivity.mork", "nCurPos = " << nCurPos); + switch( pos ) { + case NEXT_POS: + nCurPos++; + break; + case PRIOR_POS: + if ( nCurPos > 0 ) + nCurPos--; + break; + case FIRST_POS: + nCurPos = 1; + break; + case LAST_POS: + nCurPos = nRetrievedRows; + break; + case ABSOLUTE_POS: + nCurPos = nOffset; + break; + case RELATIVE_POS: + nCurPos += sal_uInt32( nOffset ); + break; + } + + if ( nCurPos <= 0 ) { + m_nRowPos = 0; + SAL_INFO( + "connectivity.mork", "return False, m_nRowPos = " << m_nRowPos); + return false; + } + sal_Int32 nCurCard; + if ( nCurPos < static_cast<sal_Int32>(m_pKeySet->size()) ) //The requested row is exist in m_pKeySet, so we just use it + { + nCurCard = (*m_pKeySet)[nCurPos-1]; + } + else //The requested row has not been retrieved until now. We should get the right card for it. + nCurCard = nCurPos + deletedCount(); + + if ( nCurCard > nNumberOfRecords) { + fillKeySet(nNumberOfRecords); + m_nRowPos = static_cast<sal_uInt32>(m_pKeySet->size() + 1); + SAL_INFO( + "connectivity.mork", "return False, m_nRowPos = " << m_nRowPos); + return false; + } + //Insert new retrieved items for later use + fillKeySet(nNumberOfRecords); + m_nRowPos = static_cast<sal_uInt32>(nCurPos); + SAL_INFO("connectivity.mork", "return True, m_nRowPos = " << m_nRowPos); + fetchCurrentRow(); + return true; +} + +void OResultSet::setColumnMapping(const std::vector<sal_Int32>& _aColumnMapping) +{ + m_aColMapping = _aColumnMapping; +#if OSL_DEBUG_LEVEL > 0 + for ( size_t i = 0; i < m_aColMapping.size(); i++ ) + SAL_INFO( + "connectivity.mork", + "Set Mapped: " << i << " -> " << m_aColMapping[i]); +#endif +} + + +css::uno::Any OResultSet::getBookmark( ) +{ + SAL_INFO("connectivity.mork", "m_nRowPos = " << m_nRowPos); + ResultSetEntryGuard aGuard( *this ); + if ( !fetchCurrentRow() ) { + m_pStatement->getOwnConnection()->throwSQLException( STR_ERROR_GET_ROW, *this ); + } + + OSL_ENSURE((!m_aRow->isDeleted()),"getBookmark called for deleted row"); + return makeAny(static_cast<sal_Int32>((*m_aRow)[0])); +} +sal_Bool OResultSet::moveToBookmark( const css::uno::Any& bookmark ) +{ + ResultSetEntryGuard aGuard( *this ); + SAL_INFO( + "connectivity.mork", "bookmark = " << comphelper::getINT32(bookmark)); + sal_Int32 nCardNum = comphelper::getINT32(bookmark); + m_nRowPos = getRowForCardNumber(nCardNum); + fetchCurrentRow(); + return true; +} +sal_Bool OResultSet::moveRelativeToBookmark( const css::uno::Any& bookmark, sal_Int32 rows ) +{ + ResultSetEntryGuard aGuard( *this ); + SAL_INFO( + "connectivity.mork", + "bookmark = " << comphelper::getINT32(bookmark) << " rows= " << rows); + sal_Int32 nCardNum = comphelper::getINT32(bookmark); + m_nRowPos = getRowForCardNumber(nCardNum); + return seekRow(RELATIVE_POS,rows ); +} +sal_Int32 OResultSet::compareBookmarks( const css::uno::Any& lhs, const css::uno::Any& rhs ) +{ + ResultSetEntryGuard aGuard( *this ); + SAL_INFO("connectivity.mork", "m_nRowPos = " << m_nRowPos); + sal_Int32 nFirst=0; + sal_Int32 nSecond=0; + sal_Int32 nResult=0; + + if ( !( lhs >>= nFirst ) || !( rhs >>= nSecond ) ) { + m_pStatement->getOwnConnection()->throwSQLException( STR_INVALID_BOOKMARK, *this ); + } + + if(nFirst < nSecond) + nResult = CompareBookmark::LESS; + else if(nFirst > nSecond) + nResult = CompareBookmark::GREATER; + else + nResult = CompareBookmark::EQUAL; + + return nResult; +} +sal_Bool OResultSet::hasOrderedBookmarks( ) +{ + ResultSetEntryGuard aGuard( *this ); + SAL_INFO("connectivity.mork", "m_nRowPos = " << m_nRowPos); + return true; +} +sal_Int32 OResultSet::hashBookmark( const css::uno::Any& bookmark ) +{ + ResultSetEntryGuard aGuard( *this ); + SAL_INFO("connectivity.mork", "m_nRowPos = " << m_nRowPos); + return comphelper::getINT32(bookmark); +} + +sal_Int32 OResultSet::getCurrentCardNumber() +{ + if ( ( m_nRowPos == 0 ) || !m_pKeySet.is() ) + return 0; + if (m_pKeySet->size() < m_nRowPos) + return 0; + return (*m_pKeySet)[m_nRowPos-1]; +} +void OResultSet::checkPendingUpdate() +{ + OSL_FAIL( "OResultSet::checkPendingUpdate() not implemented" ); +/* + const sal_Int32 nCurrentRow = getCurrentCardNumber(); + + if ((m_nNewRow && nCurrentRow != m_nNewRow) + || ( m_nUpdatedRow && m_nUpdatedRow != nCurrentRow)) + { + const OUString sError( m_pStatement->getOwnConnection()->getResources().getResourceStringWithSubstitution( + STR_COMMIT_ROW, + "$position$", OUString::valueOf(nCurrentRow) + ) ); + ::dbtools::throwGenericSQLException(sError,*this); + } +*/ + +} +void OResultSet::updateValue(sal_Int32 columnIndex ,const ORowSetValue& x) +{ + SAL_INFO("connectivity.mork", "m_nRowPos = " << m_nRowPos); + ResultSetEntryGuard aGuard( *this ); + if ( !fetchCurrentRow() ) { + m_pStatement->getOwnConnection()->throwSQLException( STR_ERROR_GET_ROW, *this ); + } + + checkPendingUpdate(); + + checkIndex(columnIndex ); + columnIndex = mapColumn(columnIndex); + + (*m_aRow)[columnIndex].setBound(true); + (*m_aRow)[columnIndex] = x; +} + + +void SAL_CALL OResultSet::updateNull( sal_Int32 columnIndex ) +{ + SAL_INFO("connectivity.mork", "m_nRowPos = " << m_nRowPos); + ResultSetEntryGuard aGuard( *this ); + if ( !fetchCurrentRow() ) + m_pStatement->getOwnConnection()->throwSQLException( STR_ERROR_GET_ROW, *this ); + + checkPendingUpdate(); + checkIndex(columnIndex ); + columnIndex = mapColumn(columnIndex); + + (*m_aRow)[columnIndex].setBound(true); + (*m_aRow)[columnIndex].setNull(); +} + + +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 ) +{ + ResultSetEntryGuard aGuard( *this ); + + 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::updateObject( sal_Int32 columnIndex, const Any& x ) +{ + if (!::dbtools::implUpdateObject(this, columnIndex, x)) + { + const OUString sError( m_pStatement->getOwnConnection()->getResources().getResourceStringWithSubstitution( + STR_COLUMN_NOT_UPDATEABLE, + "$position$", OUString::number(columnIndex) + ) ); + ::dbtools::throwGenericSQLException(sError,*this); + } // if (!::dbtools::implUpdateObject(this, columnIndex, x)) + } + + +void SAL_CALL OResultSet::updateNumericObject( sal_Int32 columnIndex, const Any& x, sal_Int32 /*scale*/ ) +{ + if (!::dbtools::implUpdateObject(this, columnIndex, x)) + { + const OUString sError( m_pStatement->getOwnConnection()->getResources().getResourceStringWithSubstitution( + STR_COLUMN_NOT_UPDATEABLE, + "$position$", OUString::number(columnIndex) + ) ); + ::dbtools::throwGenericSQLException(sError,*this); + } +} + +// XResultSetUpdate + +void SAL_CALL OResultSet::insertRow( ) +{ + ResultSetEntryGuard aGuard( *this ); + SAL_INFO("connectivity.mork", "in, m_nRowPos = " << m_nRowPos); +// m_RowStates = RowStates_Inserted; + updateRow(); + //m_aQueryHelper.setRowStates(getCurrentCardNumber(),m_RowStates); + SAL_INFO("connectivity.mork", "out, m_nRowPos = " << m_nRowPos); +} + +void SAL_CALL OResultSet::updateRow( ) +{ + OSL_FAIL( "OResultSet::updateRow( ) not implemented" ); +} + +void SAL_CALL OResultSet::deleteRow( ) +{ + OSL_FAIL( "OResultSet::deleteRow( ) not implemented" ); +} + +void SAL_CALL OResultSet::cancelRowUpdates( ) +{ + OSL_FAIL( "OResultSet::cancelRowUpdates( ) not implemented" ); +} + +void SAL_CALL OResultSet::moveToInsertRow( ) +{ + OSL_FAIL( "OResultSet::moveToInsertRow( ) not implemented" ); +} + +void SAL_CALL OResultSet::moveToCurrentRow( ) +{ + ResultSetEntryGuard aGuard( *this ); + SAL_INFO("connectivity.mork", "m_nRowPos = " << m_nRowPos); + if (rowInserted()) + { + m_nRowPos = 0; + fetchCurrentRow(); + } +} + +bool OResultSet::determineReadOnly() +{ +// OSL_FAIL( "OResultSet::determineReadOnly( ) not implemented" ); + + if (m_bIsReadOnly == TRISTATE_INDET) + { + m_bIsReadOnly = TRISTATE_TRUE; +// OConnection* xConnection = static_cast<OConnection*>(m_pStatement->getConnection().get()); +// m_bIsReadOnly = !m_aQueryHelper.isWritable(xConnection) || m_bIsAlwaysFalseQuery; + } + + return m_bIsReadOnly != TRISTATE_FALSE; +} + +void OResultSet::setTable(OTable* _rTable) +{ + m_xTable = _rTable; + m_xTableColumns = m_xTable->getColumns(); + if(m_xTableColumns.is()) + m_aColumnNames = m_xTableColumns->getElementNames(); +} + +void OResultSet::setOrderByColumns(const std::vector<sal_Int32>& _aColumnOrderBy) +{ + m_aOrderbyColumnNumber = _aColumnOrderBy; +} + +void OResultSet::setOrderByAscending(const std::vector<TAscendingOrder>& _aOrderbyAsc) +{ + m_aOrderbyAscending = _aOrderbyAsc; +} +Sequence< sal_Int32 > SAL_CALL OResultSet::deleteRows( const Sequence< Any >& /*rows*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XDeleteRows::deleteRows", *this ); + return Sequence< sal_Int32 >(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MResultSet.hxx b/connectivity/source/drivers/mork/MResultSet.hxx new file mode 100644 index 000000000..0531781d7 --- /dev/null +++ b/connectivity/source/drivers/mork/MResultSet.hxx @@ -0,0 +1,350 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MRESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MRESULTSET_HXX + +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/util/XCancellable.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdbcx/XDeleteRows.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <cppuhelper/compbase.hxx> +#include <comphelper/proparrhlp.hxx> +#include <tools/gen.hxx> +#include <rtl/ref.hxx> +#include "MStatement.hxx" +#include "MQueryHelper.hxx" +#include <connectivity/CommonTools.hxx> +#include <connectivity/FValue.hxx> +#include <connectivity/sqliterator.hxx> +#include <TSortIndex.hxx> + +namespace connectivity +{ + namespace mork + { + + /* + ** java_sql_ResultSet + */ + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XResultSet, + css::sdbc::XRow, + css::sdbc::XResultSetMetaDataSupplier, + css::util::XCancellable, + css::sdbc::XWarningsSupplier, + css::sdbc::XCloseable, + css::sdbc::XColumnLocate, + css::sdbc::XResultSetUpdate, + css::sdbc::XRowUpdate, + css::sdbcx::XRowLocate, + css::sdbcx::XDeleteRows, + css::lang::XServiceInfo> OResultSet_BASE; + + + typedef sal_Int64 TVoidPtr; + typedef std::allocator< TVoidPtr > TVoidAlloc; + typedef std::vector<TVoidPtr> TVoidVector; + + class OResultSet : public cppu::BaseMutex, + public OResultSet_BASE, + public ::cppu::OPropertySetHelper, + public ::comphelper::OPropertyArrayUsageHelper<OResultSet> + { + protected: + OCommonStatement* m_pStatement; + css::uno::Reference< css::uno::XInterface> m_xStatement; + css::uno::Reference< css::sdbc::XResultSetMetaData> m_xMetaData; + sal_uInt32 m_nRowPos; + bool m_bWasNull; + sal_Int32 m_nResultSetType; + sal_Int32 m_nFetchDirection; + + + std::shared_ptr< ::connectivity::OSQLParseTreeIterator > + m_pSQLIterator; + const connectivity::OSQLParseNode* m_pParseTree; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue + ) override; + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle + ) const override; + + // you can't delete objects of this type + virtual ~OResultSet() override; + public: + DECLARE_SERVICE_INFO(); + + OResultSet(OCommonStatement* pStmt, const std::shared_ptr< ::connectivity::OSQLParseTreeIterator >& _pSQLIterator ); + + // ::cppu::OComponentHelper + virtual void SAL_CALL disposing() override; + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XResultSet + virtual sal_Bool SAL_CALL next( ) override; + virtual sal_Bool SAL_CALL isBeforeFirst( ) override; + virtual sal_Bool SAL_CALL isAfterLast( ) override; + virtual sal_Bool SAL_CALL isFirst( ) override; + virtual sal_Bool SAL_CALL isLast( ) override; + virtual void SAL_CALL beforeFirst( ) override; + virtual void SAL_CALL afterLast( ) override; + virtual sal_Bool SAL_CALL first( ) override; + virtual sal_Bool SAL_CALL last( ) override; + virtual sal_Int32 SAL_CALL getRow( ) override; + virtual sal_Bool SAL_CALL absolute( sal_Int32 row ) override; + virtual sal_Bool SAL_CALL relative( sal_Int32 rows ) override; + virtual sal_Bool SAL_CALL previous( ) override; + virtual void SAL_CALL refreshRow( ) override; + virtual sal_Bool SAL_CALL rowUpdated( ) override; + virtual sal_Bool SAL_CALL rowInserted( ) override; + virtual sal_Bool SAL_CALL rowDeleted( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getStatement( ) override; + // XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + // XCancellable + virtual void SAL_CALL cancel( ) override; + // XCloseable + virtual void SAL_CALL close( ) override; + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + // XColumnLocate + virtual sal_Int32 SAL_CALL findColumn( const OUString& columnName ) override; + + // XResultSetUpdate + virtual void SAL_CALL insertRow( ) override; + virtual void SAL_CALL updateRow( ) override; + virtual void SAL_CALL deleteRow( ) override; + virtual void SAL_CALL cancelRowUpdates( ) override; + virtual void SAL_CALL moveToInsertRow( ) override; + virtual void SAL_CALL moveToCurrentRow( ) override; + // XRowUpdate + virtual void SAL_CALL updateNull( sal_Int32 columnIndex ) override; + virtual void SAL_CALL updateBoolean( sal_Int32 columnIndex, sal_Bool x ) override; + virtual void SAL_CALL updateByte( sal_Int32 columnIndex, sal_Int8 x ) override; + virtual void SAL_CALL updateShort( sal_Int32 columnIndex, sal_Int16 x ) override; + virtual void SAL_CALL updateInt( sal_Int32 columnIndex, sal_Int32 x ) override; + virtual void SAL_CALL updateLong( sal_Int32 columnIndex, sal_Int64 x ) override; + virtual void SAL_CALL updateFloat( sal_Int32 columnIndex, float x ) override; + virtual void SAL_CALL updateDouble( sal_Int32 columnIndex, double x ) override; + virtual void SAL_CALL updateString( sal_Int32 columnIndex, const OUString& x ) override; + virtual void SAL_CALL updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL updateDate( sal_Int32 columnIndex, const css::util::Date& x ) override; + virtual void SAL_CALL updateTime( sal_Int32 columnIndex, const css::util::Time& x ) override; + virtual void SAL_CALL updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL updateBinaryStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateCharacterStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateObject( sal_Int32 columnIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL updateNumericObject( sal_Int32 columnIndex, const css::uno::Any& x, sal_Int32 scale ) override; + // XRowLocate + virtual css::uno::Any SAL_CALL getBookmark( ) override; + virtual sal_Bool SAL_CALL moveToBookmark( const css::uno::Any& bookmark ) override; + virtual sal_Bool SAL_CALL moveRelativeToBookmark( const css::uno::Any& bookmark, sal_Int32 rows ) override; + virtual sal_Int32 SAL_CALL compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ) override; + virtual sal_Bool SAL_CALL hasOrderedBookmarks( ) override; + virtual sal_Int32 SAL_CALL hashBookmark( const css::uno::Any& bookmark ) override; + // XDeleteRows + virtual css::uno::Sequence< sal_Int32 > SAL_CALL deleteRows( const css::uno::Sequence< css::uno::Any >& rows ) override; + +protected: + //MQuery m_aQuery; + MQueryHelper m_aQueryHelper; + rtl::Reference<OTable> m_xTable; + sal_Int32 m_CurrentRowCount; + css::uno::Reference< css::container::XNameAccess > + m_xTableColumns; + + std::vector<sal_Int32> m_aColMapping; // pos 0 is unused so we don't have to decrement 1 every time + std::vector<sal_Int32> m_aOrderbyColumnNumber; + std::vector<TAscendingOrder> m_aOrderbyAscending; + css::uno::Sequence< OUString> m_aColumnNames; + OValueRow m_aRow; + OValueRow m_aParameterRow; + sal_Int32 m_nParamIndex; + bool m_bIsAlwaysFalseQuery; + ::rtl::Reference<OKeySet> m_pKeySet; + TriState m_bIsReadOnly; + void resetParameters() { m_nParamIndex = 0; } + + ::rtl::Reference<connectivity::OSQLColumns> m_xColumns; // this are the select columns + + void parseParameter( const OSQLParseNode* pNode, OUString& rMatchString ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void fillRowData(); + void analyseWhereClause( const OSQLParseNode* parseTree, + MQueryExpression &queryExpression); + + bool isCount() const; + + bool IsSorted() const { return !m_aOrderbyColumnNumber.empty(); } + + enum eRowPosition { + NEXT_POS, PRIOR_POS, FIRST_POS, LAST_POS, ABSOLUTE_POS, RELATIVE_POS + }; + + sal_uInt32 currentRowCount(); + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + bool fetchRow(sal_Int32 rowIndex,bool bForceReload=false); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + bool fetchCurrentRow(); + bool validRow( sal_uInt32 nRow ); + bool seekRow( eRowPosition pos, sal_Int32 nOffset = 0 ); + sal_Int32 deletedCount(); + void fillKeySet(sal_Int32 nMaxCardNumber); //When we get new rows, fill the m_pKeySet items for them + sal_Int32 getRowForCardNumber(sal_Int32 nCardNum); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + const ORowSetValue& getValue(sal_Int32 rowIndex, sal_Int32 columnIndex); + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void updateValue(sal_Int32 columnIndex,const ORowSetValue& x ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + static void checkPendingUpdate(); + sal_Int32 getCurrentCardNumber(); + +public: + bool determineReadOnly(); + // MozAddressbook Specific methods + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void executeQuery(); + + void setTable(OTable* _rTable); + + void setParameterRow(const OValueRow& _rParaRow) + { m_aParameterRow = _rParaRow; } + + void setBindingRow(const OValueRow& _aRow) + { m_aRow = _aRow; } + + void setColumnMapping(const std::vector<sal_Int32>& _aColumnMapping); + + void setOrderByColumns(const std::vector<sal_Int32>& _aColumnOrderBy); + + void setOrderByAscending(const std::vector<TAscendingOrder>& _aOrderbyAsc); + + inline sal_Int32 mapColumn(sal_Int32 column); + + /// @throws css::sdbc::SQLException + void checkIndex(sal_Int32 columnIndex ); + + static void setBoundedColumns( + const OValueRow& _rRow, + const ::rtl::Reference<connectivity::OSQLColumns>& _rxColumns, + const css::uno::Reference< css::container::XIndexAccess>& _xNames, + bool _bSetColumnMapping, + const css::uno::Reference< css::sdbc::XDatabaseMetaData>& _xMetaData, + std::vector<sal_Int32>& _rColMapping); + + ::osl::Mutex& getMutex() { return m_aMutex; } + void methodEntry(); + + private: + void impl_ensureKeySet() + { + if ( !m_pKeySet.is() ) + m_pKeySet = new OKeySet(); + } + + protected: + using OPropertySetHelper::getFastPropertyValue; + }; + + inline sal_Int32 OResultSet::mapColumn(sal_Int32 column) + { + sal_Int32 map = column; + + OSL_ENSURE(column > 0, "OResultSet::mapColumn: invalid column index!"); + // the first column (index 0) is for convenience only. The + // first real select column is no 1. + if ((column > 0) && (column < static_cast<sal_Int32>(m_aColMapping.size()))) + map = m_aColMapping[column]; + + return map; + } + + class ResultSetEntryGuard : public ::osl::MutexGuard + { + public: + explicit ResultSetEntryGuard( OResultSet& _rRS ) : ::osl::MutexGuard( _rRS.getMutex() ) + { + _rRS.methodEntry(); + } + }; + + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MRESULTSET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MResultSetMetaData.cxx b/connectivity/source/drivers/mork/MResultSetMetaData.cxx new file mode 100644 index 000000000..e05eced22 --- /dev/null +++ b/connectivity/source/drivers/mork/MResultSetMetaData.cxx @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <connectivity/dbexception.hxx> +#include <comphelper/types.hxx> +#include <comphelper/extract.hxx> +#include <tools/diagnose_ex.h> +#include "MResultSetMetaData.hxx" +#include <com/sun/star/sdbc/DataType.hpp> + +using namespace connectivity::mork; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::beans; +using namespace ::dbtools; +using namespace ::comphelper; + + +OResultSetMetaData::~OResultSetMetaData() +{ + m_xColumns = nullptr; +} + + +void OResultSetMetaData::checkColumnIndex(sal_Int32 column) +{ + if(column <= 0 || column > static_cast<sal_Int32>(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*/ ) +{ + return DataType::VARCHAR; // at the moment there exists only this type +} + + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount( ) +{ + return static_cast<sal_Int32>(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); + + OUString sColumnName; + try + { + Reference< XPropertySet > xColumnProps( (*m_xColumns)[column-1], UNO_SET_THROW ); + OSL_VERIFY( xColumnProps->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_NAME ) ) >>= sColumnName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.mork"); + } + return sColumnName; +} + +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 /*column*/ ) +{ + return false; +} + +sal_Bool SAL_CALL OResultSetMetaData::isSigned( sal_Int32 /*column*/ ) +{ + return false; +} + +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*/ ) +{ + if ( !m_pTable || !m_pTable->getConnection() ) + { + OSL_FAIL( "OResultSetMetaData::isSearchable: suspicious: called without table or connection!" ); + return false; + } + + return true; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isReadOnly( sal_Int32 column ) +{ + checkColumnIndex(column); + bool bReadOnly = (*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))); + + return m_bReadOnly || bReadOnly || OTable::isReadOnly(); +} + + +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/mork/MResultSetMetaData.hxx b/connectivity/source/drivers/mork/MResultSetMetaData.hxx new file mode 100644 index 000000000..5362a4ac0 --- /dev/null +++ b/connectivity/source/drivers/mork/MResultSetMetaData.hxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MRESULTSETMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MRESULTSETMETADATA_HXX + +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include "MTable.hxx" + +namespace connectivity +{ + namespace mork + { + + //************ Class: ResultSetMetaData + + typedef ::cppu::WeakImplHelper<css::sdbc::XResultSetMetaData> OResultSetMetaData_BASE; + + class OResultSetMetaData : public OResultSetMetaData_BASE + { + OUString m_aTableName; + ::rtl::Reference<connectivity::OSQLColumns> m_xColumns; + OTable* m_pTable; + bool m_bReadOnly; + + protected: + virtual ~OResultSetMetaData() override; + public: + // a constructor that is needed to return the object: + // OResultSetMetaData(OConnection* _pConnection) : m_pConnection(_pConnection){} + OResultSetMetaData(const ::rtl::Reference<connectivity::OSQLColumns>& _rxColumns, + const OUString& _aTableName,OTable* _pTable,bool aReadOnly + ) + :m_aTableName(_aTableName) + ,m_xColumns(_rxColumns) + ,m_pTable(_pTable) + ,m_bReadOnly(aReadOnly) + {} + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkColumnIndex(sal_Int32 column); + virtual sal_Int32 SAL_CALL getColumnCount( ) override; + virtual sal_Bool SAL_CALL isAutoIncrement( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCaseSensitive( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSearchable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCurrency( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL isNullable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSigned( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnDisplaySize( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnLabel( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnName( sal_Int32 column ) override; + virtual OUString SAL_CALL getSchemaName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getPrecision( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getScale( sal_Int32 column ) override; + virtual OUString SAL_CALL getTableName( sal_Int32 column ) override; + virtual OUString SAL_CALL getCatalogName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnType( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnTypeName( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isReadOnly( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isWritable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isDefinitelyWritable( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnServiceName( sal_Int32 column ) override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MRESULTSETMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MStatement.cxx b/connectivity/source/drivers/mork/MStatement.cxx new file mode 100644 index 000000000..bd153afa6 --- /dev/null +++ b/connectivity/source/drivers/mork/MStatement.cxx @@ -0,0 +1,471 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/processfactory.hxx> +#include <connectivity/dbexception.hxx> + +#include <algorithm> + +#include "MDriver.hxx" +#include "MStatement.hxx" +#include <sqlbison.hxx> +#include "MResultSet.hxx" + +#include <strings.hrc> + +static ::osl::Mutex m_ThreadMutex; + +using namespace ::comphelper; +using namespace connectivity::mork; +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::container; +using namespace com::sun::star::io; +using namespace com::sun::star::util; + + +OStatement::OStatement( OConnection* _pConnection) : OCommonStatement( _pConnection) +{ +} + +OCommonStatement::OCommonStatement(OConnection* _pConnection ) + :OCommonStatement_IBASE(m_aMutex) + ,OPropertySetHelper(OCommonStatement_IBASE::rBHelper) + ,m_xDBMetaData (_pConnection->getMetaData()) + ,m_pTable(nullptr) + ,m_pConnection(_pConnection) + ,m_aParser( comphelper::getComponentContext(_pConnection->getDriver()->getFactory()) ) + ,m_pSQLIterator( std::make_shared<OSQLParseTreeIterator>( _pConnection, _pConnection->createCatalog()->getTables(), m_aParser ) ) +{ +} + + +OCommonStatement::~OCommonStatement() +{ +} + + +void OCommonStatement::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + clearWarnings(); + clearCachedResultSet(); + + m_pConnection.clear(); + + m_pSQLIterator->dispose(); + m_pParseTree.reset(); + + OCommonStatement_IBASE::disposing(); +} + +Any SAL_CALL OCommonStatement::queryInterface( const Type & rType ) +{ + Any aRet = OCommonStatement_IBASE::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OPropertySetHelper::queryInterface(rType); + return aRet; +} + +Sequence< Type > SAL_CALL OCommonStatement::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),OCommonStatement_IBASE::getTypes()); +} + +void SAL_CALL OCommonStatement::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + } + dispose(); +} + +OCommonStatement::StatementType OCommonStatement::parseSql( const OUString& sql , bool bAdjusted) +{ + OUString aErr; + + m_pParseTree = m_aParser.parseTree(aErr,sql); + + if(m_pParseTree) + { + m_pSQLIterator->setParseTree(m_pParseTree.get()); + m_pSQLIterator->traverseAll(); + const OSQLTables& rTabs = m_pSQLIterator->getTables(); + + if (rTabs.empty()) + { + getOwnConnection()->throwSQLException( STR_QUERY_AT_LEAST_ONE_TABLES, *this ); + } + + Reference<XIndexAccess> xNames; + switch(m_pSQLIterator->getStatementType()) + { + case OSQLStatementType::Select: + + // at this moment we support only one table per select statement + + OSL_ENSURE( !rTabs.empty(), "Need a Table"); + + m_pTable = static_cast< OTable* > (rTabs.begin()->second.get()); + m_xColNames = m_pTable->getColumns(); + xNames.set(m_xColNames,UNO_QUERY); + // set the binding of the resultrow + m_aRow = new OValueVector(xNames->getCount()); + (*m_aRow)[0].setBound(true); + std::for_each(m_aRow->begin()+1,m_aRow->end(),TSetBound(false)); + // create the column mapping + createColumnMapping(); + + analyseSQL(); + return eSelect; + + case OSQLStatementType::CreateTable: + return eCreateTable; + + default: + break; + } + } + else if(!bAdjusted) //Our sql parser does not support a statement like "create table foo" + // So we append ("E-mail" varchar) to the last of it to make it work + { + return parseSql(sql + "(""E-mail"" character)", true); + } + + getOwnConnection()->throwSQLException( STR_QUERY_TOO_COMPLEX, *this ); + OSL_FAIL( "OCommonStatement::parseSql: unreachable!" ); + return eSelect; + +} + +Reference< XResultSet > OCommonStatement::impl_executeCurrentQuery() +{ + clearCachedResultSet(); + + ::rtl::Reference pResult( new OResultSet( this, m_pSQLIterator ) ); + initializeResultSet( pResult.get() ); + + pResult->executeQuery(); + cacheResultSet( pResult ); // only cache if we survived the execution + + return pResult.get(); + +} + + +void OCommonStatement::initializeResultSet( OResultSet* _pResult ) +{ + ENSURE_OR_THROW( _pResult, "invalid result set" ); + + _pResult->setColumnMapping(m_aColMapping); + _pResult->setOrderByColumns(m_aOrderbyColumnNumber); + _pResult->setOrderByAscending(m_aOrderbyAscending); + _pResult->setBindingRow(m_aRow); + _pResult->setTable(m_pTable); +} + + +void OCommonStatement::clearCachedResultSet() +{ + Reference< XResultSet > xResultSet( m_xResultSet.get(), UNO_QUERY ); + if ( !xResultSet.is() ) + return; + + Reference< XCloseable >( xResultSet, UNO_QUERY_THROW )->close(); + + m_xResultSet.clear(); +} + + +void OCommonStatement::cacheResultSet( const ::rtl::Reference< OResultSet >& _pResult ) +{ + ENSURE_OR_THROW( _pResult.is(), "invalid result set" ); + m_xResultSet = Reference< XResultSet >( _pResult.get() ); +} + + +sal_Bool SAL_CALL OCommonStatement::execute( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + Reference< XResultSet > xRS = executeQuery( sql ); + // returns true when a resultset is available + return xRS.is(); +} + + +Reference< XResultSet > SAL_CALL OCommonStatement::executeQuery( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_ThreadMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + // parse the statement + StatementType eStatementType = parseSql( sql ); + if ( eStatementType != eSelect ) + return nullptr; + + return impl_executeCurrentQuery(); +} + + +Reference< XConnection > SAL_CALL OCommonStatement::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + // just return our connection here + return Reference< XConnection >(m_pConnection.get()); +} + +Any SAL_CALL OStatement::queryInterface( const Type & rType ) +{ + Any aRet = ::cppu::queryInterface(rType,static_cast< XServiceInfo*> (this)); + if(!aRet.hasValue()) + aRet = OCommonStatement::queryInterface(rType); + return aRet; +} + +sal_Int32 SAL_CALL OCommonStatement::executeUpdate( const OUString& /*sql*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XStatement::executeUpdate", *this ); + return 0; + +} + +Any SAL_CALL OCommonStatement::getWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + return makeAny(m_aLastWarning); +} + + +void SAL_CALL OCommonStatement::clearWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OCommonStatement_IBASE::rBHelper.bDisposed); + + + m_aLastWarning = SQLWarning(); +} + +::cppu::IPropertyArrayHelper* OCommonStatement::createArrayHelper( ) const +{ + // this properties are define by the service resultset + // they must in alphabetic order + Sequence< Property > aProps(9); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING), + PROPERTY_ID_ESCAPEPROCESSING, cppu::UnoType<bool>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE), + PROPERTY_ID_MAXFIELDSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS), + PROPERTY_ID_MAXROWS, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT), + PROPERTY_ID_QUERYTIMEOUT, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +::cppu::IPropertyArrayHelper & OCommonStatement::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OCommonStatement::convertFastPropertyValue( + Any & /*rConvertedValue*/, + Any & /*rOldValue*/, + sal_Int32 /*nHandle*/, + const Any& /*rValue*/ ) +{ + // here we have to try to convert + return false; +} + +void OCommonStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& /*rValue*/) +{ + // set the value to whatever is necessary + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + case PROPERTY_ID_MAXFIELDSIZE: + case PROPERTY_ID_MAXROWS: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + case PROPERTY_ID_ESCAPEPROCESSING: + default: + ; + } +} + +void OCommonStatement::getFastPropertyValue(Any& /*rValue*/,sal_Int32 nHandle) const +{ + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + case PROPERTY_ID_MAXFIELDSIZE: + case PROPERTY_ID_MAXROWS: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + case PROPERTY_ID_ESCAPEPROCESSING: + default: + ; + } +} + +IMPLEMENT_SERVICE_INFO(OStatement,"com.sun.star.sdbcx.OStatement","com.sun.star.sdbc.Statement"); + +void SAL_CALL OCommonStatement::acquire() throw() +{ + OCommonStatement_IBASE::acquire(); +} + +void SAL_CALL OCommonStatement::release() throw() +{ + OCommonStatement_IBASE::release(); +} + +void SAL_CALL OStatement::acquire() throw() +{ + OCommonStatement::acquire(); +} + +void SAL_CALL OStatement::release() throw() +{ + OCommonStatement::release(); +} + +Reference< css::beans::XPropertySetInfo > SAL_CALL OCommonStatement::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +void OCommonStatement::createColumnMapping() +{ + size_t i; + + // initialize the column index map (mapping select columns to table columns) + ::rtl::Reference<connectivity::OSQLColumns> xColumns = m_pSQLIterator->getSelectColumns(); + m_aColMapping.resize(xColumns->size() + 1); + for (i=0; i<m_aColMapping.size(); ++i) + m_aColMapping[i] = static_cast<sal_Int32>(i); + + Reference<XIndexAccess> xNames(m_xColNames,UNO_QUERY); + // now check which columns are bound +#if OSL_DEBUG_LEVEL > 0 + for ( i = 0; i < m_aColMapping.size(); i++ ) + SAL_INFO( + "connectivity.mork", + "BEFORE Mapped: " << i << " -> " << m_aColMapping[i]); +#endif + OResultSet::setBoundedColumns(m_aRow,xColumns,xNames,true,m_xDBMetaData,m_aColMapping); +#if OSL_DEBUG_LEVEL > 0 + for ( i = 0; i < m_aColMapping.size(); i++ ) + SAL_INFO( + "connectivity.mork", + "AFTER Mapped: " << i << " -> " << m_aColMapping[i]); +#endif +} + + +void OCommonStatement::analyseSQL() +{ + const OSQLParseNode* pOrderbyClause = m_pSQLIterator->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 OCommonStatement::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; + + m_aOrderbyColumnNumber.push_back(xColLocate->findColumn(aColumnName)); + + // Ascending or Descending? + m_aOrderbyAscending.push_back(SQL_ISTOKEN(pAscendingDescending,DESC) ? TAscendingOrder::DESC : TAscendingOrder::ASC); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MStatement.hxx b/connectivity/source/drivers/mork/MStatement.hxx new file mode 100644 index 000000000..3cd7113a8 --- /dev/null +++ b/connectivity/source/drivers/mork/MStatement.hxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MSTATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MSTATEMENT_HXX + +#include <comphelper/proparrhlp.hxx> +#include <connectivity/sqliterator.hxx> +#include <connectivity/sqlparse.hxx> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <TSortIndex.hxx> +#include "MTable.hxx" + +#include <memory> + +namespace connectivity +{ + namespace mork + { + class OResultSet; + + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XStatement, + css::sdbc::XWarningsSupplier, + css::sdbc::XCloseable> OCommonStatement_IBASE; + + + //************ Class: OCommonStatement + // is a base class for the normal statement and for the prepared statement + + class OCommonStatement; + + class OCommonStatement :public cppu::BaseMutex + ,public OCommonStatement_IBASE + ,public ::cppu::OPropertySetHelper + ,public ::comphelper::OPropertyArrayUsageHelper< OCommonStatement > + { + private: + css::sdbc::SQLWarning m_aLastWarning; + + protected: + css::uno::WeakReference< css::sdbc::XResultSet > m_xResultSet; + css::uno::Reference< css::sdbc::XDatabaseMetaData> m_xDBMetaData; + css::uno::Reference< css::container::XNameAccess> m_xColNames; // table columns + + // for this Statement + + OTable* m_pTable; + rtl::Reference<OConnection> m_pConnection; // The owning Connection object + + OValueRow m_aRow; + + connectivity::OSQLParser m_aParser; + std::shared_ptr< ::connectivity::OSQLParseTreeIterator > + m_pSQLIterator; + + std::unique_ptr<connectivity::OSQLParseNode> m_pParseTree; + + std::vector<sal_Int32> m_aColMapping; + std::vector<sal_Int32> m_aOrderbyColumnNumber; + std::vector<TAscendingOrder> m_aOrderbyAscending; + + protected: + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle) const override; + virtual ~OCommonStatement() override; + + protected: + + // Driver Internal Methods + + enum StatementType { eSelect, eCreateTable }; + /** called to do the parsing of a to-be-executed SQL statement, and set all members as needed + + @throws css::sdbc::SQLException + @throws css::uno::RuntimeException + */ + virtual StatementType + parseSql( const OUString& sql , bool bAdjusted = false); + /** called to initialize a result set, according to a previously parsed SQL statement + */ + virtual void initializeResultSet( OResultSet* _pResult ); + /** called when a possible cached instance of our last result set should be cleared + */ + virtual void clearCachedResultSet(); + /** caches a result set which has just been created by an execution of an SQL statement + */ + virtual void cacheResultSet( const ::rtl::Reference< OResultSet >& _pResult ); + + + /** executes the current query (the one which has been passed to the last parseSql call) + */ + css::uno::Reference< css::sdbc::XResultSet > + impl_executeCurrentQuery(); + + void createColumnMapping(); + void analyseSQL(); + void setOrderbyColumn( connectivity::OSQLParseNode const * pColumnRef, + connectivity::OSQLParseNode const * pAscendingDescending); + + public: + // other methods + OConnection* getOwnConnection() const { return m_pConnection.get(); } + + explicit OCommonStatement(OConnection* _pConnection ); + using OCommonStatement_IBASE::operator css::uno::Reference< css::uno::XInterface >; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XInterface + virtual void SAL_CALL release() throw() override; + virtual void SAL_CALL acquire() throw() override; + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( const OUString& sql ) override ; + virtual sal_Int32 SAL_CALL executeUpdate( const OUString& sql ) override ; + virtual sal_Bool SAL_CALL execute( const OUString& sql ) override ; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override ; + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + // XCloseable + virtual void SAL_CALL close( ) override; + + protected: + using OPropertySetHelper::getFastPropertyValue; + }; + + class OStatement : public OCommonStatement, + public css::lang::XServiceInfo + { + protected: + virtual ~OStatement() override {} + public: + // a constructor, for when the object needs to be returned: + explicit OStatement( OConnection* _pConnection); + DECLARE_SERVICE_INFO(); + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + }; + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MSTATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MTable.cxx b/connectivity/source/drivers/mork/MTable.cxx new file mode 100644 index 000000000..76db19fc6 --- /dev/null +++ b/connectivity/source/drivers/mork/MTable.cxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "MTable.hxx" +#include "MColumns.hxx" +#include <connectivity/TKeys.hxx> +#include <connectivity/TIndexes.hxx> + + +using namespace connectivity::mork; +using namespace connectivity; + + +OTable::OTable( sdbcx::OCollection* _pTables, OConnection* _pConnection, + const OUString& Name, const OUString& Type, const OUString& Description ) + :OTable_Base(_pTables, _pConnection, true, Name, Type, Description ) + ,m_pConnection( _pConnection ) +{ + construct(); +} + + +sdbcx::OCollection* OTable::createColumns( const ::std::vector< OUString>& _rNames ) +{ + return new OColumns( this, m_aMutex, _rNames ); +} + + +sdbcx::OCollection* OTable::createKeys(const ::std::vector< OUString>& _rNames) +{ + return new OKeysHelper( this, m_aMutex, _rNames ); +} + + +sdbcx::OCollection* OTable::createIndexes(const ::std::vector< OUString>& _rNames) +{ + return new OIndexesHelper( this, m_aMutex, _rNames ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MTable.hxx b/connectivity/source/drivers/mork/MTable.hxx new file mode 100644 index 000000000..7cbe216be --- /dev/null +++ b/connectivity/source/drivers/mork/MTable.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MTABLE_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MTABLE_HXX + +#include <connectivity/TTableHelper.hxx> +#include "MConnection.hxx" + +namespace connectivity +{ + namespace mork + { + typedef ::connectivity::OTableHelper OTable_Base; + + class OTable : public OTable_Base + { + OConnection* m_pConnection; + + public: + OTable( sdbcx::OCollection* _pTables, + OConnection* _pConnection, + const OUString& Name, + const OUString& Type, + const OUString& Description ); + + OConnection* getConnection() { return m_pConnection;} + + static bool isReadOnly() { return false; } + + const OUString& getTableName() const { return m_Name; } + const OUString& getSchema() const { return m_SchemaName; } + + // OTableHelper overridables + virtual sdbcx::OCollection* createColumns( const ::std::vector< OUString>& _rNames ) override; + virtual sdbcx::OCollection* createKeys(const ::std::vector< OUString>& _rNames) override; + virtual sdbcx::OCollection* createIndexes(const ::std::vector< OUString>& _rNames) override; + private: + using OTable_Base::getConnection; + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MTABLE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MTables.cxx b/connectivity/source/drivers/mork/MTables.cxx new file mode 100644 index 000000000..f1f169e2a --- /dev/null +++ b/connectivity/source/drivers/mork/MTables.cxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MTables.hxx" +#include "MTable.hxx" +#include "MCatalog.hxx" +#include <comphelper/types.hxx> + +#include <com/sun/star/sdbc/XRow.hpp> + +using namespace connectivity; +using namespace connectivity::mork; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; + +sdbcx::ObjectType OTables::createObject(const OUString& _rName) +{ + OUString aName,aSchema; + aSchema = "%"; + aName = _rName; + + Sequence< OUString > aTypes { "%" }; + + Reference< XResultSet > xResult = m_xMetaData->getTables(Any(),aSchema,aName,aTypes); + + sdbcx::ObjectType xRet; + if(xResult.is()) + { + Reference< XRow > xRow(xResult,UNO_QUERY); + if(xResult->next()) // there can be only one table with this name + { + OTable* pRet = new OTable( this, static_cast<OCatalog&>(m_rParent).getConnection(), + aName,xRow->getString(4),xRow->getString(5)); + xRet = pRet; + } + } + ::comphelper::disposeComponent(xResult); + + return xRet; +} + +void OTables::impl_refresh( ) +{ + static_cast<OCatalog&>(m_rParent).refreshTables(); +} + +void OTables::disposing() +{ + m_xMetaData.clear(); + OCollection::disposing(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MTables.hxx b/connectivity/source/drivers/mork/MTables.hxx new file mode 100644 index 000000000..ac3730f9a --- /dev/null +++ b/connectivity/source/drivers/mork/MTables.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MTABLES_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MTABLES_HXX + +#include <connectivity/sdbcx/VCollection.hxx> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +namespace connectivity +{ + namespace mork + { + class OTables : public sdbcx::OCollection + { + css::uno::Reference< css::sdbc::XDatabaseMetaData > m_xMetaData; + protected: + virtual sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override; + public: + OTables(const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _rMetaData,::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, + const ::std::vector< OUString> &_rVector) : sdbcx::OCollection(_rParent, true, _rMutex, _rVector) + ,m_xMetaData(_rMetaData) + {} + + // only the name is identical to ::cppu::OComponentHelper + virtual void disposing() override; + }; + } +} +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MTABLES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MorkParser.cxx b/connectivity/source/drivers/mork/MorkParser.cxx new file mode 100644 index 000000000..188f8e6fb --- /dev/null +++ b/connectivity/source/drivers/mork/MorkParser.cxx @@ -0,0 +1,757 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2006, ScalingWeb.com + * All rights reserved. + * + * Redistribution and use of this software in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * * Neither the name of ScalingWeb.com nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission of ScalingWeb.com. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "MorkParser.hxx" +#include <boost/io/ios_state.hpp> +#include <stdlib.h> +#include <string> +#include <string.h> +#include <fstream> +#include <iostream> + +std::string const g_Empty = ""; + +const char * const MorkDictColumnMeta = "<(a=c)>"; + +static const int defaultScope_ = 0x80; + +MorkParser::MorkParser() : + columns_(), + values_(), + mork_(), + currentCells_(nullptr), + error_(NoError), + morkData_(), + morkPos_(0), + nextAddValueId_(0x7fffffff), + defaultTableId_(1), + nowParsing_(NP::Values) +{ +} + +bool MorkParser::open( const std::string &path ) +{ + initVars(); + std::string line; + std::ifstream infile(path.c_str(), std::ios_base::in); + if(!infile.is_open()) + { + error_ = FailedToOpen; + return false; + } + + while (getline(infile, line, '\n')) + { + morkData_.append(line); + morkData_.append("\n"); + } + + // Parse mork + return parse(); +} + +void MorkParser::initVars() +{ + error_ = NoError; + morkPos_ = 0; + nowParsing_ = NP::Values; + currentCells_ = nullptr; + nextAddValueId_ = 0x7fffffff; +} + +bool MorkParser::parse() +{ + bool Result = true; + + // Run over mork chars and parse each term + char cur = nextChar(); + + while ( Result && cur ) + { + if ( !isWhiteSpace( cur ) ) + { + // Figure out what a term + switch ( cur ) + { + case '<': + // Dict + Result = parseDict(); + break; + case '/': + // Comment + Result = parseComment(); + break; + case '{': + Result = parseTable(); + // Table + break; + case '[': + Result = parseRow( 0, 0 ); + // Row + break; + case '@': + parseGroup(); + // Group + break; + default: + error_ = DefectedFormat; + Result = false; + break; + } + } + + // Get next char + cur = nextChar(); + } + + return Result; +} + +bool MorkParser::isWhiteSpace( char c ) +{ + switch ( c ) + { + case ' ': + case '\t': + case '\r': + case '\n': + case '\f': + return true; + default: + return false; + } +} + +inline char MorkParser::nextChar() +{ + char cur = 0; + + + if ( morkPos_ < morkData_.length() ) + { + cur = morkData_[ morkPos_ ]; + morkPos_++; + } + + if ( !cur ) + { + cur = 0; + } + + return cur; +} + +bool MorkParser::parseDict() +{ + char cur = nextChar(); + bool Result = true; + nowParsing_ = NP::Values; + + while ( Result && cur != '>' && cur ) + { + if ( !isWhiteSpace( cur ) ) + { + switch ( cur ) + { + case '<': + { + + if ( morkData_.substr( morkPos_ - 1, strlen( MorkDictColumnMeta ) ) == MorkDictColumnMeta ) + { + nowParsing_ = NP::Columns; + morkPos_ += strlen( MorkDictColumnMeta ) - 1; + } + + + break; + } + case '(': + Result = parseCell(); + break; + case '/': + Result = parseComment(); + break; + + } + } + + cur = nextChar(); + } + + return Result; +} + +inline bool MorkParser::parseComment() +{ + char cur = nextChar(); + if ( '/' != cur ) return false; + + while ( cur != '\r' && cur != '\n' && cur ) + { + cur = nextChar(); + } + + return true; +} + +bool MorkParser::parseCell() +{ + bool Result = true; + bool bValueOid = false; + bool bColumn = true; + int Corners = 0; + + // Column = Value + std::string Column; + std::string Text; + Column.reserve( 4 ); + Text.reserve( 32 ); + + char cur = nextChar(); + + // Process cell start with column (bColumn == true) + while ( Result && cur != ')' && cur ) + { + switch ( cur ) + { + case '^': + // Oids + Corners++; + if ( 1 == Corners ) + { + } + else if ( 2 == Corners ) + { + bColumn = false; + bValueOid = true; + } + else + { + Text += cur; + } + + break; + case '=': + // From column to value + if ( bColumn ) + { + bColumn = false; + } + else + { + Text += cur; + } + break; + case '\\': + { + // Get next two chars + char NextChar= nextChar(); + if ( '\r' != NextChar && '\n' != NextChar ) + { + Text += NextChar; + } + else + { + (void)nextChar(); + } + } + break; + case '$': + { + // Get next two chars + std::string HexChar; + HexChar += nextChar(); + HexChar += nextChar(); + Text += static_cast<char>(strtoul(HexChar.c_str(), nullptr, 16)); + } + break; + default: + // Just a char + if ( bColumn ) + { + Column += cur; + } + else + { + Text += cur; + } + break; + } + + cur = nextChar(); + } + + // Apply column and text + int ColumnId = strtoul(Column.c_str(), nullptr, 16); + + if ( NP::Rows != nowParsing_ ) + { + // Dicts + if ( !Text.empty() ) + { + if ( nowParsing_ == NP::Columns ) + { + columns_[ ColumnId ] = Text; + } + else + { + values_[ ColumnId ] = Text; + } + } + } + else + { + if ( !Text.empty() ) + { + // Rows + //int ValueId = string( Text.c_str() ).toInt( 0, 16 ); + int ValueId = strtoul(Text.c_str(), nullptr, 16); + + if ( bValueOid ) + { + ( *currentCells_ )[ ColumnId ] = ValueId; + } + else + { + nextAddValueId_--; + values_[ nextAddValueId_ ] = Text; + ( *currentCells_ )[ ColumnId ] = nextAddValueId_; + } + } + } + + return Result; +} + +bool MorkParser::parseTable() +{ + bool Result = true; + std::string TextId; + int Id = 0, Scope = 0; + + char cur = nextChar(); + + // Get id + while ( cur != '{' && cur != '[' && cur != '}' && cur ) + { + if ( !isWhiteSpace( cur ) ) + { + TextId += cur; + } + + cur = nextChar(); + } + + parseScopeId( TextId, &Id, &Scope ); + + // Parse the table + while ( Result && cur != '}' && cur ) + { + if ( !isWhiteSpace( cur ) ) + { + switch ( cur ) + { + case '{': + parseMeta( '}' ); + break; + case '[': + Result = parseRow( Id, Scope ); + break; + case '-': + case '+': + break; + default: + { + std::string JustId; + while ( !isWhiteSpace( cur ) && cur ) + { + JustId += cur; + cur = nextChar(); + + if ( cur == '}' ) + { + return Result; + } + } + + int JustIdNum = 0, JustScopeNum = 0; + parseScopeId( JustId, &JustIdNum, &JustScopeNum ); + + setCurrentRow( Scope, Id, JustScopeNum, JustIdNum ); + } + break; + } + } + + cur = nextChar(); + } + + return Result; +} + +void MorkParser::parseScopeId( const std::string &TextId, int *Id, int *Scope ) +{ + int Pos = 0; + + if ( ( Pos = TextId.find( ':' ) ) >= 0 ) + { + std::string tId = TextId.substr( 0, Pos ); + std::string tSc = TextId.substr( Pos + 1, TextId.length() - Pos ); + + if ( tSc.length() > 1 && '^' == tSc[ 0 ] ) + { + // Delete '^' + tSc.erase( 0, 1 ); + } + + *Id = strtoul(tId.c_str(), nullptr, 16); + + *Scope = strtoul(tSc.c_str(), nullptr, 16); + } + else + { + *Id = strtoul(TextId.c_str(), nullptr, 16); + } +} + +inline void MorkParser::setCurrentRow( int TableScope, int TableId, int RowScope, int RowId ) +{ + if ( !RowScope ) + { + RowScope = defaultScope_; + } + + if ( !TableScope ) + { + TableScope = defaultScope_; + } + + // 01.08.2012 davido + // TableId 0 is wrong here. + // Straying rows (rows that defined outside the table) belong to the default scope and table is the last was seen: 1:^80 + // (at least i read so the specification) + if (TableId) + { + defaultTableId_ = TableId; + } + + if (!TableId) + { + TableId = defaultTableId_; + } + + currentCells_ = &( mork_.map[ abs( TableScope ) ].map[ abs( TableId ) ].map[ abs( RowScope ) ].map[ abs( RowId ) ] ); +} + +bool MorkParser::parseRow( int TableId, int TableScope ) +{ + bool Result = true; + std::string TextId; + int Id = 0, Scope = 0; + nowParsing_ = NP::Rows; + + char cur = nextChar(); + + // Get id + while ( cur != '(' && cur != ']' && cur != '[' && cur ) + { + if ( !isWhiteSpace( cur ) ) + { + TextId += cur; + } + + cur = nextChar(); + } + + parseScopeId( TextId, &Id, &Scope ); + setCurrentRow( TableScope, TableId, Scope, Id ); + + // Parse the row + while ( Result && cur != ']' && cur ) + { + if ( !isWhiteSpace( cur ) ) + { + switch ( cur ) + { + case '(': + Result = parseCell(); + break; + case '[': + parseMeta( ']' ); + break; + default: + Result = false; + break; + } + } + + cur = nextChar(); + } + + return Result; +} + +void MorkParser::parseGroup() +{ + parseMeta( '@' ); +} + +void MorkParser::parseMeta( char c ) +{ + char cur = nextChar(); + + while ( cur != c && cur ) + { + cur = nextChar(); + } +} + +MorkTableMap *MorkParser::getTables( int TableScope ) +{ + TableScopeMap::Map::iterator iter = mork_.map.find( TableScope ); + + if ( iter == mork_.map.end() ) + { + return nullptr; + } + + return &iter->second; +} + +MorkRowMap *MorkParser::getRows( int RowScope, RowScopeMap *table ) +{ + RowScopeMap::Map::iterator iter = table->map.find( RowScope ); + + if ( iter == table->map.end() ) + { + return nullptr; + } + + return &iter->second; +} + +std::string const &MorkParser::getValue( int oid ) +{ + MorkDict::iterator foundIter = values_.find( oid ); + + if ( values_.end() == foundIter ) + { + return g_Empty; + } + + return foundIter->second; +} + +std::string const &MorkParser::getColumn( int oid ) +{ + MorkDict::iterator foundIter = columns_.find( oid ); + + if ( columns_.end() == foundIter ) + { + return g_Empty; + } + + return foundIter->second; +} + +void MorkParser::retrieveLists(std::set<std::string>& lists) +{ +#ifdef VERBOSE + boost::io::ios_all_saver ias(std::cout); + std::cout << std::hex << std::uppercase; +#endif + + MorkTableMap* tables = getTables(defaultScope_); + if (!tables) return; + for (auto& rTable : tables->map) + { +#ifdef VERBOSE + std::cout << "\t Table:" + << ( ( int ) rTable.first < 0 ? "-" : " " ) + << rTable.first << std::endl; +#endif + MorkRowMap* rows = getRows( 0x81/*defaultListScope*/, &rTable.second ); + if (!rows) return; + for ( const auto& rRow : rows->map ) + { +#ifdef VERBOSE + std::cout << "\t\t\t Row Id:" + << ( ( int ) rRow.first < 0 ? "-" : " ") + << rRow.first << std::endl; + std::cout << "\t\t\t\t Cells:\r\n"; +#endif + // Get cells + MorkCells::const_iterator cellsIter = rRow.second.find(0xC1); + if (cellsIter != rRow.second.end()) + lists.insert(getValue( cellsIter->second )); + } + } +} + +void MorkParser::getRecordKeysForListTable(std::string const & listName, std::set<int>& records) +{ +#ifdef VERBOSE + boost::io::ios_all_saver ias(std::cout); + std::cout << std::hex << std::uppercase; +#endif + + MorkTableMap* tables = getTables(defaultScope_); + if (!tables) return; + for (auto& rTable : tables->map) + { +#ifdef VERBOSE + std::cout << "\t Table:" + << ( ( int ) rTable.first < 0 ? "-" : " " ) + << rTable.first << std::endl; +#endif + MorkRowMap* rows = getRows( 0x81, &rTable.second ); + if (!rows) return; + for ( const auto& rRow : rows->map ) + { +#ifdef VERBOSE + std::cout << "\t\t\t Row Id:" + << ( ( int ) rRow.first < 0 ? "-" : " ") + << rRow.first << std::endl; + std::cout << "\t\t\t\t Cells:\r\n"; +#endif + // Get cells + bool isListFound = false; + for ( const auto& [rColumnId, rValueId] : rRow.second ) + { + if (isListFound) + { + if (rColumnId >= 0xC7) + { + std::string value = getValue(rValueId); + int id = strtoul(value.c_str(), nullptr, 16); + records.insert(id); + } + } + else if ((rColumnId == 0xC1) && + listName == getValue( rValueId )) + { + isListFound = true; + } + } + + } + } +} + +void MorkParser::dump() +{ + boost::io::ios_all_saver ias(std::cout); + std::cout << std::hex << std::uppercase; + + std::cout << "Column Dict:\r\n"; + std::cout << "=============================================\r\n\r\n"; + + //// columns dict + for ( const auto& [rColumnId, rText] : columns_ ) + { + std::cout << rColumnId + << " : " + << rText + << std::endl; + } + + //// values dict + std::cout << "\r\nValues Dict:\r\n"; + std::cout << "=============================================\r\n\r\n"; + + for ( const auto& [rValueId, rText] : values_ ) + { + if (rValueId >= nextAddValueId_) { + continue; + } + + std::cout << rValueId + << " : " + << rText + << "\r\n"; + } + + std::cout << std::endl << "Data:" << std::endl; + std::cout << "=============================================" + << std::endl << std::endl; + + //// Mork data + for ( const auto& [rTableScopeId, rTableScope] : mork_.map ) + { + std::cout << "\r\n Scope:" << rTableScopeId << std::endl; + + for ( const auto& [rTableId, rTable] : rTableScope.map ) + { + std::cout << "\t Table:" + << ( rTableId < 0 ? "-" : " " ) + << rTableId << std::endl; + + for (const auto& [rRowScopeId, rRowScope] : rTable.map) + { + std::cout << "\t\t RowScope:" + << rRowScopeId << std::endl; + + for (const auto& [rRowId, rRow] : rRowScope.map) + { + std::cout << "\t\t\t Row Id:" + << (rRowId < 0 ? "-" : " ") + << rRowId << std::endl; + std::cout << "\t\t\t\t Cells:" << std::endl; + + for (const auto& [rColumnId, rValueId] : rRow) + { + // Write ids + std::cout << "\t\t\t\t\t" + << rColumnId + << " : " + << rValueId + << " => "; + + MorkDict::const_iterator FoundIter = values_.find( rValueId ); + if ( FoundIter != values_.end() ) + { + // Write string values + std::cout << columns_[ rColumnId ].c_str() + << " : " + << FoundIter->second.c_str() + << std::endl; + } + } + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/MorkParser.hxx b/connectivity/source/drivers/mork/MorkParser.hxx new file mode 100644 index 000000000..d16fc417d --- /dev/null +++ b/connectivity/source/drivers/mork/MorkParser.hxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2006, ScalingWeb.com + * All rights reserved. + * + * Redistribution and use of this software in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * * Neither the name of ScalingWeb.com nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission of ScalingWeb.com. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MORKPARSER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_MORKPARSER_HXX + +#include <rtl/ustring.hxx> + +#include <string> +#include <map> +#include <set> +#include <vector> + +#include "dllapi.h" + +// Types + +typedef std::map< int, std::string > MorkDict; +typedef std::map< int, int > MorkCells; // ColumnId : ValueId +struct MorkRowMap { typedef std::map< int, MorkCells > Map; Map map; }; // Row id +struct RowScopeMap { typedef std::map< int, MorkRowMap > Map; Map map; }; // Row scope +struct MorkTableMap { typedef std::map< int, RowScopeMap > Map; Map map; }; // Table id +struct TableScopeMap { typedef std::map< int, MorkTableMap > Map; Map map; }; // Table Scope + +// Error codes +enum MorkErrors +{ + NoError = 0, + FailedToOpen, + DefectedFormat +}; + + +/// Class MorkParser + +class LO_DLLPUBLIC_MORK MorkParser final +{ +public: + + explicit MorkParser(); + + /// Open and parse mork file + + bool open( const std::string &path ); + + /// Returns all tables of specified scope + + MorkTableMap *getTables( int tableScope ); + + /// Returns all rows under specified scope + + static MorkRowMap *getRows( int rowScope, RowScopeMap *table ); + + /// Return value of specified value oid + + std::string const &getValue( int oid ); + + /// Return value of specified column oid + + std::string const &getColumn( int oid ); + + void retrieveLists(std::set<std::string>& lists); + void getRecordKeysForListTable(std::string const & listName, std::set<int>& records); + + void dump(); + + // All lists + std::vector<OUString> lists_; + +private: // Members + + void initVars(); + + static bool isWhiteSpace( char c ); + char nextChar(); + + static void parseScopeId( const std::string &TextId, int *Id, int *Scope ); + void setCurrentRow( int TableScope, int TableId, int RowScope, int RowId ); + + // Parse methods + bool parse(); + bool parseDict(); + bool parseComment(); + bool parseCell(); + bool parseTable(); + void parseMeta( char c ); + bool parseRow( int TableId, int TableScope ); + void parseGroup(); + +private: // Data + + // Columns in mork means value names + MorkDict columns_; + MorkDict values_; + + // All mork file data + TableScopeMap mork_; + MorkCells *currentCells_; + + // Error status of last operation + MorkErrors error_; + + // All Mork data + std::string morkData_; + + + unsigned morkPos_; + int nextAddValueId_; + int defaultTableId_; + + // Indicates entity is being parsed + enum class NP { Columns, Values, Rows } nowParsing_; + + MorkParser(const MorkParser &) = delete; + MorkParser &operator=(const MorkParser &) = delete; + +}; + +#endif // __MorkParser_h__ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/README b/connectivity/source/drivers/mork/README new file mode 100644 index 000000000..0d4207b0c --- /dev/null +++ b/connectivity/source/drivers/mork/README @@ -0,0 +1,41 @@ +Mork Format Parsing Library +============================= + +Description +----------- + +Cross Platform Mozilla Mork format reader. + + +Compilation +------------ + +g++ -o MorkParser main.cpp MorkParser.cpp + + +License +------- + +This program is licensed under permissive BSD license. +See the license.txt file for more information. + + +Date: October 16th, 2007 + +Project Maintainers: + Yuriy Soroka + ysoroka@scalingweb.com + + http://www.scalingweb.com/ + +Thanks +------------- +Thanks to Petr Stejskal <stejsky@volny.cz> who helped with porting this code from Qt to STL. + + +How you can help +---------------- + + Comments, patches, bug reports are welcome. + + diff --git a/connectivity/source/drivers/mork/dllapi.h b/connectivity/source/drivers/mork/dllapi.h new file mode 100644 index 000000000..adf269107 --- /dev/null +++ b/connectivity/source/drivers/mork/dllapi.h @@ -0,0 +1,25 @@ + +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_DLLAPI_H +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MORK_DLLAPI_H + +#include <sal/config.h> +#include <sal/types.h> + +#if defined LO_DLLIMPLEMENTATION_MORK +#define LO_DLLPUBLIC_MORK SAL_DLLPUBLIC_EXPORT +#else +#define LO_DLLPUBLIC_MORK SAL_DLLPUBLIC_IMPORT +#endif + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mork/license.txt b/connectivity/source/drivers/mork/license.txt new file mode 100644 index 000000000..3e41befc7 --- /dev/null +++ b/connectivity/source/drivers/mork/license.txt @@ -0,0 +1,31 @@ +Software License Agreement (BSD License) + +Copyright (c) 2006, ScalingWeb.com +All rights reserved. + +Redistribution and use of this software in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of ScalingWeb.com nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of ScalingWeb.com. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/connectivity/source/drivers/mork/mork.component b/connectivity/source/drivers/mork/mork.component new file mode 100644 index 000000000..1eeb6affb --- /dev/null +++ b/connectivity/source/drivers/mork/mork.component @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * +--> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.MorkDriver" + constructor="com_sun_star_comp_sdbc_MorkDriver_get_implementation"> + <service name="com.sun.star.sdbc.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/mork/mork_helper.cxx b/connectivity/source/drivers/mork/mork_helper.cxx new file mode 100644 index 000000000..4cb11864f --- /dev/null +++ b/connectivity/source/drivers/mork/mork_helper.cxx @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "MorkParser.hxx" +#include <iostream> +#include <sal/log.hxx> + +static bool openAddressBook(const std::string& path) +{ + MorkParser mork; + // Open and parse mork file + if (!mork.open(path)) + { + return false; + } + + const int defaultScope = 0x80; + MorkTableMap *Tables = mork.getTables( defaultScope ); + if ( Tables ) + { + // Iterate all tables + for (auto const& table : Tables->map) + { + if ( 0 == table.first ) continue; + SAL_INFO("connectivity.mork", "table->first : " << table.first); + std::string column = mork.getColumn( table.first ); + std::string value = mork.getValue( table.first ); + SAL_INFO("connectivity.mork", "table.column : " << column); + SAL_INFO("connectivity.mork", "table.value : " << value); + } + } + + mork.dump(); + + return true; +} + +int main(int argc, char* argv[]) +{ + if (argc < 2) + { + std::cerr << "Usage: " << argv[0] << " <path-to>/abook.mab" << std::endl; + std::cerr << "Example: " << argv[0] << " /home/johndoe/.thunderbird/m0tpqlky.default/abook.mab" << std::endl; + + return 1; + } + + OString aOString(argv[1]); + SAL_INFO("connectivity.mork", "abook.mab: " << aOString); + openAddressBook(aOString.getStr()); + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/MMozillaBootstrap.cxx b/connectivity/source/drivers/mozab/bootstrap/MMozillaBootstrap.cxx new file mode 100644 index 000000000..8dc776877 --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/MMozillaBootstrap.cxx @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <cppuhelper/supportsservice.hxx> +#include "MMozillaBootstrap.hxx" +#include "MNSProfileDiscover.hxx" + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::mozilla; +using namespace connectivity::mozab; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +static MozillaBootstrap *pMozillaBootstrap=nullptr; +static Reference<XMozillaBootstrap> xMozillaBootstrap; +extern "C" SAL_DLLPUBLIC_EXPORT void* OMozillaBootstrap_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& ) +{ + if (!pMozillaBootstrap) + { + pMozillaBootstrap=new connectivity::mozab::MozillaBootstrap; + pMozillaBootstrap->Init(); + xMozillaBootstrap = pMozillaBootstrap; + } + return pMozillaBootstrap; +} + +MozillaBootstrap::MozillaBootstrap() + : OMozillaBootstrap_BASE(m_aMutex) +{ +} + +MozillaBootstrap::~MozillaBootstrap() +{ +} + +void MozillaBootstrap::Init() +{ + m_ProfileAccess.reset(new ProfileAccess); + bootupProfile(css::mozilla::MozillaProductType_Mozilla,OUString()); +} + +void MozillaBootstrap::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + OMozillaBootstrap_BASE::disposing(); +} + +OUString SAL_CALL MozillaBootstrap::getImplementationName( ) +{ + return MOZAB_MozillaBootstrap_IMPL_NAME; +} + +sal_Bool SAL_CALL MozillaBootstrap::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > SAL_CALL MozillaBootstrap::getSupportedServiceNames( ) +{ + // which service is supported + // for more information @see com.sun.star.mozilla.MozillaBootstrap + return { "com.sun.star.mozilla.MozillaBootstrap" }; +} + + +// XProfileDiscover +::sal_Int32 SAL_CALL MozillaBootstrap::getProfileCount( css::mozilla::MozillaProductType product) +{ + return m_ProfileAccess->getProfileCount(product); +} +::sal_Int32 SAL_CALL MozillaBootstrap::getProfileList( css::mozilla::MozillaProductType product, css::uno::Sequence< OUString >& list ) +{ + return m_ProfileAccess->getProfileList(product,list); +} +OUString SAL_CALL MozillaBootstrap::getDefaultProfile( css::mozilla::MozillaProductType product ) +{ + return m_ProfileAccess->getDefaultProfile(product); +} +OUString SAL_CALL MozillaBootstrap::getProfilePath( css::mozilla::MozillaProductType product, const OUString& profileName ) +{ + return m_ProfileAccess->getProfilePath(product,profileName); +} +sal_Bool SAL_CALL MozillaBootstrap::isProfileLocked( css::mozilla::MozillaProductType /*product*/, const OUString& /*profileName*/ ) +{ + return true; +} +sal_Bool SAL_CALL MozillaBootstrap::getProfileExists( css::mozilla::MozillaProductType product, const OUString& profileName ) +{ + return m_ProfileAccess->getProfileExists(product,profileName); +} + +// XProfileManager +::sal_Int32 SAL_CALL MozillaBootstrap::bootupProfile( css::mozilla::MozillaProductType, const OUString& ) +{ + return -1; +} +::sal_Int32 SAL_CALL MozillaBootstrap::shutdownProfile( ) +{ + return -1; +} +css::mozilla::MozillaProductType SAL_CALL MozillaBootstrap::getCurrentProduct( ) +{ + return css::mozilla::MozillaProductType_Default; +} +OUString SAL_CALL MozillaBootstrap::getCurrentProfile( ) +{ + return OUString(); +} +sal_Bool SAL_CALL MozillaBootstrap::isCurrentProfileLocked( ) +{ + return true; +} +OUString SAL_CALL MozillaBootstrap::setCurrentProfile( css::mozilla::MozillaProductType, const OUString& ) +{ + return OUString(); +} + +// XProxyRunner +::sal_Int32 SAL_CALL MozillaBootstrap::Run( const css::uno::Reference< css::mozilla::XCodeProxy >& ) +{ + return -1; +} + +static Reference< XInterface > createInstance( const Reference< XMultiServiceFactory >& rServiceManager ) +{ + MozillaBootstrap * pBootstrap = static_cast<MozillaBootstrap*>(OMozillaBootstrap_CreateInstance(rServiceManager)); + return *pBootstrap; +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* mozbootstrap_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + + if (pServiceManager) + { + OUString aImplName( OUString::createFromAscii( pImplementationName ) ); + Reference< XSingleServiceFactory > xFactory; + if ( aImplName == "com.sun.star.comp.mozilla.MozillaBootstrap" ) + { + Sequence<OUString> aSNS { "com.sun.star.mozilla.MozillaBootstrap" }; + + xFactory = ::cppu::createSingleFactory( + static_cast< XMultiServiceFactory* > ( pServiceManager), + aImplName, createInstance, aSNS ); + } + if ( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + + return pRet; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/MMozillaBootstrap.hxx b/connectivity/source/drivers/mozab/bootstrap/MMozillaBootstrap.hxx new file mode 100644 index 000000000..a727e3579 --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/MMozillaBootstrap.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MMOZILLABOOTSTRAP_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MMOZILLABOOTSTRAP_HXX + +#include <sal/config.h> + +#include <memory> + +#include <com/sun/star/mozilla/XMozillaBootstrap.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/compbase.hxx> + + +#define MOZAB_MozillaBootstrap_IMPL_NAME "com.sun.star.comp.mozilla.MozillaBootstrap" + +namespace connectivity +{ + namespace mozab + { + typedef ::cppu::WeakComponentImplHelper< css::mozilla::XMozillaBootstrap, + css::lang::XServiceInfo > OMozillaBootstrap_BASE; + class ProfileAccess; + class ProfileManager; + class MozillaBootstrap : public OMozillaBootstrap_BASE + { + private: + ::osl::Mutex m_aMutex; // mutex is need to control member access + virtual ~MozillaBootstrap() override; + std::unique_ptr<ProfileAccess> m_ProfileAccess; + public: + + void Init(); + MozillaBootstrap(); + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XMozillaBootstrap + + // XProfileDiscover + virtual ::sal_Int32 SAL_CALL getProfileCount( css::mozilla::MozillaProductType product) override; + virtual ::sal_Int32 SAL_CALL getProfileList( css::mozilla::MozillaProductType product, css::uno::Sequence< OUString >& list ) override; + virtual OUString SAL_CALL getDefaultProfile( css::mozilla::MozillaProductType product ) override; + virtual OUString SAL_CALL getProfilePath( css::mozilla::MozillaProductType product, const OUString& profileName ) override; + virtual sal_Bool SAL_CALL isProfileLocked( css::mozilla::MozillaProductType product, const OUString& profileName ) override; + virtual sal_Bool SAL_CALL getProfileExists( css::mozilla::MozillaProductType product, const OUString& profileName ) override; + + // XProfileManager + virtual ::sal_Int32 SAL_CALL bootupProfile( css::mozilla::MozillaProductType product, const OUString& profileName ) override; + virtual ::sal_Int32 SAL_CALL shutdownProfile( ) override; + virtual css::mozilla::MozillaProductType SAL_CALL getCurrentProduct( ) override; + virtual OUString SAL_CALL getCurrentProfile( ) override; + virtual sal_Bool SAL_CALL isCurrentProfileLocked( ) override; + virtual OUString SAL_CALL setCurrentProfile( css::mozilla::MozillaProductType product, const OUString& profileName ) override; + + // XProxyRunner + virtual ::sal_Int32 SAL_CALL Run( const css::uno::Reference< css::mozilla::XCodeProxy >& aCode ) override; + }; + } + +} + +#endif // CONNECTIVITY_SMozillaBootstrap_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/MNSFolders.cxx b/connectivity/source/drivers/mozab/bootstrap/MNSFolders.cxx new file mode 100644 index 000000000..2c92613d2 --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/MNSFolders.cxx @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "MNSFolders.hxx" + +#ifdef UNIX +#include <string.h> +#endif // End UNIX + +#ifdef _WIN32 +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <stdlib.h> +#include <shlobj.h> +#include <objidl.h> +#endif // End _WIN32 +#include <osl/security.hxx> +#include <osl/file.hxx> +#include <osl/thread.h> + +using namespace ::com::sun::star::mozilla; + +namespace +{ + + OUString lcl_getUserDataDirectory() + { + ::osl::Security aSecurity; + OUString aConfigPath; + + #if defined(_WIN32) || defined(MACOSX) + aSecurity.getConfigDir( aConfigPath ); + #else + //This is to find the dir under which .mozilla/.thunderbird is created. + //mozilla doesn't honour XDG_CONFIG_HOME, so raw home dir required here + //not xdg-config dir + aSecurity.getHomeDir( aConfigPath ); + #endif + + return aConfigPath + "/"; + } + + + const size_t NB_PRODUCTS = 3; + const size_t NB_CANDIDATES = 4; + + // The order (index) of entries in DefaultProductDir and + // ProductRootEnvironmentVariable *must* match the constants + // (minus 1) in offapi/com/sun/star/mozilla/MozillaProductType.idl + // DO NOT CHANGE THE ORDER; ADD ONLY TO THE END + static const char* DefaultProductDir[NB_PRODUCTS][NB_CANDIDATES] = + { + #if defined(_WIN32) + { "Mozilla/SeaMonkey/", nullptr, nullptr, nullptr }, + { "Mozilla/Firefox/", nullptr, nullptr, nullptr }, + { "Thunderbird/", "Mozilla/Thunderbird/", nullptr, nullptr } + #elif defined(MACOSX) + { "../Mozilla/SeaMonkey/", nullptr, nullptr, nullptr }, + { "Firefox/", nullptr, nullptr, nullptr }, + { "../Thunderbird/", nullptr, nullptr, nullptr } + #else + { ".mozilla/seamonkey/", nullptr, nullptr, nullptr }, + { ".mozilla/firefox/", nullptr, nullptr, nullptr }, + { ".thunderbird/", ".mozilla-thunderbird/", ".mozilla/thunderbird/", ".icedove/" } + #endif + }; + + static const char* ProductRootEnvironmentVariable[NB_PRODUCTS] = + { + "MOZILLA_PROFILE_ROOT", + "MOZILLA_FIREFOX_PROFILE_ROOT", + "MOZILLA_THUNDERBIRD_PROFILE_ROOT", + }; + + + OUString const & lcl_guessProfileRoot( MozillaProductType _product ) + { + size_t productIndex = static_cast<int>(_product) - 1; + + static OUString s_productDirectories[NB_PRODUCTS]; + + if ( s_productDirectories[ productIndex ].isEmpty() ) + { + OUString sProductPath; + + // check whether we have an environment variable which help us + const char* pProfileByEnv = getenv( ProductRootEnvironmentVariable[ productIndex ] ); + if ( pProfileByEnv ) + { + sProductPath = OUString( pProfileByEnv, rtl_str_getLength( pProfileByEnv ), osl_getThreadTextEncoding() ); + // assume that this is fine, no further checks + } + else + { + OUString sProductDirCandidate; + const char pProfileRegistry[] = "profiles.ini"; + + // check all possible candidates + for ( size_t i=0; i<NB_CANDIDATES; ++i ) + { + if ( nullptr == DefaultProductDir[ productIndex ][ i ] ) + break; + + sProductDirCandidate = lcl_getUserDataDirectory() + + OUString::createFromAscii( DefaultProductDir[ productIndex ][ i ] ); + + // check existence + ::osl::DirectoryItem aRegistryItem; + ::osl::FileBase::RC result = ::osl::DirectoryItem::get( sProductDirCandidate + pProfileRegistry, aRegistryItem ); + if ( result == ::osl::FileBase::E_None ) + { + ::osl::FileStatus aStatus( osl_FileStatus_Mask_Validate ); + result = aRegistryItem.getFileStatus( aStatus ); + if ( result == ::osl::FileBase::E_None ) + { + // the registry file exists + break; + } + } + } + + ::osl::FileBase::getSystemPathFromFileURL( sProductDirCandidate, sProductPath ); + } + + s_productDirectories[ productIndex ] = sProductPath; + } + + return s_productDirectories[ productIndex ]; + } +} + + +OUString getRegistryDir(MozillaProductType product) +{ + if (product == MozillaProductType_Default) + return OUString(); + + return lcl_guessProfileRoot( product ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/MNSFolders.hxx b/connectivity/source/drivers/mozab/bootstrap/MNSFolders.hxx new file mode 100644 index 000000000..50c992f23 --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/MNSFolders.hxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MNSFOLDERS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MNSFOLDERS_HXX + +#include <com/sun/star/mozilla/MozillaProductType.hpp> + +#include <rtl/ustring.hxx> + +OUString getRegistryDir(css::mozilla::MozillaProductType product); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/MNSINIParser.cxx b/connectivity/source/drivers/mozab/bootstrap/MNSINIParser.cxx new file mode 100644 index 000000000..ae775b6fc --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/MNSINIParser.cxx @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MNSINIParser.hxx" +#include <com/sun/star/io/IOException.hpp> +#include <osl/file.h> +#include <rtl/byteseq.hxx> +#include <sal/log.hxx> + +IniParser::IniParser(OUString const & rIniName) +{ + OUString iniUrl; + if (osl_File_E_None != osl_getFileURLFromSystemPath(rIniName.pData, &iniUrl.pData)) + return; + + + oslFileHandle handle=nullptr; + oslFileError fileError = osl_File_E_INVAL; + try{ + if (!iniUrl.isEmpty()) + fileError = osl_openFile(iniUrl.pData, &handle, osl_File_OpenFlag_Read); + } + catch(const css::io::IOException&) + { + SAL_WARN("connectivity.mozab", "couldn't open file: " << iniUrl ); + } + + if (osl_File_E_None == fileError) + { + rtl::ByteSequence seq; + sal_uInt64 nSize = 0; + + osl_getFileSize(handle, &nSize); + OUString sectionName( "no name section" ); + while (true) + { + sal_uInt64 nPos; + if (osl_File_E_None != osl_getFilePos(handle, &nPos) || nPos >= nSize) + break; + if (osl_File_E_None != osl_readLine(handle, reinterpret_cast<sal_Sequence **>(&seq))) + break; + OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength() ); + sal_Int32 nIndex = line.indexOf('='); + if (nIndex >= 1) + { + ini_Section *aSection = &mAllSection[sectionName]; + struct ini_NameValue nameValue; + nameValue.sName = OStringToOUString( + line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US ); + nameValue.sValue = OStringToOUString( + line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 ); + + aSection->vVector.push_back(nameValue); + + } + else + { + sal_Int32 nIndexStart = line.indexOf('['); + sal_Int32 nIndexEnd = line.indexOf(']'); + if ( nIndexEnd > nIndexStart && nIndexStart >=0) + { + sectionName = OStringToOUString( + line.copy(nIndexStart + 1,nIndexEnd - nIndexStart -1).trim(), RTL_TEXTENCODING_ASCII_US ); + if (sectionName.isEmpty()) + sectionName = "no name section"; + } + } + } + osl_closeFile(handle); + } + else + { + SAL_INFO("connectivity.mozab", "couldn't open file: " << iniUrl ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/MNSINIParser.hxx b/connectivity/source/drivers/mozab/bootstrap/MNSINIParser.hxx new file mode 100644 index 000000000..210553b73 --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/MNSINIParser.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MNSINIPARSER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MNSINIPARSER_HXX + +#include <rtl/ustring.hxx> + +#include <map> +#include <vector> + +struct ini_NameValue +{ + OUString sName; + OUString sValue; +}; + +typedef std::vector< + ini_NameValue +> NameValueVector; + +struct ini_Section +{ + NameValueVector vVector; +}; +typedef std::map<OUString, + ini_Section + >IniSectionMap; + + +class IniParser +{ + IniSectionMap mAllSection; +public: + IniSectionMap& getAllSection() { return mAllSection; } + /// @throws css::io::IOException + explicit IniParser(OUString const & rIniName); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/MNSProfileDiscover.cxx b/connectivity/source/drivers/mozab/bootstrap/MNSProfileDiscover.cxx new file mode 100644 index 000000000..4dd9110a6 --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/MNSProfileDiscover.cxx @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "MNSProfileDiscover.hxx" +#include "MNSFolders.hxx" +#include "MNSINIParser.hxx" + +namespace connectivity::mozab +{ + ProfileStruct::ProfileStruct() + { + } + + ProfileStruct::ProfileStruct(const OUString& aProfileName, + const OUString& aProfilePath) + : profileName(aProfileName) + , profilePath(aProfilePath) + { + } + + const OUString& ProfileStruct::getProfilePath() const + { + return profilePath; + } + + ProfileAccess::~ProfileAccess() + { + } + + ProfileAccess::ProfileAccess() + { + LoadProductsInfo(); + } + + void ProfileAccess::LoadProductsInfo() + { + //tdf#39279: LO should search Thunderbird first then Seamonkey and finally Firefox + //load thunderbird profiles to m_ProductProfileList + LoadXPToolkitProfiles(MozillaProductType_Thunderbird); + + //load SeaMonkey 2 profiles to m_ProductProfileList + LoadXPToolkitProfiles(MozillaProductType_Mozilla); + + //load firefox profiles to m_ProductProfileList + //firefox profile does not contain address book, but maybe others need them + LoadXPToolkitProfiles(MozillaProductType_Firefox); + } + //Thunderbird and firefox profiles are saved in profiles.ini + void ProfileAccess::LoadXPToolkitProfiles(MozillaProductType product) + { + sal_Int32 index=static_cast<sal_Int32>(product); + ProductStruct &rProduct = m_ProductProfileList[index]; + + OUString regDir = getRegistryDir(product); + OUString profilesIni = regDir + "profiles.ini"; + IniParser parser( profilesIni ); + IniSectionMap &rAllSection = parser.getAllSection(); + + for(auto& rSection : rAllSection) + { + ini_Section *aSection = &rSection.second; + OUString profileName; + OUString profilePath; + OUString sIsRelative; + OUString sIsDefault; + + for(auto& rValue : aSection->vVector) + { + struct ini_NameValue * aValue = &rValue; + if ( aValue->sName == "Name" ) + { + profileName = aValue->sValue; + } + else if ( aValue->sName == "IsRelative" ) + { + sIsRelative = aValue->sValue; + } + else if ( aValue->sName == "Path" ) + { + profilePath = aValue->sValue; + } + else if ( aValue->sName == "Default" ) + { + sIsDefault = aValue->sValue; + } + } + if (!(profileName.isEmpty() && profilePath.isEmpty())) + { + sal_Int32 isRelative = 0; + if (!sIsRelative.isEmpty()) + { + isRelative = sIsRelative.toInt32(); + } + + OUString fullProfilePath; + if(isRelative) + { + fullProfilePath = regDir + profilePath; + } + else + { + fullProfilePath = profilePath; + } + + rProduct.mProfileList[profileName] = ProfileStruct(profileName,fullProfilePath); + + sal_Int32 isDefault = 0; + if (!sIsDefault.isEmpty()) + { + isDefault = sIsDefault.toInt32(); + } + if (isDefault) + rProduct.mCurrentProfileName = profileName; + + } + + // Depending on TB versions, some generate "default" profile + // others "default-release" profile + // See https://support.mozilla.org/gl/questions/1264072 + // for some background info (the link quotes Firefox but it seems + // the same for TB). + if (profileName == "default-release") + { + rProduct.mCurrentProfileName = profileName; + break; + } + } + } + + OUString ProfileAccess::getProfilePath( css::mozilla::MozillaProductType product, const OUString& profileName ) + { + sal_Int32 index=static_cast<sal_Int32>(product); + ProductStruct &rProduct = m_ProductProfileList[index]; + if (rProduct.mProfileList.empty() || rProduct.mProfileList.find(profileName) == rProduct.mProfileList.end()) + { + //Profile not found + return OUString(); + } + else + return rProduct.mProfileList[profileName].getProfilePath(); + } + + ::sal_Int32 ProfileAccess::getProfileCount( css::mozilla::MozillaProductType product) + { + sal_Int32 index=static_cast<sal_Int32>(product); + ProductStruct &rProduct = m_ProductProfileList[index]; + return static_cast< ::sal_Int32 >(rProduct.mProfileList.size()); + } + ::sal_Int32 ProfileAccess::getProfileList( css::mozilla::MozillaProductType product, css::uno::Sequence< OUString >& list ) + { + sal_Int32 index=static_cast<sal_Int32>(product); + ProductStruct &rProduct = m_ProductProfileList[index]; + list.realloc(static_cast<sal_Int32>(rProduct.mProfileList.size())); + sal_Int32 i=0; + for(const auto& rEntry : rProduct.mProfileList) + { + const ProfileStruct& rProfile = rEntry.second; + list[i] = rProfile.getProfileName(); + i++; + } + + return static_cast< ::sal_Int32 >(rProduct.mProfileList.size()); + } + + OUString ProfileAccess::getDefaultProfile( css::mozilla::MozillaProductType product ) + { + sal_Int32 index=static_cast<sal_Int32>(product); + ProductStruct &rProduct = m_ProductProfileList[index]; + if (!rProduct.mCurrentProfileName.isEmpty()) + { + //default profile set in mozilla registry + return rProduct.mCurrentProfileName; + } + if (rProduct.mProfileList.empty()) + { + //there are not any profiles + return OUString(); + } + const ProfileStruct& rProfile = (*rProduct.mProfileList.begin()).second; + return rProfile.getProfileName(); + } + + bool ProfileAccess::getProfileExists( css::mozilla::MozillaProductType product, const OUString& profileName ) + { + sal_Int32 index=static_cast<sal_Int32>(product); + ProductStruct &rProduct = m_ProductProfileList[index]; + return rProduct.mProfileList.find(profileName) != rProduct.mProfileList.end(); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/MNSProfileDiscover.hxx b/connectivity/source/drivers/mozab/bootstrap/MNSProfileDiscover.hxx new file mode 100644 index 000000000..8921a9c4b --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/MNSProfileDiscover.hxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MNSPROFILEDISCOVER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MNSPROFILEDISCOVER_HXX + +#include <sal/types.h> +#include <com/sun/star/mozilla/MozillaProductType.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <rtl/ustring.hxx> + +#include <vector> +#include <map> + +using namespace com::sun::star::mozilla; +namespace connectivity +{ + namespace mozab + { + class ProfileStruct; + } +} +typedef std::map<OUString, ::connectivity::mozab::ProfileStruct> ProfileList; +namespace connectivity +{ + namespace mozab + { + class ProfileStruct + { + public: + ProfileStruct(); + ProfileStruct(const OUString& aProfileName, const OUString &aProfilePath); + const OUString& getProfileName() const { return profileName;} + const OUString& getProfilePath() const; + private: + OUString profileName; + OUString profilePath; + }; + + class ProductStruct + { + public: + OUString mCurrentProfileName; + ProfileList mProfileList; + }; + + //Used to query profiles information + class ProfileAccess final + { + public: + ~ProfileAccess(); + ProfileAccess(); + /// @throws css::uno::RuntimeException + OUString getProfilePath( css::mozilla::MozillaProductType product, const OUString& profileName ); + /// @throws css::uno::RuntimeException + ::sal_Int32 getProfileCount( css::mozilla::MozillaProductType product ); + /// @throws css::uno::RuntimeException + ::sal_Int32 getProfileList( css::mozilla::MozillaProductType product, css::uno::Sequence< OUString >& list ); + /// @throws css::uno::RuntimeException + OUString getDefaultProfile( css::mozilla::MozillaProductType product ); + /// @throws css::uno::RuntimeException + bool getProfileExists( css::mozilla::MozillaProductType product, const OUString& profileName ); + private: + ProductStruct m_ProductProfileList[4]; + void LoadProductsInfo(); + void LoadXPToolkitProfiles(MozillaProductType product); + }; + + } +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_MOZAB_BOOTSTRAP_MNSPROFILEDISCOVER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mozab/bootstrap/mozbootstrap.component b/connectivity/source/drivers/mozab/bootstrap/mozbootstrap.component new file mode 100644 index 000000000..848112c64 --- /dev/null +++ b/connectivity/source/drivers/mozab/bootstrap/mozbootstrap.component @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="mozbootstrap" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.mozilla.MozillaBootstrap"> + <service name="com.sun.star.mozilla.MozillaBootstrap"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/mysql_jdbc/YCatalog.cxx b/connectivity/source/drivers/mysql_jdbc/YCatalog.cxx new file mode 100644 index 000000000..ad98bfa1a --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/YCatalog.cxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <mysql/YCatalog.hxx> +#include <mysql/YUsers.hxx> +#include <mysql/YTables.hxx> +#include <mysql/YViews.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <comphelper/types.hxx> + +using namespace connectivity; +using namespace connectivity::mysql; +using namespace connectivity::sdbcx; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OMySQLCatalog::OMySQLCatalog(const Reference<XConnection>& _xConnection) + : OCatalog(_xConnection) + , m_xConnection(_xConnection) +{ +} + +void OMySQLCatalog::refreshObjects(const Sequence<OUString>& _sKindOfObject, + ::std::vector<OUString>& _rNames) +{ + Reference<XResultSet> xResult = m_xMetaData->getTables(Any(), "%", "%", _sKindOfObject); + fillNames(xResult, _rNames); +} + +void OMySQLCatalog::refreshTables() +{ + ::std::vector<OUString> aVector; + + Sequence<OUString> sTableTypes(3); + sTableTypes[0] = "VIEW"; + sTableTypes[1] = "TABLE"; + sTableTypes[2] = "%"; // just to be sure to include anything else... + + refreshObjects(sTableTypes, aVector); + + if (m_pTables) + m_pTables->reFill(aVector); + else + m_pTables.reset(new OTables(m_xMetaData, *this, m_aMutex, aVector)); +} + +void OMySQLCatalog::refreshViews() +{ + Sequence<OUString> aTypes{ "VIEW" }; + + // let's simply assume the server is new enough to support views. Current drivers + // as of this writing might not return the proper information in getTableTypes, so + // don't rely on it. + + ::std::vector<OUString> aVector; + refreshObjects(aTypes, aVector); + + if (m_pViews) + m_pViews->reFill(aVector); + else + m_pViews.reset(new OViews(m_xMetaData, *this, m_aMutex, aVector)); +} + +void OMySQLCatalog::refreshGroups() {} + +void OMySQLCatalog::refreshUsers() +{ + ::std::vector<OUString> aVector; + Reference<XStatement> xStmt = m_xConnection->createStatement(); + Reference<XResultSet> xResult = xStmt->executeQuery( + "SELECT grantee FROM information_schema.user_privileges GROUP BY grantee"); + if (xResult.is()) + { + Reference<XRow> xRow(xResult, UNO_QUERY); + while (xResult->next()) + aVector.push_back(xRow->getString(1)); + ::comphelper::disposeComponent(xResult); + } + ::comphelper::disposeComponent(xStmt); + + if (m_pUsers) + m_pUsers->reFill(aVector); + else + m_pUsers.reset(new OUsers(*this, m_aMutex, aVector, m_xConnection, this)); +} + +Any SAL_CALL OMySQLCatalog::queryInterface(const Type& rType) +{ + if (rType == cppu::UnoType<XGroupsSupplier>::get()) + return Any(); + + return OCatalog::queryInterface(rType); +} + +Sequence<Type> SAL_CALL OMySQLCatalog::getTypes() +{ + Sequence<Type> aTypes = OCatalog::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()) + { + aOwnTypes.push_back(*pBegin); + } + } + return Sequence<Type>(aOwnTypes.data(), aOwnTypes.size()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/YColumns.cxx b/connectivity/source/drivers/mysql_jdbc/YColumns.cxx new file mode 100644 index 000000000..54beb77ca --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/YColumns.cxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <mysql/YColumns.hxx> +#include <TConnection.hxx> + +using namespace ::comphelper; +using namespace connectivity::mysql; +using namespace connectivity::sdbcx; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OMySQLColumns::OMySQLColumns(::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, + const ::std::vector<OUString>& _rVector) + : OColumnsHelper(_rParent, true /*_bCase*/, _rMutex, _rVector, true /*_bUseHardRef*/) +{ +} + +Reference<XPropertySet> OMySQLColumns::createDescriptor() { return new OMySQLColumn; } + +OMySQLColumn::OMySQLColumn() + : connectivity::sdbcx::OColumn(true) +{ + construct(); +} + +void OMySQLColumn::construct() +{ + m_sAutoIncrement = "auto_increment"; + registerProperty( + OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION), + PROPERTY_ID_AUTOINCREMENTCREATION, 0, &m_sAutoIncrement, + cppu::UnoType<decltype(m_sAutoIncrement)>::get()); +} + +::cppu::IPropertyArrayHelper* OMySQLColumn::createArrayHelper(sal_Int32 /*_nId*/) const +{ + return doCreateArrayHelper(); +} + +::cppu::IPropertyArrayHelper& SAL_CALL OMySQLColumn::getInfoHelper() +{ + return *OMySQLColumn_PROP::getArrayHelper(isNew() ? 1 : 0); +} + +Sequence<OUString> SAL_CALL OMySQLColumn::getSupportedServiceNames() +{ + return { "com.sun.star.sdbcx.Column" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/YDriver.cxx b/connectivity/source/drivers/mysql_jdbc/YDriver.cxx new file mode 100644 index 000000000..de6c0fdab --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/YDriver.cxx @@ -0,0 +1,418 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <mysql/YDriver.hxx> +#include <mysql/YCatalog.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbcharset.hxx> +#include <com/sun/star/sdbc/DriverManager.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <TConnection.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + +namespace connectivity +{ +using namespace mysql; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; + +namespace mysql +{ +Reference<XInterface> +ODriverDelegator_CreateInstance(const Reference<css::lang::XMultiServiceFactory>& _rxFac) +{ + return *(new ODriverDelegator(comphelper::getComponentContext(_rxFac))); +} +} + +namespace +{ +OUString getJavaDriverClass(css::uno::Sequence<css::beans::PropertyValue> const& info) +{ + return comphelper::NamedValueCollection(info).getOrDefault("JavaDriverClass", + OUString("com.mysql.jdbc.Driver")); +} +} + +ODriverDelegator::ODriverDelegator(const Reference<XComponentContext>& _rxContext) + : ODriverDelegator_BASE(m_aMutex) + , m_xContext(_rxContext) +{ +} + +ODriverDelegator::~ODriverDelegator() +{ + try + { + ::comphelper::disposeComponent(m_xODBCDriver); + ::comphelper::disposeComponent(m_xNativeDriver); + for (auto& rEntry : m_aJdbcDrivers) + ::comphelper::disposeComponent(rEntry.second); + } + catch (const Exception&) + { + } +} + +void ODriverDelegator::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + for (auto const& connection : m_aConnections) + { + Reference<XInterface> xTemp = connection.first.get(); + ::comphelper::disposeComponent(xTemp); + } + m_aConnections.clear(); + TWeakPairVector().swap(m_aConnections); + + ODriverDelegator_BASE::disposing(); +} + +namespace +{ +enum class T_DRIVERTYPE +{ + Odbc, + Jdbc, + Native +}; + +bool isOdbcUrl(const OUString& _sUrl) { return _sUrl.startsWith("sdbc:mysql:odbc:"); } + +bool isNativeUrl(const OUString& _sUrl) { return _sUrl.startsWith("sdbc:mysql:mysqlc:"); } + +T_DRIVERTYPE lcl_getDriverType(const OUString& _sUrl) +{ + T_DRIVERTYPE eRet = T_DRIVERTYPE::Jdbc; + if (isOdbcUrl(_sUrl)) + eRet = T_DRIVERTYPE::Odbc; + else if (isNativeUrl(_sUrl)) + eRet = T_DRIVERTYPE::Native; + return eRet; +} + +OUString transformUrl(const OUString& _sUrl) +{ + OUString sNewUrl = _sUrl.copy(11); + if (isOdbcUrl(_sUrl)) + sNewUrl = "sdbc:" + sNewUrl; + else if (isNativeUrl(_sUrl)) + sNewUrl = "sdbc:" + sNewUrl; + else + { + sNewUrl = "jdbc:mysql://" + sNewUrl.copy(5); + } + return sNewUrl; +} + +Reference<XDriver> lcl_loadDriver(const Reference<XComponentContext>& _rxContext, + const OUString& _sUrl) +{ + Reference<XDriverManager2> xDriverAccess = DriverManager::create(_rxContext); + Reference<XDriver> xDriver = xDriverAccess->getDriverByURL(_sUrl); + return xDriver; +} + +Sequence<PropertyValue> lcl_convertProperties(T_DRIVERTYPE _eType, + const Sequence<PropertyValue>& info, + const OUString& _sUrl) +{ + std::vector<PropertyValue> aProps; + const PropertyValue* pSupported = info.getConstArray(); + const PropertyValue* pEnd = pSupported + info.getLength(); + + aProps.reserve(info.getLength() + 5); + bool jdc = false; + for (; pSupported != pEnd; ++pSupported) + { + aProps.push_back(*pSupported); + if (pSupported->Name == "JavaDriverClass") + { + jdc = true; + } + } + + if (_eType == T_DRIVERTYPE::Odbc) + { + aProps.push_back(PropertyValue("Silent", 0, makeAny(true), PropertyState_DIRECT_VALUE)); + aProps.push_back(PropertyValue("PreventGetVersionColumns", 0, makeAny(true), + PropertyState_DIRECT_VALUE)); + } + else if (_eType == T_DRIVERTYPE::Jdbc) + { + if (!jdc) + { + aProps.push_back(PropertyValue("JavaDriverClass", 0, + makeAny(OUString("com.mysql.jdbc.Driver")), + PropertyState_DIRECT_VALUE)); + } + } + else + { + aProps.push_back( + PropertyValue("PublicConnectionURL", 0, makeAny(_sUrl), PropertyState_DIRECT_VALUE)); + } + aProps.push_back( + PropertyValue("IsAutoRetrievingEnabled", 0, makeAny(true), PropertyState_DIRECT_VALUE)); + aProps.push_back(PropertyValue("AutoRetrievingStatement", 0, + makeAny(OUString("SELECT LAST_INSERT_ID()")), + PropertyState_DIRECT_VALUE)); + aProps.push_back( + PropertyValue("ParameterNameSubstitution", 0, makeAny(true), PropertyState_DIRECT_VALUE)); + return Sequence<PropertyValue>(aProps.data(), aProps.size()); +} +} + +Reference<XDriver> ODriverDelegator::loadDriver(const OUString& url, + const Sequence<PropertyValue>& info) +{ + Reference<XDriver> xDriver; + const OUString sCuttedUrl = transformUrl(url); + const T_DRIVERTYPE eType = lcl_getDriverType(url); + if (eType == T_DRIVERTYPE::Odbc) + { + if (!m_xODBCDriver.is()) + m_xODBCDriver = lcl_loadDriver(m_xContext, sCuttedUrl); + xDriver = m_xODBCDriver; + } // if ( bIsODBC ) + else if (eType == T_DRIVERTYPE::Native) + { + if (!m_xNativeDriver.is()) + m_xNativeDriver = lcl_loadDriver(m_xContext, sCuttedUrl); + xDriver = m_xNativeDriver; + } + else + { + OUString sDriverClass(getJavaDriverClass(info)); + TJDBCDrivers::iterator aFind = m_aJdbcDrivers.find(sDriverClass); + if (aFind == m_aJdbcDrivers.end()) + aFind = m_aJdbcDrivers.emplace(sDriverClass, lcl_loadDriver(m_xContext, sCuttedUrl)) + .first; + xDriver = aFind->second; + } + + return xDriver; +} + +Reference<XConnection> SAL_CALL ODriverDelegator::connect(const OUString& url, + const Sequence<PropertyValue>& info) +{ + Reference<XConnection> xConnection; + if (acceptsURL(url)) + { + Reference<XDriver> xDriver = loadDriver(url, info); + if (xDriver.is()) + { + OUString sCuttedUrl = transformUrl(url); + const T_DRIVERTYPE eType = lcl_getDriverType(url); + Sequence<PropertyValue> aConvertedProperties = lcl_convertProperties(eType, info, url); + if (eType == T_DRIVERTYPE::Jdbc) + { + ::comphelper::NamedValueCollection aSettings(info); + OUString sIanaName = aSettings.getOrDefault("CharSet", OUString()); + if (!sIanaName.isEmpty()) + { + ::dbtools::OCharsetMap aLookupIanaName; + ::dbtools::OCharsetMap::const_iterator aLookup + = aLookupIanaName.findIanaName(sIanaName); + if (aLookup != aLookupIanaName.end()) + { + OUString sAdd; + if (RTL_TEXTENCODING_UTF8 == (*aLookup).getEncoding()) + { + static const char s_sCharSetOp[] = "useUnicode=true&"; + if (!sCuttedUrl.matchIgnoreAsciiCase(s_sCharSetOp)) + { + sAdd = s_sCharSetOp; + } // if ( !sCuttedUrl.matchIgnoreAsciiCase(s_sCharSetOp) ) + } // if ( RTL_TEXTENCODING_UTF8 == (*aLookup).getEncoding() ) + if (sCuttedUrl.indexOf('?') == -1) + sCuttedUrl += "?"; + else + sCuttedUrl += "&"; + sCuttedUrl += sAdd + "characterEncoding=" + sIanaName; + } + } + } // if ( !bIsODBC ) + + xConnection = xDriver->connect(sCuttedUrl, aConvertedProperties); + if (xConnection.is()) + { + // now we have to set the URL to get the correct answer for metadata()->getURL() + auto pMetaConnection + = comphelper::getUnoTunnelImplementation<OMetaConnection>(xConnection); + if (pMetaConnection) + pMetaConnection->setURL(url); + m_aConnections.push_back( + TWeakPair(WeakReferenceHelper(xConnection), + TWeakConnectionPair(WeakReferenceHelper(), pMetaConnection))); + } + } + } + return xConnection; +} + +sal_Bool SAL_CALL ODriverDelegator::acceptsURL(const OUString& url) +{ + Sequence<PropertyValue> info; + + bool bOK = url.startsWith("sdbc:mysql:odbc:") || url.startsWith("sdbc:mysql:jdbc:") + || (url.startsWith("sdbc:mysql:mysqlc:") && loadDriver(url, info).is()); + return bOK; +} + +Sequence<DriverPropertyInfo> SAL_CALL +ODriverDelegator::getPropertyInfo(const OUString& url, const Sequence<PropertyValue>& info) +{ + std::vector<DriverPropertyInfo> aDriverInfo; + if (!acceptsURL(url)) + return Sequence<DriverPropertyInfo>(); + + Sequence<OUString> aBoolean(2); + aBoolean[0] = "0"; + aBoolean[1] = "1"; + + aDriverInfo.push_back(DriverPropertyInfo("CharSet", "CharSet of the database.", false, + OUString(), Sequence<OUString>())); + aDriverInfo.push_back(DriverPropertyInfo("SuppressVersionColumns", + "Display version columns (when available).", false, + "0", aBoolean)); + const T_DRIVERTYPE eType = lcl_getDriverType(url); + if (eType == T_DRIVERTYPE::Jdbc) + { + aDriverInfo.push_back(DriverPropertyInfo("JavaDriverClass", "The JDBC driver class name.", + true, getJavaDriverClass(info), + Sequence<OUString>())); + } + else if (eType == T_DRIVERTYPE::Native) + { + aDriverInfo.push_back(DriverPropertyInfo( + "LocalSocket", "The file path of a socket to connect to a local MySQL server.", false, + OUString(), Sequence<OUString>())); + aDriverInfo.push_back(DriverPropertyInfo( + "NamedPipe", "The name of a pipe to connect to a local MySQL server.", false, + OUString(), Sequence<OUString>())); + } + + return Sequence<DriverPropertyInfo>(aDriverInfo.data(), aDriverInfo.size()); +} + +sal_Int32 SAL_CALL ODriverDelegator::getMajorVersion() { return 1; } + +sal_Int32 SAL_CALL ODriverDelegator::getMinorVersion() { return 0; } + +Reference<XTablesSupplier> SAL_CALL +ODriverDelegator::getDataDefinitionByConnection(const Reference<XConnection>& connection) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(ODriverDelegator_BASE::rBHelper.bDisposed); + + Reference<XTablesSupplier> xTab; + auto pConnection = comphelper::getUnoTunnelImplementation<OMetaConnection>(connection); + if (pConnection) + { + TWeakPairVector::iterator i + = std::find_if(m_aConnections.begin(), m_aConnections.end(), + [&pConnection](const TWeakPairVector::value_type& rConnection) { + return rConnection.second.second == pConnection; + }); + if (i != m_aConnections.end()) + { + xTab.set(i->second.first.get(), UNO_QUERY); + if (!xTab.is()) + { + xTab = new OMySQLCatalog(connection); + i->second.first = WeakReferenceHelper(xTab); + } + } + } // if (pConnection) + if (!xTab.is()) + { + TWeakPairVector::iterator i + = std::find_if(m_aConnections.begin(), m_aConnections.end(), + [&connection](const TWeakPairVector::value_type& rConnection) { + Reference<XConnection> xTemp(rConnection.first.get(), UNO_QUERY); + return xTemp == connection; + }); + if (i != m_aConnections.end()) + { + xTab.set(i->second.first.get(), UNO_QUERY); + if (!xTab.is()) + { + xTab = new OMySQLCatalog(connection); + i->second.first = WeakReferenceHelper(xTab); + } + } + } + return xTab; +} + +Reference<XTablesSupplier> SAL_CALL +ODriverDelegator::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); + } // if ( ! acceptsURL(url) ) + + return getDataDefinitionByConnection(connect(url, info)); +} + +// XServiceInfo + +OUString ODriverDelegator::getImplementationName_Static() +{ + return "org.openoffice.comp.drivers.MySQL.Driver"; +} + +Sequence<OUString> ODriverDelegator::getSupportedServiceNames_Static() +{ + return { "com.sun.star.sdbc.Driver", "com.sun.star.sdbcx.Driver" }; +} + +OUString SAL_CALL ODriverDelegator::getImplementationName() +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL ODriverDelegator::supportsService(const OUString& _rServiceName) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence<OUString> SAL_CALL ODriverDelegator::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +} // namespace connectivity + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/YTable.cxx b/connectivity/source/drivers/mysql_jdbc/YTable.cxx new file mode 100644 index 000000000..7959bd9d3 --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/YTable.cxx @@ -0,0 +1,330 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <mysql/YTable.hxx> +#include <mysql/YTables.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <comphelper/property.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/sdbcx/VColumn.hxx> +#include <connectivity/TKeys.hxx> +#include <connectivity/TIndexes.hxx> +#include <mysql/YColumns.hxx> +#include <TConnection.hxx> + +using namespace ::comphelper; +using namespace connectivity::mysql; +using namespace connectivity::sdbcx; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +namespace connectivity::mysql +{ +namespace +{ +class OMySQLKeysHelper : public OKeysHelper +{ +protected: + virtual OUString getDropForeignKey() const override { return " DROP FOREIGN KEY "; } + +public: + OMySQLKeysHelper(OTableHelper* _pTable, ::osl::Mutex& _rMutex, + const ::std::vector<OUString>& _rVector) + : OKeysHelper(_pTable, _rMutex, _rVector) + { + } +}; +} +} + +OMySQLTable::OMySQLTable(sdbcx::OCollection* _pTables, const Reference<XConnection>& _xConnection) + : OTableHelper(_pTables, _xConnection, true) +{ + // we create a new table here, so we should have all the rights or ;-) + m_nPrivileges = Privilege::DROP | Privilege::REFERENCE | Privilege::ALTER | Privilege::CREATE + | Privilege::READ | Privilege::DELETE | Privilege::UPDATE | Privilege::INSERT + | Privilege::SELECT; + construct(); +} + +OMySQLTable::OMySQLTable(sdbcx::OCollection* _pTables, const Reference<XConnection>& _xConnection, + const OUString& Name, const OUString& Type, const OUString& Description, + const OUString& SchemaName, const OUString& CatalogName, + sal_Int32 _nPrivileges) + : OTableHelper(_pTables, _xConnection, true, Name, Type, Description, SchemaName, CatalogName) + , m_nPrivileges(_nPrivileges) +{ + construct(); +} + +void OMySQLTable::construct() +{ + OTableHelper::construct(); + if (!isNew()) + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRIVILEGES), + PROPERTY_ID_PRIVILEGES, PropertyAttribute::READONLY, &m_nPrivileges, + cppu::UnoType<decltype(m_nPrivileges)>::get()); +} + +::cppu::IPropertyArrayHelper* OMySQLTable::createArrayHelper(sal_Int32 /*_nId*/) const +{ + return doCreateArrayHelper(); +} + +::cppu::IPropertyArrayHelper& OMySQLTable::getInfoHelper() +{ + return *static_cast<OMySQLTable_PROP*>(this)->getArrayHelper(isNew() ? 1 : 0); +} + +sdbcx::OCollection* OMySQLTable::createColumns(const ::std::vector<OUString>& _rNames) +{ + OMySQLColumns* pColumns = new OMySQLColumns(*this, m_aMutex, _rNames); + pColumns->setParent(this); + return pColumns; +} + +sdbcx::OCollection* OMySQLTable::createKeys(const ::std::vector<OUString>& _rNames) +{ + return new OMySQLKeysHelper(this, m_aMutex, _rNames); +} + +sdbcx::OCollection* OMySQLTable::createIndexes(const ::std::vector<OUString>& _rNames) +{ + return new OIndexesHelper(this, m_aMutex, _rNames); +} + +Sequence<sal_Int8> OMySQLTable::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +// css::lang::XUnoTunnel + +sal_Int64 OMySQLTable::getSomething(const Sequence<sal_Int8>& rId) +{ + return (isUnoTunnelId<OMySQLTable>(rId)) ? reinterpret_cast<sal_Int64>(this) + : OTable_TYPEDEF::getSomething(rId); +} + +// XAlterTable +void SAL_CALL OMySQLTable::alterColumnByName(const OUString& colName, + const Reference<XPropertySet>& descriptor) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed( +#ifdef __GNUC__ + ::connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed +#else + rBHelper.bDisposed +#endif + ); + + if (!m_xColumns || !m_xColumns->hasByName(colName)) + throw NoSuchElementException(colName, *this); + + if (!isNew()) + { + // first we have to check what should be altered + Reference<XPropertySet> xProp; + m_xColumns->getByName(colName) >>= xProp; + // first check the types + sal_Int32 nOldType = 0, nNewType = 0, nOldPrec = 0, nNewPrec = 0, nOldScale = 0, + nNewScale = 0; + + ::dbtools::OPropertyMap& rProp = OMetaConnection::getPropMap(); + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPE)) >>= nOldType; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPE)) >>= nNewType; + // and precisions and scale + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_PRECISION)) >>= nOldPrec; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_PRECISION)) >>= nNewPrec; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_SCALE)) >>= nOldScale; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_SCALE)) >>= nNewScale; + // second: check the "is nullable" value + sal_Int32 nOldNullable = 0, nNewNullable = 0; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISNULLABLE)) >>= nOldNullable; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISNULLABLE)) >>= nNewNullable; + + // check also the auto_increment + bool bOldAutoIncrement = false, bAutoIncrement = false; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT)) + >>= bOldAutoIncrement; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT)) + >>= bAutoIncrement; + bool bColumnNameChanged = false; + OUString sOldDesc, sNewDesc; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DESCRIPTION)) >>= sOldDesc; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DESCRIPTION)) >>= sNewDesc; + + if (nOldType != nNewType || nOldPrec != nNewPrec || nOldScale != nNewScale + || nNewNullable != nOldNullable || bOldAutoIncrement != bAutoIncrement + || sOldDesc != sNewDesc) + { + // special handling because they changed the type names to distinguish + // if a column should be an auto_increment one + if (bOldAutoIncrement != bAutoIncrement) + { + OUString sTypeName; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME)) + >>= sTypeName; + + static const char s_sAutoIncrement[] = "auto_increment"; + if (bAutoIncrement) + { + if (sTypeName.indexOf(s_sAutoIncrement) == -1) + { + sTypeName += OUStringLiteral(" ") + s_sAutoIncrement; + descriptor->setPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME), + makeAny(sTypeName)); + } + } + else + { + if (!sTypeName.isEmpty()) + { + sal_Int32 nIndex = sTypeName.indexOf(s_sAutoIncrement); + if (nIndex != -1) + { + sTypeName = sTypeName.copy(0, nIndex); + descriptor->setPropertyValue(rProp.getNameByIndex(PROPERTY_ID_TYPENAME), + makeAny(sTypeName)); + } + } + } + } + alterColumnType(nNewType, colName, descriptor); + bColumnNameChanged = true; + } + + // third: check the default values + OUString sNewDefault, sOldDefault; + xProp->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DEFAULTVALUE)) >>= sOldDefault; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_DEFAULTVALUE)) + >>= sNewDefault; + + if (!sOldDefault.isEmpty()) + { + dropDefaultValue(colName); + if (!sNewDefault.isEmpty() && sOldDefault != sNewDefault) + alterDefaultValue(sNewDefault, colName); + } + else if (!sNewDefault.isEmpty()) + alterDefaultValue(sNewDefault, colName); + + // now we should look if the name of the column changed + OUString sNewColumnName; + descriptor->getPropertyValue(rProp.getNameByIndex(PROPERTY_ID_NAME)) >>= sNewColumnName; + if (!sNewColumnName.equalsIgnoreAsciiCase(colName) && !bColumnNameChanged) + { + const OUString sQuote = getMetaData()->getIdentifierQuoteString(); + OUString sSql = getAlterTableColumnPart() + " CHANGE " + + ::dbtools::quoteName(sQuote, colName) + " " + + OTables::adjustSQL(::dbtools::createStandardColumnPart( + descriptor, getConnection(), static_cast<OTables*>(m_pTables), + getTypeCreatePattern())); + executeStatement(sSql); + } + m_xColumns->refresh(); + } + else + { + if (m_xColumns) + { + m_xColumns->dropByName(colName); + m_xColumns->appendByDescriptor(descriptor); + } + } +} + +void OMySQLTable::alterColumnType(sal_Int32 nNewType, const OUString& _rColName, + const Reference<XPropertySet>& _xDescriptor) +{ + const OUString sQuote = getMetaData()->getIdentifierQuoteString(); + OUString sSql + = getAlterTableColumnPart() + " CHANGE " + ::dbtools::quoteName(sQuote, _rColName) + " "; + + OColumn* pColumn = new OColumn(true); + Reference<XPropertySet> xProp = pColumn; + ::comphelper::copyProperties(_xDescriptor, xProp); + xProp->setPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE), + makeAny(nNewType)); + + sSql += OTables::adjustSQL(::dbtools::createStandardColumnPart( + xProp, getConnection(), static_cast<OTables*>(m_pTables), getTypeCreatePattern())); + executeStatement(sSql); +} + +OUString OMySQLTable::getTypeCreatePattern() const { return "(M,D)"; } + +void OMySQLTable::alterDefaultValue(const OUString& _sNewDefault, const OUString& _rColName) +{ + const OUString sQuote = getMetaData()->getIdentifierQuoteString(); + OUString sSql = getAlterTableColumnPart() + " ALTER " + ::dbtools::quoteName(sQuote, _rColName) + + " SET DEFAULT '" + _sNewDefault + "'"; + + executeStatement(sSql); +} + +void OMySQLTable::dropDefaultValue(const OUString& _rColName) +{ + const OUString sQuote = getMetaData()->getIdentifierQuoteString(); + OUString sSql = getAlterTableColumnPart() + " ALTER " + ::dbtools::quoteName(sQuote, _rColName) + + " DROP DEFAULT"; + + executeStatement(sSql); +} + +OUString OMySQLTable::getAlterTableColumnPart() const +{ + OUString sSql("ALTER TABLE "); + + OUString sComposedName( + ::dbtools::composeTableName(getMetaData(), m_CatalogName, m_SchemaName, m_Name, true, + ::dbtools::EComposeRule::InTableDefinitions)); + sSql += sComposedName; + + return sSql; +} + +void OMySQLTable::executeStatement(const OUString& _rStatement) +{ + OUString sSQL = _rStatement; + if (sSQL.endsWith(",")) + sSQL = sSQL.replaceAt(sSQL.getLength() - 1, 1, ")"); + + Reference<XStatement> xStmt = getConnection()->createStatement(); + if (xStmt.is()) + { + xStmt->execute(sSQL); + ::comphelper::disposeComponent(xStmt); + } +} + +OUString OMySQLTable::getRenameStart() const { return "RENAME TABLE "; } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/YTables.cxx b/connectivity/source/drivers/mysql_jdbc/YTables.cxx new file mode 100644 index 000000000..e21e05fb2 --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/YTables.cxx @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <mysql/YTables.hxx> +#include <mysql/YViews.hxx> +#include <mysql/YTable.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <mysql/YCatalog.hxx> +#include <connectivity/dbtools.hxx> +#include <comphelper/types.hxx> +#include <TConnection.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace ::cppu; +using namespace connectivity::mysql; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace dbtools; + +sdbcx::ObjectType OTables::createObject(const OUString& _rName) +{ + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData, _rName, sCatalog, sSchema, sTable, + ::dbtools::EComposeRule::InDataManipulation); + + Sequence<OUString> sTableTypes(3); + sTableTypes[0] = "VIEW"; + sTableTypes[1] = "TABLE"; + sTableTypes[2] = "%"; // just to be sure to include anything else... + + Any aCatalog; + if (!sCatalog.isEmpty()) + aCatalog <<= sCatalog; + Reference<XResultSet> xResult = m_xMetaData->getTables(aCatalog, sSchema, sTable, sTableTypes); + + sdbcx::ObjectType xRet; + if (xResult.is()) + { + Reference<XRow> xRow(xResult, UNO_QUERY); + if (xResult->next()) // there can be only one table with this name + { + sal_Int32 const nPrivileges = Privilege::DROP | Privilege::REFERENCE | Privilege::ALTER + | Privilege::CREATE | Privilege::READ | Privilege::DELETE + | Privilege::UPDATE | Privilege::INSERT + | Privilege::SELECT; + + OMySQLTable* pRet = new OMySQLTable( + this, static_cast<OMySQLCatalog&>(m_rParent).getConnection(), sTable, + xRow->getString(4), xRow->getString(5), sSchema, sCatalog, nPrivileges); + xRet = pRet; + } + ::comphelper::disposeComponent(xResult); + } + + return xRet; +} + +void OTables::impl_refresh() { static_cast<OMySQLCatalog&>(m_rParent).refreshTables(); } + +void OTables::disposing() +{ + m_xMetaData.clear(); + OCollection::disposing(); +} + +Reference<XPropertySet> OTables::createDescriptor() +{ + return new OMySQLTable(this, static_cast<OMySQLCatalog&>(m_rParent).getConnection()); +} + +// XAppend +sdbcx::ObjectType OTables::appendObject(const OUString& _rForName, + const Reference<XPropertySet>& descriptor) +{ + createTable(descriptor); + return createObject(_rForName); +} + +// XDrop +void OTables::dropObject(sal_Int32 _nPos, const OUString& _sElementName) +{ + Reference<XInterface> xObject(getObject(_nPos)); + bool bIsNew = connectivity::sdbcx::ODescriptor::isNew(xObject); + if (bIsNew) + return; + + Reference<XConnection> xConnection = static_cast<OMySQLCatalog&>(m_rParent).getConnection(); + + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData, _sElementName, sCatalog, sSchema, sTable, + ::dbtools::EComposeRule::InDataManipulation); + + OUString aSql("DROP "); + + Reference<XPropertySet> xProp(xObject, UNO_QUERY); + bool bIsView = xProp.is() + && ::comphelper::getString(xProp->getPropertyValue( + OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))) + == "VIEW"; + if (bIsView) // here we have a view + aSql += "VIEW "; + else + aSql += "TABLE "; + + OUString sComposedName(::dbtools::composeTableName( + m_xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InDataManipulation)); + aSql += sComposedName; + Reference<XStatement> xStmt = xConnection->createStatement(); + if (xStmt.is()) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + // if no exception was thrown we must delete it from the views + if (bIsView) + { + OViews* pViews + = static_cast<OViews*>(static_cast<OMySQLCatalog&>(m_rParent).getPrivateViews()); + if (pViews && pViews->hasByName(_sElementName)) + pViews->dropByNameImpl(_sElementName); + } +} + +OUString OTables::adjustSQL(const OUString& _sSql) +{ + OUString sSQL = _sSql; + static const char s_sUNSIGNED[] = "UNSIGNED"; + sal_Int32 nIndex = sSQL.indexOf(s_sUNSIGNED); + while (nIndex != -1) + { + sal_Int32 nParen = sSQL.indexOf(')', nIndex); + sal_Int32 nPos = nIndex + strlen(s_sUNSIGNED); + OUString sNewUnsigned(sSQL.copy(nPos, nParen - nPos + 1)); + sSQL = sSQL.replaceAt(nIndex, strlen(s_sUNSIGNED) + sNewUnsigned.getLength(), + sNewUnsigned + s_sUNSIGNED); + nIndex = sSQL.indexOf(s_sUNSIGNED, nIndex + strlen(s_sUNSIGNED) + sNewUnsigned.getLength()); + } + return sSQL; +} + +void OTables::createTable(const Reference<XPropertySet>& descriptor) +{ + const Reference<XConnection> xConnection + = static_cast<OMySQLCatalog&>(m_rParent).getConnection(); + const OUString aSql + = adjustSQL(::dbtools::createSqlCreateTableStatement(descriptor, xConnection)); + Reference<XStatement> xStmt = xConnection->createStatement(); + if (xStmt.is()) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } +} + +void OTables::appendNew(const OUString& _rsNewTable) +{ + insertElement(_rsNewTable, nullptr); + + // notify our container listeners + ContainerEvent aEvent(static_cast<XContainer*>(this), makeAny(_rsNewTable), Any(), Any()); + OInterfaceIteratorHelper2 aListenerLoop(m_aContainerListeners); + while (aListenerLoop.hasMoreElements()) + static_cast<XContainerListener*>(aListenerLoop.next())->elementInserted(aEvent); +} + +OUString OTables::getNameForObject(const sdbcx::ObjectType& _xObject) +{ + OSL_ENSURE(_xObject.is(), "OTables::getNameForObject: Object is NULL!"); + return ::dbtools::composeTableName(m_xMetaData, _xObject, + ::dbtools::EComposeRule::InDataManipulation, false); +} + +void OTables::addComment(const Reference<XPropertySet>& descriptor, OUStringBuffer& _rOut) +{ + OUString sDesc; + descriptor->getPropertyValue( + OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DESCRIPTION)) + >>= sDesc; + if (!sDesc.isEmpty()) + { + _rOut.append(" COMMENT '"); + _rOut.append(sDesc); + _rOut.append("'"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/YUser.cxx b/connectivity/source/drivers/mysql_jdbc/YUser.cxx new file mode 100644 index 000000000..b683fe2b2 --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/YUser.cxx @@ -0,0 +1,325 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <mysql/YUser.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/sdbcx/PrivilegeObject.hpp> +#include <TConnection.hxx> +#include <strings.hrc> +#include <comphelper/types.hxx> + +using namespace connectivity; +using namespace connectivity::mysql; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OMySQLUser::OMySQLUser(const css::uno::Reference<css::sdbc::XConnection>& _xConnection) + : connectivity::sdbcx::OUser(true) + , m_xConnection(_xConnection) +{ + construct(); +} + +OMySQLUser::OMySQLUser(const css::uno::Reference<css::sdbc::XConnection>& _xConnection, + const OUString& Name) + : connectivity::sdbcx::OUser(Name, true) + , m_xConnection(_xConnection) +{ + construct(); +} + +void OMySQLUser::refreshGroups() {} + +OUserExtend::OUserExtend(const css::uno::Reference<css::sdbc::XConnection>& _xConnection) + : OMySQLUser(_xConnection) +{ + construct(); +} + +void OUserExtend::construct() +{ + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD), + PROPERTY_ID_PASSWORD, 0, &m_Password, ::cppu::UnoType<OUString>::get()); +} + +cppu::IPropertyArrayHelper* OUserExtend::createArrayHelper() const +{ + Sequence<Property> aProps; + describeProperties(aProps); + return new cppu::OPropertyArrayHelper(aProps); +} + +cppu::IPropertyArrayHelper& OUserExtend::getInfoHelper() +{ + return *OUserExtend_PROP::getArrayHelper(); +} +typedef connectivity::sdbcx::OUser_BASE OUser_BASE_RBHELPER; + +sal_Int32 SAL_CALL OMySQLUser::getPrivileges(const OUString& objName, sal_Int32 objType) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_RBHELPER::rBHelper.bDisposed); + + sal_Int32 nRights, nRightsWithGrant; + findPrivilegesAndGrantPrivileges(objName, objType, nRights, nRightsWithGrant); + return nRights; +} + +void OMySQLUser::findPrivilegesAndGrantPrivileges(const OUString& objName, sal_Int32 objType, + sal_Int32& nRights, sal_Int32& nRightsWithGrant) +{ + nRightsWithGrant = nRights = 0; + // first we need to create the sql stmt to select the privs + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents(xMeta, objName, sCatalog, sSchema, sTable, + ::dbtools::EComposeRule::InDataManipulation); + Reference<XResultSet> xRes; + switch (objType) + { + case PrivilegeObject::TABLE: + case PrivilegeObject::VIEW: + { + Any aCatalog; + if (!sCatalog.isEmpty()) + aCatalog <<= sCatalog; + xRes = xMeta->getTablePrivileges(aCatalog, sSchema, sTable); + } + break; + + case PrivilegeObject::COLUMN: + { + Any aCatalog; + if (!sCatalog.isEmpty()) + aCatalog <<= sCatalog; + xRes = xMeta->getColumnPrivileges(aCatalog, sSchema, sTable, "%"); + } + break; + } + + if (!xRes.is()) + return; + + static const char sYes[] = "YES"; + + nRightsWithGrant = nRights = 0; + + Reference<XRow> xCurrentRow(xRes, UNO_QUERY); + while (xCurrentRow.is() && xRes->next()) + { + OUString sGrantee = xCurrentRow->getString(5); + OUString sPrivilege = xCurrentRow->getString(6); + OUString sGrantable = xCurrentRow->getString(7); + + if (!m_Name.equalsIgnoreAsciiCase(sGrantee)) + continue; + + if (sPrivilege.equalsIgnoreAsciiCase("SELECT")) + { + nRights |= Privilege::SELECT; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::SELECT; + } + else if (sPrivilege.equalsIgnoreAsciiCase("INSERT")) + { + nRights |= Privilege::INSERT; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::INSERT; + } + else if (sPrivilege.equalsIgnoreAsciiCase("UPDATE")) + { + nRights |= Privilege::UPDATE; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::UPDATE; + } + else if (sPrivilege.equalsIgnoreAsciiCase("DELETE")) + { + nRights |= Privilege::DELETE; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::DELETE; + } + else if (sPrivilege.equalsIgnoreAsciiCase("READ")) + { + nRights |= Privilege::READ; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::READ; + } + else if (sPrivilege.equalsIgnoreAsciiCase("CREATE")) + { + nRights |= Privilege::CREATE; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::CREATE; + } + else if (sPrivilege.equalsIgnoreAsciiCase("ALTER")) + { + nRights |= Privilege::ALTER; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::ALTER; + } + else if (sPrivilege.equalsIgnoreAsciiCase("REFERENCES")) + { + nRights |= Privilege::REFERENCE; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::REFERENCE; + } + else if (sPrivilege.equalsIgnoreAsciiCase("DROP")) + { + nRights |= Privilege::DROP; + if (sGrantable.equalsIgnoreAsciiCase(sYes)) + nRightsWithGrant |= Privilege::DROP; + } + } + ::comphelper::disposeComponent(xRes); +} + +sal_Int32 SAL_CALL OMySQLUser::getGrantablePrivileges(const OUString& objName, sal_Int32 objType) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_RBHELPER::rBHelper.bDisposed); + + sal_Int32 nRights, nRightsWithGrant; + findPrivilegesAndGrantPrivileges(objName, objType, nRights, nRightsWithGrant); + return nRightsWithGrant; +} + +void SAL_CALL OMySQLUser::grantPrivileges(const OUString& objName, sal_Int32 objType, + sal_Int32 objPrivileges) +{ + if (objType != PrivilegeObject::TABLE) + { + ::connectivity::SharedResources aResources; + const OUString sError(aResources.getResourceString(STR_PRIVILEGE_NOT_GRANTED)); + ::dbtools::throwGenericSQLException(sError, *this); + } // if ( objType != PrivilegeObject::TABLE ) + + ::osl::MutexGuard aGuard(m_aMutex); + + OUString sPrivs = getPrivilegeString(objPrivileges); + if (sPrivs.isEmpty()) + return; + + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + OUString sGrant + = "GRANT " + sPrivs + " ON " + + ::dbtools::quoteTableName(xMeta, objName, ::dbtools::EComposeRule::InDataManipulation) + + " TO " + m_Name; + + Reference<XStatement> xStmt = m_xConnection->createStatement(); + if (xStmt.is()) + xStmt->execute(sGrant); + ::comphelper::disposeComponent(xStmt); +} + +void SAL_CALL OMySQLUser::revokePrivileges(const OUString& objName, sal_Int32 objType, + sal_Int32 objPrivileges) +{ + if (objType != PrivilegeObject::TABLE) + { + ::connectivity::SharedResources aResources; + const OUString sError(aResources.getResourceString(STR_PRIVILEGE_NOT_REVOKED)); + ::dbtools::throwGenericSQLException(sError, *this); + } + + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_RBHELPER::rBHelper.bDisposed); + OUString sPrivs = getPrivilegeString(objPrivileges); + if (sPrivs.isEmpty()) + return; + + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + OUString sGrant + = "REVOKE " + sPrivs + " ON " + + ::dbtools::quoteTableName(xMeta, objName, ::dbtools::EComposeRule::InDataManipulation) + + " FROM " + m_Name; + + Reference<XStatement> xStmt = m_xConnection->createStatement(); + if (xStmt.is()) + xStmt->execute(sGrant); + ::comphelper::disposeComponent(xStmt); +} + +// XUser +void SAL_CALL OMySQLUser::changePassword(const OUString& /*oldPassword*/, + const OUString& newPassword) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OUser_BASE_RBHELPER::rBHelper.bDisposed); + OUString sAlterPwd = "SET PASSWORD FOR " + m_Name + "@\"%\" = PASSWORD('" + newPassword + "')"; + + Reference<XStatement> xStmt = m_xConnection->createStatement(); + if (xStmt.is()) + { + xStmt->execute(sAlterPwd); + ::comphelper::disposeComponent(xStmt); + } +} + +OUString OMySQLUser::getPrivilegeString(sal_Int32 nRights) +{ + OUString sPrivs; + if ((nRights & Privilege::INSERT) == Privilege::INSERT) + sPrivs += "INSERT"; + + if ((nRights & Privilege::DELETE) == Privilege::DELETE) + { + if (!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "DELETE"; + } + + if ((nRights & Privilege::UPDATE) == Privilege::UPDATE) + { + if (!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "UPDATE"; + } + + if ((nRights & Privilege::ALTER) == Privilege::ALTER) + { + if (!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "ALTER"; + } + + if ((nRights & Privilege::SELECT) == Privilege::SELECT) + { + if (!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "SELECT"; + } + + if ((nRights & Privilege::REFERENCE) == Privilege::REFERENCE) + { + if (!sPrivs.isEmpty()) + sPrivs += ","; + sPrivs += "REFERENCES"; + } + + return sPrivs; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/YUsers.cxx b/connectivity/source/drivers/mysql_jdbc/YUsers.cxx new file mode 100644 index 000000000..17fc78817 --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/YUsers.cxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <mysql/YUsers.hxx> +#include <mysql/YUser.hxx> +#include <connectivity/sdbcx/IRefreshable.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <TConnection.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::mysql; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +OUsers::OUsers(::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, + const ::std::vector<OUString>& _rVector, + const css::uno::Reference<css::sdbc::XConnection>& _xConnection, + connectivity::sdbcx::IRefreshableUsers* _pParent) + : sdbcx::OCollection(_rParent, true, _rMutex, _rVector) + , m_xConnection(_xConnection) + , m_pParent(_pParent) +{ +} + +sdbcx::ObjectType OUsers::createObject(const OUString& _rName) +{ + return new OMySQLUser(m_xConnection, _rName); +} + +void OUsers::impl_refresh() { m_pParent->refreshUsers(); } + +Reference<XPropertySet> OUsers::createDescriptor() +{ + OUserExtend* pNew = new OUserExtend(m_xConnection); + return pNew; +} + +// XAppend +sdbcx::ObjectType OUsers::appendObject(const OUString& _rForName, + const Reference<XPropertySet>& descriptor) +{ + OUString aSql("GRANT USAGE ON * TO "); + OUString aQuote = m_xConnection->getMetaData()->getIdentifierQuoteString(); + aSql += ::dbtools::quoteName(aQuote, _rForName) + " @\"%\" "; + OUString sPassword; + descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PASSWORD)) + >>= sPassword; + if (!sPassword.isEmpty()) + { + aSql += " IDENTIFIED BY '" + sPassword + "'"; + } + + Reference<XStatement> xStmt = m_xConnection->createStatement(); + if (xStmt.is()) + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + + return createObject(_rForName); +} + +// XDrop +void OUsers::dropObject(sal_Int32 /*_nPos*/, const OUString& _sElementName) +{ + OUString aSql("DROP USER "); + OUString aQuote = m_xConnection->getMetaData()->getIdentifierQuoteString(); + aSql += ::dbtools::quoteName(aQuote, _sElementName); + + Reference<XStatement> xStmt = m_xConnection->createStatement(); + if (xStmt.is()) + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/YViews.cxx b/connectivity/source/drivers/mysql_jdbc/YViews.cxx new file mode 100644 index 000000000..4bdeca959 --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/YViews.cxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <mysql/YViews.hxx> +#include <mysql/YTables.hxx> +#include <mysql/YCatalog.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/sdbcx/VView.hxx> +#include <comphelper/types.hxx> +#include <TConnection.hxx> + +using namespace ::comphelper; + +using namespace ::cppu; +using namespace connectivity; +using namespace connectivity::mysql; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace dbtools; +typedef connectivity::sdbcx::OCollection OCollection_TYPE; + +sdbcx::ObjectType OViews::createObject(const OUString& _rName) +{ + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData, _rName, sCatalog, sSchema, sTable, + ::dbtools::EComposeRule::InDataManipulation); + return new ::connectivity::sdbcx::OView(isCaseSensitive(), sTable, m_xMetaData, OUString(), + sSchema, sCatalog); +} + +void OViews::impl_refresh() { static_cast<OMySQLCatalog&>(m_rParent).refreshTables(); } + +void OViews::disposing() +{ + m_xMetaData.clear(); + OCollection::disposing(); +} + +Reference<XPropertySet> OViews::createDescriptor() +{ + Reference<XConnection> xConnection = static_cast<OMySQLCatalog&>(m_rParent).getConnection(); + connectivity::sdbcx::OView* pNew + = new connectivity::sdbcx::OView(true, xConnection->getMetaData()); + return pNew; +} + +// XAppend +sdbcx::ObjectType OViews::appendObject(const OUString& _rForName, + const Reference<XPropertySet>& descriptor) +{ + createView(descriptor); + return createObject(_rForName); +} + +// XDrop +void OViews::dropObject(sal_Int32 _nPos, const OUString& /*_sElementName*/) +{ + if (m_bInDrop) + return; + + Reference<XInterface> xObject(getObject(_nPos)); + bool bIsNew = connectivity::sdbcx::ODescriptor::isNew(xObject); + if (bIsNew) + return; + + OUString aSql("DROP VIEW"); + + Reference<XPropertySet> xProp(xObject, UNO_QUERY); + aSql += ::dbtools::composeTableName(m_xMetaData, xProp, + ::dbtools::EComposeRule::InTableDefinitions, true); + + Reference<XConnection> xConnection = static_cast<OMySQLCatalog&>(m_rParent).getConnection(); + Reference<XStatement> xStmt = xConnection->createStatement(); + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); +} + +void OViews::dropByNameImpl(const OUString& elementName) +{ + m_bInDrop = true; + OCollection_TYPE::dropByName(elementName); + m_bInDrop = false; +} + +void OViews::createView(const Reference<XPropertySet>& descriptor) +{ + Reference<XConnection> xConnection = static_cast<OMySQLCatalog&>(m_rParent).getConnection(); + + OUString aSql("CREATE VIEW "); + OUString sCommand; + + aSql += ::dbtools::composeTableName(m_xMetaData, descriptor, + ::dbtools::EComposeRule::InTableDefinitions, true) + + " AS "; + + descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_COMMAND)) + >>= sCommand; + aSql += sCommand; + + Reference<XStatement> xStmt = xConnection->createStatement(); + if (xStmt.is()) + { + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + + // insert the new view also in the tables collection + OTables* pTables + = static_cast<OTables*>(static_cast<OMySQLCatalog&>(m_rParent).getPrivateTables()); + if (pTables) + { + OUString sName = ::dbtools::composeTableName( + m_xMetaData, descriptor, ::dbtools::EComposeRule::InDataManipulation, false); + pTables->appendNew(sName); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/Yservices.cxx b/connectivity/source/drivers/mysql_jdbc/Yservices.cxx new file mode 100644 index 000000000..a19e489bd --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/Yservices.cxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <mysql/YDriver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::mysql; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +typedef Reference<XSingleServiceFactory> (*createFactoryFunc)( + const Reference<XMultiServiceFactory>& rServiceManager, const OUString& rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, const Sequence<OUString>& rServiceNames, + rtl_ModuleCount*); + +extern "C" SAL_DLLPUBLIC_EXPORT void* +mysql_jdbc_component_getFactory(const char* pImplementationName, void* pServiceManager, + void* /*pRegistryKey*/) +{ + if (!pServiceManager) + { + return nullptr; + } + + Reference<XSingleServiceFactory> xRet; + const Reference<XMultiServiceFactory> xServiceManager( + static_cast<XMultiServiceFactory*>(pServiceManager)); + const OUString sImplementationName(OUString::createFromAscii(pImplementationName)); + + if (ODriverDelegator::getImplementationName_Static() == sImplementationName) + try + { + xRet = ::cppu::createSingleFactory(xServiceManager, sImplementationName, + ODriverDelegator_CreateInstance, + ODriverDelegator::getSupportedServiceNames_Static()); + } + catch (...) + { + } + + if (xRet.is()) + xRet->acquire(); + + return xRet.get(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysql_jdbc/mysql_jdbc.component b/connectivity/source/drivers/mysql_jdbc/mysql_jdbc.component new file mode 100644 index 000000000..0a1d157e9 --- /dev/null +++ b/connectivity/source/drivers/mysql_jdbc/mysql_jdbc.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="mysql_jdbc" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="org.openoffice.comp.drivers.MySQL.Driver"> + <service name="com.sun.star.sdbc.Driver"/> + <service name="com.sun.star.sdbcx.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/mysqlc/DataAccess.xcu b/connectivity/source/drivers/mysqlc/DataAccess.xcu new file mode 100644 index 000000000..2b652ec87 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/DataAccess.xcu @@ -0,0 +1,35 @@ +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> +<oor:node xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="DataAccess" oor:package="org.openoffice.Office"> + <node oor:name="UserDefinedDriverSettings"> + <node oor:name="org.openoffice.comp.connectivity.mysql_native.Driver" oor:op="replace"> + <prop oor:name="DriverName"> + <value>org.openoffice.comp.connectivity.mysql_native.Driver</value> + </prop> + <prop oor:name="DriverPageDisplayName"> + <value>MySQL native driver</value> + </prop> + <prop oor:name="DriverTypeDisplayName"> + <value>MySQL native driver</value> + </prop> + <prop oor:name="DriverDsnPrefix"> + <value>sdbc:mysqlc:</value> + </prop> + </node> + </node> +</oor:node> diff --git a/connectivity/source/drivers/mysqlc/mysqlc.component b/connectivity/source/drivers/mysqlc/mysqlc.component new file mode 100644 index 000000000..e4295110f --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc.component @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * +--> +<component xmlns="http://openoffice.org/2010/uno-components" + loader="com.sun.star.loader.SharedLibrary"> + <implementation name="com.sun.star.comp.sdbc.mysqlc.MysqlCDriver"> + <service name="com.sun.star.sdbc.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/mysqlc/mysqlc_connection.cxx b/connectivity/source/drivers/mysqlc/mysqlc_connection.cxx new file mode 100644 index 000000000..600e131b8 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_connection.cxx @@ -0,0 +1,489 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "mysqlc_connection.hxx" +#include "mysqlc_databasemetadata.hxx" + +#include "mysqlc_driver.hxx" +#include "mysqlc_statement.hxx" +#include "mysqlc_preparedstatement.hxx" +#include "mysqlc_general.hxx" + +#include <com/sun/star/beans/NamedValue.hpp> + +#include <osl/diagnose.h> +#include <cppuhelper/supportsservice.hxx> + +using namespace connectivity::mysqlc; + +using namespace com::sun::star::uno; +using namespace com::sun::star::container; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; +using ::osl::MutexGuard; + +#define MYSQLC_URI_PREFIX "sdbc:mysqlc:" + +namespace +{ +void lcl_executeUpdate(MYSQL* pMySql, const OString& sql) +{ + mysql_real_query(pMySql, sql.getStr(), sql.getLength()); + // TODO handle error +} +} + +OConnection::OConnection(MysqlCDriver& _rDriver) + : OMetaConnection_BASE(m_aMutex) + , m_mysql() + , m_xMetaData(nullptr) + , m_xDriver(&_rDriver) +{ +} + +OConnection::~OConnection() +{ + if (!isClosed()) + { + close(); + } +} + +void OConnection::construct(const OUString& url, const Sequence<PropertyValue>& info) +{ + MutexGuard aGuard(m_aMutex); + + mysql_library_init(0, nullptr, nullptr); + mysql_init(&m_mysql); + + // use TCP as connection + mysql_protocol_type protocol = MYSQL_PROTOCOL_TCP; + mysql_options(&m_mysql, MYSQL_OPT_PROTOCOL, &protocol); + OString charset_name{ "utf8mb4" }; + mysql_options(&m_mysql, MYSQL_SET_CHARSET_NAME, charset_name.getStr()); + + sal_Int32 nIndex; + OUString token; + OUString aHostName("localhost"); + sal_Int32 nPort = 3306; + OUString aDbName; + + m_settings.encoding = MysqlCDriver::getDefaultEncoding(); + + // parse url. Url has the following format: + // external server: sdbc:mysqlc:[hostname]:[port]/[dbname] + + if (url.startsWith(MYSQLC_URI_PREFIX)) + { + nIndex = 12; + } + else + { + // sdbc:mysql:mysqlc:[hostname]:[port]/[dbname] + nIndex = 18; + } + + token = url.getToken(0, '/', nIndex); + if (!token.isEmpty()) + { + sal_Int32 nIndex1 = 0; + OUString hostandport = token.getToken(0, ':', nIndex1); + if (!hostandport.isEmpty()) + { + aHostName = hostandport; + hostandport = token.getToken(0, ':', nIndex1); + if (!hostandport.isEmpty() && nIndex1) + { + nPort = hostandport.toInt32(); + } + token = url.getToken(0, '/', nIndex); + if (!token.isEmpty() && nIndex) + { + aDbName = token; + } + } + } + + // get user and password for mysql connection + const PropertyValue* pIter = info.getConstArray(); + const PropertyValue* pEnd = pIter + info.getLength(); + OUString aUser, aPass, sUnixSocket, sNamedPipe; + bool unixSocketPassed = false; + bool namedPipePassed = false; + + m_settings.connectionURL = url; + for (; pIter != pEnd; ++pIter) + { + if (pIter->Name == "user") + { + OSL_VERIFY(pIter->Value >>= aUser); + } + else if (pIter->Name == "password") + { + OSL_VERIFY(pIter->Value >>= aPass); + } + else if (pIter->Name == "LocalSocket") + { + OSL_VERIFY(pIter->Value >>= sUnixSocket); + unixSocketPassed = !sUnixSocket.isEmpty(); + } + else if (pIter->Name == "NamedPipe") + { + OSL_VERIFY(pIter->Value >>= sNamedPipe); + namedPipePassed = !sNamedPipe.isEmpty(); + } + else if (pIter->Name == "PublicConnectionURL") + { + OSL_VERIFY(pIter->Value >>= m_settings.connectionURL); + } + else if (pIter->Name == "NewURL") + { // legacy name for "PublicConnectionURL" + OSL_VERIFY(pIter->Value >>= m_settings.connectionURL); + } + } + + OString host_str = OUStringToOString(aHostName, m_settings.encoding); + OString user_str = OUStringToOString(aUser, m_settings.encoding); + OString pass_str = OUStringToOString(aPass, m_settings.encoding); + OString schema_str = OUStringToOString(aDbName, m_settings.encoding); + OString socket_str; + if (unixSocketPassed) + { + socket_str = OUStringToOString(sUnixSocket, m_settings.encoding); + } + else if (namedPipePassed) + { + socket_str = OUStringToOString(sNamedPipe, m_settings.encoding); + } + + // flags can also be passed as last parameter + if (!mysql_real_connect(&m_mysql, host_str.getStr(), user_str.getStr(), pass_str.getStr(), + schema_str.getStr(), nPort, socket_str.getStr(), + CLIENT_MULTI_STATEMENTS)) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg( + mysql_error(&m_mysql), mysql_sqlstate(&m_mysql), mysql_errno(&m_mysql), *this, + getConnectionEncoding()); + + // Check if the server is 4.1 or above + if (getMysqlVersion() < 40100) + { + throw SQLException("MariaDB LibreOffice Connector requires MySQL Server 4.1 or above", + *this, OUString(), 0, Any()); + } + + lcl_executeUpdate(&m_mysql, + OString{ "SET session sql_mode='ANSI_QUOTES,NO_AUTO_VALUE_ON_ZERO'" }); + lcl_executeUpdate(&m_mysql, OString{ "SET NAMES utf8mb4" }); +} + +OUString OConnection::getImplementationName() +{ + return "com.sun.star.sdbc.drivers.mysqlc.OConnection"; +} + +css::uno::Sequence<OUString> OConnection::getSupportedServiceNames() +{ + return { "com.sun.star.sdbc.Connection" }; +} + +sal_Bool OConnection::supportsService(OUString const& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Reference<XStatement> SAL_CALL OConnection::createStatement() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + // create a statement + // the statement can only be executed once + Reference<XStatement> xReturn = new OStatement(this); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + + return xReturn; +} + +Reference<XPreparedStatement> SAL_CALL OConnection::prepareStatement(const OUString& _sSql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + const OString sSqlStatement + = OUStringToOString(_sSql, getConnectionEncoding()); // FIXME transform statement ? + + MYSQL_STMT* pStmt = mysql_stmt_init(&m_mysql); + mysql_stmt_prepare(pStmt, sSqlStatement.getStr(), sSqlStatement.getLength()); + + unsigned int nErrorNum = mysql_errno(&m_mysql); + if (nErrorNum != 0) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(&m_mysql), + mysql_sqlstate(&m_mysql), nErrorNum, *this, + getConnectionEncoding()); + + Reference<XPreparedStatement> xStatement = new OPreparedStatement(this, pStmt); + m_aStatements.push_back(WeakReferenceHelper(xStatement)); + return xStatement; +} + +Reference<XPreparedStatement> SAL_CALL OConnection::prepareCall(const OUString& /*_sSql*/) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OConnection::prepareCall", *this); + return Reference<XPreparedStatement>(); +} + +OUString SAL_CALL OConnection::nativeSQL(const OUString& /*_sSql*/) +{ + MutexGuard aGuard(m_aMutex); + + // const OUString sSqlStatement = transFormPreparedStatement( _sSql ); + // TODO + return OUString(); +} + +void SAL_CALL OConnection::setAutoCommit(sal_Bool autoCommit) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + if (!mysql_autocommit(&m_mysql, autoCommit)) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg( + mysql_error(&m_mysql), mysql_sqlstate(&m_mysql), mysql_errno(&m_mysql), *this, + getConnectionEncoding()); +} + +sal_Bool SAL_CALL OConnection::getAutoCommit() +{ + // you have to distinguish which if you are in autocommit mode or not + // at normal case true should be fine here + + // TODO use SELECT @@autocommit query for that + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + return false; +} + +void SAL_CALL OConnection::commit() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + if (!mysql_commit(&m_mysql)) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg( + mysql_error(&m_mysql), mysql_sqlstate(&m_mysql), mysql_errno(&m_mysql), *this, + getConnectionEncoding()); +} + +void SAL_CALL OConnection::rollback() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + if (!mysql_rollback(&m_mysql)) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg( + mysql_error(&m_mysql), mysql_sqlstate(&m_mysql), mysql_errno(&m_mysql), *this, + getConnectionEncoding()); +} + +sal_Bool SAL_CALL OConnection::isClosed() +{ + MutexGuard aGuard(m_aMutex); + + // just simple -> we are close when we are disposed that means someone called dispose(); (XComponent) + return OConnection_BASE::rBHelper.bDisposed; +} + +Reference<XDatabaseMetaData> SAL_CALL OConnection::getMetaData() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + Reference<XDatabaseMetaData> xMetaData = m_xMetaData; + if (!xMetaData.is()) + { + xMetaData = new ODatabaseMetaData(*this, &m_mysql); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +void SAL_CALL OConnection::setReadOnly(sal_Bool readOnly) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + m_settings.readOnly = readOnly; +} + +sal_Bool SAL_CALL OConnection::isReadOnly() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + // return if your connection to readonly + return m_settings.readOnly; +} + +void SAL_CALL OConnection::setCatalog(const OUString& /*catalog*/) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + // TODO How? +} + +OUString SAL_CALL OConnection::getCatalog() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + // TODO How? + return OUString{}; +} + +void SAL_CALL OConnection::setTransactionIsolation(sal_Int32 /*level*/) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + // TODO +} + +sal_Int32 SAL_CALL OConnection::getTransactionIsolation() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + return 0; // TODO +} + +Reference<XNameAccess> SAL_CALL OConnection::getTypeMap() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + Reference<XNameAccess> t = m_typeMap; + return t; +} + +void SAL_CALL OConnection::setTypeMap(const Reference<XNameAccess>& typeMap) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + m_typeMap = typeMap; +} + +// XCloseable +void SAL_CALL OConnection::close() +{ + /* + we need block, because the mutex is a local variable, + which will guard the block + */ + { + // we just dispose us + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + } + mysql_close(&m_mysql); + mysql_library_end(); + dispose(); +} + +// XWarningsSupplier +Any SAL_CALL OConnection::getWarnings() +{ + Any x; + // when you collected some warnings -> return it + return x; +} + +void SAL_CALL OConnection::clearWarnings() +{ + // you should clear your collected warnings here# +} + +void OConnection::disposing() +{ + // we noticed that we should be destroyed in near future so we have to dispose our statements + MutexGuard aGuard(m_aMutex); + + for (auto const& statement : m_aStatements) + { + Reference<XComponent> xComp(statement.get(), UNO_QUERY); + if (xComp.is()) + { + xComp->dispose(); + } + } + m_aStatements.clear(); + + m_xMetaData = WeakReference<XDatabaseMetaData>(); + + OConnection_BASE::disposing(); +} + +sal_Int32 OConnection::getMysqlVersion() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + unsigned long version = mysql_get_server_version(&m_mysql); + return static_cast<sal_Int32>(version); +} + +OUString OConnection::transFormPreparedStatement(const OUString& _sSQL) +{ + OUString sSqlStatement = _sSQL; + if (!m_xParameterSubstitution.is()) + { + try + { + Sequence<Any> aArgs(1); + Reference<XConnection> xCon = this; + aArgs[0] <<= NamedValue("ActiveConnection", makeAny(xCon)); + + m_xParameterSubstitution.set( + m_xDriver->getFactory()->createInstanceWithArguments( + "org.openoffice.comp.helper.ParameterSubstitution", aArgs), + UNO_QUERY); + } + catch (const Exception&) + { + } + } + if (m_xParameterSubstitution.is()) + { + try + { + sSqlStatement = m_xParameterSubstitution->substituteVariables(sSqlStatement, true); + } + catch (const Exception&) + { + } + } + return sSqlStatement; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_connection.hxx b/connectivity/source/drivers/mysqlc/mysqlc_connection.hxx new file mode 100644 index 000000000..283b1d964 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_connection.hxx @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_CONNECTION_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_CONNECTION_HXX + +#include <memory> +#include "mysqlc_subcomponent.hxx" +#include "mysqlc_types.hxx" + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/util/XStringSubstitution.hpp> + +#include <cppuhelper/compbase3.hxx> +#include <cppuhelper/weakref.hxx> +#include <rtl/string.hxx> +#include <rtl/ref.hxx> + +#include <mysql.h> + +#include <map> + +namespace sql +{ +class SQLException; +} + +namespace connectivity +{ +class OMetaConnection; +class ODatabaseMetaData; + +namespace mysqlc +{ +using ::com::sun::star::sdbc::SQLException; +using ::com::sun::star::sdbc::SQLWarning; +using ::com::sun::star::uno::RuntimeException; + +typedef ::cppu::WeakComponentImplHelper3<css::sdbc::XConnection, css::sdbc::XWarningsSupplier, + css::lang::XServiceInfo> + OMetaConnection_BASE; + +struct ConnectionSettings +{ + rtl_TextEncoding encoding; + OUString connectionURL; + bool readOnly; + ConnectionSettings() + : encoding(RTL_TEXTENCODING_DONTKNOW) + , readOnly(false) + { + } +}; + +class MysqlCDriver; + +typedef OMetaConnection_BASE OConnection_BASE; + +typedef std::vector<css::uno::WeakReferenceHelper> OWeakRefArray; + +class OConnection final : public OBase_Mutex, public OConnection_BASE +{ +private: + MYSQL m_mysql; + ConnectionSettings m_settings; + css::uno::Reference<css::container::XNameAccess> m_typeMap; + css::uno::Reference<css::util::XStringSubstitution> m_xParameterSubstitution; + + // Data attributes + + css::uno::WeakReference<css::sdbc::XDatabaseMetaData> m_xMetaData; + + OWeakRefArray m_aStatements; // vector containing a list + // of all the Statement objects + // for this Connection + + rtl::Reference<MysqlCDriver> m_xDriver; // Pointer to the owning driver object +public: + MYSQL* getMysqlConnection() { return &m_mysql; } + + /// @throws SQLException + /// @throws RuntimeException + sal_Int32 getMysqlVersion(); + + /// @throws SQLException + void construct(const OUString& url, const css::uno::Sequence<css::beans::PropertyValue>& info); + + OConnection(MysqlCDriver& _rDriver); + virtual ~OConnection() override; + + rtl_TextEncoding getConnectionEncoding() const { return m_settings.encoding; } + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XConnection + css::uno::Reference<css::sdbc::XStatement> SAL_CALL createStatement() override; + + css::uno::Reference<css::sdbc::XPreparedStatement> + SAL_CALL prepareStatement(const OUString& sql) override; + + css::uno::Reference<css::sdbc::XPreparedStatement> + SAL_CALL prepareCall(const OUString& sql) override; + + OUString SAL_CALL nativeSQL(const OUString& sql) override; + + void SAL_CALL setAutoCommit(sal_Bool autoCommit) override; + + sal_Bool SAL_CALL getAutoCommit() override; + + void SAL_CALL commit() override; + + void SAL_CALL rollback() override; + + sal_Bool SAL_CALL isClosed() override; + + css::uno::Reference<css::sdbc::XDatabaseMetaData> SAL_CALL getMetaData() override; + + void SAL_CALL setReadOnly(sal_Bool readOnly) override; + + sal_Bool SAL_CALL isReadOnly() override; + + void SAL_CALL setCatalog(const OUString& catalog) override; + + OUString SAL_CALL getCatalog() override; + + void SAL_CALL setTransactionIsolation(sal_Int32 level) override; + + sal_Int32 SAL_CALL getTransactionIsolation() override; + + css::uno::Reference<css::container::XNameAccess> SAL_CALL getTypeMap() override; + + void SAL_CALL + setTypeMap(const css::uno::Reference<css::container::XNameAccess>& typeMap) override; + // XCloseable + void SAL_CALL close() override; + // XWarningsSupplier + css::uno::Any SAL_CALL getWarnings() override; + void SAL_CALL clearWarnings() override; + + // TODO: Not used + //sal_Int32 sdbcColumnType(OUString typeName); + const ConnectionSettings& getConnectionSettings() const { return m_settings; } + OUString transFormPreparedStatement(const OUString& _sSQL); + + const MysqlCDriver& getDriver() const { return *m_xDriver; } + +}; /* OConnection */ +// TODO: Not used. +//inline OUString getPattern(OUString p) { return (p.getLength()) ? p : ASC2OU("%"); } +} /* mysqlc */ +} /* connectivity */ +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_CONNECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_databasemetadata.cxx b/connectivity/source/drivers/mysqlc/mysqlc_databasemetadata.cxx new file mode 100644 index 000000000..a30e5d618 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_databasemetadata.cxx @@ -0,0 +1,1076 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "mysqlc_databasemetadata.hxx" +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <com/sun/star/sdbc/Deferrability.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/sequence.hxx> + +#include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> +#include "mysqlc_general.hxx" +#include "mysqlc_driver.hxx" +#include "mysqlc_preparedstatement.hxx" + +using namespace connectivity::mysqlc; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + +static std::string wild("%"); + +static void lcl_setRows_throw(const Reference<XResultSet>& _xResultSet, sal_Int32 _nType, + const std::vector<std::vector<Any>>& _rRows) +{ + Reference<XInitialization> xIni(_xResultSet, UNO_QUERY); + Sequence<Any> aArgs(2); + aArgs[0] <<= _nType; + + Sequence<Sequence<Any>> aRows(_rRows.size()); + + Sequence<Any>* pRowsIter = aRows.getArray(); + for (const auto& rRow : _rRows) + { + if (!rRow.empty()) + { + (*pRowsIter) = comphelper::containerToSequence(rRow); + } + ++pRowsIter; + } + aArgs[1] <<= aRows; + xIni->initialize(aArgs); +} + +ODatabaseMetaData::ODatabaseMetaData(OConnection& _rCon, MYSQL* pMySql) + : m_rConnection(_rCon) + , m_pMySql(pMySql) +{ +} + +ODatabaseMetaData::~ODatabaseMetaData() {} + +OUString SAL_CALL ODatabaseMetaData::getCatalogSeparator() { return OUString(); } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxBinaryLiteralLength() { return 16777208L; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxRowSize() +{ + return 2147483647L - 8; // Max buffer size - HEADER +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCatalogNameLength() { return 32; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCharLiteralLength() { return 16777208; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnNameLength() { return 64; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInIndex() { return 16; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCursorNameLength() { return 64; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxConnections() +{ + SAL_WARN("connectivity.mysqlc", "method not implemented"); + // TODO + // SELECT @@max_connections + return 100; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInTable() { return 512; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatementLength() +{ + SAL_WARN("connectivity.mysqlc", "method not implemented"); + // TODO + // "SHOW VARIABLES LIKE 'max_allowed_packet'" + return 32767; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTableNameLength() { return 64; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTablesInSelect() { return 256; } + +sal_Bool SAL_CALL ODatabaseMetaData::doesMaxRowSizeIncludeBlobs() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseQuotedIdentifiers() +{ + SAL_WARN("connectivity.mysqlc", "method not implemented"); + // TODO + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseIdentifiers() +{ + SAL_WARN("connectivity.mysqlc", "method not implemented"); + //TODO; + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseQuotedIdentifiers() +{ + SAL_WARN("connectivity.mysqlc", "method not implemented"); + // TODO + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseIdentifiers() +{ + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseQuotedIdentifiers() +{ + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseIdentifiers() +{ + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsAlterTableWithAddColumn() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsAlterTableWithDropColumn() { return true; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxIndexLength() { return 256; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsNonNullableColumns() { return true; } + +OUString SAL_CALL ODatabaseMetaData::getCatalogTerm() { return "n/a"; } + +OUString SAL_CALL ODatabaseMetaData::getIdentifierQuoteString() { return "\""; } + +OUString SAL_CALL ODatabaseMetaData::getExtraNameCharacters() { return "#@"; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDifferentTableCorrelationNames() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::isCatalogAtStart() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionIgnoredInTransactions() { return false; } + +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 true; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInDataManipulation() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92FullSQL() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92EntryLevelSQL() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsIntegrityEnhancementFacility() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInIndexDefinitions() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInTableDefinitions() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInTableDefinitions() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInIndexDefinitions() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInDataManipulation() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOuterJoins() { return true; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatements() { return 0; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxProcedureNameLength() { return 64; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxSchemaNameLength() { return 64; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactions() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::allProceduresAreCallable() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsStoredProcedures() +{ + return m_rConnection.getMysqlVersion() >= 50000; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSelectForUpdate() +{ + return m_rConnection.getMysqlVersion() >= 40000; +} + +sal_Bool SAL_CALL ODatabaseMetaData::allTablesAreSelectable() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::isReadOnly() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFiles() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFilePerTable() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTypeConversion() { return true; } + +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*/) +{ + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExpressionsInOrderBy() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupBy() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByBeyondSelect() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByUnrelated() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleTransactions() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleResultSets() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLikeEscapeClause() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOrderByUnrelated() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnion() +{ + return m_rConnection.getMysqlVersion() >= 40000; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnionAll() +{ + return m_rConnection.getMysqlVersion() >= 40000; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseIdentifiers() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseQuotedIdentifiers() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtEnd() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtStart() +{ + return m_rConnection.getMysqlVersion() > 40001 && m_rConnection.getMysqlVersion() < 40011; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedHigh() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedLow() { return !nullsAreSortedHigh(); } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInProcedureCalls() +{ + return m_rConnection.getMysqlVersion() >= 32200; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInPrivilegeDefinitions() +{ + return m_rConnection.getMysqlVersion() >= 32200; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInProcedureCalls() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInPrivilegeDefinitions() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCorrelatedSubqueries() +{ + return m_rConnection.getMysqlVersion() >= 40100; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInComparisons() +{ + return m_rConnection.getMysqlVersion() >= 40100; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInExists() +{ + return m_rConnection.getMysqlVersion() >= 40100; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInIns() +{ + return m_rConnection.getMysqlVersion() >= 40100; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInQuantifieds() +{ + return m_rConnection.getMysqlVersion() >= 40100; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92IntermediateSQL() { return false; } + +OUString SAL_CALL ODatabaseMetaData::getURL() +{ + return m_rConnection.getConnectionSettings().connectionURL; +} + +OUString SAL_CALL ODatabaseMetaData::getUserName() +{ + Reference<XStatement> statement = m_rConnection.createStatement(); + Reference<XResultSet> rs = statement->executeQuery("select user()"); + Reference<XRow> xRow(rs, UNO_QUERY_THROW); + (void)rs->next(); // the first and only result + // e.g. root@localhost + OUString userWithConnection = xRow->getString(1); + sal_Int32 nIndexOfAt = userWithConnection.indexOf("@"); + if (nIndexOfAt > 0) + { + OUString user = userWithConnection.copy(0, nIndexOfAt); + return user; + } + return userWithConnection; +} + +OUString SAL_CALL ODatabaseMetaData::getDriverName() { return "MySQL Connector/OO.org"; } + +OUString SAL_CALL ODatabaseMetaData::getDriverVersion() { return "0.9.2"; } + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductVersion() +{ + return OStringToOUString(mysql_get_server_info(m_pMySql), + m_rConnection.getConnectionEncoding()); +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductName() { return "MySQL"; } + +OUString SAL_CALL ODatabaseMetaData::getProcedureTerm() { return "procedure"; } + +OUString SAL_CALL ODatabaseMetaData::getSchemaTerm() { return "database"; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMajorVersion() +{ + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + return MARIADBC_VERSION_MAJOR; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDefaultTransactionIsolation() +{ + return TransactionIsolation::REPEATABLE_READ; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMinorVersion() +{ + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + return MARIADBC_VERSION_MINOR; +} + +OUString SAL_CALL ODatabaseMetaData::getSQLKeywords() +{ + return "ACCESSIBLE, ADD, ALL," + "ALTER, ANALYZE, AND, AS, ASC, ASENSITIVE, BEFORE," + "BETWEEN, BIGINT, BINARY, BLOB, BOTH, BY, CALL," + "CASCADE, CASE, CHANGE, CHAR, CHARACTER, CHECK," + "COLLATE, COLUMN, CONDITION, CONNECTION, CONSTRAINT," + "CONTINUE, CONVERT, CREATE, CROSS, CURRENT_DATE," + "CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR," + "DATABASE, DATABASES, DAY_HOUR, DAY_MICROSECOND," + "DAY_MINUTE, DAY_SECOND, DEC, DECIMAL, DECLARE," + "DEFAULT, DELAYED, DELETE, DESC, DESCRIBE," + "DETERMINISTIC, DISTINCT, DISTINCTROW, DIV, DOUBLE," + "DROP, DUAL, EACH, ELSE, ELSEIF, ENCLOSED," + "ESCAPED, EXISTS, EXIT, EXPLAIN, FALSE, FETCH," + "FLOAT, FLOAT4, FLOAT8, FOR, FORCE, FOREIGN, FROM," + "FULLTEXT, GRANT, GROUP, HAVING, HIGH_PRIORITY," + "HOUR_MICROSECOND, HOUR_MINUTE, HOUR_SECOND, IF," + "IGNORE, IN, INDEX, INFILE, INNER, INOUT," + "INSENSITIVE, INSERT, INT, INT1, INT2, INT3, INT4," + "INT8, INTEGER, INTERVAL, INTO, IS, ITERATE, JOIN," + "KEY, KEYS, KILL, LEADING, LEAVE, LEFT, LIKE," + "LOCALTIMESTAMP, LOCK, LONG, LONGBLOB, LONGTEXT," + "LOOP, LOW_PRIORITY, MATCH, MEDIUMBLOB, MEDIUMINT," + "MEDIUMTEXT, MIDDLEINT, MINUTE_MICROSECOND," + "MINUTE_SECOND, MOD, MODIFIES, NATURAL, NOT," + "NO_WRITE_TO_BINLOG, NULL, NUMERIC, ON, OPTIMIZE," + "OPTION, OPTIONALLY, OR, ORDER, OUT, OUTER," + "OUTFILE, PRECISION, PRIMARY, PROCEDURE, PURGE," + "RANGE, READ, READS, READ_ONLY, READ_WRITE, REAL," + "REFERENCES, REGEXP, RELEASE, RENAME, REPEAT," + "REPLACE, REQUIRE, RESTRICT, RETURN, REVOKE, RIGHT," + "RLIKE, SCHEMA, SCHEMAS, SECOND_MICROSECOND, SELECT," + "SENSITIVE, SEPARATOR, SET, SHOW, SMALLINT, SPATIAL," + "SPECIFIC, SQL, SQLEXCEPTION, SQLSTATE, SQLWARNING," + "SQL_BIG_RESULT, SQL_CALC_FOUND_ROWS, SQL_SMALL_RESULT," + "SSL, STARTING, STRAIGHT_JOIN, TABLE, TERMINATED," + "THEN, TINYBLOB, TINYINT, TINYTEXT, TO, TRAILING," + "TRIGGER, TRUE, UNDO, UNION, UNIQUE, UNLOCK," + "UNSIGNED, UPDATE, USAGE, USE, USING, UTC_DATE," + "UTC_TIME, UTC_TIMESTAMP, VALUES, VARBINARY, VARCHAR," + "VARCHARACTER, VARYING, WHEN, WHERE, WHILE, WITH," + "WRITE, X509, XOR, YEAR_MONTH, ZEROFILL" + "GENERAL, IGNORE_SERVER_IDS, MASTER_HEARTBEAT_PERIOD," + "MAXVALUE, RESIGNAL, SIGNAL, SLOW"; +} + +OUString SAL_CALL ODatabaseMetaData::getSearchStringEscape() { return "\\"; } + +OUString SAL_CALL ODatabaseMetaData::getStringFunctions() +{ + return "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT," + "CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT," + "INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD," + "LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION," + "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX," + "SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING," + "SUBSTRING_INDEX,TRIM,UCASE,UPPER"; +} + +OUString SAL_CALL ODatabaseMetaData::getTimeDateFunctions() +{ + return "DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME," + "MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD," + "PERIOD_DIFF,TO_DAYS,FROM_DAYS,DATE_FORMAT,TIME_FORMAT," + "CURDATE,CURRENT_DATE,CURTIME,CURRENT_TIME,NOW,SYSDATE," + "CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME," + "SEC_TO_TIME,TIME_TO_SEC"; +} + +OUString SAL_CALL ODatabaseMetaData::getSystemFunctions() +{ + return "DATABASE,USER,SYSTEM_USER," + "SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION"; +} + +OUString SAL_CALL ODatabaseMetaData::getNumericFunctions() +{ + return "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS," + "COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW," + "POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE"; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExtendedSQLGrammar() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCoreSQLGrammar() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMinimumSQLGrammar() { return true; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsFullOuterJoins() { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLimitedOuterJoins() { return true; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInGroupBy() { return 64; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInOrderBy() { return 64; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInSelect() { return 256; } + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxUserNameLength() { return 16; } + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetType(sal_Int32 setType) +{ + return setType == ResultSetType::SCROLL_SENSITIVE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetConcurrency(sal_Int32 /*setType*/, + sal_Int32 /*concurrency*/) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownUpdatesAreVisible(sal_Int32 /*setType*/) { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::ownDeletesAreVisible(sal_Int32 /*setType*/) { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::ownInsertsAreVisible(sal_Int32 /*setType*/) { return false; } + +sal_Bool SAL_CALL ODatabaseMetaData::othersUpdatesAreVisible(sal_Int32 /*setType*/) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersDeletesAreVisible(sal_Int32 /*setType*/) +{ + return false; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersInsertsAreVisible(sal_Int32 /*setType*/) +{ + return false; +} + +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<XConnection> SAL_CALL ODatabaseMetaData::getConnection() { return &m_rConnection; } + +/* + Here follow all methods which return(a resultset + the first methods is an example implementation how to use this resultset + of course you could implement it on your and you should do this because + the general way is more memory expensive +*/ + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getTableTypes() +{ + const char* const table_types[] = { "TABLE", "VIEW" }; + sal_Int32 const requiredVersion[] = { 0, 50000 }; + + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> rRows; + rtl_TextEncoding encoding = m_rConnection.getConnectionEncoding(); + + for (sal_uInt32 i = 0; i < 2; i++) + { + if (m_rConnection.getMysqlVersion() >= requiredVersion[i]) + { + std::vector<Any> aRow{ Any() }; + aRow.push_back(makeAny(mysqlc_sdbc_driver::convert(table_types[i], encoding))); + rRows.push_back(aRow); + } + } + lcl_setRows_throw(xResultSet, 5, rRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getTypeInfo() +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + + std::vector<std::vector<Any>> rRows; + + rtl_TextEncoding encoding = m_rConnection.getConnectionEncoding(); + unsigned int i = 0; + while (mysqlc_types[i].typeName) + { + std::vector<Any> aRow{ Any() }; + + aRow.push_back(makeAny(mysqlc_sdbc_driver::convert(mysqlc_types[i].typeName, encoding))); + aRow.push_back(makeAny(mysqlc_types[i].dataType)); + aRow.push_back(makeAny(mysqlc_types[i].precision)); + aRow.push_back( + makeAny(mysqlc_sdbc_driver::convert(mysqlc_types[i].literalPrefix, encoding))); + aRow.push_back( + makeAny(mysqlc_sdbc_driver::convert(mysqlc_types[i].literalSuffix, encoding))); + aRow.push_back( + makeAny(mysqlc_sdbc_driver::convert(mysqlc_types[i].createParams, encoding))); + aRow.push_back(makeAny(mysqlc_types[i].nullable)); + aRow.push_back(makeAny(mysqlc_types[i].caseSensitive)); + aRow.push_back(makeAny(mysqlc_types[i].searchable)); + aRow.push_back(makeAny(mysqlc_types[i].isUnsigned)); + aRow.push_back(makeAny(mysqlc_types[i].fixedPrecScale)); + aRow.push_back(makeAny(mysqlc_types[i].autoIncrement)); + aRow.push_back( + makeAny(mysqlc_sdbc_driver::convert(mysqlc_types[i].localTypeName, encoding))); + aRow.push_back(makeAny(mysqlc_types[i].minScale)); + aRow.push_back(makeAny(mysqlc_types[i].maxScale)); + aRow.push_back(makeAny(sal_Int32(0))); + aRow.push_back(makeAny(sal_Int32(0))); + aRow.push_back(makeAny(sal_Int32(10))); + + rRows.push_back(aRow); + i++; + } + + lcl_setRows_throw(xResultSet, 14, rRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getCatalogs() +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getSchemas() +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> rRows; + + OUString sSql + = m_rConnection.getMysqlVersion() > 49999 + ? OUString{ "SELECT SCHEMA_NAME AS TABLE_SCHEM, CATALOG_NAME AS TABLE_CATALOG " + "FROM INFORMATION_SCHEMA.SCHEMATA ORDER BY SCHEMA_NAME" } + : OUString{ "SHOW DATABASES" }; + + Reference<XStatement> statement = m_rConnection.createStatement(); + Reference<XInterface> executed = statement->executeQuery(sSql); + Reference<XResultSet> rs(executed, UNO_QUERY_THROW); + Reference<XResultSetMetaDataSupplier> supp(executed, UNO_QUERY_THROW); + Reference<XResultSetMetaData> rs_meta = supp->getMetaData(); + + Reference<XRow> xRow(rs, UNO_QUERY_THROW); + sal_uInt32 columns = rs_meta->getColumnCount(); + while (rs->next()) + { + std::vector<Any> aRow{ Any() }; + bool informationSchema = false; + for (sal_uInt32 i = 1; i <= columns; i++) + { + OUString columnStringValue = xRow->getString(i); + if (i == 1) + { // TABLE_SCHEM + informationSchema = columnStringValue.equalsIgnoreAsciiCase("information_schema"); + } + aRow.push_back(makeAny(columnStringValue)); + } + if (!informationSchema) + { + rRows.push_back(aRow); + } + } + + lcl_setRows_throw(xResultSet, 1, rRows); + return xResultSet; +} + +Reference<XResultSet> + SAL_CALL ODatabaseMetaData::getColumnPrivileges(const Any& /*catalog*/, const OUString& schema, + const OUString& table, + const OUString& columnNamePattern) +{ + OUString query("SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA AS " + "TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, NULL AS GRANTOR, " + "GRANTEE, PRIVILEGE_TYPE AS PRIVILEGE, IS_GRANTABLE FROM " + "INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE TABLE_SCHEMA LIKE " + "'?' AND TABLE_NAME='?' AND COLUMN_NAME LIKE '?' ORDER BY " + "COLUMN_NAME, PRIVILEGE_TYPE"); + + query = query.replaceFirst("?", schema); + query = query.replaceFirst("?", table); + query = query.replaceFirst("?", columnNamePattern); + + Reference<XStatement> statement = m_rConnection.createStatement(); + Reference<XResultSet> rs = statement->executeQuery(query); + return rs; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getColumns(const Any& /*catalog*/, + const OUString& schemaPattern, + const OUString& tableNamePattern, + const OUString& columnNamePattern) +{ + OUStringBuffer queryBuf("SELECT TABLE_CATALOG, " // 1 + "TABLE_SCHEMA, " // 2 + "TABLE_NAME, " // 3 + "COLUMN_NAME, " // 4 + "DATA_TYPE, " // 5 + // TYPE_NAME missing + "CHARACTER_MAXIMUM_LENGTH, " // 6 + "NUMERIC_PRECISION, " // 7 + // buffer length missing + "NUMERIC_SCALE AS DECIMAL_DIGITS, " // 8 + // NUM_PREC_RADIX missing + // NULLABLE missing + "COLUMN_COMMENT AS REMARKS, " // 9 + "COLUMN_DEFAULT AS COLUMN_DEF," // 10 + "CHARACTER_OCTET_LENGTH, " // 11 + "ORDINAL_POSITION, " // 12 + "IS_NULLABLE, " // 13 + "COLUMN_TYPE " // 14 + "FROM INFORMATION_SCHEMA.COLUMNS " + "WHERE (1 = 1) "); + if (!tableNamePattern.isEmpty()) + { + OUString sAppend; + if (tableNamePattern.match("%")) + sAppend = "AND TABLE_NAME LIKE '%' "; + else + sAppend = "AND TABLE_NAME = '%' "; + queryBuf.append(sAppend.replaceAll("%", tableNamePattern)); + } + if (!schemaPattern.isEmpty()) + { + OUString sAppend; + if (schemaPattern.match("%")) + sAppend = "AND TABLE_SCHEMA LIKE '%' "; + else + sAppend = "AND TABLE_SCHEMA = '%' "; + queryBuf.append(sAppend.replaceAll("%", schemaPattern)); + } + if (!columnNamePattern.isEmpty()) + { + OUString sAppend; + if (columnNamePattern.match("%")) + sAppend = "AND COLUMN_NAME LIKE '%' "; + else + sAppend = "AND COLUMN_NAME = '%' "; + queryBuf.append(sAppend.replaceAll("%", columnNamePattern)); + } + + OUString query = queryBuf.makeStringAndClear(); + Reference<XStatement> statement = m_rConnection.createStatement(); + Reference<XResultSet> rs = statement->executeQuery(query.getStr()); + Reference<XRow> xRow(rs, UNO_QUERY_THROW); + + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> aRows; + while (rs->next()) + { + std::vector<Any> aRow{ Any() }; // 0. element is unused + + // catalog name + aRow.push_back(makeAny(xRow->getString(1))); + // schema name + aRow.push_back(makeAny(xRow->getString(2))); + // table name + aRow.push_back(makeAny(xRow->getString(3))); + // column name + aRow.push_back(makeAny(xRow->getString(4))); + // data type + OUString sDataType = xRow->getString(5); + aRow.push_back(makeAny(mysqlc_sdbc_driver::mysqlStrToOOOType(sDataType))); + // type name + aRow.push_back(makeAny(sDataType)); // TODO + // column size + sal_Int32 nColumnSize = 0; + OUString sColumnType = xRow->getString(14); + sal_Int32 nCharMaxLen = xRow->getShort(6); + bool bIsCharMax = !xRow->wasNull(); + if (sDataType.equalsIgnoreAsciiCase("year")) + nColumnSize = sColumnType.copy(6, 1).toInt32(); // 'year(' length is 5 + else if (sDataType.equalsIgnoreAsciiCase("date")) + nColumnSize = 10; + else if (sDataType.equalsIgnoreAsciiCase("time")) + nColumnSize = 8; + else if (sDataType.equalsIgnoreAsciiCase("datetime") + || sDataType.equalsIgnoreAsciiCase("timestamp")) + nColumnSize = 19; + else if (!bIsCharMax) + nColumnSize = xRow->getShort(7); // numeric precision + else + nColumnSize = nCharMaxLen; + aRow.push_back(makeAny(nColumnSize)); + aRow.push_back(Any()); // buffer length - unused + // decimal digits (scale) + aRow.push_back(makeAny(xRow->getShort(8))); + // num_prec_radix + aRow.push_back(makeAny(sal_Int32(10))); + // nullable + OUString sIsNullable = xRow->getString(13); + if (xRow->wasNull()) + aRow.push_back(makeAny(ColumnValue::NULLABLE_UNKNOWN)); + else if (sIsNullable.equalsIgnoreAsciiCase("YES")) + aRow.push_back(makeAny(ColumnValue::NULLABLE)); + else + aRow.push_back(makeAny(ColumnValue::NO_NULLS)); + // remarks + aRow.push_back(makeAny(xRow->getString(9))); + // default + aRow.push_back(makeAny(xRow->getString(10))); + + aRow.push_back(Any{}); // sql_data_type - unused + aRow.push_back(Any{}); // sql_datetime_sub - unused + + // character octet length + aRow.push_back(makeAny(xRow->getString(11))); + // ordinal position + aRow.push_back(makeAny(xRow->getString(12))); + // is nullable + aRow.push_back(makeAny(sIsNullable)); + aRows.push_back(aRow); + } + lcl_setRows_throw(xResultSet, 1, aRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getTables(const Any& /*catalog*/, + const OUString& schemaPattern, + const OUString& tableNamePattern, + const Sequence<OUString>& types) +{ + OUStringBuffer buffer{ + "SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA AS TABLE_SCHEM, TABLE_NAME," + "IF(STRCMP(TABLE_TYPE,'BASE TABLE'), TABLE_TYPE, 'TABLE') AS TABLE_TYPE, TABLE_COMMENT AS " + "REMARKS " + "FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE '?' AND TABLE_NAME LIKE '?' " + }; + + if (types.getLength() == 1) + { + buffer.append("AND TABLE_TYPE LIKE '"); + buffer.append(types[0]); + buffer.append("'"); + } + else if (types.getLength() > 1) + { + buffer.append("AND (TABLE_TYPE LIKE '"); + buffer.append(types[0]); + buffer.append("'"); + for (sal_Int32 i = 1; i < types.getLength(); ++i) + { + buffer.append(" OR TABLE_TYPE LIKE '"); + buffer.append(types[i]); + buffer.append("'"); + } + buffer.append(")"); + } + + buffer.append(" ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME"); + OUString query = buffer.makeStringAndClear(); + + // TODO use prepared stmt instead + // TODO escape schema, table name ? + query = query.replaceFirst("?", schemaPattern); + query = query.replaceFirst("?", tableNamePattern); + + Reference<XStatement> statement = m_rConnection.createStatement(); + Reference<XResultSet> rs = statement->executeQuery(query); + return rs; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getProcedureColumns( + const Any& /* catalog */, const OUString& /* schemaPattern */, + const OUString& /* procedureNamePattern */, const OUString& /* columnNamePattern */) +{ + // Currently there is no information available + return nullptr; +} + +Reference<XResultSet> + SAL_CALL ODatabaseMetaData::getProcedures(const Any& /*catalog*/, + const OUString& /*schemaPattern*/, + const OUString& /*procedureNamePattern*/) +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> rRows; + // TODO IMPL + SAL_WARN("connectivity.mysqlc", "method not implemented"); + lcl_setRows_throw(xResultSet, 7, rRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getVersionColumns(const Any& /* catalog */, + const OUString& /* schema */, + const OUString& /* table */) +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> rRows; + lcl_setRows_throw(xResultSet, 16, rRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getExportedKeys(const Any& /*catalog */, + const OUString& /*schema */, + const OUString& /*table */) +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> rRows; + // TODO implement + SAL_WARN("connectivity.mysqlc", "method not implemented"); + lcl_setRows_throw(xResultSet, 8, rRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getImportedKeys(const Any& /*catalog*/, + const OUString& schema, + const OUString& table) +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + + OUString query("SELECT refi.CONSTRAINT_CATALOG," // 1: foreign catalog + " k.COLUMN_NAME," // 2: foreign column name + " refi.UNIQUE_CONSTRAINT_CATALOG," // 3: primary catalog FIXME + " k.REFERENCED_TABLE_SCHEMA," // 4: primary schema + " refi.REFERENCED_TABLE_NAME," // 5: primary table name + " k.REFERENCED_COLUMN_NAME," // 6: primary column name + " refi.UPDATE_RULE, refi.DELETE_RULE," // 7,8: update, delete rule + " refi.CONSTRAINT_NAME, " // 9: name of constraint itself + " refi.TABLE_NAME, " // 10: foreign table name + " refi.CONSTRAINT_SCHEMA " // 11: foreign schema name FIXME + " FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as refi" + " INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE as k ON k.CONSTRAINT_NAME = " + "refi.CONSTRAINT_NAME " + " and k.TABLE_NAME = refi.TABLE_NAME " + " WHERE k.REFERENCED_TABLE_SCHEMA LIKE " + "'?' AND refi.TABLE_NAME='?'"); + query = query.replaceFirst("?", schema); // TODO what if schema is NULL? + query = query.replaceFirst("?", table); + + std::vector<std::vector<Any>> aRows; + Reference<XStatement> statement = m_rConnection.createStatement(); + Reference<XResultSet> rs = statement->executeQuery(query.getStr()); + Reference<XRow> xRow(rs, UNO_QUERY_THROW); + + while (rs->next()) + { + std::vector<Any> aRow{ Any() }; // 0. element is unused + + // primary key catalog + aRow.push_back(makeAny(xRow->getString(3))); + // primary key schema + aRow.push_back(makeAny(xRow->getString(4))); + // primary key table + aRow.push_back(makeAny(xRow->getString(5))); + // primary column name + aRow.push_back(makeAny(xRow->getString(6))); + + // fk table catalog + aRow.push_back(makeAny(xRow->getString(1))); + // fk schema + aRow.push_back(makeAny(xRow->getString(11))); + // fk table + aRow.push_back(makeAny(xRow->getString(10))); + // fk column name + aRow.push_back(makeAny(xRow->getString(2))); + // KEY_SEQ + aRow.push_back(makeAny(sal_Int32{ 0 })); // TODO + // update rule + aRow.push_back(makeAny(xRow->getShort(7))); + // delete rule + aRow.push_back(makeAny(xRow->getShort(8))); + // foreign key name + aRow.push_back(makeAny(xRow->getString(9))); + // primary key name + aRow.push_back(makeAny(OUString{})); // TODO + // deferrability + aRow.push_back(makeAny(Deferrability::NONE)); + aRows.push_back(aRow); + } + lcl_setRows_throw(xResultSet, 1, aRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getPrimaryKeys(const Any& /*catalog*/, + const OUString& schema, + const OUString& table) +{ + OUString query("SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA " + "AS TABLE_SCHEM, TABLE_NAME, " + "COLUMN_NAME, SEQ_IN_INDEX AS KEY_SEQ," + "INDEX_NAME AS PK_NAME FROM INFORMATION_SCHEMA.STATISTICS " + "WHERE TABLE_SCHEMA LIKE '?' AND TABLE_NAME LIKE '?' AND INDEX_NAME='PRIMARY' " + "ORDER BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX"); + + // TODO use prepared stmt instead + // TODO escape schema, table name ? + query = query.replaceFirst("?", schema); + query = query.replaceFirst("?", table); + + Reference<XStatement> statement = m_rConnection.createStatement(); + Reference<XResultSet> rs = statement->executeQuery(query); + return rs; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getIndexInfo(const Any& /*catalog*/, + const OUString& /*schema*/, + const OUString& /*table*/, + sal_Bool /*unique*/, + sal_Bool /*approximate*/) +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> rRows; + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + lcl_setRows_throw(xResultSet, 11, rRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getBestRowIdentifier(const Any& /*catalog*/, + const OUString& /*schema*/, + const OUString& /*table*/, + sal_Int32 /*scope*/, + sal_Bool /*nullable*/) +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> rRows; + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + lcl_setRows_throw(xResultSet, 15, rRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getTablePrivileges( + const Any& /*catalog*/, const OUString& /*schemaPattern*/, const OUString& /*tableNamePattern*/) +{ + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + throw SQLException("getTablePrivileges method not implemented", *this, "IM001", 0, Any()); +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getCrossReference( + const Any& /*primaryCatalog*/, const OUString& /*primarySchema_*/, + const OUString& /*primaryTable_*/, const Any& /*foreignCatalog*/, + const OUString& /*foreignSchema*/, const OUString& /*foreignTable*/) +{ + Reference<XResultSet> xResultSet(getOwnConnection().getDriver().getFactory()->createInstance( + "org.openoffice.comp.helper.DatabaseMetaDataResultSet"), + UNO_QUERY); + std::vector<std::vector<Any>> rRows; + // TODO + SAL_WARN("connectivity.mysqlc", "method not implemented"); + lcl_setRows_throw(xResultSet, 13, rRows); + return xResultSet; +} + +Reference<XResultSet> SAL_CALL ODatabaseMetaData::getUDTs(const Any& /* catalog */, + const OUString& /* schemaPattern */, + const OUString& /* typeNamePattern */, + const Sequence<sal_Int32>& /* types */) +{ + mysqlc_sdbc_driver::throwFeatureNotImplementedException("ODatabaseMetaData::getUDTs", *this); + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_databasemetadata.hxx b/connectivity/source/drivers/mysqlc/mysqlc_databasemetadata.hxx new file mode 100644 index 000000000..6814a574c --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_databasemetadata.hxx @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_DATABASEMETADATA_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_DATABASEMETADATA_HXX + +#include "mysqlc_connection.hxx" + +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <cppuhelper/implbase1.hxx> + +namespace connectivity +{ +namespace mysqlc +{ +using ::com::sun::star::uno::Any; + +//************ Class: ODatabaseMetaData + +typedef ::cppu::WeakImplHelper1<css::sdbc::XDatabaseMetaData> ODatabaseMetaData_BASE; + +class ODatabaseMetaData final : public ODatabaseMetaData_BASE +{ + OConnection& m_rConnection; + MYSQL* m_pMySql; + +public: + const OConnection& getOwnConnection() const { return m_rConnection; } + + explicit ODatabaseMetaData(OConnection& _rCon, MYSQL* pMySql); + virtual ~ODatabaseMetaData() override; + + // as I mentioned before this interface is really BIG + // XDatabaseMetaData + sal_Bool SAL_CALL allProceduresAreCallable() override; + sal_Bool SAL_CALL allTablesAreSelectable() override; + OUString SAL_CALL getURL() override; + OUString SAL_CALL getUserName() override; + sal_Bool SAL_CALL isReadOnly() override; + sal_Bool SAL_CALL nullsAreSortedHigh() override; + sal_Bool SAL_CALL nullsAreSortedLow() override; + sal_Bool SAL_CALL nullsAreSortedAtStart() override; + sal_Bool SAL_CALL nullsAreSortedAtEnd() override; + OUString SAL_CALL getDatabaseProductName() override; + OUString SAL_CALL getDatabaseProductVersion() override; + OUString SAL_CALL getDriverName() override; + OUString SAL_CALL getDriverVersion() override; + sal_Int32 SAL_CALL getDriverMajorVersion() override; + sal_Int32 SAL_CALL getDriverMinorVersion() override; + sal_Bool SAL_CALL usesLocalFiles() override; + sal_Bool SAL_CALL usesLocalFilePerTable() override; + sal_Bool SAL_CALL supportsMixedCaseIdentifiers() override; + sal_Bool SAL_CALL storesUpperCaseIdentifiers() override; + sal_Bool SAL_CALL storesLowerCaseIdentifiers() override; + sal_Bool SAL_CALL storesMixedCaseIdentifiers() override; + sal_Bool SAL_CALL supportsMixedCaseQuotedIdentifiers() override; + sal_Bool SAL_CALL storesUpperCaseQuotedIdentifiers() override; + sal_Bool SAL_CALL storesLowerCaseQuotedIdentifiers() override; + sal_Bool SAL_CALL storesMixedCaseQuotedIdentifiers() override; + OUString SAL_CALL getIdentifierQuoteString() override; + OUString SAL_CALL getSQLKeywords() override; + OUString SAL_CALL getNumericFunctions() override; + OUString SAL_CALL getStringFunctions() override; + OUString SAL_CALL getSystemFunctions() override; + OUString SAL_CALL getTimeDateFunctions() override; + OUString SAL_CALL getSearchStringEscape() override; + OUString SAL_CALL getExtraNameCharacters() override; + sal_Bool SAL_CALL supportsAlterTableWithAddColumn() override; + sal_Bool SAL_CALL supportsAlterTableWithDropColumn() override; + sal_Bool SAL_CALL supportsColumnAliasing() override; + sal_Bool SAL_CALL nullPlusNonNullIsNull() override; + sal_Bool SAL_CALL supportsTypeConversion() override; + sal_Bool SAL_CALL supportsConvert(sal_Int32 fromType, sal_Int32 toType) override; + sal_Bool SAL_CALL supportsTableCorrelationNames() override; + sal_Bool SAL_CALL supportsDifferentTableCorrelationNames() override; + sal_Bool SAL_CALL supportsExpressionsInOrderBy() override; + sal_Bool SAL_CALL supportsOrderByUnrelated() override; + sal_Bool SAL_CALL supportsGroupBy() override; + sal_Bool SAL_CALL supportsGroupByUnrelated() override; + sal_Bool SAL_CALL supportsGroupByBeyondSelect() override; + sal_Bool SAL_CALL supportsLikeEscapeClause() override; + sal_Bool SAL_CALL supportsMultipleResultSets() override; + sal_Bool SAL_CALL supportsMultipleTransactions() override; + sal_Bool SAL_CALL supportsNonNullableColumns() override; + sal_Bool SAL_CALL supportsMinimumSQLGrammar() override; + sal_Bool SAL_CALL supportsCoreSQLGrammar() override; + sal_Bool SAL_CALL supportsExtendedSQLGrammar() override; + sal_Bool SAL_CALL supportsANSI92EntryLevelSQL() override; + sal_Bool SAL_CALL supportsANSI92IntermediateSQL() override; + sal_Bool SAL_CALL supportsANSI92FullSQL() override; + sal_Bool SAL_CALL supportsIntegrityEnhancementFacility() override; + sal_Bool SAL_CALL supportsOuterJoins() override; + sal_Bool SAL_CALL supportsFullOuterJoins() override; + sal_Bool SAL_CALL supportsLimitedOuterJoins() override; + OUString SAL_CALL getSchemaTerm() override; + OUString SAL_CALL getProcedureTerm() override; + OUString SAL_CALL getCatalogTerm() override; + sal_Bool SAL_CALL isCatalogAtStart() override; + OUString SAL_CALL getCatalogSeparator() override; + sal_Bool SAL_CALL supportsSchemasInDataManipulation() override; + sal_Bool SAL_CALL supportsSchemasInProcedureCalls() override; + sal_Bool SAL_CALL supportsSchemasInTableDefinitions() override; + sal_Bool SAL_CALL supportsSchemasInIndexDefinitions() override; + sal_Bool SAL_CALL supportsSchemasInPrivilegeDefinitions() override; + sal_Bool SAL_CALL supportsCatalogsInDataManipulation() override; + sal_Bool SAL_CALL supportsCatalogsInProcedureCalls() override; + sal_Bool SAL_CALL supportsCatalogsInTableDefinitions() override; + sal_Bool SAL_CALL supportsCatalogsInIndexDefinitions() override; + sal_Bool SAL_CALL supportsCatalogsInPrivilegeDefinitions() override; + sal_Bool SAL_CALL supportsPositionedDelete() override; + sal_Bool SAL_CALL supportsPositionedUpdate() override; + sal_Bool SAL_CALL supportsSelectForUpdate() override; + sal_Bool SAL_CALL supportsStoredProcedures() override; + sal_Bool SAL_CALL supportsSubqueriesInComparisons() override; + sal_Bool SAL_CALL supportsSubqueriesInExists() override; + sal_Bool SAL_CALL supportsSubqueriesInIns() override; + sal_Bool SAL_CALL supportsSubqueriesInQuantifieds() override; + sal_Bool SAL_CALL supportsCorrelatedSubqueries() override; + sal_Bool SAL_CALL supportsUnion() override; + sal_Bool SAL_CALL supportsUnionAll() override; + sal_Bool SAL_CALL supportsOpenCursorsAcrossCommit() override; + sal_Bool SAL_CALL supportsOpenCursorsAcrossRollback() override; + sal_Bool SAL_CALL supportsOpenStatementsAcrossCommit() override; + sal_Bool SAL_CALL supportsOpenStatementsAcrossRollback() override; + sal_Int32 SAL_CALL getMaxBinaryLiteralLength() override; + sal_Int32 SAL_CALL getMaxCharLiteralLength() override; + sal_Int32 SAL_CALL getMaxColumnNameLength() override; + sal_Int32 SAL_CALL getMaxColumnsInGroupBy() override; + sal_Int32 SAL_CALL getMaxColumnsInIndex() override; + sal_Int32 SAL_CALL getMaxColumnsInOrderBy() override; + sal_Int32 SAL_CALL getMaxColumnsInSelect() override; + sal_Int32 SAL_CALL getMaxColumnsInTable() override; + sal_Int32 SAL_CALL getMaxConnections() override; + sal_Int32 SAL_CALL getMaxCursorNameLength() override; + sal_Int32 SAL_CALL getMaxIndexLength() override; + sal_Int32 SAL_CALL getMaxSchemaNameLength() override; + sal_Int32 SAL_CALL getMaxProcedureNameLength() override; + sal_Int32 SAL_CALL getMaxCatalogNameLength() override; + sal_Int32 SAL_CALL getMaxRowSize() override; + sal_Bool SAL_CALL doesMaxRowSizeIncludeBlobs() override; + sal_Int32 SAL_CALL getMaxStatementLength() override; + sal_Int32 SAL_CALL getMaxStatements() override; + sal_Int32 SAL_CALL getMaxTableNameLength() override; + sal_Int32 SAL_CALL getMaxTablesInSelect() override; + sal_Int32 SAL_CALL getMaxUserNameLength() override; + sal_Int32 SAL_CALL getDefaultTransactionIsolation() override; + sal_Bool SAL_CALL supportsTransactions() override; + sal_Bool SAL_CALL supportsTransactionIsolationLevel(sal_Int32 level) override; + sal_Bool SAL_CALL supportsDataDefinitionAndDataManipulationTransactions() override; + sal_Bool SAL_CALL supportsDataManipulationTransactionsOnly() override; + sal_Bool SAL_CALL dataDefinitionCausesTransactionCommit() override; + sal_Bool SAL_CALL dataDefinitionIgnoredInTransactions() override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getProcedures(const Any& catalog, const OUString& schemaPattern, + const OUString& procedureNamePattern) override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getProcedureColumns(const Any& catalog, const OUString& schemaPattern, + const OUString& procedureNamePattern, + const OUString& columnNamePattern) override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getTables(const Any& catalog, const OUString& schemaPattern, + const OUString& tableNamePattern, + const css::uno::Sequence<OUString>& types) override; + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL getSchemas() override; + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL getCatalogs() override; + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL getTableTypes() override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getColumns(const Any& catalog, const OUString& schemaPattern, + const OUString& tableNamePattern, + const OUString& columnNamePattern) override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getColumnPrivileges(const Any& catalog, const OUString& schema, + const OUString& table, + const OUString& columnNamePattern) override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getTablePrivileges(const Any& catalog, const OUString& schemaPattern, + const OUString& tableNamePattern) override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getBestRowIdentifier(const Any& catalog, const OUString& schema, + const OUString& table, sal_Int32 scope, + sal_Bool nullable) override; + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL + getVersionColumns(const Any& catalog, const OUString& schema, const OUString& table) override; + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL + getPrimaryKeys(const Any& catalog, const OUString& schema, const OUString& table) override; + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL + getImportedKeys(const Any& catalog, const OUString& schema, const OUString& table) override; + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL + getExportedKeys(const Any& catalog, const OUString& schema, const OUString& table) override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getCrossReference(const Any& primaryCatalog, const OUString& primarySchema, + const OUString& primaryTable, const Any& foreignCatalog, + const OUString& foreignSchema, + const OUString& foreignTable) override; + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL getTypeInfo() override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getIndexInfo(const Any& catalog, const OUString& schema, const OUString& table, + sal_Bool unique, sal_Bool approximate) override; + sal_Bool SAL_CALL supportsResultSetType(sal_Int32 setType) override; + sal_Bool SAL_CALL supportsResultSetConcurrency(sal_Int32 setType, + sal_Int32 concurrency) override; + sal_Bool SAL_CALL ownUpdatesAreVisible(sal_Int32 setType) override; + sal_Bool SAL_CALL ownDeletesAreVisible(sal_Int32 setType) override; + sal_Bool SAL_CALL ownInsertsAreVisible(sal_Int32 setType) override; + sal_Bool SAL_CALL othersUpdatesAreVisible(sal_Int32 setType) override; + sal_Bool SAL_CALL othersDeletesAreVisible(sal_Int32 setType) override; + sal_Bool SAL_CALL othersInsertsAreVisible(sal_Int32 setType) override; + sal_Bool SAL_CALL updatesAreDetected(sal_Int32 setType) override; + sal_Bool SAL_CALL deletesAreDetected(sal_Int32 setType) override; + sal_Bool SAL_CALL insertsAreDetected(sal_Int32 setType) override; + sal_Bool SAL_CALL supportsBatchUpdates() override; + css::uno::Reference<css::sdbc::XResultSet> + SAL_CALL getUDTs(const Any& catalog, const OUString& schemaPattern, + const OUString& typeNamePattern, + const css::uno::Sequence<sal_Int32>& types) override; + css::uno::Reference<css::sdbc::XConnection> SAL_CALL getConnection() override; +}; +} +} + +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_DATABASEMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_driver.cxx b/connectivity/source/drivers/mysqlc/mysqlc_driver.cxx new file mode 100644 index 000000000..c7c88d03e --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_driver.cxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "mysqlc_driver.hxx" +#include "mysqlc_connection.hxx" + +using namespace css::uno; +using namespace css::lang; +using namespace css::beans; +using namespace css::sdbc; +using namespace connectivity::mysqlc; + +#include <cppuhelper/supportsservice.hxx> + +MysqlCDriver::MysqlCDriver(const Reference<XMultiServiceFactory>& _rxFactory) + : ODriver_BASE(m_aMutex) + , m_xFactory(_rxFactory) +{ +} + +void MysqlCDriver::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + // when driver will be destroyed so all our connections have to be destroyed as well + for (auto const& connection : m_xConnections) + { + Reference<XComponent> xComp(connection.get(), UNO_QUERY); + if (xComp.is()) + { + xComp->dispose(); + } + } + m_xConnections.clear(); + + ODriver_BASE::disposing(); +} + +// static ServiceInfo +OUString MysqlCDriver::getImplementationName_Static() +{ + return "com.sun.star.comp.sdbc.mysqlc.MysqlCDriver"; +} + +Sequence<OUString> MysqlCDriver::getSupportedServiceNames_Static() +{ + return { "com.sun.star.sdbc.Driver" }; +} + +OUString SAL_CALL MysqlCDriver::getImplementationName() { return getImplementationName_Static(); } + +sal_Bool SAL_CALL MysqlCDriver::supportsService(const OUString& _rServiceName) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence<OUString> SAL_CALL MysqlCDriver::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +Reference<XConnection> SAL_CALL MysqlCDriver::connect(const OUString& url, + const Sequence<PropertyValue>& info) +{ + ::osl::MutexGuard aGuard(m_aMutex); + + if (!acceptsURL(url)) + { + return nullptr; + } + + Reference<XConnection> xConn; + // create a new connection with the given properties and append it to our vector + OConnection* pCon = new OConnection(*this); + xConn = pCon; + + pCon->construct(url, info); + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + return xConn; +} + +sal_Bool SAL_CALL MysqlCDriver::acceptsURL(const OUString& url) +{ + return url.startsWith("sdbc:mysqlc:") || url.startsWith("sdbc:mysql:mysqlc:"); +} + +Sequence<DriverPropertyInfo> SAL_CALL +MysqlCDriver::getPropertyInfo(const OUString& url, const Sequence<PropertyValue>& /* info */) +{ + if (acceptsURL(url)) + { + ::std::vector<DriverPropertyInfo> aDriverInfo; + + aDriverInfo.push_back(DriverPropertyInfo("Hostname", "Name of host", true, "localhost", + Sequence<OUString>())); + aDriverInfo.push_back( + DriverPropertyInfo("Port", "Port", true, "3306", Sequence<OUString>())); + return Sequence<DriverPropertyInfo>(aDriverInfo.data(), aDriverInfo.size()); + } + + return Sequence<DriverPropertyInfo>(); +} + +sal_Int32 SAL_CALL MysqlCDriver::getMajorVersion() { return MARIADBC_VERSION_MAJOR; } + +sal_Int32 SAL_CALL MysqlCDriver::getMinorVersion() { return MARIADBC_VERSION_MINOR; } + +namespace connectivity::mysqlc +{ +Reference<XInterface> MysqlCDriver_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory) +{ + return (*(new MysqlCDriver(_rxFactory))); +} + +void checkDisposed(bool _bThrow) +{ + if (_bThrow) + { + throw DisposedException(); + } +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_driver.hxx b/connectivity/source/drivers/mysqlc/mysqlc_driver.hxx new file mode 100644 index 000000000..b55d9cfee --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_driver.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_DRIVER_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_DRIVER_HXX + +#include "mysqlc_connection.hxx" + +#include <com/sun/star/sdbc/XDriver.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <cppuhelper/compbase2.hxx> +#include <osl/module.h> + +namespace connectivity +{ +namespace mysqlc +{ +using css::sdbc::SQLException; +using css::uno::Exception; +using css::uno::Reference; +using css::uno::RuntimeException; +using css::uno::Sequence; + +Reference<css::uno::XInterface> +MysqlCDriver_CreateInstance(const Reference<css::lang::XMultiServiceFactory>& _rxFactory); + +typedef ::cppu::WeakComponentImplHelper2<css::sdbc::XDriver, css::lang::XServiceInfo> ODriver_BASE; + +typedef void* (*OMysqlCConnection_CreateInstanceFunction)(void* _pDriver); + +class MysqlCDriver : public ODriver_BASE +{ +protected: + Reference<css::lang::XMultiServiceFactory> m_xFactory; + ::osl::Mutex m_aMutex; // mutex is need to control member access + OWeakRefArray m_xConnections; // vector containing a list + // of all the Connection objects + // for this Driver +#ifdef BUNDLE_MARIADB + oslModule m_hCConnModule; + bool m_bAttemptedLoadCConn; +#endif +public: + explicit MysqlCDriver(const Reference<css::lang::XMultiServiceFactory>& _rxFactory); + + // OComponentHelper + void SAL_CALL disposing() override; + // XInterface + static OUString getImplementationName_Static(); + static Sequence<OUString> getSupportedServiceNames_Static(); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XDriver + Reference<css::sdbc::XConnection> SAL_CALL + connect(const OUString& url, const Sequence<css::beans::PropertyValue>& info) override; + + sal_Bool SAL_CALL acceptsURL(const OUString& url) override; + Sequence<css::sdbc::DriverPropertyInfo> SAL_CALL + getPropertyInfo(const OUString& url, const Sequence<css::beans::PropertyValue>& info) override; + + sal_Int32 SAL_CALL getMajorVersion() override; + sal_Int32 SAL_CALL getMinorVersion() override; + + const Reference<css::lang::XMultiServiceFactory>& getFactory() const { return m_xFactory; } + + static rtl_TextEncoding getDefaultEncoding() { return RTL_TEXTENCODING_UTF8; } +}; +} /* mysqlc */ +} /* connectivity */ + +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_DRIVER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_general.cxx b/connectivity/source/drivers/mysqlc/mysqlc_general.cxx new file mode 100644 index 000000000..7ed11fe3f --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_general.cxx @@ -0,0 +1,340 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "mysqlc_general.hxx" + +#include <sal/log.hxx> +#include <rtl/ustring.hxx> + +#include <com/sun/star/sdbc/DataType.hpp> + +using com::sun::star::sdbc::SQLException; + +using com::sun::star::uno::Any; +using com::sun::star::uno::Reference; +using com::sun::star::uno::XInterface; + +using namespace rtl; + +namespace mysqlc_sdbc_driver +{ +void allocateSqlVar(void** mem, enum_field_types eType, unsigned nSize) +{ + assert(mem); + switch (eType) + { + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_INT24: + *mem = malloc(sizeof(sal_Int32)); + break; + case MYSQL_TYPE_SHORT: + *mem = malloc(sizeof(sal_Int16)); + break; + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_TINY: + *mem = malloc(sizeof(sal_Int8)); + break; + case MYSQL_TYPE_LONGLONG: + *mem = malloc(sizeof(sal_Int64)); + break; + case MYSQL_TYPE_FLOAT: + *mem = malloc(sizeof(float)); + break; + case MYSQL_TYPE_DOUBLE: + *mem = malloc(sizeof(double)); + break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_YEAR: // FIXME below + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + *mem = malloc(sizeof(MYSQL_TIME)); + break; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + *mem = malloc(sizeof(char) * nSize); + break; + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + *mem = nullptr; + break; + default: + SAL_WARN("connectivity.mysqlc", "unknown enum_field_type"); + } +} + +void throwFeatureNotImplementedException(const char* _pAsciiFeatureName, + const css::uno::Reference<XInterface>& _rxContext) +{ + const OUString sMessage + = OUString::createFromAscii(_pAsciiFeatureName) + ": feature not implemented."; + throw SQLException(sMessage, _rxContext, "HYC00", 0, Any()); +} + +void throwInvalidArgumentException(const char* _pAsciiFeatureName, + const css::uno::Reference<XInterface>& _rxContext) +{ + const OUString sMessage + = OUString::createFromAscii(_pAsciiFeatureName) + ": invalid arguments."; + throw SQLException(sMessage, _rxContext, "HYC00", 0, Any()); +} + +void throwSQLExceptionWithMsg(const char* msg, const char* SQLSTATE, unsigned int errorNum, + const css::uno::Reference<css::uno::XInterface>& _context, + const rtl_TextEncoding encoding) +{ + OString errorMsg{ msg }; + throwSQLExceptionWithMsg(OStringToOUString(errorMsg, encoding), SQLSTATE, errorNum, _context); +} + +void throwSQLExceptionWithMsg(const OUString& msg, const char* SQLSTATE, unsigned int errorNum, + const css::uno::Reference<css::uno::XInterface>& _context) +{ + throw SQLException(msg, _context, OStringToOUString(SQLSTATE, RTL_TEXTENCODING_ASCII_US), + errorNum, Any()); +} + +sal_Int32 mysqlToOOOType(int eType, int charsetnr) noexcept +{ + // charset number 63 indicates binary + switch (eType) + { + case MYSQL_TYPE_BIT: + return css::sdbc::DataType::VARCHAR; + + case MYSQL_TYPE_TINY: + return css::sdbc::DataType::TINYINT; + + case MYSQL_TYPE_SHORT: + return css::sdbc::DataType::SMALLINT; + + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + return css::sdbc::DataType::INTEGER; + + case MYSQL_TYPE_LONGLONG: + return css::sdbc::DataType::BIGINT; + + case MYSQL_TYPE_FLOAT: + return css::sdbc::DataType::REAL; + + case MYSQL_TYPE_DOUBLE: + return css::sdbc::DataType::DOUBLE; + + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return css::sdbc::DataType::DECIMAL; + + case MYSQL_TYPE_STRING: + if (charsetnr == 63) + return css::sdbc::DataType::BINARY; + return css::sdbc::DataType::CHAR; + + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_VAR_STRING: + if (charsetnr == 63) + return css::sdbc::DataType::VARBINARY; + return css::sdbc::DataType::VARCHAR; + + case MYSQL_TYPE_BLOB: + if (charsetnr == 63) + return css::sdbc::DataType::LONGVARBINARY; + return css::sdbc::DataType::LONGVARCHAR; + + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + return css::sdbc::DataType::TIMESTAMP; + + case MYSQL_TYPE_DATE: + return css::sdbc::DataType::DATE; + + case MYSQL_TYPE_TIME: + return css::sdbc::DataType::TIME; + + case MYSQL_TYPE_GEOMETRY: + return css::sdbc::DataType::VARCHAR; + + case MYSQL_TYPE_NULL: + return css::sdbc::DataType::SQLNULL; + } + + OSL_FAIL("mysqlToOOOType: unhandled case, falling back to VARCHAR"); + return css::sdbc::DataType::VARCHAR; +} + +sal_Int32 mysqlStrToOOOType(const OUString& sType) +{ + // TODO other types. + if (sType.equalsIgnoreAsciiCase("tiny") || sType.equalsIgnoreAsciiCase("tinyint")) + return css::sdbc::DataType::TINYINT; + if (sType.equalsIgnoreAsciiCase("smallint") || sType.equalsIgnoreAsciiCase("mediumint")) + return css::sdbc::DataType::SMALLINT; + if (sType.equalsIgnoreAsciiCase("longtext")) + return css::sdbc::DataType::LONGVARCHAR; + if (sType.equalsIgnoreAsciiCase("int")) + return css::sdbc::DataType::INTEGER; + if (sType.equalsIgnoreAsciiCase("varchar") || sType.equalsIgnoreAsciiCase("set") + || sType.equalsIgnoreAsciiCase("enum")) + return css::sdbc::DataType::VARCHAR; + if (sType.equalsIgnoreAsciiCase("bigint")) + return css::sdbc::DataType::BIGINT; + if (sType.equalsIgnoreAsciiCase("blob") || sType.equalsIgnoreAsciiCase("longblob")) + return css::sdbc::DataType::BLOB; + if (sType.equalsIgnoreAsciiCase("varbinary")) + return css::sdbc::DataType::VARBINARY; + if (sType.equalsIgnoreAsciiCase("char")) + return css::sdbc::DataType::CHAR; + if (sType.equalsIgnoreAsciiCase("text")) + return css::sdbc::DataType::CLOB; + if (sType.equalsIgnoreAsciiCase("binary")) + return css::sdbc::DataType::BINARY; + if (sType.equalsIgnoreAsciiCase("time")) + return css::sdbc::DataType::TIME; + if (sType.equalsIgnoreAsciiCase("date")) + return css::sdbc::DataType::DATE; + if (sType.equalsIgnoreAsciiCase("datetime") || sType.equalsIgnoreAsciiCase("timestamp")) + return css::sdbc::DataType::TIMESTAMP; + if (sType.equalsIgnoreAsciiCase("decimal")) + return css::sdbc::DataType::DECIMAL; + if (sType.equalsIgnoreAsciiCase("real") || sType.equalsIgnoreAsciiCase("float")) + return css::sdbc::DataType::REAL; + if (sType.equalsIgnoreAsciiCase("double")) + return css::sdbc::DataType::DOUBLE; + if (sType.equalsIgnoreAsciiCase("bit") || sType.equalsIgnoreAsciiCase("bool") + || sType.equalsIgnoreAsciiCase("boolean")) + return css::sdbc::DataType::BOOLEAN; + OSL_FAIL("Unknown type name from string, failing back to varchar."); + return css::sdbc::DataType::VARCHAR; +} + +OUString mysqlTypeToStr(unsigned type, unsigned flags) +{ + bool isUnsigned = (flags & UNSIGNED_FLAG) != 0; + bool isZerofill = (flags & ZEROFILL_FLAG) != 0; + switch (type) + { + case MYSQL_TYPE_BIT: + return OUString{ "BIT" }; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return isUnsigned ? (isZerofill ? OUString{ "DECIMAL UNSIGNED ZEROFILL" } + : OUString{ "DECIMAL UNSIGNED" }) + : OUString{ "DECIMAL" }; + case MYSQL_TYPE_TINY: + return isUnsigned ? (isZerofill ? OUString{ "TINYINT UNSIGNED ZEROFILL" } + : OUString{ "TINYINT UNSIGNED" }) + : OUString{ "TINYINT" }; + case MYSQL_TYPE_SHORT: + return isUnsigned ? (isZerofill ? OUString{ "SMALLINT UNSIGNED ZEROFILL" } + : OUString{ "SMALLINT UNSIGNED" }) + : OUString{ "SMALLINT" }; + case MYSQL_TYPE_LONG: + return isUnsigned ? (isZerofill ? OUString{ "INT UNSIGNED ZEROFILL" } + : OUString{ "INT UNSIGNED" }) + : OUString{ "INT" }; + case MYSQL_TYPE_FLOAT: + return isUnsigned ? (isZerofill ? OUString{ "FLOAT UNSIGNED ZEROFILL" } + : OUString{ "FLOAT UNSIGNED" }) + : OUString{ "FLOAT" }; + case MYSQL_TYPE_DOUBLE: + return isUnsigned ? (isZerofill ? OUString{ "DOUBLE UNSIGNED ZEROFILL" } + : OUString{ "DOUBLE UNSIGNED" }) + : OUString{ "DOUBLE" }; + case MYSQL_TYPE_NULL: + return OUString{ "NULL" }; + case MYSQL_TYPE_TIMESTAMP: + return OUString{ "TIMESTAMP" }; + case MYSQL_TYPE_LONGLONG: + return isUnsigned ? (isZerofill ? OUString{ "BIGINT UNSIGNED ZEROFILL" } + : OUString{ "BIGINT UNSIGNED" }) + : OUString{ "BIGINT" }; + case MYSQL_TYPE_INT24: + return isUnsigned ? (isZerofill ? OUString{ "MEDIUMINT UNSIGNED ZEROFILL" } + : OUString{ "MEDIUMINT UNSIGNED" }) + : OUString{ "MEDIUMINT" }; + case MYSQL_TYPE_DATE: + return OUString{ "DATE" }; + case MYSQL_TYPE_TIME: + return OUString{ "TIME" }; + case MYSQL_TYPE_DATETIME: + return OUString{ "DATETIME" }; + case MYSQL_TYPE_TINY_BLOB: + { + return OUString{ "TINYBLOB" }; + } + case MYSQL_TYPE_MEDIUM_BLOB: + { + return OUString{ "MEDIUMBLOB" }; + } + case MYSQL_TYPE_LONG_BLOB: + { + return OUString{ "LONGBLOB" }; + } + case MYSQL_TYPE_BLOB: + { + return OUString{ "BLOB" }; + } + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + if (flags & ENUM_FLAG) + { + return OUString{ "ENUM" }; + } + if (flags & SET_FLAG) + { + return OUString{ "SET" }; + } + return OUString{ "VARCHAR" }; + case MYSQL_TYPE_STRING: + if (flags & ENUM_FLAG) + { + return OUString{ "ENUM" }; + } + if (flags & SET_FLAG) + { + return OUString{ "SET" }; + } + return OUString{ "CHAR" }; + case MYSQL_TYPE_YEAR: + return OUString{ "YEAR" }; + case MYSQL_TYPE_GEOMETRY: + return OUString{ "GEOMETRY" }; + default: + return OUString{ "UNKNOWN" }; + } +} + +OUString convert(const ::std::string& _string, const rtl_TextEncoding encoding) +{ + return OUString(_string.c_str(), _string.size(), encoding); +} + +} /* namespace */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_general.hxx b/connectivity/source/drivers/mysqlc/mysqlc_general.hxx new file mode 100644 index 000000000..c00a11f41 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_general.hxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_GENERAL_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_GENERAL_HXX + +#include <config_lgpl.h> + +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +#include <osl/diagnose.h> +#include <mysql.h> + +#if defined __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" +#endif + +#if defined __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace mysqlc_sdbc_driver +{ +template <typename T> +void resetSqlVar(void** target, T* pValue, enum_field_types type, sal_Int32 nSize = 0) +{ + if (*target) + { + free(*target); + *target = nullptr; + } + constexpr auto nUnitSize = sizeof(T); + switch (type) + { + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + *target = malloc(nUnitSize); + memcpy(*target, pValue, nUnitSize); + break; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + *target = malloc(nUnitSize * nSize); + memcpy(*target, pValue, nUnitSize * nSize); + break; + case MYSQL_TYPE_NULL: + // nothing I guess + break; + default: + OSL_FAIL("resetSqlVar: unknown enum_field_type"); + } +} + +void allocateSqlVar(void** mem, enum_field_types eType, unsigned nSize = 0); + +void throwFeatureNotImplementedException( + const char* _pAsciiFeatureName, const css::uno::Reference<css::uno::XInterface>& _rxContext); + +void throwInvalidArgumentException(const char* _pAsciiFeatureName, + const css::uno::Reference<css::uno::XInterface>& _rxContext); + +void throwSQLExceptionWithMsg(const char* msg, const char* SQLSTATE, unsigned int errorNum, + const css::uno::Reference<css::uno::XInterface>& _context, + const rtl_TextEncoding encoding); + +void throwSQLExceptionWithMsg(const OUString& msg, const char* SQLSTATE, unsigned int errorNum, + const css::uno::Reference<css::uno::XInterface>& _context); + +sal_Int32 mysqlToOOOType(int eType, int charsetnr) noexcept; + +OUString mysqlTypeToStr(unsigned mysql_type, unsigned mysql_flags); + +sal_Int32 mysqlStrToOOOType(const OUString& sType); + +OUString convert(const ::std::string& _string, const rtl_TextEncoding encoding); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx new file mode 100644 index 000000000..8031bfdf2 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx @@ -0,0 +1,1084 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "mysqlc_propertyids.hxx" +#include "mysqlc_general.hxx" +#include "mysqlc_prepared_resultset.hxx" +#include "mysqlc_resultsetmetadata.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#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/sdbcx/CompareBookmark.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <sal/log.hxx> + +using namespace rtl; + +#include <cstdlib> + +using namespace connectivity::mysqlc; +using namespace connectivity; +using namespace cppu; +using namespace com::sun::star; +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::io; +using namespace com::sun::star::util; +using namespace ::comphelper; +using ::osl::MutexGuard; + +#include <typeindex> + +namespace +{ +std::type_index getTypeFromMysqlType(enum_field_types type) +{ + switch (type) + { + case MYSQL_TYPE_TINY: + return std::type_index(typeid(sal_Int8)); + case MYSQL_TYPE_SHORT: + return std::type_index(typeid(sal_Int16)); + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_INT24: + return std::type_index(typeid(sal_Int32)); + case MYSQL_TYPE_LONGLONG: + return std::type_index(typeid(sal_Int64)); + case MYSQL_TYPE_FLOAT: + return std::type_index(typeid(float)); + case MYSQL_TYPE_DOUBLE: + return std::type_index(typeid(double)); + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + return std::type_index(typeid(DateTime)); + case MYSQL_TYPE_DATE: + return std::type_index(typeid(Date)); + case MYSQL_TYPE_TIME: + return std::type_index(typeid(Time)); + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return std::type_index(typeid(OUString)); + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_NULL: + default: + return std::type_index(typeid(nullptr)); + } +} +} + +bool OPreparedResultSet::fetchResult() +{ + // allocate array if it does not exist + if (m_aData == nullptr) + { + m_aData.reset(new MYSQL_BIND[m_nColumnCount]); + memset(m_aData.get(), 0, m_nColumnCount * sizeof(MYSQL_BIND)); + m_aMetaData.reset(new BindMetaData[m_nColumnCount]); + } + for (sal_Int32 i = 0; i < m_nColumnCount; ++i) + { + m_aMetaData[i].is_null = false; + m_aMetaData[i].length = 0l; + m_aMetaData[i].error = false; + + m_aData[i].is_null = &m_aMetaData[i].is_null; + m_aData[i].buffer_length = m_aFields[i].type == MYSQL_TYPE_BLOB ? 0 : m_aFields[i].length; + m_aData[i].length = &m_aMetaData[i].length; + m_aData[i].error = &m_aMetaData[i].error; + m_aData[i].buffer = nullptr; + m_aData[i].buffer_type = m_aFields[i].type; + + // allocates memory, if it is a fixed size type. If not then nullptr + mysqlc_sdbc_driver::allocateSqlVar(&m_aData[i].buffer, m_aData[i].buffer_type, + m_aFields[i].length); + } + mysql_stmt_bind_result(m_pStmt, m_aData.get()); + int failure = mysql_stmt_fetch(m_pStmt); + + for (sal_Int32 i = 0; i < m_nColumnCount; ++i) + { + if (*m_aData[i].error) + { + // expected if we have a BLOB, as buffer_length is set to 0. We want to + // fetch it piece by piece + // see https://bugs.mysql.com/file.php?id=12361&bug_id=33086 + if (m_aData[i].buffer == nullptr) + { + m_aData[i].buffer_length = *m_aData[i].length; + m_aData[i].buffer = malloc(*m_aData[i].length); + mysql_stmt_fetch_column(m_pStmt, &m_aData[i], i, 0); + } + } + } + + if (failure == 1) + { + MYSQL* pMysql = m_rConnection.getMysqlConnection(); + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(pMysql), mysql_sqlstate(pMysql), + mysql_errno(pMysql), *this, m_encoding); + } + else if (failure == MYSQL_NO_DATA) + return false; + return true; +} + +OUString SAL_CALL OPreparedResultSet::getImplementationName() +{ + return "com.sun.star.sdbcx.mysqlc.ResultSet"; +} + +uno::Sequence<OUString> SAL_CALL OPreparedResultSet::getSupportedServiceNames() +{ + return { "com.sun.star.sdbc.ResultSet", "com.sun.star.sdbcx.ResultSet" }; +} + +sal_Bool SAL_CALL OPreparedResultSet::supportsService(const OUString& _rServiceName) +{ + return cppu::supportsService(this, _rServiceName); +} +OPreparedResultSet::OPreparedResultSet(OConnection& rConn, OPreparedStatement* pStmt, + MYSQL_STMT* pMyStmt) + : OPreparedResultSet_BASE(m_aMutex) + , OPropertySetHelper(OPreparedResultSet_BASE::rBHelper) + , m_rConnection(rConn) + , m_aStatement(static_cast<OWeakObject*>(pStmt)) + , m_pStmt(pMyStmt) + , m_encoding(rConn.getConnectionEncoding()) + , m_nColumnCount(mysql_stmt_field_count(pMyStmt)) +{ + m_pResult = mysql_stmt_result_metadata(m_pStmt); + if (m_pResult != nullptr) + mysql_stmt_store_result(m_pStmt); + m_aFields = mysql_fetch_fields(m_pResult); + m_nRowCount = mysql_stmt_num_rows(pMyStmt); +} + +void OPreparedResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + MutexGuard aGuard(m_aMutex); + + m_aStatement = nullptr; + m_xMetaData = nullptr; +} + +Any SAL_CALL OPreparedResultSet::queryInterface(const Type& rType) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + if (!aRet.hasValue()) + { + aRet = OPreparedResultSet_BASE::queryInterface(rType); + } + return aRet; +} + +uno::Sequence<Type> SAL_CALL OPreparedResultSet::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get()); + + return concatSequences(aTypes.getTypes(), OPreparedResultSet_BASE::getTypes()); +} + +sal_Int32 SAL_CALL OPreparedResultSet::findColumn(const OUString& columnName) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + MYSQL_FIELD* pFields = mysql_fetch_fields(m_pResult); + for (sal_Int32 i = 0; i < m_nColumnCount; ++i) + { + if (columnName.equalsIgnoreAsciiCaseAscii(pFields[i].name)) + return i + 1; // sdbc indexes from 1 + } + + throw SQLException("The column name '" + columnName + "' is not valid.", *this, "42S22", 0, + Any()); +} + +template <typename T> T OPreparedResultSet::safelyRetrieveValue(sal_Int32 nColumnIndex) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(nColumnIndex); + if (*m_aData[nColumnIndex - 1].is_null) + { + m_bWasNull = true; + return T(); + } + m_bWasNull = false; + + return retrieveValue<T>(nColumnIndex); +} + +template <typename T> T OPreparedResultSet::retrieveValue(sal_Int32 nColumnIndex) +{ + if (getTypeFromMysqlType(m_aFields[nColumnIndex - 1].type) == std::type_index(typeid(T))) + return *static_cast<T*>(m_aData[nColumnIndex - 1].buffer); + else + return getRowSetValue(nColumnIndex); +} + +template <> uno::Sequence<sal_Int8> OPreparedResultSet::retrieveValue(sal_Int32 column) +{ + // TODO make conversion possible + return uno::Sequence<sal_Int8>(static_cast<sal_Int8 const*>(m_aData[column - 1].buffer), + *m_aData[column - 1].length); +} + +template <> Date OPreparedResultSet::retrieveValue(sal_Int32 column) +{ + if (getTypeFromMysqlType(m_aFields[column - 1].type) != std::type_index(typeid(Date))) + return getRowSetValue(column); + const MYSQL_TIME* pTime = static_cast<MYSQL_TIME*>(m_aData[column - 1].buffer); + + Date d; + d.Year = pTime->year; + d.Month = pTime->month; + d.Day = pTime->day; + return d; +} + +template <> Time OPreparedResultSet::retrieveValue(sal_Int32 column) +{ + if (getTypeFromMysqlType(m_aFields[column - 1].type) != std::type_index(typeid(Time))) + return getRowSetValue(column); + const MYSQL_TIME* pTime = static_cast<MYSQL_TIME*>(m_aData[column - 1].buffer); + + Time t; + t.Hours = pTime->hour; + t.Minutes = pTime->minute; + t.Seconds = pTime->second; + return t; +} + +template <> DateTime OPreparedResultSet::retrieveValue(sal_Int32 column) +{ + if (getTypeFromMysqlType(m_aFields[column - 1].type) != std::type_index(typeid(DateTime))) + return getRowSetValue(column); + const MYSQL_TIME* pTime = static_cast<MYSQL_TIME*>(m_aData[column - 1].buffer); + + DateTime t; + t.Year = pTime->year; + t.Month = pTime->month; + t.Day = pTime->day; + t.Hours = pTime->hour; + t.Minutes = pTime->minute; + t.Seconds = pTime->second; + return t; +} + +template <> OUString OPreparedResultSet::retrieveValue(sal_Int32 column) +{ + // redirect call to the appropriate method if needed + // BLOB can be simply read out as string + if (getTypeFromMysqlType(m_aFields[column - 1].type) != std::type_index(typeid(OUString)) + && m_aFields[column - 1].type != MYSQL_TYPE_BLOB) + return getRowSetValue(column); + const char* sStr = static_cast<const char*>(m_aData[column - 1].buffer); + + return OUString(sStr, *m_aData[column - 1].length, m_encoding); +} + +ORowSetValue OPreparedResultSet::getRowSetValue(sal_Int32 nColumnIndex) +{ + switch (m_aFields[nColumnIndex - 1].type) + { + case MYSQL_TYPE_TINY: + return getByte(nColumnIndex); + case MYSQL_TYPE_SHORT: + return getShort(nColumnIndex); + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_INT24: + return getInt(nColumnIndex); + case MYSQL_TYPE_LONGLONG: + return getLong(nColumnIndex); + case MYSQL_TYPE_FLOAT: + return getFloat(nColumnIndex); + case MYSQL_TYPE_DOUBLE: + return getDouble(nColumnIndex); + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + return getTimestamp(nColumnIndex); + case MYSQL_TYPE_DATE: + return getDate(nColumnIndex); + case MYSQL_TYPE_TIME: + return getTime(nColumnIndex); + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return getString(nColumnIndex); + case MYSQL_TYPE_BLOB: + throw SQLException("Column with type BLOB cannot be converted", *this, "22000", 1, + Any()); + default: + SAL_WARN("connectivity.mysqlc", "OPreparedResultSet::getRowSetValue: unknown type: " + << m_aFields[nColumnIndex - 1].type); + throw SQLException("Unknown column type when fetching result", *this, "22000", 1, + Any()); + } +} + +uno::Reference<XInputStream> SAL_CALL OPreparedResultSet::getBinaryStream(sal_Int32 /*column*/) +{ + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getBinaryStream", + *this); + return nullptr; +} + +uno::Reference<XInputStream> SAL_CALL OPreparedResultSet::getCharacterStream(sal_Int32 /*column*/) +{ + mysqlc_sdbc_driver::throwFeatureNotImplementedException( + "OPreparedResultSet::getCharacterStream", *this); + return nullptr; +} + +sal_Bool SAL_CALL OPreparedResultSet::getBoolean(sal_Int32 column) +{ + return safelyRetrieveValue<bool>(column); +} + +sal_Int8 SAL_CALL OPreparedResultSet::getByte(sal_Int32 column) +{ + return safelyRetrieveValue<sal_Int8>(column); +} + +uno::Sequence<sal_Int8> SAL_CALL OPreparedResultSet::getBytes(sal_Int32 column) +{ + return safelyRetrieveValue<uno::Sequence<sal_Int8>>(column); +} + +Date SAL_CALL OPreparedResultSet::getDate(sal_Int32 column) +{ + return safelyRetrieveValue<Date>(column); +} + +double SAL_CALL OPreparedResultSet::getDouble(sal_Int32 column) +{ + return safelyRetrieveValue<double>(column); +} + +float SAL_CALL OPreparedResultSet::getFloat(sal_Int32 column) +{ + return safelyRetrieveValue<float>(column); +} + +sal_Int32 SAL_CALL OPreparedResultSet::getInt(sal_Int32 column) +{ + return safelyRetrieveValue<sal_Int32>(column); +} + +sal_Int32 SAL_CALL OPreparedResultSet::getRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return static_cast<sal_Int32>(mysql_field_tell(m_pResult)); +} + +sal_Int64 SAL_CALL OPreparedResultSet::getLong(sal_Int32 column) +{ + return safelyRetrieveValue<sal_Int64>(column); +} + +uno::Reference<XResultSetMetaData> SAL_CALL OPreparedResultSet::getMetaData() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + if (!m_xMetaData.is()) + { + m_xMetaData = new OResultSetMetaData(m_rConnection, m_pResult); + } + return m_xMetaData; +} + +uno::Reference<XArray> SAL_CALL OPreparedResultSet::getArray(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getArray", *this); + return nullptr; +} + +uno::Reference<XClob> SAL_CALL OPreparedResultSet::getClob(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getClob", *this); + return nullptr; +} + +uno::Reference<XBlob> SAL_CALL OPreparedResultSet::getBlob(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getBlob", *this); + return nullptr; +} + +uno::Reference<XRef> SAL_CALL OPreparedResultSet::getRef(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getRef", *this); + return nullptr; +} + +Any SAL_CALL OPreparedResultSet::getObject(sal_Int32 column, + const uno::Reference<XNameAccess>& /* typeMap */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + + Any aRet; + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getObject", *this); + return aRet; +} + +sal_Int16 SAL_CALL OPreparedResultSet::getShort(sal_Int32 column) +{ + return safelyRetrieveValue<sal_Int16>(column); +} + +OUString SAL_CALL OPreparedResultSet::getString(sal_Int32 column) +{ + return safelyRetrieveValue<OUString>(column); +} + +Time SAL_CALL OPreparedResultSet::getTime(sal_Int32 column) +{ + return safelyRetrieveValue<Time>(column); +} + +DateTime SAL_CALL OPreparedResultSet::getTimestamp(sal_Int32 column) +{ + return safelyRetrieveValue<DateTime>(column); +} + +sal_Bool SAL_CALL OPreparedResultSet::isBeforeFirst() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return m_nCurrentRow == 0; +} + +sal_Bool SAL_CALL OPreparedResultSet::isAfterLast() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return m_nCurrentRow > m_nRowCount; +} + +sal_Bool SAL_CALL OPreparedResultSet::isFirst() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return m_nCurrentRow == 1 && !isAfterLast(); +} + +sal_Bool SAL_CALL OPreparedResultSet::isLast() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return m_nCurrentRow == m_nRowCount; +} + +void SAL_CALL OPreparedResultSet::beforeFirst() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + mysql_stmt_data_seek(m_pStmt, 0); + + m_nCurrentRow = 0; + m_aData.reset(); +} + +void SAL_CALL OPreparedResultSet::afterLast() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::afterLast", *this); +} + +void SAL_CALL OPreparedResultSet::close() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + m_aData.reset(); + m_aMetaData.reset(); + + if (m_pResult) + mysql_free_result(m_pResult); + mysql_stmt_free_result(m_pStmt); + dispose(); +} + +sal_Bool SAL_CALL OPreparedResultSet::first() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + mysql_stmt_data_seek(m_pStmt, 0); + m_nCurrentRow = 0; + next(); + + return true; +} + +sal_Bool SAL_CALL OPreparedResultSet::last() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + mysql_stmt_data_seek(m_pStmt, m_nRowCount - 1); + next(); + + return true; +} + +sal_Bool SAL_CALL OPreparedResultSet::absolute(sal_Int32 row) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + sal_Int32 nToGo = row < 0 ? m_nRowCount - row : row - 1; + + if (nToGo >= m_nRowCount) + nToGo = m_nRowCount - 1; + if (nToGo < 0) + nToGo = 0; + + mysql_stmt_data_seek(m_pStmt, nToGo); + next(); + + return true; +} + +sal_Bool SAL_CALL OPreparedResultSet::relative(sal_Int32 row) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + if (row == 0) + return true; + + sal_Int32 nToGo = m_nCurrentRow + row; + if (nToGo >= m_nRowCount) + nToGo = m_nRowCount - 1; + if (nToGo < 0) + nToGo = 0; + + mysql_stmt_data_seek(m_pStmt, nToGo); + next(); + m_nCurrentRow += row; + + return true; +} + +sal_Bool SAL_CALL OPreparedResultSet::previous() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + if (m_nCurrentRow <= 1) + return false; + + mysql_stmt_data_seek(m_pStmt, m_nCurrentRow - 2); + next(); + --m_nCurrentRow; + return true; +} + +uno::Reference<uno::XInterface> SAL_CALL OPreparedResultSet::getStatement() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return m_aStatement.get(); +} + +sal_Bool SAL_CALL OPreparedResultSet::rowDeleted() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OPreparedResultSet::rowInserted() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OPreparedResultSet::rowUpdated() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OPreparedResultSet::next() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + bool hasData = fetchResult(); + + // current field cannot be asked as a number. We have to keep track it + // manually. + m_nCurrentRow += 1; + + return hasData; +} + +sal_Bool SAL_CALL OPreparedResultSet::wasNull() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return m_bWasNull; +} + +void SAL_CALL OPreparedResultSet::cancel() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL OPreparedResultSet::clearWarnings() {} + +Any SAL_CALL OPreparedResultSet::getWarnings() { return Any(); } + +void SAL_CALL OPreparedResultSet::insertRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + // you only have to implement this if you want to insert new rows + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::insertRow", *this); +} + +void SAL_CALL OPreparedResultSet::updateRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + // only when you allow updates + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateRow", *this); +} + +void SAL_CALL OPreparedResultSet::deleteRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::deleteRow", *this); +} + +void SAL_CALL OPreparedResultSet::cancelRowUpdates() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::cancelRowUpdates", + *this); +} + +void SAL_CALL OPreparedResultSet::moveToInsertRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + // only when you allow insert's + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::moveToInsertRow", + *this); +} + +void SAL_CALL OPreparedResultSet::moveToCurrentRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL OPreparedResultSet::updateNull(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateNull", + *this); +} + +void SAL_CALL OPreparedResultSet::updateBoolean(sal_Int32 column, sal_Bool /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateBoolean", + *this); +} + +void SAL_CALL OPreparedResultSet::updateByte(sal_Int32 column, sal_Int8 /* x */) +{ + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateByte", + *this); +} + +void SAL_CALL OPreparedResultSet::updateShort(sal_Int32 column, sal_Int16 /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateShort", + *this); +} + +void SAL_CALL OPreparedResultSet::updateInt(sal_Int32 column, sal_Int32 /* x */) +{ + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateInt", *this); +} + +void SAL_CALL OPreparedResultSet::updateLong(sal_Int32 column, sal_Int64 /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateLong", + *this); +} + +void SAL_CALL OPreparedResultSet::updateFloat(sal_Int32 column, float /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateFloat", + *this); +} + +void SAL_CALL OPreparedResultSet::updateDouble(sal_Int32 column, double /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateDouble", + *this); +} + +void SAL_CALL OPreparedResultSet::updateString(sal_Int32 column, const OUString& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateString", + *this); +} + +void SAL_CALL OPreparedResultSet::updateBytes(sal_Int32 column, + const uno::Sequence<sal_Int8>& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateBytes", + *this); +} + +void SAL_CALL OPreparedResultSet::updateDate(sal_Int32 column, const Date& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateDate", + *this); +} + +void SAL_CALL OPreparedResultSet::updateTime(sal_Int32 column, const Time& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateTime", + *this); +} + +void SAL_CALL OPreparedResultSet::updateTimestamp(sal_Int32 column, const DateTime& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateTimestamp", + *this); +} + +void SAL_CALL OPreparedResultSet::updateBinaryStream(sal_Int32 column, + const uno::Reference<XInputStream>& /* x */, + sal_Int32 /* length */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException( + "OPreparedResultSet::updateBinaryStream", *this); +} + +void SAL_CALL OPreparedResultSet::updateCharacterStream(sal_Int32 column, + const uno::Reference<XInputStream>& /* x */, + sal_Int32 /* length */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException( + "OPreparedResultSet::updateCharacterStream", *this); +} + +void SAL_CALL OPreparedResultSet::refreshRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::refreshRow", + *this); +} + +void SAL_CALL OPreparedResultSet::updateObject(sal_Int32 column, const Any& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::updateObject", + *this); +} + +void SAL_CALL OPreparedResultSet::updateNumericObject(sal_Int32 column, const Any& /* x */, + sal_Int32 /* scale */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException( + "OPreparedResultSet::updateNumericObject", *this); +} + +// XRowLocate +Any SAL_CALL OPreparedResultSet::getBookmark() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + // if you don't want to support bookmark you must remove the XRowLocate interface + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getBookmark", + *this); + + return Any(); +} + +sal_Bool SAL_CALL OPreparedResultSet::moveToBookmark(const Any& /* bookmark */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OPreparedResultSet::moveRelativeToBookmark(const Any& /* bookmark */, + sal_Int32 /* rows */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException( + "OPreparedResultSet::moveRelativeToBookmark", *this); + return false; +} + +sal_Int32 SAL_CALL OPreparedResultSet::compareBookmarks(const Any& /* n1 */, const Any& /* n2 */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::compareBookmarks", + *this); + + return CompareBookmark::NOT_EQUAL; +} + +sal_Bool SAL_CALL OPreparedResultSet::hasOrderedBookmarks() { return false; } + +sal_Int32 SAL_CALL OPreparedResultSet::hashBookmark(const Any& /* bookmark */) +{ + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::hashBookmark", + *this); + return 0; +} + +// XDeleteRows +uno::Sequence<sal_Int32> + SAL_CALL OPreparedResultSet::deleteRows(const uno::Sequence<Any>& /* rows */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::deleteRows", + *this); + return uno::Sequence<sal_Int32>(); +} + +IPropertyArrayHelper* OPreparedResultSet::createArrayHelper() const +{ + uno::Sequence<Property> aProps(5); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = Property("FetchDirection", PROPERTY_ID_FETCHDIRECTION, + cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] + = Property("FetchSize", PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = Property("IsBookmarkable", PROPERTY_ID_ISBOOKMARKABLE, + cppu::UnoType<bool>::get(), PropertyAttribute::READONLY); + pProperties[nPos++] = Property("ResultSetConcurrency", PROPERTY_ID_RESULTSETCONCURRENCY, + cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + pProperties[nPos++] = Property("ResultSetType", PROPERTY_ID_RESULTSETTYPE, + cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + return new OPropertyArrayHelper(aProps); +} + +IPropertyArrayHelper& OPreparedResultSet::getInfoHelper() { return *getArrayHelper(); } + +sal_Bool OPreparedResultSet::convertFastPropertyValue(Any& /* rConvertedValue */, + Any& /* rOldValue */, sal_Int32 nHandle, + const Any& /* rValue */) +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + default:; + } + return false; +} + +void OPreparedResultSet::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, + const Any& /* rValue */) +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw uno::Exception("cannot set prop " + OUString::number(nHandle), nullptr); + case PROPERTY_ID_FETCHDIRECTION: + break; + case PROPERTY_ID_FETCHSIZE: + break; + default:; + } +} + +void OPreparedResultSet::getFastPropertyValue(Any& _rValue, sal_Int32 nHandle) const +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + _rValue <<= false; + break; + case PROPERTY_ID_CURSORNAME: + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + _rValue <<= ResultSetConcurrency::READ_ONLY; + break; + case PROPERTY_ID_RESULTSETTYPE: + _rValue <<= ResultSetType::SCROLL_INSENSITIVE; + break; + case PROPERTY_ID_FETCHDIRECTION: + _rValue <<= FetchDirection::FORWARD; + break; + case PROPERTY_ID_FETCHSIZE: + _rValue <<= sal_Int32(50); + break; + ; + default:; + } +} + +void SAL_CALL OPreparedResultSet::acquire() throw() { OPreparedResultSet_BASE::acquire(); } + +void SAL_CALL OPreparedResultSet::release() throw() { OPreparedResultSet_BASE::release(); } + +css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL OPreparedResultSet::getPropertySetInfo() +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +void OPreparedResultSet::checkColumnIndex(sal_Int32 index) +{ + if (!m_aData) + throw SQLException("Cursor out of range", *this, "HY109", 1, Any()); + if (index < 1 || index > static_cast<int>(m_nColumnCount)) + { + /* static object for efficiency or thread safety is a problem ? */ + throw SQLException("index out of range", *this, "42S22", 1, Any()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx new file mode 100644 index 000000000..4ff64d5b4 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_RESULTSET_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_RESULTSET_HXX + +#include "mysqlc_preparedstatement.hxx" +#include "mysqlc_statement.hxx" +#include "mysqlc_subcomponent.hxx" +#include "mysqlc_connection.hxx" + +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbcx/XDeleteRows.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/util/XCancellable.hpp> +#include <connectivity/FValue.hxx> + +#include <cppuhelper/compbase12.hxx> + +namespace connectivity +{ +namespace mysqlc +{ +using ::com::sun::star::sdbc::SQLException; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::RuntimeException; + +typedef ::cppu::WeakComponentImplHelper12< + css::sdbc::XResultSet, css::sdbc::XRow, css::sdbc::XResultSetMetaDataSupplier, + css::util::XCancellable, css::sdbc::XWarningsSupplier, css::sdbc::XResultSetUpdate, + css::sdbc::XRowUpdate, css::sdbcx::XRowLocate, css::sdbcx::XDeleteRows, css::sdbc::XCloseable, + css::sdbc::XColumnLocate, css::lang::XServiceInfo> + OPreparedResultSet_BASE; + +class OPreparedResultSet final : public OBase_Mutex, + public OPreparedResultSet_BASE, + public ::cppu::OPropertySetHelper, + public OPropertyArrayUsageHelper<OPreparedResultSet> +{ + OConnection& m_rConnection; + css::uno::WeakReferenceHelper m_aStatement; + css::uno::Reference<css::sdbc::XResultSetMetaData> m_xMetaData; + + // non-owning pointers + MYSQL_RES* m_pResult; + MYSQL_STMT* m_pStmt; + MYSQL_FIELD* m_aFields; + + rtl_TextEncoding m_encoding; + sal_Int32 m_nCurrentRow = 0; + sal_Int32 m_nColumnCount; + sal_Int32 m_nRowCount; + + // Use c style arrays, because we have to work with pointers + // on these. + std::unique_ptr<MYSQL_BIND[]> m_aData; + std::unique_ptr<BindMetaData[]> m_aMetaData; + + bool m_bWasNull = false; + + // OPropertyArrayUsageHelper + ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + // OPropertySetHelper + ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + sal_Bool SAL_CALL convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, + sal_Int32 nHandle, const Any& rValue) override; + + void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue) override; + + void SAL_CALL getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const override; + + template <typename T> T safelyRetrieveValue(const sal_Int32 nColumnIndex); + template <typename T> T retrieveValue(const sal_Int32 nColumnIndex); + connectivity::ORowSetValue getRowSetValue(sal_Int32 nColumnIndex); + + bool fetchResult(); + + // you can't delete objects of this type + virtual ~OPreparedResultSet() override = default; + +public: + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + OPreparedResultSet(OConnection& rConn, OPreparedStatement* pStmt, MYSQL_STMT* pMyStmt); + + // ::cppu::OComponentHelper + void SAL_CALL disposing() override; + + // XInterface + Any SAL_CALL queryInterface(const css::uno::Type& rType) override; + + void SAL_CALL acquire() throw() override; + void SAL_CALL release() throw() override; + + //XTypeProvider + css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + + // XPropertySet + css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + + // XResultSet + sal_Bool SAL_CALL next() override; + sal_Bool SAL_CALL isBeforeFirst() override; + sal_Bool SAL_CALL isAfterLast() override; + sal_Bool SAL_CALL isFirst() override; + sal_Bool SAL_CALL isLast() override; + + void SAL_CALL beforeFirst() override; + void SAL_CALL afterLast() override; + + sal_Bool SAL_CALL first() override; + sal_Bool SAL_CALL last() override; + + sal_Int32 SAL_CALL getRow() override; + + sal_Bool SAL_CALL absolute(sal_Int32 row) override; + sal_Bool SAL_CALL relative(sal_Int32 rows) override; + sal_Bool SAL_CALL previous() override; + + void SAL_CALL refreshRow() override; + + sal_Bool SAL_CALL rowUpdated() override; + sal_Bool SAL_CALL rowInserted() override; + sal_Bool SAL_CALL rowDeleted() override; + + css::uno::Reference<css::uno::XInterface> SAL_CALL getStatement() override; + // XRow + sal_Bool SAL_CALL wasNull() override; + + OUString SAL_CALL getString(sal_Int32 column) override; + + sal_Bool SAL_CALL getBoolean(sal_Int32 column) override; + sal_Int8 SAL_CALL getByte(sal_Int32 column) override; + sal_Int16 SAL_CALL getShort(sal_Int32 column) override; + sal_Int32 SAL_CALL getInt(sal_Int32 column) override; + sal_Int64 SAL_CALL getLong(sal_Int32 column) override; + + float SAL_CALL getFloat(sal_Int32 column) override; + double SAL_CALL getDouble(sal_Int32 column) override; + + css::uno::Sequence<sal_Int8> SAL_CALL getBytes(sal_Int32 column) override; + css::util::Date SAL_CALL getDate(sal_Int32 column) override; + css::util::Time SAL_CALL getTime(sal_Int32 column) override; + css::util::DateTime SAL_CALL getTimestamp(sal_Int32 column) override; + + css::uno::Reference<css::io::XInputStream> SAL_CALL getBinaryStream(sal_Int32 column) override; + css::uno::Reference<css::io::XInputStream> + SAL_CALL getCharacterStream(sal_Int32 column) override; + + Any SAL_CALL getObject( + sal_Int32 column, const css::uno::Reference<css::container::XNameAccess>& typeMap) override; + + css::uno::Reference<css::sdbc::XRef> SAL_CALL getRef(sal_Int32 column) override; + css::uno::Reference<css::sdbc::XBlob> SAL_CALL getBlob(sal_Int32 column) override; + css::uno::Reference<css::sdbc::XClob> SAL_CALL getClob(sal_Int32 column) override; + css::uno::Reference<css::sdbc::XArray> SAL_CALL getArray(sal_Int32 column) override; + + // XResultSetMetaDataSupplier + css::uno::Reference<css::sdbc::XResultSetMetaData> SAL_CALL getMetaData() override; + + // XCancellable + void SAL_CALL cancel() override; + + // XCloseable + void SAL_CALL close() override; + + // XWarningsSupplier + Any SAL_CALL getWarnings() override; + + void SAL_CALL clearWarnings() override; + + // XResultSetUpdate + void SAL_CALL insertRow() override; + void SAL_CALL updateRow() override; + void SAL_CALL deleteRow() override; + void SAL_CALL cancelRowUpdates() override; + void SAL_CALL moveToInsertRow() override; + void SAL_CALL moveToCurrentRow() override; + + // XRowUpdate + void SAL_CALL updateNull(sal_Int32 column) override; + void SAL_CALL updateBoolean(sal_Int32 column, sal_Bool x) override; + void SAL_CALL updateByte(sal_Int32 column, sal_Int8 x) override; + void SAL_CALL updateShort(sal_Int32 column, sal_Int16 x) override; + void SAL_CALL updateInt(sal_Int32 column, sal_Int32 x) override; + void SAL_CALL updateLong(sal_Int32 column, sal_Int64 x) override; + void SAL_CALL updateFloat(sal_Int32 column, float x) override; + void SAL_CALL updateDouble(sal_Int32 column, double x) override; + void SAL_CALL updateString(sal_Int32 column, const OUString& x) override; + void SAL_CALL updateBytes(sal_Int32 column, const css::uno::Sequence<sal_Int8>& x) override; + void SAL_CALL updateDate(sal_Int32 column, const css::util::Date& x) override; + void SAL_CALL updateTime(sal_Int32 column, const css::util::Time& x) override; + void SAL_CALL updateTimestamp(sal_Int32 column, const css::util::DateTime& x) override; + void SAL_CALL updateBinaryStream(sal_Int32 column, + const css::uno::Reference<css::io::XInputStream>& x, + sal_Int32 length) override; + void SAL_CALL updateCharacterStream(sal_Int32 column, + const css::uno::Reference<css::io::XInputStream>& x, + sal_Int32 length) override; + void SAL_CALL updateObject(sal_Int32 column, const Any& x) override; + void SAL_CALL updateNumericObject(sal_Int32 column, const Any& x, sal_Int32 scale) override; + + // XColumnLocate + sal_Int32 SAL_CALL findColumn(const OUString& columnName) override; + + // XRowLocate + Any SAL_CALL getBookmark() override; + + sal_Bool SAL_CALL moveToBookmark(const Any& bookmark) override; + sal_Bool SAL_CALL moveRelativeToBookmark(const Any& bookmark, sal_Int32 rows) override; + sal_Int32 SAL_CALL compareBookmarks(const Any& first, const Any& second) override; + sal_Bool SAL_CALL hasOrderedBookmarks() override; + sal_Int32 SAL_CALL hashBookmark(const Any& bookmark) override; + + // XDeleteRows + css::uno::Sequence<sal_Int32> SAL_CALL deleteRows(const css::uno::Sequence<Any>& rows) override; + + /// @throws SQLException + /// @throws RuntimeException + void checkColumnIndex(sal_Int32 index); + +private: + using ::cppu::OPropertySetHelper::getFastPropertyValue; +}; +} /* mysqlc */ +} /* connectivity */ +#endif // CONNECTIVITY_SRESULTSET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_preparedstatement.cxx b/connectivity/source/drivers/mysqlc/mysqlc_preparedstatement.cxx new file mode 100644 index 000000000..2b344843d --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_preparedstatement.cxx @@ -0,0 +1,578 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "mysqlc_general.hxx" +#include "mysqlc_prepared_resultset.hxx" +#include "mysqlc_preparedstatement.hxx" +#include "mysqlc_propertyids.hxx" +#include "mysqlc_resultsetmetadata.hxx" + +#include <sal/log.hxx> + +#include <com/sun/star/sdbc/DataType.hpp> + +#include <stdio.h> + +using namespace connectivity::mysqlc; +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::container; +using namespace com::sun::star::io; +using namespace com::sun::star::util; +using ::osl::MutexGuard; + +OUString OPreparedStatement::getImplementationName() +{ + return "com.sun.star.sdbcx.mysqlc.PreparedStatement"; +} + +css::uno::Sequence<OUString> OPreparedStatement::getSupportedServiceNames() +{ + return { "com.sun.star.sdbc.PreparedStatement" }; +} + +sal_Bool OPreparedStatement::supportsService(OUString const& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +OPreparedStatement::OPreparedStatement(OConnection* _pConnection, MYSQL_STMT* pStmt) + : OCommonStatement(_pConnection) + , m_pStmt(pStmt) +{ + m_paramCount = mysql_stmt_param_count(m_pStmt); + m_binds.reserve(m_paramCount); + m_bindMetas.reserve(m_paramCount); + for (unsigned i = 0; i < m_paramCount; ++i) + { + m_binds.push_back(MYSQL_BIND{}); + m_bindMetas.push_back(BindMetaData{}); + m_binds.back().is_null = &m_bindMetas.back().is_null; + m_binds.back().length = &m_bindMetas.back().length; + m_binds.back().buffer = nullptr; + } +} + +OPreparedStatement::~OPreparedStatement() {} + +void SAL_CALL OPreparedStatement::acquire() throw() { OCommonStatement::acquire(); } + +void SAL_CALL OPreparedStatement::release() throw() { OCommonStatement::release(); } + +Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType) +{ + Any aRet = OCommonStatement::queryInterface(rType); + if (!aRet.hasValue()) + { + aRet = OPreparedStatement_BASE::queryInterface(rType); + } + return aRet; +} + +Sequence<Type> SAL_CALL OPreparedStatement::getTypes() +{ + return concatSequences(OPreparedStatement_BASE::getTypes(), OCommonStatement::getTypes()); +} + +Reference<XResultSetMetaData> SAL_CALL OPreparedStatement::getMetaData() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + + if (!m_xMetaData.is()) + { + MYSQL_RES* pRes = mysql_stmt_result_metadata(m_pStmt); + // TODO warning or error if no meta data. + m_xMetaData = new OResultSetMetaData(*m_xConnection, pRes); + } + return m_xMetaData; +} + +void SAL_CALL OPreparedStatement::close() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + + if (mysql_stmt_close(m_pStmt)) + { + SAL_WARN("connectivity.mysqlc", "failed to close mysql prepared statement"); + } + m_pStmt = nullptr; // it's deallocated already + clearWarnings(); + clearParameters(); + OCommonStatement::close(); +} + +sal_Bool SAL_CALL OPreparedStatement::execute() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + + if (!m_binds.empty() && mysql_stmt_bind_param(m_pStmt, m_binds.data())) + { + MYSQL* pMysql = m_xConnection->getMysqlConnection(); + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt), + mysql_sqlstate(pMysql), mysql_errno(pMysql), + *this, m_xConnection->getConnectionEncoding()); + } + + int nFail = mysql_stmt_execute(m_pStmt); + if (nFail != 0) + { + MYSQL* pMysql = m_xConnection->getMysqlConnection(); + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt), + mysql_sqlstate(pMysql), mysql_errno(pMysql), + *this, m_xConnection->getConnectionEncoding()); + } + + return !nFail; +} + +sal_Int32 SAL_CALL OPreparedStatement::executeUpdate() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + + if (!m_binds.empty() && mysql_stmt_bind_param(m_pStmt, m_binds.data())) + { + MYSQL* pMysql = m_xConnection->getMysqlConnection(); + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt), + mysql_sqlstate(pMysql), mysql_errno(pMysql), + *this, m_xConnection->getConnectionEncoding()); + } + + int nFail = mysql_stmt_execute(m_pStmt); + + if (nFail != 0) + { + MYSQL* pMysql = m_xConnection->getMysqlConnection(); + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt), + mysql_sqlstate(pMysql), mysql_errno(pMysql), + *this, m_xConnection->getConnectionEncoding()); + } + + sal_Int32 affectedRows = mysql_stmt_affected_rows(m_pStmt); + return affectedRows; +} + +void SAL_CALL OPreparedStatement::setString(sal_Int32 parameter, const OUString& x) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + OString stringie(OUStringToOString(x, m_xConnection->getConnectionEncoding())); + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_STRING; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, stringie.getStr(), MYSQL_TYPE_STRING, + stringie.getLength()); + m_bindMetas[nIndex].is_null = false; + m_bindMetas[nIndex].length = stringie.getLength(); +} + +Reference<XConnection> SAL_CALL OPreparedStatement::getConnection() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + + return m_xConnection.get(); +} + +Reference<XResultSet> SAL_CALL OPreparedStatement::executeQuery() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + + if (!m_binds.empty() && mysql_stmt_bind_param(m_pStmt, m_binds.data())) + { + MYSQL* pMysql = m_xConnection->getMysqlConnection(); + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt), + mysql_sqlstate(pMysql), mysql_errno(pMysql), + *this, m_xConnection->getConnectionEncoding()); + } + + int nFail = mysql_stmt_execute(m_pStmt); + + if (nFail != 0) + { + MYSQL* pMysql = m_xConnection->getMysqlConnection(); + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt), + mysql_sqlstate(pMysql), mysql_errno(pMysql), + *this, m_xConnection->getConnectionEncoding()); + } + + Reference<XResultSet> xResultSet = new OPreparedResultSet(*m_xConnection, this, m_pStmt); + return xResultSet; +} + +void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 parameter, sal_Bool x) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_TINY; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_TINY); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setByte(sal_Int32 parameter, sal_Int8 x) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_TINY; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_TINY); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setDate(sal_Int32 parameter, const Date& aData) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + MYSQL_TIME my_time = {}; + + my_time.year = aData.Year; + my_time.month = aData.Month; + my_time.day = aData.Day; + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_DATE; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &my_time, MYSQL_TYPE_DATE); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setTime(sal_Int32 parameter, const Time& aVal) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + MYSQL_TIME my_time = {}; + + my_time.hour = aVal.Hours; + my_time.minute = aVal.Minutes; + my_time.second = aVal.Seconds; + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_TIME; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &my_time, MYSQL_TYPE_TIME); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 parameter, const DateTime& aVal) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + MYSQL_TIME my_time = {}; + + my_time.hour = aVal.Hours; + my_time.minute = aVal.Minutes; + my_time.second = aVal.Seconds; + my_time.year = aVal.Year; + my_time.month = aVal.Month; + my_time.day = aVal.Day; + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_DATETIME; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &my_time, MYSQL_TYPE_DATETIME); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setDouble(sal_Int32 parameter, double x) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_DOUBLE; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_DOUBLE); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setFloat(sal_Int32 parameter, float x) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_FLOAT; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_FLOAT); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setInt(sal_Int32 parameter, sal_Int32 x) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_LONG; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_LONG); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setLong(sal_Int32 parameter, sal_Int64 aVal) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_LONGLONG; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &aVal, MYSQL_TYPE_LONGLONG); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setNull(sal_Int32 parameter, sal_Int32 /*sqlType*/) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_bindMetas[nIndex].is_null = true; + free(m_binds[nIndex].buffer); + m_binds[nIndex].buffer = nullptr; +} + +void SAL_CALL OPreparedStatement::setClob(sal_Int32 parameter, const Reference<XClob>& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setClob", *this); +} + +void SAL_CALL OPreparedStatement::setBlob(sal_Int32 parameter, const Reference<XBlob>& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setBlob", *this); +} + +void SAL_CALL OPreparedStatement::setArray(sal_Int32 parameter, const Reference<XArray>& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setArray", *this); +} + +void SAL_CALL OPreparedStatement::setRef(sal_Int32 parameter, const Reference<XRef>& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setRef", *this); +} + +void SAL_CALL OPreparedStatement::setObjectWithInfo(sal_Int32 parameterIndex, const Any& value, + sal_Int32 targetSqlType, sal_Int32 /* scale */) +{ + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + checkParameterIndex(parameterIndex); + + const sal_Int32 nIndex = parameterIndex - 1; + if (!value.hasValue()) + { + free(m_binds[nIndex].buffer); + m_binds[nIndex].buffer = nullptr; + m_bindMetas[parameterIndex - 1].is_null = true; + return; + } + + switch (targetSqlType) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + { + double nValue(0.0); + OUString sValue; + if (value >>= nValue) + { + setDouble(parameterIndex, nValue); + break; + } + else if (value >>= sValue) + { + OString sAscii + = OUStringToOString(sValue, getOwnConnection()->getConnectionEncoding()); + std::stringstream sStream{ sAscii.getStr() }; + sStream >> nValue; + m_binds[nIndex].buffer_type = MYSQL_TYPE_DOUBLE; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &nValue, MYSQL_TYPE_DOUBLE, + sValue.getLength()); + m_bindMetas[nIndex].is_null = false; + break; + } + + [[fallthrough]]; + } + + // TODO other types + + default: + mysqlc_sdbc_driver::throwInvalidArgumentException( + "OPreparedStatement::setObjectWithInfo", *this); + break; + } +} + +void SAL_CALL OPreparedStatement::setObjectNull(sal_Int32 parameter, sal_Int32 /* sqlType */, + const OUString& /* typeName */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setObjectNull", + *this); +} + +void SAL_CALL OPreparedStatement::setObject(sal_Int32 parameter, const Any& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setObject", *this); +} + +void SAL_CALL OPreparedStatement::setShort(sal_Int32 parameter, sal_Int16 x) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_SHORT; + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_SHORT); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setBytes(sal_Int32 parameter, const Sequence<sal_Int8>& x) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + const sal_Int32 nIndex = parameter - 1; + m_binds[nIndex].buffer_type = MYSQL_TYPE_BLOB; // FIXME + mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_BLOB); + m_bindMetas[nIndex].is_null = false; +} + +void SAL_CALL OPreparedStatement::setCharacterStream(sal_Int32 parameter, + const Reference<XInputStream>& /* x */, + sal_Int32 /* length */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException( + "OPreparedStatement::setCharacterStream", *this); +} + +void SAL_CALL OPreparedStatement::setBinaryStream(sal_Int32 parameter, + const Reference<XInputStream>& /* x */, + sal_Int32 /* length */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + checkParameterIndex(parameter); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setBinaryStream", + *this); +} + +void SAL_CALL OPreparedStatement::clearParameters() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedStatement::rBHelper.bDisposed); + + for (size_t i = 0; i < m_binds.size(); ++i) + { + m_bindMetas[i].is_null = true; + free(m_binds[i].buffer); + m_binds[i].buffer = nullptr; + } +} + +// void SAL_CALL OPreparedStatement::clearBatch() +// { +// mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::clearBatch", +// *this); +// } + +// void SAL_CALL OPreparedStatement::addBatch() +// { +// mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::addBatch", *this); +// } + +// Sequence<sal_Int32> SAL_CALL OPreparedStatement::executeBatch() { +// mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::executeBatch", *this); +// } + +void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue) +{ + switch (nHandle) + { + case PROPERTY_ID_RESULTSETCONCURRENCY: + break; + case PROPERTY_ID_RESULTSETTYPE: + break; + case PROPERTY_ID_FETCHDIRECTION: + break; + case PROPERTY_ID_USEBOOKMARKS: + break; + default: + /* XXX: Recursion ?? */ + OPreparedStatement::setFastPropertyValue_NoBroadcast(nHandle, rValue); + } +} + +void OPreparedStatement::checkParameterIndex(sal_Int32 column) +{ + if (column < 1 || column > static_cast<sal_Int32>(m_paramCount)) + { + throw SQLException("Parameter index out of range", *this, OUString(), 1, Any()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_preparedstatement.hxx b/connectivity/source/drivers/mysqlc/mysqlc_preparedstatement.hxx new file mode 100644 index 000000000..0177b15db --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_preparedstatement.hxx @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_PREPAREDSTATEMENT_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_PREPAREDSTATEMENT_HXX +#include "mysqlc_statement.hxx" +#include "mysqlc_resultset.hxx" + +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XPreparedBatchExecution.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <cppuhelper/compbase4.hxx> + +namespace connectivity +{ +namespace mysqlc +{ +using ::com::sun::star::sdbc::SQLException; +using ::com::sun::star::sdbc::XResultSetMetaData; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::Type; + +#if defined MYSQL_VERSION_ID && (MYSQL_VERSION_ID >= 80000) && !defined MARIADB_BASE_VERSION +using my_bool = bool; +#else +using my_bool = char; +#endif + +struct BindMetaData +{ + my_bool is_null = false; + unsigned long length = 0; + my_bool error = false; +}; + +typedef ::cppu::ImplHelper4<css::sdbc::XPreparedStatement, css::sdbc::XParameters, + css::sdbc::XResultSetMetaDataSupplier, css::lang::XServiceInfo> + OPreparedStatement_BASE; + +class OPreparedStatement final : public OCommonStatement, public OPreparedStatement_BASE +{ + unsigned int m_paramCount = 0; // number of placeholders + Reference<XResultSetMetaData> m_xMetaData; + MYSQL_STMT* m_pStmt; + std::vector<MYSQL_BIND> m_binds; + std::vector<BindMetaData> m_bindMetas; + + void checkParameterIndex(sal_Int32 parameter); + + void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue) override; + virtual ~OPreparedStatement() override; + +public: + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + OPreparedStatement(OConnection* _pConnection, MYSQL_STMT* pStmt); + + //XInterface + Any SAL_CALL queryInterface(const Type& rType) override; + void SAL_CALL acquire() throw() override; + void SAL_CALL release() throw() override; + + //XTypeProvider + css::uno::Sequence<Type> SAL_CALL getTypes() override; + + // XPreparedStatement + Reference<css::sdbc::XResultSet> SAL_CALL executeQuery() override; + sal_Int32 SAL_CALL executeUpdate() override; + sal_Bool SAL_CALL execute() override; + Reference<css::sdbc::XConnection> SAL_CALL getConnection() override; + + // XParameters + void SAL_CALL setNull(sal_Int32 parameter, sal_Int32 sqlType) override; + + void SAL_CALL setObjectNull(sal_Int32 parameter, sal_Int32 sqlType, + const OUString& typeName) override; + + void SAL_CALL setBoolean(sal_Int32 parameter, sal_Bool x) override; + + void SAL_CALL setByte(sal_Int32 parameter, sal_Int8 x) override; + + void SAL_CALL setShort(sal_Int32 parameter, sal_Int16 x) override; + + void SAL_CALL setInt(sal_Int32 parameter, sal_Int32 x) override; + + void SAL_CALL setLong(sal_Int32 parameter, sal_Int64 x) override; + + void SAL_CALL setFloat(sal_Int32 parameter, float x) override; + + void SAL_CALL setDouble(sal_Int32 parameter, double x) override; + + void SAL_CALL setString(sal_Int32 parameter, const OUString& x) override; + + void SAL_CALL setBytes(sal_Int32 parameter, const css::uno::Sequence<sal_Int8>& x) override; + + void SAL_CALL setDate(sal_Int32 parameter, const css::util::Date& x) override; + + void SAL_CALL setTime(sal_Int32 parameter, const css::util::Time& x) override; + void SAL_CALL setTimestamp(sal_Int32 parameter, const css::util::DateTime& x) override; + + void SAL_CALL setBinaryStream(sal_Int32 parameter, const Reference<css::io::XInputStream>& x, + sal_Int32 length) override; + + void SAL_CALL setCharacterStream(sal_Int32 parameter, const Reference<css::io::XInputStream>& x, + sal_Int32 length) override; + + void SAL_CALL setObject(sal_Int32 parameter, const Any& x) override; + + void SAL_CALL setObjectWithInfo(sal_Int32 parameter, const Any& x, sal_Int32 targetSqlType, + sal_Int32 scale) override; + + void SAL_CALL setRef(sal_Int32 parameter, const Reference<css::sdbc::XRef>& x) override; + + void SAL_CALL setBlob(sal_Int32 parameter, const Reference<css::sdbc::XBlob>& x) override; + + void SAL_CALL setClob(sal_Int32 parameter, const Reference<css::sdbc::XClob>& x) override; + + void SAL_CALL setArray(sal_Int32 parameter, const Reference<css::sdbc::XArray>& x) override; + + void SAL_CALL clearParameters() override; + + // XPreparedBatchExecution + // void SAL_CALL addBatch() override; + // void SAL_CALL clearBatch() override; + // css::uno::Sequence<sal_Int32> SAL_CALL executeBatch() override; + + // XCloseable + void SAL_CALL close() override; + + // XResultSetMetaDataSupplier + Reference<css::sdbc::XResultSetMetaData> SAL_CALL getMetaData() override; +}; +} /* mysqlc */ +} /* connectivity */ +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_PREPAREDSTATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_propertyids.hxx b/connectivity/source/drivers/mysqlc/mysqlc_propertyids.hxx new file mode 100644 index 000000000..b2c1a1be0 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_propertyids.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_PROPERTYIDS_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_PROPERTYIDS_HXX + +// this define has to be set to split the names into different dll's or so's +// every dll has his own set of property names + +namespace connectivity +{ +namespace mysqlc +{ +enum +{ + PROPERTY_ID_QUERYTIMEOUT = 1, + PROPERTY_ID_MAXFIELDSIZE, + PROPERTY_ID_MAXROWS, + PROPERTY_ID_CURSORNAME, + PROPERTY_ID_RESULTSETCONCURRENCY, + PROPERTY_ID_RESULTSETTYPE, + PROPERTY_ID_FETCHDIRECTION, + PROPERTY_ID_FETCHSIZE, + PROPERTY_ID_ESCAPEPROCESSING, + PROPERTY_ID_USEBOOKMARKS, + PROPERTY_ID_ISBOOKMARKABLE +}; +} /* mysqlc */ +} /* connectivity */ + +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_PROPERTYIDS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_resultset.cxx b/connectivity/source/drivers/mysqlc/mysqlc_resultset.cxx new file mode 100644 index 000000000..75c229823 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_resultset.cxx @@ -0,0 +1,1114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "mysqlc_propertyids.hxx" +#include "mysqlc_general.hxx" +#include "mysqlc_resultset.hxx" +#include "mysqlc_resultsetmetadata.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#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/sdbcx/CompareBookmark.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/seqstream.hxx> + +using namespace rtl; + +using namespace connectivity::mysqlc; +using namespace cppu; +using namespace com::sun::star; +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::io; +using namespace com::sun::star::uno; +using namespace com::sun::star::util; +using namespace ::comphelper; +using ::osl::MutexGuard; + +namespace +{ +// copied from string misc, it should be replaced when library is not an +// extension anymore +std::vector<OString> lcl_split(const OString& rStr, char cSeparator) +{ + std::vector<OString> vec; + sal_Int32 idx = 0; + do + { + OString kw = rStr.getToken(0, cSeparator, idx); + kw = kw.trim(); + if (!kw.isEmpty()) + { + vec.push_back(kw); + } + } while (idx >= 0); + return vec; +} +} + +void OResultSet::checkRowIndex() +{ + if (m_nRowPosition < 0 || m_nRowPosition >= m_nRowCount) + { + throw SQLException("Cursor position out of range", *this, OUString(), 1, Any()); + } +} + +bool OResultSet::checkNull(sal_Int32 column) +{ + if (m_aRows[m_nRowPosition][column - 1].isEmpty()) + { + m_bWasNull = true; + return true; + } + m_bWasNull = false; + return false; +} + +OUString SAL_CALL OResultSet::getImplementationName() +{ + return "com.sun.star.sdbcx.mysqlc.ResultSet"; +} + +uno::Sequence<OUString> SAL_CALL OResultSet::getSupportedServiceNames() +{ + return { "com.sun.star.sdbc.ResultSet", "com.sun.star.sdbcx.ResultSet" }; +} + +sal_Bool SAL_CALL OResultSet::supportsService(const OUString& _rServiceName) +{ + return cppu::supportsService(this, _rServiceName); +} + +OResultSet::OResultSet(OConnection& rConn, OCommonStatement* pStmt, MYSQL_RES* pResult, + rtl_TextEncoding _encoding) + : OResultSet_BASE(m_aMutex) + , OPropertySetHelper(OResultSet_BASE::rBHelper) + , m_pMysql(rConn.getMysqlConnection()) + , m_aStatement(static_cast<OWeakObject*>(pStmt)) + , m_pResult(pResult) + , m_encoding(_encoding) +{ + assert(m_pResult); + m_xMetaData = new OResultSetMetaData(rConn, m_pResult); +} + +void OResultSet::ensureResultFetched() +{ + if (m_pResult) + { + fetchResult(); + } +} + +void OResultSet::ensureFieldInfoFetched() +{ + if (m_pResult == nullptr) + return; // already fetched + + // it works only if result set is produced via mysql_store_result + // TODO ensure that + m_nRowCount = mysql_num_rows(m_pResult); + + if (!m_aFields.empty()) + return; + unsigned nFieldCount = mysql_num_fields(m_pResult); + MYSQL_FIELD* pFields = mysql_fetch_fields(m_pResult); + m_aFields.reserve(nFieldCount); + for (unsigned i = 0; i < nFieldCount; ++i) + m_aFields.push_back(OUString{ + pFields[i].name, static_cast<sal_Int32>(strlen(pFields[i].name)), m_encoding }); +} + +void OResultSet::fetchResult() +{ + // Mysql C API does not allow simultaneously opened result sets, but sdbc does. + // Because of that we need to fetch all of the data ASAP + ensureFieldInfoFetched(); + + // fetch all the data + m_aRows.reserve(m_nRowCount); + + for (sal_Int32 row = 0; row < m_nRowCount; ++row) + { + MYSQL_ROW data = mysql_fetch_row(m_pResult); + unsigned long* lengths = mysql_fetch_lengths(m_pResult); + m_aRows.push_back(DataFields{}); + // MYSQL_ROW is char**, array of strings + for (std::size_t col = 0; col < m_aFields.size(); ++col) + { + m_aRows.back().push_back(OString{ data[col], static_cast<sal_Int32>(lengths[col]) }); + } + } + unsigned errorNum = mysql_errno(m_pMysql); + if (errorNum) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg( + mysql_error(m_pMysql), mysql_sqlstate(m_pMysql), errorNum, *this, m_encoding); + mysql_free_result(m_pResult); + m_pResult = nullptr; +} + +void OResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + MutexGuard aGuard(m_aMutex); + + if (m_pResult != nullptr) + { + mysql_free_result(m_pResult); + m_pResult = nullptr; + } + m_aStatement = nullptr; + m_xMetaData = nullptr; +} + +Any SAL_CALL OResultSet::queryInterface(const Type& rType) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + if (!aRet.hasValue()) + { + aRet = OResultSet_BASE::queryInterface(rType); + } + return aRet; +} + +uno::Sequence<Type> SAL_CALL OResultSet::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get()); + + return concatSequences(aTypes.getTypes(), OResultSet_BASE::getTypes()); +} + +sal_Int32 SAL_CALL OResultSet::findColumn(const OUString& columnName) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + + for (std::size_t i = 0; i < m_aFields.size(); ++i) + { + if (columnName.equalsIgnoreAsciiCase(m_aFields[i])) + return static_cast<sal_Int32>(i) + 1; // sdbc indexes from 1 + } + + throw SQLException("The column name '" + columnName + "' is not valid.", *this, "42S22", 0, + Any()); +} + +uno::Reference<XInputStream> SAL_CALL OResultSet::getBinaryStream(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + if (checkNull(column)) + return nullptr; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + return new SequenceInputStream{ uno::Sequence<sal_Int8>( + reinterpret_cast<sal_Int8 const*>(sVal.getStr()), getDataLength(column)) }; +} + +uno::Reference<XInputStream> SAL_CALL OResultSet::getCharacterStream(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::getCharacterStream", + *this); + return nullptr; +} + +sal_Bool SAL_CALL OResultSet::getBoolean(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + if (checkNull(column)) + return false; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + return sVal.toInt32() != 0; +} + +sal_Int8 SAL_CALL OResultSet::getByte(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + if (checkNull(column)) + return 0; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + + return static_cast<sal_Int8>(sVal.toInt32()); +} + +uno::Sequence<sal_Int8> SAL_CALL OResultSet::getBytes(sal_Int32 column) +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + checkBordersAndEnsureFetched(column); + OString sVal = m_aRows[m_nRowPosition][column - 1]; + if (checkNull(column)) + return uno::Sequence<sal_Int8>(); + + return uno::Sequence<sal_Int8>(reinterpret_cast<sal_Int8 const*>(sVal.getStr()), + getDataLength(column)); +} + +Date SAL_CALL OResultSet::getDate(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + + Date d; + + if (checkNull(column)) + return d; + + OString dateStr = m_aRows[m_nRowPosition][column - 1]; + + OString dateString(dateStr); + OString token; + sal_Int32 nIndex = 0, i = 0; + do + { + token = dateString.getToken(0, '-', nIndex); + switch (i) + { + case 0: + d.Year = static_cast<sal_uInt16>(token.toUInt32()); + break; + case 1: + d.Month = static_cast<sal_uInt16>(token.toUInt32()); + break; + case 2: + d.Day = static_cast<sal_uInt16>(token.toUInt32()); + break; + default:; + } + i++; + } while (nIndex >= 0); + return d; +} + +double SAL_CALL OResultSet::getDouble(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + + if (checkNull(column)) + return 0.0; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + return sVal.toDouble(); +} + +float SAL_CALL OResultSet::getFloat(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + if (checkNull(column)) + return 0.0f; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + return sVal.toFloat(); +} + +sal_Int32 SAL_CALL OResultSet::getInt(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + if (checkNull(column)) + return 0; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + return sVal.toInt32(); +} + +sal_Int32 SAL_CALL OResultSet::getRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nRowPosition + 1; // indexed from 1 +} + +sal_Int64 SAL_CALL OResultSet::getLong(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + if (checkNull(column)) + return 0LL; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + return sVal.toInt64(); +} + +uno::Reference<XResultSetMetaData> SAL_CALL OResultSet::getMetaData() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + return m_xMetaData; +} + +uno::Reference<XArray> SAL_CALL OResultSet::getArray(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::getArray", *this); + return nullptr; +} + +uno::Reference<XClob> SAL_CALL OResultSet::getClob(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::getClob", *this); + return nullptr; +} + +uno::Reference<XBlob> SAL_CALL OResultSet::getBlob(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::getBlob", *this); + return nullptr; +} + +uno::Reference<XRef> SAL_CALL OResultSet::getRef(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::getRef", *this); + return nullptr; +} + +Any SAL_CALL OResultSet::getObject(sal_Int32 column, + const uno::Reference<XNameAccess>& /* typeMap */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::getObject", *this); + return Any(); +} + +sal_Int16 SAL_CALL OResultSet::getShort(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + if (checkNull(column)) + return 0; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + return sVal.toInt32(); +} + +OUString SAL_CALL OResultSet::getString(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkBordersAndEnsureFetched(column); + if (checkNull(column)) + return rtl::OUString{}; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + return OStringToOUString(sVal, m_encoding); +} + +Time SAL_CALL OResultSet::getTime(sal_Int32 column) +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + checkBordersAndEnsureFetched(column); + + Time t; + if (checkNull(column)) + return t; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + OString timeString{ sVal.getStr(), getDataLength(column) }; + OString token; + sal_Int32 nIndex, i = 0; + + nIndex = timeString.indexOf(' ') + 1; + do + { + token = timeString.getToken(0, ':', nIndex); + switch (i) + { + case 0: + t.Hours = static_cast<sal_uInt16>(token.toUInt32()); + break; + case 1: + t.Minutes = static_cast<sal_uInt16>(token.toUInt32()); + break; + case 2: + t.Seconds = static_cast<sal_uInt16>(token.toUInt32()); + break; + } + i++; + } while (nIndex >= 0); + + return t; +} + +DateTime SAL_CALL OResultSet::getTimestamp(sal_Int32 column) +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + checkBordersAndEnsureFetched(column); + + if (checkNull(column)) + return DateTime{}; + + OString sVal = m_aRows[m_nRowPosition][column - 1]; + + // YY-MM-DD HH:MM:SS + std::vector<OString> dateAndTime + = lcl_split(OString{ sVal.getStr(), getDataLength(column) }, ' '); + + auto dateParts = lcl_split(dateAndTime.at(0), '-'); + auto timeParts = lcl_split(dateAndTime.at(1), ':'); + + if (dateParts.size() < 2 || timeParts.size() < 2) + throw SQLException("Timestamp has a wrong format", *this, OUString(), 1, Any()); + + DateTime dt; + + dt.Year = dateParts.at(0).toUInt32(); + dt.Month = dateParts.at(1).toUInt32(); + dt.Day = dateParts.at(2).toUInt32(); + dt.Hours = timeParts.at(0).toUInt32(); + dt.Minutes = timeParts.at(1).toUInt32(); + dt.Seconds = timeParts.at(2).toUInt32(); + return dt; +} + +sal_Bool SAL_CALL OResultSet::isBeforeFirst() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nRowPosition < 0; +} + +sal_Bool SAL_CALL OResultSet::isAfterLast() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + + return m_nRowPosition >= m_nRowCount; +} + +sal_Bool SAL_CALL OResultSet::isFirst() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + + return m_nRowPosition == 0 && !isAfterLast(); +} + +sal_Bool SAL_CALL OResultSet::isLast() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + + return m_nRowPosition == m_nRowCount - 1; +} + +void SAL_CALL OResultSet::beforeFirst() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + m_nRowPosition = -1; +} + +void SAL_CALL OResultSet::afterLast() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + m_nRowPosition = m_nRowCount; +} + +void SAL_CALL OResultSet::close() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if (m_pResult != nullptr) + { + mysql_free_result(m_pResult); + m_pResult = nullptr; + } + dispose(); +} + +sal_Bool SAL_CALL OResultSet::first() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + m_nRowPosition = 0; + + return true; +} + +sal_Bool SAL_CALL OResultSet::last() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + m_nRowPosition = m_nRowCount - 1; + + return true; +} + +sal_Bool SAL_CALL OResultSet::absolute(sal_Int32 row) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + + sal_Int32 nToGo = row < 0 ? (m_nRowCount - 1) - row : row - 1; + + if (nToGo >= m_nRowCount) + nToGo = m_nRowCount - 1; + if (nToGo < 0) + nToGo = 0; + + m_nRowPosition = nToGo; + + return true; +} + +sal_Bool SAL_CALL OResultSet::relative(sal_Int32 row) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + + if (row == 0) + return true; + + sal_Int32 nToGo = m_nRowPosition + row; + if (nToGo >= m_nRowCount) + nToGo = m_nRowCount - 1; + if (nToGo < 0) + nToGo = 0; + + m_nRowPosition = nToGo; + + return true; +} + +sal_Bool SAL_CALL OResultSet::previous() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if (m_nRowPosition == 0) + { + m_nRowPosition--; + return false; + } + else if (m_nRowPosition < 0) + { + return false; + } + + m_nRowPosition--; + return true; +} + +uno::Reference<uno::XInterface> SAL_CALL OResultSet::getStatement() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_aStatement.get(); +} + +sal_Bool SAL_CALL OResultSet::rowDeleted() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OResultSet::rowInserted() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OResultSet::rowUpdated() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OResultSet::next() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + ensureFieldInfoFetched(); + if (m_nRowPosition + 1 > m_nRowCount) // afterlast + return false; + if (m_nRowPosition + 1 == m_nRowCount) // last + { + // return false but take it to afterlast anyway + ++m_nRowPosition; + return false; + } + ++m_nRowPosition; + return true; +} + +sal_Bool SAL_CALL OResultSet::wasNull() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_bWasNull; +} + +void SAL_CALL OResultSet::cancel() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL OResultSet::clearWarnings() {} + +Any SAL_CALL OResultSet::getWarnings() { return Any(); } + +void SAL_CALL OResultSet::insertRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + // you only have to implement this if you want to insert new rows + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::insertRow", *this); +} + +void SAL_CALL OResultSet::updateRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + // only when you allow updates + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateRow", *this); +} + +void SAL_CALL OResultSet::deleteRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::deleteRow", *this); +} + +void SAL_CALL OResultSet::cancelRowUpdates() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::cancelRowUpdates", *this); +} + +void SAL_CALL OResultSet::moveToInsertRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + // only when you allow insert's + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::moveToInsertRow", *this); +} + +void SAL_CALL OResultSet::moveToCurrentRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); +} + +void SAL_CALL OResultSet::updateNull(sal_Int32 column) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateNull", *this); +} + +void SAL_CALL OResultSet::updateBoolean(sal_Int32 column, sal_Bool /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateBoolean", *this); +} + +void SAL_CALL OResultSet::updateByte(sal_Int32 column, sal_Int8 /* x */) +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateByte", *this); +} + +void SAL_CALL OResultSet::updateShort(sal_Int32 column, sal_Int16 /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateShort", *this); +} + +void SAL_CALL OResultSet::updateInt(sal_Int32 column, sal_Int32 /* x */) +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateInt", *this); +} + +void SAL_CALL OResultSet::updateLong(sal_Int32 column, sal_Int64 /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateLong", *this); +} + +void SAL_CALL OResultSet::updateFloat(sal_Int32 column, float /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateFloat", *this); +} + +void SAL_CALL OResultSet::updateDouble(sal_Int32 column, double /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateDouble", *this); +} + +void SAL_CALL OResultSet::updateString(sal_Int32 column, const OUString& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateString", *this); +} + +void SAL_CALL OResultSet::updateBytes(sal_Int32 column, const uno::Sequence<sal_Int8>& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateBytes", *this); +} + +void SAL_CALL OResultSet::updateDate(sal_Int32 column, const Date& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateDate", *this); +} + +void SAL_CALL OResultSet::updateTime(sal_Int32 column, const Time& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateTime", *this); +} + +void SAL_CALL OResultSet::updateTimestamp(sal_Int32 column, const DateTime& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateTimestamp", *this); +} + +void SAL_CALL OResultSet::updateBinaryStream(sal_Int32 column, + const uno::Reference<XInputStream>& /* x */, + sal_Int32 /* length */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateBinaryStream", + *this); +} + +void SAL_CALL OResultSet::updateCharacterStream(sal_Int32 column, + const uno::Reference<XInputStream>& /* x */, + sal_Int32 /* length */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateCharacterStream", + *this); +} + +void SAL_CALL OResultSet::refreshRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::refreshRow", *this); +} + +void SAL_CALL OResultSet::updateObject(sal_Int32 column, const Any& /* x */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateObject", *this); +} + +void SAL_CALL OResultSet::updateNumericObject(sal_Int32 column, const Any& /* x */, + sal_Int32 /* scale */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + checkColumnIndex(column); + checkRowIndex(); + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::updateNumericObject", + *this); +} + +// XRowLocate +Any SAL_CALL OResultSet::getBookmark() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + // if you don't want to support bookmark you must remove the XRowLocate interface + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::getBookmark", *this); + + return Any(); +} + +sal_Bool SAL_CALL OResultSet::moveToBookmark(const Any& /* bookmark */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return false; +} + +sal_Bool SAL_CALL OResultSet::moveRelativeToBookmark(const Any& /* bookmark */, + sal_Int32 /* rows */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::moveRelativeToBookmark", + *this); + return false; +} + +sal_Int32 SAL_CALL OResultSet::compareBookmarks(const Any& /* n1 */, const Any& /* n2 */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::compareBookmarks", *this); + + return CompareBookmark::NOT_EQUAL; +} + +sal_Bool SAL_CALL OResultSet::hasOrderedBookmarks() { return false; } + +sal_Int32 SAL_CALL OResultSet::hashBookmark(const Any& /* bookmark */) +{ + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::hashBookmark", *this); + return 0; +} + +// XDeleteRows +uno::Sequence<sal_Int32> SAL_CALL OResultSet::deleteRows(const uno::Sequence<Any>& /* rows */) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OResultSet::deleteRows", *this); + return uno::Sequence<sal_Int32>(); +} + +IPropertyArrayHelper* OResultSet::createArrayHelper() const +{ + uno::Sequence<Property> aProps(5); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = Property("FetchDirection", PROPERTY_ID_FETCHDIRECTION, + cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] + = Property("FetchSize", PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = Property("IsBookmarkable", PROPERTY_ID_ISBOOKMARKABLE, + cppu::UnoType<bool>::get(), PropertyAttribute::READONLY); + pProperties[nPos++] = Property("ResultSetConcurrency", PROPERTY_ID_RESULTSETCONCURRENCY, + cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + pProperties[nPos++] = Property("ResultSetType", PROPERTY_ID_RESULTSETTYPE, + cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + return new OPropertyArrayHelper(aProps); +} + +IPropertyArrayHelper& OResultSet::getInfoHelper() { return *getArrayHelper(); } + +sal_Bool OResultSet::convertFastPropertyValue(Any& /* rConvertedValue */, Any& /* rOldValue */, + sal_Int32 nHandle, const Any& /* rValue */) +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + default:; + } + return false; +} + +void OResultSet::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& /* rValue */) +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw uno::Exception("cannot set prop " + OUString::number(nHandle), nullptr); + case PROPERTY_ID_FETCHDIRECTION: + break; + case PROPERTY_ID_FETCHSIZE: + break; + default:; + } +} + +void OResultSet::getFastPropertyValue(Any& _rValue, sal_Int32 nHandle) const +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + _rValue <<= false; + break; + case PROPERTY_ID_CURSORNAME: + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + _rValue <<= ResultSetConcurrency::READ_ONLY; + break; + case PROPERTY_ID_RESULTSETTYPE: + _rValue <<= ResultSetType::SCROLL_INSENSITIVE; + break; + case PROPERTY_ID_FETCHDIRECTION: + _rValue <<= FetchDirection::FORWARD; + break; + case PROPERTY_ID_FETCHSIZE: + _rValue <<= sal_Int32(50); + break; + ; + default:; + } +} + +void SAL_CALL OResultSet::acquire() throw() { OResultSet_BASE::acquire(); } + +void SAL_CALL OResultSet::release() throw() { OResultSet_BASE::release(); } + +css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL OResultSet::getPropertySetInfo() +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +void OResultSet::checkColumnIndex(sal_Int32 index) +{ + if (index < 1 || index > static_cast<int>(m_aFields.size())) + { + /* static object for efficiency or thread safety is a problem ? */ + throw SQLException("index out of range", *this, OUString(), 1, Any()); + } +} + +void OResultSet::checkBordersAndEnsureFetched(sal_Int32 index) +{ + ensureResultFetched(); + checkColumnIndex(index); + checkRowIndex(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_resultset.hxx b/connectivity/source/drivers/mysqlc/mysqlc_resultset.hxx new file mode 100644 index 000000000..dca2bb4a9 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_resultset.hxx @@ -0,0 +1,279 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_RESULTSET_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_RESULTSET_HXX + +#include "mysqlc_preparedstatement.hxx" +#include "mysqlc_statement.hxx" +#include "mysqlc_subcomponent.hxx" +#include "mysqlc_connection.hxx" + +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbcx/XDeleteRows.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/util/XCancellable.hpp> + +#include <cppuhelper/compbase12.hxx> + +namespace connectivity +{ +namespace mysqlc +{ +using ::com::sun::star::sdbc::SQLException; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::RuntimeException; + +/* + ** OResultSet + */ +typedef ::cppu::WeakComponentImplHelper12< + css::sdbc::XResultSet, css::sdbc::XRow, css::sdbc::XResultSetMetaDataSupplier, + css::util::XCancellable, css::sdbc::XWarningsSupplier, css::sdbc::XResultSetUpdate, + css::sdbc::XRowUpdate, css::sdbcx::XRowLocate, css::sdbcx::XDeleteRows, css::sdbc::XCloseable, + css::sdbc::XColumnLocate, css::lang::XServiceInfo> + OResultSet_BASE; + +class OResultSet final : public OBase_Mutex, + public OResultSet_BASE, + public ::cppu::OPropertySetHelper, + public OPropertyArrayUsageHelper<OResultSet> +{ + using DataFields = std::vector<OString>; + std::vector<DataFields> m_aRows; + std::vector<OUString> m_aFields; + MYSQL* m_pMysql = nullptr; + css::uno::WeakReferenceHelper m_aStatement; + css::uno::Reference<css::sdbc::XResultSetMetaData> m_xMetaData; + MYSQL_RES* m_pResult; + rtl_TextEncoding m_encoding; + bool m_bWasNull = false; // did the last getXXX result null? + + sal_Int32 getDataLength(sal_Int32 column) + { + return m_aRows[m_nRowPosition][column - 1].getLength(); + } + bool checkNull(sal_Int32 column); + + /** + * Position of cursor indexed from 0 + */ + sal_Int32 m_nRowPosition = -1; + sal_Int32 m_nRowCount = 0; + + // OPropertyArrayUsageHelper + ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + // OPropertySetHelper + ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + sal_Bool SAL_CALL convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, + sal_Int32 nHandle, const Any& rValue) override; + + void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue) override; + + void SAL_CALL getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const override; + + virtual ~OResultSet() override = default; + + /** + * Ensures that the results of the query has already been fetched. + */ + void ensureResultFetched(); + + /** + * Ensures that meta data of the corresponding result set has been already + * queried. It should be called before freeing the result set, unless the + * information is lost. + */ + void ensureFieldInfoFetched(); + + /** + * Check the following things: + * - cursor is out of range. Throws exception if true. + * - column index is out of range. Throws exception if true. + * - result set is fetched. If no, then it fetches the result. + */ + void checkBordersAndEnsureFetched(sal_Int32 index); + + /** + * Fetches all the data from the MYSQL_RES object related to the class. It + * frees the MYSQL_RES object afterwards, so it cannot be used anymore. + */ + void fetchResult(); + +public: + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + OResultSet(OConnection& rConn, OCommonStatement* pStmt, MYSQL_RES* pResult, + rtl_TextEncoding _encoding); + + // ::cppu::OComponentHelper + void SAL_CALL disposing() override; + + // XInterface + Any SAL_CALL queryInterface(const css::uno::Type& rType) override; + + void SAL_CALL acquire() throw() override; + void SAL_CALL release() throw() override; + + //XTypeProvider + css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + + // XPropertySet + css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + + // XResultSet + sal_Bool SAL_CALL next() override; + sal_Bool SAL_CALL isBeforeFirst() override; + sal_Bool SAL_CALL isAfterLast() override; + sal_Bool SAL_CALL isFirst() override; + sal_Bool SAL_CALL isLast() override; + + void SAL_CALL beforeFirst() override; + void SAL_CALL afterLast() override; + + sal_Bool SAL_CALL first() override; + sal_Bool SAL_CALL last() override; + + sal_Int32 SAL_CALL getRow() override; + + sal_Bool SAL_CALL absolute(sal_Int32 row) override; + sal_Bool SAL_CALL relative(sal_Int32 rows) override; + sal_Bool SAL_CALL previous() override; + + void SAL_CALL refreshRow() override; + + sal_Bool SAL_CALL rowUpdated() override; + sal_Bool SAL_CALL rowInserted() override; + sal_Bool SAL_CALL rowDeleted() override; + + css::uno::Reference<css::uno::XInterface> SAL_CALL getStatement() override; + // XRow + sal_Bool SAL_CALL wasNull() override; + + OUString SAL_CALL getString(sal_Int32 column) override; + + sal_Bool SAL_CALL getBoolean(sal_Int32 column) override; + sal_Int8 SAL_CALL getByte(sal_Int32 column) override; + sal_Int16 SAL_CALL getShort(sal_Int32 column) override; + sal_Int32 SAL_CALL getInt(sal_Int32 column) override; + sal_Int64 SAL_CALL getLong(sal_Int32 column) override; + + float SAL_CALL getFloat(sal_Int32 column) override; + double SAL_CALL getDouble(sal_Int32 column) override; + + css::uno::Sequence<sal_Int8> SAL_CALL getBytes(sal_Int32 column) override; + css::util::Date SAL_CALL getDate(sal_Int32 column) override; + css::util::Time SAL_CALL getTime(sal_Int32 column) override; + css::util::DateTime SAL_CALL getTimestamp(sal_Int32 column) override; + + css::uno::Reference<css::io::XInputStream> SAL_CALL getBinaryStream(sal_Int32 column) override; + css::uno::Reference<css::io::XInputStream> + SAL_CALL getCharacterStream(sal_Int32 column) override; + + Any SAL_CALL getObject( + sal_Int32 column, const css::uno::Reference<css::container::XNameAccess>& typeMap) override; + + css::uno::Reference<css::sdbc::XRef> SAL_CALL getRef(sal_Int32 column) override; + css::uno::Reference<css::sdbc::XBlob> SAL_CALL getBlob(sal_Int32 column) override; + css::uno::Reference<css::sdbc::XClob> SAL_CALL getClob(sal_Int32 column) override; + css::uno::Reference<css::sdbc::XArray> SAL_CALL getArray(sal_Int32 column) override; + + // XResultSetMetaDataSupplier + css::uno::Reference<css::sdbc::XResultSetMetaData> SAL_CALL getMetaData() override; + + // XCancellable + void SAL_CALL cancel() override; + + // XCloseable + void SAL_CALL close() override; + + // XWarningsSupplier + Any SAL_CALL getWarnings() override; + + void SAL_CALL clearWarnings() override; + + // XResultSetUpdate + void SAL_CALL insertRow() override; + void SAL_CALL updateRow() override; + void SAL_CALL deleteRow() override; + void SAL_CALL cancelRowUpdates() override; + void SAL_CALL moveToInsertRow() override; + void SAL_CALL moveToCurrentRow() override; + + // XRowUpdate + void SAL_CALL updateNull(sal_Int32 column) override; + void SAL_CALL updateBoolean(sal_Int32 column, sal_Bool x) override; + void SAL_CALL updateByte(sal_Int32 column, sal_Int8 x) override; + void SAL_CALL updateShort(sal_Int32 column, sal_Int16 x) override; + void SAL_CALL updateInt(sal_Int32 column, sal_Int32 x) override; + void SAL_CALL updateLong(sal_Int32 column, sal_Int64 x) override; + void SAL_CALL updateFloat(sal_Int32 column, float x) override; + void SAL_CALL updateDouble(sal_Int32 column, double x) override; + void SAL_CALL updateString(sal_Int32 column, const OUString& x) override; + void SAL_CALL updateBytes(sal_Int32 column, const css::uno::Sequence<sal_Int8>& x) override; + void SAL_CALL updateDate(sal_Int32 column, const css::util::Date& x) override; + void SAL_CALL updateTime(sal_Int32 column, const css::util::Time& x) override; + void SAL_CALL updateTimestamp(sal_Int32 column, const css::util::DateTime& x) override; + void SAL_CALL updateBinaryStream(sal_Int32 column, + const css::uno::Reference<css::io::XInputStream>& x, + sal_Int32 length) override; + void SAL_CALL updateCharacterStream(sal_Int32 column, + const css::uno::Reference<css::io::XInputStream>& x, + sal_Int32 length) override; + void SAL_CALL updateObject(sal_Int32 column, const Any& x) override; + void SAL_CALL updateNumericObject(sal_Int32 column, const Any& x, sal_Int32 scale) override; + + // XColumnLocate + sal_Int32 SAL_CALL findColumn(const OUString& columnName) override; + + // XRowLocate + Any SAL_CALL getBookmark() override; + + sal_Bool SAL_CALL moveToBookmark(const Any& bookmark) override; + sal_Bool SAL_CALL moveRelativeToBookmark(const Any& bookmark, sal_Int32 rows) override; + sal_Int32 SAL_CALL compareBookmarks(const Any& first, const Any& second) override; + sal_Bool SAL_CALL hasOrderedBookmarks() override; + sal_Int32 SAL_CALL hashBookmark(const Any& bookmark) override; + + // XDeleteRows + css::uno::Sequence<sal_Int32> SAL_CALL deleteRows(const css::uno::Sequence<Any>& rows) override; + + void checkColumnIndex(sal_Int32 index); + void checkRowIndex(); + +private: + using ::cppu::OPropertySetHelper::getFastPropertyValue; +}; +} /* mysqlc */ +} /* connectivity */ +#endif // CONNECTIVITY_SRESULTSET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_resultsetmetadata.cxx b/connectivity/source/drivers/mysqlc/mysqlc_resultsetmetadata.cxx new file mode 100644 index 000000000..ab9c2fb39 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_resultsetmetadata.cxx @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "mysqlc_resultsetmetadata.hxx" +#include "mysqlc_general.hxx" + +#include <com/sun/star/sdbc/XRow.hpp> +#include <rtl/ustrbuf.hxx> + +using namespace connectivity::mysqlc; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; + +OResultSetMetaData::OResultSetMetaData(OConnection& rConn, MYSQL_RES* pResult) + : m_rConnection(rConn) +{ + MYSQL_FIELD* fields = mysql_fetch_field(pResult); + unsigned nFieldCount = mysql_num_fields(pResult); + for (unsigned i = 0; i < nFieldCount; ++i) + { + MySqlFieldInfo fieldInfo; + { + fieldInfo.columnName + = OUString{ fields[i].name, static_cast<sal_Int32>(fields[i].name_length), + m_rConnection.getConnectionEncoding() }; + fieldInfo.length = static_cast<sal_Int32>(fields[i].length); + fieldInfo.type + = mysqlc_sdbc_driver::mysqlToOOOType(fields[i].type, fields[i].charsetnr); + fieldInfo.mysql_type = fields[i].type; + fieldInfo.charsetNumber = fields[i].charsetnr; + fieldInfo.flags = fields[i].flags; + fieldInfo.schemaName + = OUString{ fields[i].db, static_cast<sal_Int32>(fields[i].db_length), + m_rConnection.getConnectionEncoding() }; + fieldInfo.tableName + = OUString{ fields[i].table, static_cast<sal_Int32>(fields[i].table_length), + m_rConnection.getConnectionEncoding() }; + fieldInfo.catalogName + = OUString{ fields[i].catalog, static_cast<sal_Int32>(fields[i].catalog_length), + m_rConnection.getConnectionEncoding() }; + fieldInfo.decimals = static_cast<sal_Int32>(fields[i].decimals); + fieldInfo.max_length = static_cast<sal_Int32>(fields[i].max_length); + } + m_fields.push_back(std::move(fieldInfo)); + } +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnDisplaySize(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).length; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnType(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).type; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount() { return m_fields.size(); } + +sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive(sal_Int32 column) +{ + // MYSQL_FIELD::charsetnr is the collation identifier + // _ci postfix means it's insensitive + OUStringBuffer sql{ "SHOW COLLATION WHERE Id =" }; + sql.append(OUString::number(m_fields.at(column - 1).charsetNumber)); + + Reference<XStatement> stmt = m_rConnection.createStatement(); + Reference<XResultSet> rs = stmt->executeQuery(sql.makeStringAndClear()); + Reference<XRow> xRow(rs, UNO_QUERY_THROW); + + if (!rs->next()) // fetch first and only row + return false; + + OUString sColName = xRow->getString(1); // first column is Collation name + + return !sColName.isEmpty() && !sColName.endsWith("_ci"); +} + +OUString SAL_CALL OResultSetMetaData::getSchemaName(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).schemaName; +} + +OUString SAL_CALL OResultSetMetaData::getColumnName(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).columnName; +} + +OUString SAL_CALL OResultSetMetaData::getTableName(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).tableName; +} + +OUString SAL_CALL OResultSetMetaData::getCatalogName(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).catalogName; +} + +OUString SAL_CALL OResultSetMetaData::getColumnTypeName(sal_Int32 column) +{ + checkColumnIndex(column); + return mysqlc_sdbc_driver::mysqlTypeToStr(m_fields.at(column - 1).mysql_type, + m_fields.at(column - 1).flags); +} + +OUString SAL_CALL OResultSetMetaData::getColumnLabel(sal_Int32 column) +{ + checkColumnIndex(column); + return getColumnName(column); +} + +OUString SAL_CALL OResultSetMetaData::getColumnServiceName(sal_Int32 /*column*/) +{ + return OUString{}; +} + +sal_Bool SAL_CALL OResultSetMetaData::isCurrency(sal_Int32 /*column*/) +{ + return false; // TODO +} + +sal_Bool SAL_CALL OResultSetMetaData::isAutoIncrement(sal_Int32 column) +{ + checkColumnIndex(column); + return (m_fields.at(column - 1).flags & AUTO_INCREMENT_FLAG) != 0; +} + +sal_Bool SAL_CALL OResultSetMetaData::isSigned(sal_Int32 column) +{ + checkColumnIndex(column); + return !(m_fields.at(column - 1).flags & UNSIGNED_FLAG); +} + +sal_Int32 SAL_CALL OResultSetMetaData::getPrecision(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).max_length - m_fields.at(column - 1).decimals; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getScale(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).decimals; +} + +sal_Int32 SAL_CALL OResultSetMetaData::isNullable(sal_Int32 column) +{ + checkColumnIndex(column); + return (m_fields.at(column - 1).flags & NOT_NULL_FLAG) ? 0 : 1; +} + +sal_Bool SAL_CALL OResultSetMetaData::isSearchable(sal_Int32 column) +{ + checkColumnIndex(column); + return true; +} + +sal_Bool SAL_CALL OResultSetMetaData::isReadOnly(sal_Int32 column) +{ + checkColumnIndex(column); + return m_fields.at(column - 1).schemaName.isEmpty(); +} + +sal_Bool SAL_CALL OResultSetMetaData::isDefinitelyWritable(sal_Int32 column) +{ + checkColumnIndex(column); + return !isReadOnly(column); +} + +sal_Bool SAL_CALL OResultSetMetaData::isWritable(sal_Int32 column) +{ + checkColumnIndex(column); + return !isReadOnly(column); +} + +void OResultSetMetaData::checkColumnIndex(sal_Int32 columnIndex) +{ + auto nColCount = m_fields.size(); + if (columnIndex < 1 || columnIndex > static_cast<sal_Int32>(nColCount)) + { + OUString str = "Column index out of range (expected 1 to " + + OUString::number(sal_Int32(nColCount)) + ", got " + + OUString::number(columnIndex) + "."; + throw SQLException(str, *this, OUString(), 1, Any()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_resultsetmetadata.hxx b/connectivity/source/drivers/mysqlc/mysqlc_resultsetmetadata.hxx new file mode 100644 index 000000000..52ca9595b --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_resultsetmetadata.hxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_RESULTSETMETADATA_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_RESULTSETMETADATA_HXX + +#include "mysqlc_connection.hxx" + +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +#include <cppuhelper/implbase1.hxx> +#include <mysql.h> + +namespace connectivity +{ +namespace mysqlc +{ +using ::com::sun::star::sdbc::SQLException; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::RuntimeException; + +struct MySqlFieldInfo +{ + OUString columnName; + sal_Int32 length = 0; + sal_Int32 type = 0; + enum_field_types mysql_type = {}; + unsigned charsetNumber = 0; + unsigned flags = 0; + OUString schemaName; + OUString tableName; + OUString catalogName; + sal_Int32 decimals; + sal_Int32 max_length; +}; + +//************ Class: ResultSetMetaData + +typedef ::cppu::WeakImplHelper1<css::sdbc::XResultSetMetaData> OResultSetMetaData_BASE; + +class OResultSetMetaData final : public OResultSetMetaData_BASE +{ +private: + OConnection& m_rConnection; + std::vector<MySqlFieldInfo> m_fields; + + void checkColumnIndex(sal_Int32 columnIndex); + virtual ~OResultSetMetaData() override = default; + +public: + OResultSetMetaData(OConnection& rConn, MYSQL_RES* pResult); + + sal_Int32 SAL_CALL getColumnCount() override; + + sal_Bool SAL_CALL isAutoIncrement(sal_Int32 column) override; + sal_Bool SAL_CALL isCaseSensitive(sal_Int32 column) override; + sal_Bool SAL_CALL isSearchable(sal_Int32 column) override; + sal_Bool SAL_CALL isCurrency(sal_Int32 column) override; + + sal_Int32 SAL_CALL isNullable(sal_Int32 column) override; + + sal_Bool SAL_CALL isSigned(sal_Int32 column) override; + + sal_Int32 SAL_CALL getColumnDisplaySize(sal_Int32 column) override; + + OUString SAL_CALL getColumnLabel(sal_Int32 column) override; + OUString SAL_CALL getColumnName(sal_Int32 column) override; + OUString SAL_CALL getSchemaName(sal_Int32 column) override; + + sal_Int32 SAL_CALL getPrecision(sal_Int32 column) override; + sal_Int32 SAL_CALL getScale(sal_Int32 column) override; + + OUString SAL_CALL getTableName(sal_Int32 column) override; + OUString SAL_CALL getCatalogName(sal_Int32 column) override; + + sal_Int32 SAL_CALL getColumnType(sal_Int32 column) override; + + OUString SAL_CALL getColumnTypeName(sal_Int32 column) override; + + sal_Bool SAL_CALL isReadOnly(sal_Int32 column) override; + sal_Bool SAL_CALL isWritable(sal_Int32 column) override; + sal_Bool SAL_CALL isDefinitelyWritable(sal_Int32 column) override; + + OUString SAL_CALL getColumnServiceName(sal_Int32 column) override; +}; +} +} + +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_RESULTSETMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_services.cxx b/connectivity/source/drivers/mysqlc/mysqlc_services.cxx new file mode 100644 index 000000000..de611375c --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_services.cxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "mysqlc_driver.hxx" + +#include <cppuhelper/factory.hxx> +#include <uno/lbnames.h> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::mysqlc; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +typedef Reference<XSingleServiceFactory> (*createFactoryFunc)( + const Reference<XMultiServiceFactory>& rServiceManager, const OUString& rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, const Sequence<OUString>& rServiceNames, + rtl_ModuleCount*); + +namespace +{ +struct ProviderRequest +{ + Reference<XSingleServiceFactory> xRet; + Reference<XMultiServiceFactory> const xServiceManager; + OUString const sImplementationName; + + ProviderRequest(void* pServiceManager, char const* pImplementationName) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER(const OUString& Implname, const Sequence<OUString>& Services, + ::cppu::ComponentInstantiation Factory, createFactoryFunc creator) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator(xServiceManager, sImplementationName, Factory, Services, nullptr); + } + catch (...) + { + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* component_getFactory(const char* pImplementationName, + void* pServiceManager, + void* /* pRegistryKey */) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager, pImplementationName); + + aReq.CREATE_PROVIDER(MysqlCDriver::getImplementationName_Static(), + MysqlCDriver::getSupportedServiceNames_Static(), + MysqlCDriver_CreateInstance, ::cppu::createSingleFactory); + + if (aReq.xRet.is()) + { + aReq.xRet->acquire(); + } + + pRet = aReq.getProvider(); + } + + return pRet; +}; + +extern "C" SAL_DLLPUBLIC_EXPORT void +component_getImplementationEnvironment(char const** ppEnvTypeName, uno_Environment**) +{ + *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_statement.cxx b/connectivity/source/drivers/mysqlc/mysqlc_statement.cxx new file mode 100644 index 000000000..0082f96b6 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_statement.cxx @@ -0,0 +1,404 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include "mysqlc_connection.hxx" +#include "mysqlc_propertyids.hxx" +#include "mysqlc_resultset.hxx" +#include "mysqlc_statement.hxx" +#include "mysqlc_general.hxx" + +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace connectivity::mysqlc; + +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::io; +using namespace com::sun::star::util; +using ::osl::MutexGuard; + +OCommonStatement::OCommonStatement(OConnection* _pConnection) + : OCommonStatement_IBase(m_aMutex) + , OPropertySetHelper(OCommonStatement_IBase::rBHelper) + , m_xConnection(_pConnection) +{ +} + +OCommonStatement::~OCommonStatement() {} + +void OCommonStatement::closeResultSet() +{ + if (m_xResultSet.is()) + { + css::uno::Reference<css::sdbc::XCloseable> xClose(m_xResultSet, UNO_QUERY_THROW); + xClose->close(); + m_xResultSet.clear(); + } +} + +void OCommonStatement::disposing() +{ + MutexGuard aGuard(m_aMutex); + + m_xConnection.clear(); + OCommonStatement_IBase::disposing(); +} + +Any SAL_CALL OCommonStatement::queryInterface(const Type& rType) +{ + Any aRet = OCommonStatement_IBase::queryInterface(rType); + if (!aRet.hasValue()) + { + aRet = OPropertySetHelper::queryInterface(rType); + } + return aRet; +} + +Sequence<Type> SAL_CALL OCommonStatement::getTypes() +{ + ::cppu::OTypeCollection aTypes(cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get()); + + return concatSequences(aTypes.getTypes(), OCommonStatement_IBase::getTypes()); +} + +Sequence<Type> SAL_CALL OStatement::getTypes() +{ + return concatSequences(OStatement_BASE::getTypes(), OCommonStatement::getTypes()); +} + +void SAL_CALL OCommonStatement::cancel() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + // cancel the current sql statement +} + +void SAL_CALL OCommonStatement::close() +{ + /* + We need a block for the checkDisposed call. + After the check we can call dispose() as we are not under lock ?? + */ + { + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + } + dispose(); + closeResultSet(); +} + +// void SAL_CALL OStatement::clearBatch() +// { +// mysqlc_sdbc_driver::throwFeatureNotImplementedException("com:sun:star:sdbc:XBatchExecution"); +// } + +sal_Bool SAL_CALL OStatement::execute(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + closeResultSet(); + m_nAffectedRows = -1; + + OString toExec = OUStringToOString(sql, m_xConnection->getConnectionSettings().encoding); + + MYSQL* pMySql = m_xConnection->getMysqlConnection(); + + // NOTE: differs from MySQL C API, where mysql_real_escape_string_quote() + // should be used. + // toExec = mysqlc_sdbc_driver::escapeSql(toExec); + int failure = mysql_real_query(pMySql, toExec.getStr(), toExec.getLength()); + + if (failure || mysql_errno(pMySql)) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(pMySql), mysql_sqlstate(pMySql), + mysql_errno(pMySql), *this, + m_xConnection->getConnectionEncoding()); + + return getResult(); +} + +Reference<XResultSet> SAL_CALL OStatement::executeQuery(const OUString& sql) +{ + bool isRS(execute(sql)); + // if a MySQL error occurred, it was already thrown and the below is not executed + assert(isRS == m_xResultSet.is()); + if (!isRS) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg( + "executeQuery called on SQL command that does not return a ResultSet", "02000", 0, + *this); + if (!m_xResultSet.is()) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg( + "internal MySQL-SDBC error: executeQuery: no ResultSet after execute() returned true.", + "02000", 0, *this); + + return m_xResultSet; +} + +Reference<XConnection> SAL_CALL OStatement::getConnection() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + // just return our connection here + return m_xConnection.get(); +} + +sal_Int32 SAL_CALL OStatement::getUpdateCount() { return m_nAffectedRows; } + +Any SAL_CALL OStatement::queryInterface(const Type& rType) +{ + Any aRet = OCommonStatement::queryInterface(rType); + if (!aRet.hasValue()) + { + aRet = OStatement_BASE::queryInterface(rType); + } + return aRet; +} + +// void SAL_CALL OStatement::addBatch(const OUString&) +// { +// MutexGuard aGuard(m_aMutex); +// checkDisposed(rBHelper.bDisposed); + +// mysqlc_sdbc_driver::throwFeatureNotImplementedException("com:sun:star:sdbc:XBatchExecution"); +// } + +// Sequence<sal_Int32> SAL_CALL OStatement::executeBatch() +// { +// MutexGuard aGuard(m_aMutex); +// checkDisposed(rBHelper.bDisposed); + +// mysqlc_sdbc_driver::throwFeatureNotImplementedException("com:sun:star:sdbc:XBatchExecution"); +// } + +sal_Int32 SAL_CALL OStatement::executeUpdate(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + execute(sql); + return m_nAffectedRows; +} + +Reference<XResultSet> SAL_CALL OStatement::getResultSet() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xResultSet; +} + +bool OStatement::getResult() +{ + // all callers already reset that + assert(!m_xResultSet.is()); + assert(m_nAffectedRows == -1); + + MYSQL* pMySql = m_xConnection->getMysqlConnection(); + MYSQL_RES* pMysqlResult = mysql_store_result(pMySql); + if (pMysqlResult != nullptr) + { + // MariaDB/MySQL will return the number of rows in the ResultSet from mysql_affected_rows(); + // sdbc mandates -1 when the command (query) returns a ResultSet + assert(m_nAffectedRows == -1); + m_xResultSet = new OResultSet(*getOwnConnection(), this, pMysqlResult, + m_xConnection->getConnectionEncoding()); + return true; + } + else if (mysql_field_count(pMySql) == 0) + { + m_nAffectedRows = mysql_affected_rows(pMySql); + return false; + } + else + { + mysqlc_sdbc_driver::throwSQLExceptionWithMsg( + "mysql_store_result indicated success and SQL command was supposed to return a " + "ResultSet, but did not.", + "02000", 0, *this); + } + //unreachable + assert(false); + // keep -Werror=return-type happy + return false; +} + +sal_Bool SAL_CALL OStatement::getMoreResults() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + closeResultSet(); + m_nAffectedRows = -1; + + MYSQL* pMySql = m_xConnection->getMysqlConnection(); + int status = mysql_next_result(pMySql); + + if (status > 0 || mysql_errno(pMySql)) + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(pMySql), mysql_sqlstate(pMySql), + mysql_errno(pMySql), *this, + m_xConnection->getConnectionEncoding()); + + if (status == -1) + return false; + + if (status != 0) + { + const OUString errMsg("mysql_next_result returned unexpected value: " + + OUString::number(status)); + mysqlc_sdbc_driver::throwSQLExceptionWithMsg(errMsg, "02000", 0, *this); + } + + return getResult(); +} + +Any SAL_CALL OCommonStatement::getWarnings() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return makeAny(m_aLastWarning); +} + +void SAL_CALL OCommonStatement::clearWarnings() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + m_aLastWarning = SQLWarning(); +} + +::cppu::IPropertyArrayHelper* OCommonStatement::createArrayHelper() const +{ + // this properties are define by the service statement + // they must in alphabetic order + Sequence<Property> aProps(10); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] + = Property("CursorName", PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] + = Property("EscapeProcessing", PROPERTY_ID_ESCAPEPROCESSING, cppu::UnoType<bool>::get(), 0); + pProperties[nPos++] = Property("FetchDirection", PROPERTY_ID_FETCHDIRECTION, + cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] + = Property("FetchSize", PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] + = Property("MaxFieldSize", PROPERTY_ID_MAXFIELDSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] + = Property("MaxRows", PROPERTY_ID_MAXROWS, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] + = Property("QueryTimeOut", PROPERTY_ID_QUERYTIMEOUT, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = Property("ResultSetConcurrency", PROPERTY_ID_RESULTSETCONCURRENCY, + cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] + = Property("ResultSetType", PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] + = Property("UseBookmarks", PROPERTY_ID_USEBOOKMARKS, cppu::UnoType<bool>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + +::cppu::IPropertyArrayHelper& OCommonStatement::getInfoHelper() { return *getArrayHelper(); } + +sal_Bool OCommonStatement::convertFastPropertyValue(Any& /* rConvertedValue */, + Any& /* rOldValue */, sal_Int32 /* nHandle */, + const Any& /* rValue */) +{ + // here we have to try to convert + return false; +} + +void OCommonStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& /* rValue */) +{ + // set the value to whatever is necessary + switch (nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + case PROPERTY_ID_MAXFIELDSIZE: + case PROPERTY_ID_MAXROWS: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + case PROPERTY_ID_ESCAPEPROCESSING: + case PROPERTY_ID_USEBOOKMARKS: + default:; + } +} + +void OCommonStatement::getFastPropertyValue(Any& _rValue, sal_Int32 nHandle) const +{ + switch (nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + case PROPERTY_ID_MAXFIELDSIZE: + case PROPERTY_ID_MAXROWS: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + case PROPERTY_ID_ESCAPEPROCESSING: + break; + case PROPERTY_ID_USEBOOKMARKS: + _rValue <<= false; + break; + default:; + } +} + +OUString OStatement::getImplementationName() { return "com.sun.star.sdbcx.OStatement"; } + +css::uno::Sequence<OUString> OStatement::getSupportedServiceNames() +{ + return { "com.sun.star.sdbc.Statement" }; +} + +sal_Bool OStatement::supportsService(OUString const& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +void SAL_CALL OCommonStatement::acquire() throw() { OCommonStatement_IBase::acquire(); } + +void SAL_CALL OCommonStatement::release() throw() { OCommonStatement_IBase::release(); } + +void SAL_CALL OStatement::acquire() throw() { OCommonStatement::acquire(); } + +void SAL_CALL OStatement::release() throw() { OCommonStatement::release(); } + +Reference<css::beans::XPropertySetInfo> SAL_CALL OCommonStatement::getPropertySetInfo() +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_statement.hxx b/connectivity/source/drivers/mysqlc/mysqlc_statement.hxx new file mode 100644 index 000000000..4416ccceb --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_statement.hxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_STATEMENT_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_STATEMENT_HXX + +#include "mysqlc_connection.hxx" +#include "mysqlc_subcomponent.hxx" + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/sdbc/XBatchExecution.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XMultipleResults.hpp> +#include <com/sun/star/sdbc/XStatement.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/util/XCancellable.hpp> + +#include <cppuhelper/compbase3.hxx> +#include <rtl/ref.hxx> + +namespace connectivity +{ +namespace mysqlc +{ +using ::com::sun::star::sdbc::SQLException; +using ::com::sun::star::sdbc::SQLWarning; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::Type; + +typedef ::cppu::WeakComponentImplHelper3<css::sdbc::XWarningsSupplier, css::util::XCancellable, + css::sdbc::XCloseable> + OCommonStatement_IBase; + +//************ Class: OCommonStatement +// is a base class for the normal statement and for the prepared statement + +class OCommonStatement : public OBase_Mutex, + public OCommonStatement_IBase, + public ::cppu::OPropertySetHelper, + public OPropertyArrayUsageHelper<OCommonStatement> + +{ +private: + SQLWarning m_aLastWarning; + +protected: + rtl::Reference<OConnection> m_xConnection; // The owning Connection object + + css::uno::Reference<css::sdbc::XResultSet> m_xResultSet; + + // number of rows affected by an UPDATE, DELETE or INSERT statement. + sal_Int32 m_nAffectedRows = 0; + +protected: + void closeResultSet(); + + // OPropertyArrayUsageHelper + ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + + // OPropertySetHelper + ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + sal_Bool SAL_CALL convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, + sal_Int32 nHandle, const Any& rValue) override; + + void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue) override; + + void SAL_CALL getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const override; + virtual ~OCommonStatement() override; + +protected: + OCommonStatement(OConnection* _pConnection); + +public: + using OCommonStatement_IBase::rBHelper; + using OCommonStatement_IBase::operator css::uno::Reference<css::uno::XInterface>; + + // OComponentHelper + void SAL_CALL disposing() override; + + // XInterface + void SAL_CALL release() throw() override; + void SAL_CALL acquire() throw() override; + Any SAL_CALL queryInterface(const css::uno::Type& rType) override; + + //XTypeProvider + css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + + // XPropertySet + css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + + // XWarningsSupplier + Any SAL_CALL getWarnings() override; + + void SAL_CALL clearWarnings() override; + + // XCancellable + void SAL_CALL cancel() override; + + // XCloseable + void SAL_CALL close() override; + + // other methods + OConnection* getOwnConnection() const { return m_xConnection.get(); } + +private: + using ::cppu::OPropertySetHelper::getFastPropertyValue; +}; + +typedef ::cppu::ImplHelper3<css::lang::XServiceInfo, css::sdbc::XMultipleResults, + css::sdbc::XStatement> + OStatement_BASE; + +class OStatement final : public OCommonStatement, public OStatement_BASE +{ + virtual ~OStatement() override = default; + + bool getResult(); + +public: + // A constructor which is required for the return of the objects + OStatement(OConnection* _pConnection) + : OCommonStatement(_pConnection) + { + } + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + //XInterface + Any SAL_CALL queryInterface(const css::uno::Type& rType) override; + void SAL_CALL acquire() throw() override; + void SAL_CALL release() throw() override; + + //XTypeProvider + css::uno::Sequence<Type> SAL_CALL getTypes() override; + + // XStatement + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL executeQuery(const OUString& sql) override; + sal_Int32 SAL_CALL executeUpdate(const OUString& sql) override; + sal_Bool SAL_CALL execute(const OUString& sql) override; + css::uno::Reference<css::sdbc::XConnection> SAL_CALL getConnection() override; + + // XMultipleResults + css::uno::Reference<css::sdbc::XResultSet> SAL_CALL getResultSet() override; + sal_Int32 SAL_CALL getUpdateCount() override; + sal_Bool SAL_CALL getMoreResults() override; + + // XBatchExecution + // void SAL_CALL addBatch(const OUString& sql) override; + + // void SAL_CALL clearBatch() override; + + // css::uno::Sequence<sal_Int32> SAL_CALL executeBatch() override; +}; +} +} +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_STATEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_subcomponent.hxx b/connectivity/source/drivers/mysqlc/mysqlc_subcomponent.hxx new file mode 100644 index 000000000..a1b02da51 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_subcomponent.hxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_SUBCOMPONENT_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_SUBCOMPONENT_HXX + +#include <cppuhelper/propshlp.hxx> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> + +namespace cppu +{ +class IPropertyArrayHelper; +} + +namespace com +{ +namespace sun +{ +namespace star +{ +namespace lang +{ +class XComponent; +} +} +} +} + +namespace connectivity +{ +namespace mysqlc +{ +/// @throws css::lang::DisposedException +void checkDisposed(bool _bThrow); + +template <class TYPE> class OPropertyArrayUsageHelper +{ +protected: + static sal_Int32 s_nRefCount; + static ::cppu::IPropertyArrayHelper* s_pProps; + static ::osl::Mutex s_aMutex; + +public: + OPropertyArrayUsageHelper(); + virtual ~OPropertyArrayUsageHelper(); + + /** call this in the getInfoHelper method of your derived class. The method returns the array helper of the + class, which is created if necessary. + */ + ::cppu::IPropertyArrayHelper* getArrayHelper(); + +protected: + /** used to implement the creation of the array helper which is shared amongst all instances of the class. + This method needs to be implemented in derived classes. + <BR> + The method gets called with s_aMutex acquired. + @return a pointer to the newly created array helper. Must not be NULL. + */ + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const = 0; +}; + +template <class TYPE> sal_Int32 OPropertyArrayUsageHelper<TYPE>::s_nRefCount = 0; + +template <class TYPE> +::cppu::IPropertyArrayHelper* OPropertyArrayUsageHelper<TYPE>::s_pProps = nullptr; + +template <class TYPE>::osl::Mutex OPropertyArrayUsageHelper<TYPE>::s_aMutex; + +template <class TYPE> OPropertyArrayUsageHelper<TYPE>::OPropertyArrayUsageHelper() +{ + ::osl::MutexGuard aGuard(s_aMutex); + ++s_nRefCount; +} + +template <class TYPE> OPropertyArrayUsageHelper<TYPE>::~OPropertyArrayUsageHelper() +{ + ::osl::MutexGuard aGuard(s_aMutex); + OSL_ENSURE(s_nRefCount > 0, "OPropertyArrayUsageHelper::~OPropertyArrayUsageHelper : " + "suspicious call : have a refcount of 0 !"); + if (!--s_nRefCount) + { + delete s_pProps; + s_pProps = nullptr; + } +} + +template <class TYPE>::cppu::IPropertyArrayHelper* OPropertyArrayUsageHelper<TYPE>::getArrayHelper() +{ + OSL_ENSURE( + s_nRefCount, + "OPropertyArrayUsageHelper::getArrayHelper : suspicious call : have a refcount of 0 !"); + if (!s_pProps) + { + ::osl::MutexGuard aGuard(s_aMutex); + if (!s_pProps) + { + s_pProps = createArrayHelper(); + OSL_ENSURE(s_pProps, "OPropertyArrayUsageHelper::getArrayHelper : createArrayHelper " + "returned nonsense !"); + } + } + return s_pProps; +} + +class OBase_Mutex +{ +public: + ::osl::Mutex m_aMutex; +}; + +namespace internal +{ +template <class T> void implCopySequence(const T* _pSource, T*& _pDest, sal_Int32 _nSourceLen) +{ + for (sal_Int32 i = 0; i < _nSourceLen; ++i, ++_pSource, ++_pDest) + *_pDest = *_pSource; +} +} + +/// concat two sequences +template <class T> +css::uno::Sequence<T> concatSequences(const css::uno::Sequence<T>& _rLeft, + const css::uno::Sequence<T>& _rRight) +{ + sal_Int32 nLeft(_rLeft.getLength()), nRight(_rRight.getLength()); + const T* pLeft = _rLeft.getConstArray(); + const T* pRight = _rRight.getConstArray(); + + sal_Int32 nReturnLen(nLeft + nRight); + css::uno::Sequence<T> aReturn(nReturnLen); + T* pReturn = aReturn.getArray(); + + internal::implCopySequence(pLeft, pReturn, nLeft); + internal::implCopySequence(pRight, pReturn, nRight); + + return aReturn; +} +} +} + +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_SUBCOMPONENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_types.cxx b/connectivity/source/drivers/mysqlc/mysqlc_types.cxx new file mode 100644 index 000000000..219e0d553 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_types.cxx @@ -0,0 +1,679 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include "mysqlc_types.hxx" + +using namespace com::sun::star::sdbc; + +TypeInfoDef const mysqlc_types[] = { + + // ------------- MySQL-Type: BIT. SDBC-Type: Bit ------------- + { + "BIT", // Typename + com::sun::star::sdbc::DataType::BIT, // sdbc-type + 1, // Precision + "", // Literal prefix + "", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "BIT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ------------ MySQL-Type: BOOL. SDBC-Type: Bit ------------- + { + "BOOL", // Typename + com::sun::star::sdbc::DataType::BIT, // sdbc-type + 1, // Precision + "", // Literal prefix + "", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "BOOL", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // --------- MySQL-Type: TINYINT SDBC-Type: TINYINT ---------- + { + "TINYINT", // Typename + com::sun::star::sdbc::DataType::TINYINT, // sdbc-type + 3, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M)] [UNSIGNED] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + true, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "TINYINT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: BIGINT SDBC-Type: BIGINT ---------- + { + "BIGINT", // Typename + com::sun::star::sdbc::DataType::BIGINT, // sdbc-type + 19, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M)] [UNSIGNED] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + true, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "BIGINT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: LONG VARBINARY SDBC-Type: LONGVARBINARY ---------- + { + "LONG VARBINARY", // Typename + com::sun::star::sdbc::DataType::LONGVARBINARY, // sdbc-type + 16777215, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "LONG VARBINARY", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: MEDIUMBLOB SDBC-Type: LONGVARBINARY ---------- + { + "MEDIUMBLOB", // Typename + com::sun::star::sdbc::DataType::LONGVARBINARY, // sdbc-type + 16777215, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "MEDIUMBLOB", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: LONGBLOB SDBC-Type: LONGVARBINARY ---------- + { + "LONGBLOB", // Typename + com::sun::star::sdbc::DataType::LONGVARBINARY, // sdbc-type + -1, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "LONGBLOB", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: BLOB SDBC-Type: LONGVARBINARY ---------- + { + "BLOB", // Typename + com::sun::star::sdbc::DataType::LONGVARBINARY, // sdbc-type + 0xFFFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "BLOB", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: TINYBLOB SDBC-Type: LONGVARBINARY ---------- + { + "TINYBLOB", // Typename + com::sun::star::sdbc::DataType::LONGVARBINARY, // sdbc-type + 0xFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "TINYBLOB", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: VARBINARY SDBC-Type: VARBINARY ---------- + { + "VARBINARY", // Typename + com::sun::star::sdbc::DataType::VARBINARY, // sdbc-type + 0xFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "(M)", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "VARBINARY", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: BINARY SDBC-Type: BINARY ---------- + { + "BINARY", // Typename + com::sun::star::sdbc::DataType::BINARY, // sdbc-type + 0xFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "(M)", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + true, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "VARBINARY", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: LONG VARCHAR SDBC-Type: LONG VARCHAR ---------- + { + "LONG VARCHAR", // Typename + com::sun::star::sdbc::DataType::LONGVARCHAR, // sdbc-type + 0xFFFFFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "LONG VARCHAR", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: MEDIUMTEXT SDBC-Type: LONG VARCHAR ---------- + { + "MEDIUMTEXT", // Typename + com::sun::star::sdbc::DataType::LONGVARCHAR, // sdbc-type + 0xFFFFFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "MEDIUMTEXT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: LONGTEXT SDBC-Type: LONG VARCHAR ---------- + { + "LONGTEXT", // Typename + com::sun::star::sdbc::DataType::LONGVARCHAR, // sdbc-type + 0xFFFFFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "LONGTEXT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: TEXT SDBC-Type: LONG VARCHAR ---------- + { + "TEXT", // Typename + com::sun::star::sdbc::DataType::LONGVARCHAR, // sdbc-type + 0xFFFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "TEXT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: TINYTEXT SDBC-Type: LONG VARCHAR ---------- + { + "TINYTEXT", // Typename + com::sun::star::sdbc::DataType::LONGVARCHAR, // sdbc-type + 0xFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "TINYTEXT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: CHAR SDBC-Type: CHAR ---------- + { + "CHAR", // Typename + com::sun::star::sdbc::DataType::CHAR, // sdbc-type + 0xFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "(M)", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "NUMERIC", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: DECIMAL SDBC-Type: DECIMAL ---------- + { + "DECIMAL", // Typename + com::sun::star::sdbc::DataType::DECIMAL, // sdbc-type + 17, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M[,D])] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "DECIMAL", // local type name + -308, // minimum scale + 308 // maximum scale + }, + + // ----------- MySQL-Type: NUMERIC SDBC-Type: NUMERIC ---------- + { + "NUMERIC", // Typename + com::sun::star::sdbc::DataType::NUMERIC, // sdbc-type + 17, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M[,D])] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "NUMERIC", // local type name + -308, // minimum scale + 308 // maximum scale + }, + + // ----------- MySQL-Type: INTEGER SDBC-Type: INTEGER ---------- + { + "INTEGER", // Typename + com::sun::star::sdbc::DataType::INTEGER, // sdbc-type + 10, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M)] [UNSIGNED] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + true, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "INTEGER", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: INT SDBC-Type: INTEGER ---------- + { + "INT", // Typename + com::sun::star::sdbc::DataType::INTEGER, // sdbc-type + 10, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M)] [UNSIGNED] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + true, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "INT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: MEDIUMINT SDBC-Type: INTEGER ---------- + { + "MEDIUMINT", // Typename + com::sun::star::sdbc::DataType::INTEGER, // sdbc-type + 7, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M)] [UNSIGNED] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + true, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "MEDIUMINT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: SMALLINT SDBC-Type: INTEGER ---------- + { + "SMALLINT", // Typename + com::sun::star::sdbc::DataType::SMALLINT, // sdbc-type + 5, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M)] [UNSIGNED] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + true, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "SMALLINT", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: FLOAT SDBC-Type: REAL ---------- + { + "FLOAT", // Typename + com::sun::star::sdbc::DataType::REAL, // sdbc-type + 10, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M,D)] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "FLOAT", // local type name + -38, // minimum scale + 38 // maximum scale + }, + + // ----------- MySQL-Type: DOUBLE SDBC-Type: DOUBLE ---------- + { + "DOUBLE", // Typename + com::sun::star::sdbc::DataType::DOUBLE, // sdbc-type + 17, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M,D)] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "DOUBLE", // local type name + -308, // minimum scale + 308 // maximum scale + }, + + // ----------- MySQL-Type: DOUBLE PRECISION SDBC-Type: DOUBLE ---------- + { + "DOUBLE PRECISION", // Typename + com::sun::star::sdbc::DataType::DOUBLE, // sdbc-type + 17, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M,D)] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "DOUBLE PRECISION", // local type name + -308, // minimum scale + 308 // maximum scale + }, + + // ----------- MySQL-Type: REAL SDBC-Type: DOUBLE ---------- + { + "REAL", // Typename + com::sun::star::sdbc::DataType::DOUBLE, // sdbc-type + 17, // Precision + "", // Literal prefix + "", // Literal suffix + "[(M,D)] [ZEROFILL]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + true, // auto_increment + "REAL", // local type name + -308, // minimum scale + 308 // maximum scale + }, + + // ----------- MySQL-Type: VARCHAR SDBC-Type: VARCHAR ---------- + { + "VARCHAR", // Typename + com::sun::star::sdbc::DataType::VARCHAR, // sdbc-type + 255, // Precision + "'", // Literal prefix + "'", // Literal suffix + "(M)", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "VARCHAR", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: ENUM SDBC-Type: VARCHAR ---------- + { + "ENUM", // Typename + com::sun::star::sdbc::DataType::VARCHAR, // sdbc-type + 0xFFFF, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "ENUM", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: SET SDBC-Type: VARCHAR ---------- + { + "SET", // Typename + com::sun::star::sdbc::DataType::VARCHAR, // sdbc-type + 64, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "SET", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: DATE SDBC-Type: DATE ---------- + { + "DATE", // Typename + com::sun::star::sdbc::DataType::DATE, // sdbc-type + 0, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "DATE", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: TIME SDBC-Type: TIME ---------- + { + "TIME", // Typename + com::sun::star::sdbc::DataType::TIME, // sdbc-type + 0, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "TIME", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: DATETIME SDBC-Type: TIMESTAMP ---------- + { + "DATETIME", // Typename + com::sun::star::sdbc::DataType::TIMESTAMP, // sdbc-type + 0, // Precision + "'", // Literal prefix + "'", // Literal suffix + "", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "DATETIME", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: TIMESTAMP SDBC-Type: TIMESTAMP ---------- + { + "TIMESTAMP", // Typename + com::sun::star::sdbc::DataType::TIMESTAMP, // sdbc-type + 0, // Precision + "'", // Literal prefix + "'", // Literal suffix + "[(M)]", // Create params + com::sun::star::sdbc::ColumnValue::NULLABLE, // nullable + false, // case sensitive + com::sun::star::sdbc::ColumnSearch::FULL, // searchable + false, // unsignable + false, // fixed_prec_scale + false, // auto_increment + "TIMESTAMP", // local type name + 0, // minimum scale + 0 // maximum scale + }, + + // ----------- MySQL-Type: TIMESTAMP SDBC-Type: TIMESTAMP ---------- + { nullptr, 0, 0, nullptr, nullptr, nullptr, 0, false, 0, false, false, false, nullptr, 0, 0 } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/mysqlc/mysqlc_types.hxx b/connectivity/source/drivers/mysqlc/mysqlc_types.hxx new file mode 100644 index 000000000..5f577ee03 --- /dev/null +++ b/connectivity/source/drivers/mysqlc/mysqlc_types.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_MYSQLC_SOURCE_MYSQLC_TYPES_HXX +#define INCLUDED_MYSQLC_SOURCE_MYSQLC_TYPES_HXX + +#include <sal/types.h> + +struct TypeInfoDef +{ + const char* typeName; + sal_Int32 dataType; + sal_Int32 precision; + const char* literalPrefix; + const char* literalSuffix; + const char* createParams; + sal_Int16 nullable; + bool caseSensitive; + sal_Int16 searchable; + bool isUnsigned; + bool fixedPrecScale; + bool autoIncrement; + const char* localTypeName; + sal_Int32 minScale; + sal_Int32 maxScale; +}; + +extern TypeInfoDef const mysqlc_types[]; + +#endif // INCLUDED_MYSQLC_SOURCE_MYSQLC_TYPES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/OConnection.cxx b/connectivity/source/drivers/odbc/OConnection.cxx new file mode 100644 index 000000000..26a549814 --- /dev/null +++ b/connectivity/source/drivers/odbc/OConnection.cxx @@ -0,0 +1,547 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <odbc/OTools.hxx> +#include <odbc/OConnection.hxx> +#include <odbc/ODatabaseMetaData.hxx> +#include <odbc/OFunctions.hxx> +#include <odbc/ODriver.hxx> +#include <odbc/OStatement.hxx> +#include <odbc/OPreparedStatement.hxx> +#include <connectivity/dbcharset.hxx> +#include <connectivity/dbexception.hxx> + +#include <sal/log.hxx> + +#include <string.h> + +using namespace connectivity::odbc; +using namespace connectivity; +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; + +OConnection::OConnection(const SQLHANDLE _pDriverHandle,ODBCDriver* _pDriver) + :m_xDriver(_pDriver) + ,m_aConnectionHandle(nullptr) + ,m_pDriverHandleCopy(_pDriverHandle) + ,m_nStatementCount(0) + ,m_bClosed(false) + ,m_bUseCatalog(false) + ,m_bUseOldDateFormat(false) + ,m_bIgnoreDriverPrivileges(false) + ,m_bPreventGetVersionColumns(false) + ,m_bReadOnly(true) +{ +} + +OConnection::~OConnection() +{ + if(!isClosed( )) + close(); + + if ( SQL_NULL_HANDLE != m_aConnectionHandle ) + { + SQLRETURN rc; + + if (!m_bClosed) + { + rc = N3SQLDisconnect( m_aConnectionHandle ); + OSL_ENSURE( rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO, "Failure from SQLDisconnect" ); + } + + rc = N3SQLFreeHandle( SQL_HANDLE_DBC, m_aConnectionHandle ); + OSL_ENSURE( rc == SQL_SUCCESS , "Failure from SQLFreeHandle for connection"); + + m_aConnectionHandle = SQL_NULL_HANDLE; + } +} + +oslGenericFunction OConnection::getOdbcFunction(ODBC3SQLFunctionId _nIndex) const +{ + OSL_ENSURE(m_xDriver, "OConnection::getOdbcFunction: m_xDriver is null!"); + return m_xDriver->getOdbcFunction(_nIndex); +} + +SQLRETURN OConnection::OpenConnection(const OUString& aConnectStr, sal_Int32 nTimeOut, bool bSilent) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (m_aConnectionHandle == SQL_NULL_HANDLE) + return -1; + + SQLRETURN nSQLRETURN = 0; + SDB_ODBC_CHAR szConnStrOut[4096] = {}; + SDB_ODBC_CHAR szConnStrIn[2048] = {}; + SQLSMALLINT cbConnStrOut; + OString aConStr(OUStringToOString(aConnectStr,getTextEncoding())); + memcpy(szConnStrIn, aConStr.getStr(), std::min<sal_Int32>(sal_Int32(2048),aConStr.getLength())); + +#ifndef MACOSX + N3SQLSetConnectAttr(m_aConnectionHandle,SQL_ATTR_LOGIN_TIMEOUT,reinterpret_cast<SQLPOINTER>(nTimeOut),SQL_IS_UINTEGER); +#else + (void)nTimeOut; /* WaE */ +#endif + +#ifdef LINUX + (void) bSilent; + nSQLRETURN = N3SQLDriverConnect(m_aConnectionHandle, + nullptr, + szConnStrIn, + static_cast<SQLSMALLINT>(std::min(sal_Int32(2048),aConStr.getLength())), + szConnStrOut, + SQLSMALLINT(sizeof(szConnStrOut)/sizeof(SDB_ODBC_CHAR)) -1, + &cbConnStrOut, + SQL_DRIVER_NOPROMPT); + if (nSQLRETURN == SQL_ERROR || nSQLRETURN == SQL_NO_DATA || SQL_SUCCESS_WITH_INFO == nSQLRETURN) + return nSQLRETURN; +#else + + SQLUSMALLINT nSilent = bSilent ? SQL_DRIVER_NOPROMPT : SQL_DRIVER_COMPLETE; + nSQLRETURN = N3SQLDriverConnect(m_aConnectionHandle, + nullptr, + szConnStrIn, + static_cast<SQLSMALLINT>(std::min<sal_Int32>(sal_Int32(2048),aConStr.getLength())), + szConnStrOut, + SQLSMALLINT(sizeof szConnStrOut), + &cbConnStrOut, + nSilent); + if (nSQLRETURN == SQL_ERROR || nSQLRETURN == SQL_NO_DATA) + return nSQLRETURN; + + m_bClosed = false; + +#endif //LINUX + + try + { + OUString aVal; + OTools::GetInfo(this,m_aConnectionHandle,SQL_DATA_SOURCE_READ_ONLY,aVal,*this,getTextEncoding()); + m_bReadOnly = aVal == "Y"; + } + catch(Exception&) + { + m_bReadOnly = true; + } + try + { + OUString sVersion; + OTools::GetInfo(this,m_aConnectionHandle,SQL_DRIVER_ODBC_VER,sVersion,*this,getTextEncoding()); + m_bUseOldDateFormat = sVersion == "02.50" || sVersion == "02.00"; + } + catch(Exception&) + { + } + + + // autocommit is always default + + if (!m_bReadOnly) + N3SQLSetConnectAttr(m_aConnectionHandle,SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>(SQL_AUTOCOMMIT_ON),SQL_IS_INTEGER); + + return nSQLRETURN; +} + +SQLRETURN OConnection::Construct(const OUString& url,const Sequence< PropertyValue >& info) +{ + m_aConnectionHandle = SQL_NULL_HANDLE; + m_sURL = url; + setConnectionInfo(info); + + N3SQLAllocHandle(SQL_HANDLE_DBC,m_pDriverHandleCopy,&m_aConnectionHandle); + if(m_aConnectionHandle == SQL_NULL_HANDLE) + throw SQLException(); + + sal_Int32 nLen = url.indexOf(':'); + nLen = url.indexOf(':',nLen+2); + OUString aDSN("DSN="), aUID, aPWD, aSysDrvSettings; + aDSN += url.copy(nLen+1); + + sal_Int32 nTimeout = 20; + bool bSilent = true; + const PropertyValue *pBegin = info.getConstArray(); + const PropertyValue *pEnd = pBegin + info.getLength(); + for(;pBegin != pEnd;++pBegin) + { + if( pBegin->Name == "Timeout") + { + if( ! (pBegin->Value >>= nTimeout) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property Timeout"); + } + else if( pBegin->Name == "Silent") + { + if( ! (pBegin->Value >>= bSilent) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property Silent"); + } + else if( pBegin->Name == "IgnoreDriverPrivileges") + { + if( ! (pBegin->Value >>= m_bIgnoreDriverPrivileges) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property IgnoreDriverPrivileges"); + } + else if( pBegin->Name == "PreventGetVersionColumns") + { + if( ! (pBegin->Value >>= m_bPreventGetVersionColumns) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property PreventGetVersionColumns"); + } + else if( pBegin->Name == "IsAutoRetrievingEnabled") + { + bool bAutoRetrievingEnabled = false; + if( ! (pBegin->Value >>= bAutoRetrievingEnabled) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property IsAutoRetrievingEnabled"); + enableAutoRetrievingEnabled(bAutoRetrievingEnabled); + } + else if( pBegin->Name == "AutoRetrievingStatement") + { + OUString sGeneratedValueStatement; + if( ! (pBegin->Value >>= sGeneratedValueStatement) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property AutoRetrievingStatement"); + setAutoRetrievingStatement(sGeneratedValueStatement); + } + else if( pBegin->Name == "user") + { + if( ! (pBegin->Value >>= aUID) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property user"); + aDSN += ";UID=" + aUID; + } + else if( pBegin->Name == "password") + { + if( ! (pBegin->Value >>= aPWD) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property password"); + aDSN += ";PWD=" + aPWD; + } + else if( pBegin->Name == "UseCatalog") + { + if( !( pBegin->Value >>= m_bUseCatalog) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property UseCatalog"); + } + else if( pBegin->Name == "SystemDriverSettings") + { + if( ! (pBegin->Value >>= aSysDrvSettings) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property SystemDriverSettings"); + aDSN += ";" + aSysDrvSettings; + } + else if( pBegin->Name == "CharSet") + { + OUString sIanaName; + if( ! (pBegin->Value >>= sIanaName) ) + SAL_WARN("connectivity.odbc", "Construct: unable to get property CharSet"); + + ::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; + if(m_nTextEncoding == RTL_TEXTENCODING_DONTKNOW) + m_nTextEncoding = osl_getThreadTextEncoding(); + } + } + m_sUser = aUID; + + SQLRETURN nSQLRETURN = OpenConnection(aDSN,nTimeout, bSilent); + if (nSQLRETURN == SQL_ERROR || nSQLRETURN == SQL_NO_DATA) + { + OTools::ThrowException(this,nSQLRETURN,m_aConnectionHandle,SQL_HANDLE_DBC,*this,false); + } + return nSQLRETURN; +} +// XServiceInfo + +IMPLEMENT_SERVICE_INFO(OConnection, "com.sun.star.sdbc.drivers.odbc.OConnection", "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); + + Reference< XPreparedStatement > xReturn = new OPreparedStatement(this,sql); + m_aStatements.push_back(WeakReferenceHelper(xReturn)); + return xReturn; +} + +Reference< XPreparedStatement > SAL_CALL OConnection::prepareCall( const OUString& /*sql*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::prepareCall", *this ); + return nullptr; +} + +OUString SAL_CALL OConnection::nativeSQL( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + OString aSql(OUStringToOString(sql,getTextEncoding())); + char pOut[2048]; + SQLINTEGER nOutLen; + OTools::ThrowException(this,N3SQLNativeSql(m_aConnectionHandle,reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(aSql.getStr())),aSql.getLength(),reinterpret_cast<SDB_ODBC_CHAR*>(pOut),sizeof pOut - 1,&nOutLen),m_aConnectionHandle,SQL_HANDLE_DBC,*this); + return OUString(pOut,nOutLen,getTextEncoding()); +} + +void SAL_CALL OConnection::setAutoCommit( sal_Bool autoCommit ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OTools::ThrowException(this,N3SQLSetConnectAttr(m_aConnectionHandle, + SQL_ATTR_AUTOCOMMIT, + reinterpret_cast<SQLPOINTER>((autoCommit) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF) ,SQL_IS_INTEGER), + m_aConnectionHandle,SQL_HANDLE_DBC,*this); +} + +sal_Bool SAL_CALL OConnection::getAutoCommit( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + sal_uInt32 nOption = 0; + OTools::ThrowException(this,N3SQLGetConnectAttr(m_aConnectionHandle, + SQL_ATTR_AUTOCOMMIT, &nOption,0,nullptr),m_aConnectionHandle,SQL_HANDLE_DBC,*this); + return nOption == SQL_AUTOCOMMIT_ON ; +} + +void SAL_CALL OConnection::commit( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OTools::ThrowException(this,N3SQLEndTran(SQL_HANDLE_DBC,m_aConnectionHandle,SQL_COMMIT),m_aConnectionHandle,SQL_HANDLE_DBC,*this); +} + +void SAL_CALL OConnection::rollback( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OTools::ThrowException(this,N3SQLEndTran(SQL_HANDLE_DBC,m_aConnectionHandle,SQL_ROLLBACK),m_aConnectionHandle,SQL_HANDLE_DBC,*this); +} + +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(m_aConnectionHandle,this); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +void SAL_CALL OConnection::setReadOnly( sal_Bool readOnly ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OTools::ThrowException(this, + N3SQLSetConnectAttr(m_aConnectionHandle,SQL_ATTR_ACCESS_MODE,reinterpret_cast< SQLPOINTER >( readOnly ),SQL_IS_INTEGER), + m_aConnectionHandle,SQL_HANDLE_DBC,*this); +} + +sal_Bool SAL_CALL OConnection::isReadOnly() +{ + // const member which will initialized only once + return m_bReadOnly; +} + +void SAL_CALL OConnection::setCatalog( const OUString& catalog ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OString aCat(OUStringToOString(catalog,getTextEncoding())); + OTools::ThrowException(this, + N3SQLSetConnectAttr(m_aConnectionHandle,SQL_ATTR_CURRENT_CATALOG,const_cast<char *>(aCat.getStr()),SQL_NTS), + m_aConnectionHandle,SQL_HANDLE_DBC,*this); +} + +OUString SAL_CALL OConnection::getCatalog( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + SQLINTEGER nValueLen; + char pCat[1024]; + OTools::ThrowException(this, + N3SQLGetConnectAttr(m_aConnectionHandle,SQL_ATTR_CURRENT_CATALOG,pCat,(sizeof pCat)-1,&nValueLen), + m_aConnectionHandle,SQL_HANDLE_DBC,*this); + + return OUString(pCat,nValueLen,getTextEncoding()); +} + +void SAL_CALL OConnection::setTransactionIsolation( sal_Int32 level ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + OTools::ThrowException(this,N3SQLSetConnectAttr(m_aConnectionHandle, + SQL_ATTR_TXN_ISOLATION, + reinterpret_cast<SQLPOINTER>(level),SQL_IS_INTEGER), + m_aConnectionHandle,SQL_HANDLE_DBC,*this); +} + +sal_Int32 SAL_CALL OConnection::getTransactionIsolation( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + sal_Int32 nTxn = 0; + SQLINTEGER nValueLen; + OTools::ThrowException(this, + N3SQLGetConnectAttr(m_aConnectionHandle,SQL_ATTR_TXN_ISOLATION,&nTxn,sizeof nTxn,&nValueLen), + m_aConnectionHandle,SQL_HANDLE_DBC,*this); + return nTxn; +} + +Reference< css::container::XNameAccess > SAL_CALL OConnection::getTypeMap( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + + return nullptr; +} + +void SAL_CALL OConnection::setTypeMap( const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this ); +} + +// 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(); + + for (auto const& connection : m_aConnections) + connection.second->dispose(); + + m_aConnections.clear(); + + if(!m_bClosed) + N3SQLDisconnect(m_aConnectionHandle); + m_bClosed = true; +} + +SQLHANDLE OConnection::createStatementHandle() +{ + rtl::Reference<OConnection> xConnectionTemp = this; + bool bNew = false; + try + { + sal_Int32 nMaxStatements = getMetaData()->getMaxStatements(); + if(nMaxStatements && nMaxStatements <= m_nStatementCount) + { + rtl::Reference xConnection(new OConnection(m_pDriverHandleCopy,m_xDriver.get())); + xConnection->Construct(m_sURL,getConnectionInfo()); + xConnectionTemp = xConnection; + bNew = true; + } + } + catch(SQLException&) + { + } + + SQLHANDLE aStatementHandle = SQL_NULL_HANDLE; + N3SQLAllocHandle(SQL_HANDLE_STMT,xConnectionTemp->getConnection(),&aStatementHandle); + ++m_nStatementCount; + if(bNew) + m_aConnections.emplace(aStatementHandle,xConnectionTemp); + + return aStatementHandle; + +} + +void OConnection::freeStatementHandle(SQLHANDLE& _pHandle) +{ + if( SQL_NULL_HANDLE == _pHandle ) + return; + + auto aFind = m_aConnections.find(_pHandle); + + N3SQLFreeStmt(_pHandle,SQL_RESET_PARAMS); + N3SQLFreeStmt(_pHandle,SQL_UNBIND); + N3SQLFreeStmt(_pHandle,SQL_CLOSE); + N3SQLFreeHandle(SQL_HANDLE_STMT,_pHandle); + + _pHandle = SQL_NULL_HANDLE; + + if(aFind != m_aConnections.end()) + { + aFind->second->dispose(); + m_aConnections.erase(aFind); + } + --m_nStatementCount; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/ODatabaseMetaData.cxx b/connectivity/source/drivers/odbc/ODatabaseMetaData.cxx new file mode 100644 index 000000000..c9ed165a8 --- /dev/null +++ b/connectivity/source/drivers/odbc/ODatabaseMetaData.cxx @@ -0,0 +1,1717 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <odbc/ODatabaseMetaData.hxx> +#include <odbc/OTools.hxx> +#include <odbc/ODatabaseMetaDataResultSet.hxx> +#include <FDatabaseMetaDataResultSet.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <TPrivilegesResultSet.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +using namespace connectivity::odbc; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + +ODatabaseMetaData::ODatabaseMetaData(const SQLHANDLE _pHandle,OConnection* _pCon) + : ::connectivity::ODatabaseMetaDataBase(_pCon,_pCon->getConnectionInfo()) + ,m_aConnectionHandle(_pHandle) + ,m_pConnection(_pCon) + ,m_bUseCatalog(true) +{ + OSL_ENSURE(m_pConnection,"ODatabaseMetaData::ODatabaseMetaData: No connection set!"); + if(!m_pConnection->isCatalogUsed()) + { + osl_atomic_increment( &m_refCount ); + try + { + m_bUseCatalog = !(usesLocalFiles() || usesLocalFilePerTable()); + } + catch(SQLException& ) + { // doesn't matter here + } + osl_atomic_decrement( &m_refCount ); + } +} + +ODatabaseMetaData::~ODatabaseMetaData() +{ +} + +Reference< XResultSet > ODatabaseMetaData::impl_getTypeInfo_throw( ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openTypeInfo(); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTypeInfo); + } + + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCatalogs( ) +{ + Reference< XResultSet > xRef; + if(!m_bUseCatalog) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eCatalogs); + } + else + { + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openCatalogs(); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eCatalogs); + } + } + + return xRef; +} + +OUString ODatabaseMetaData::impl_getCatalogSeparator_throw( ) +{ + OUString aVal; + if ( m_bUseCatalog ) + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CATALOG_NAME_SEPARATOR,aVal,*this,m_pConnection->getTextEncoding()); + + return aVal; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getSchemas( ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openSchemas(); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eSchemas); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumnPrivileges( + const Any& catalog, const OUString& schema, const OUString& table, + const OUString& columnNamePattern ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openColumnPrivileges(m_bUseCatalog ? catalog : Any(),schema,table,columnNamePattern); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eColumnPrivileges); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumns( + const Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openColumns(m_bUseCatalog ? catalog : Any(),schemaPattern,tableNamePattern,columnNamePattern); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eColumns); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTables( + const Any& catalog, const OUString& schemaPattern, + const OUString& tableNamePattern, const Sequence< OUString >& types ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openTables(m_bUseCatalog ? catalog : Any(),schemaPattern,tableNamePattern,types); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTables); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedureColumns( + const Any& catalog, const OUString& schemaPattern, + const OUString& procedureNamePattern, const OUString& columnNamePattern ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openProcedureColumns(m_bUseCatalog ? catalog : Any(),schemaPattern,procedureNamePattern,columnNamePattern); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eProcedureColumns); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedures( + const Any& catalog, const OUString& schemaPattern, + const OUString& procedureNamePattern ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openProcedures(m_bUseCatalog ? catalog : Any(),schemaPattern,procedureNamePattern); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eProcedures); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getVersionColumns( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + Reference< XResultSet > xRef; + bool bSuccess = false; + try + { + if ( !m_pConnection->preventGetVersionColumns() ) + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openVersionColumns(m_bUseCatalog ? catalog : Any(),schema,table); + bSuccess = true; + } + } + catch(SQLException&) + { + } + + if ( !bSuccess ) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eVersionColumns); + } + + return xRef; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_BINARY_LITERAL_LEN,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxRowSize( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_ROW_SIZE,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCatalogNameLength( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_CATALOG_NAME_LEN,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCharLiteralLength( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_CHAR_LITERAL_LEN,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnNameLength( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_COLUMN_NAME_LEN,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInIndex( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_COLUMNS_IN_INDEX,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCursorNameLength( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_CURSOR_NAME_LEN,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxConnections( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_DRIVER_CONNECTIONS/*SQL_ACTIVE_CONNECTIONS*/,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInTable( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_COLUMNS_IN_TABLE,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatementLength( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_STATEMENT_LEN,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTableNameLength( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_TABLE_NAME_LEN,nValue,*this); + return nValue; +} + +sal_Int32 ODatabaseMetaData::impl_getMaxTablesInSelect_throw( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_TABLES_IN_SELECT,nValue,*this); + return nValue; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getExportedKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openExportedKeys(m_bUseCatalog ? catalog : Any(),schema,table); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eExportedKeys); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getImportedKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openImportedKeys(m_bUseCatalog ? catalog : Any(),schema,table); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eImportedKeys); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getPrimaryKeys( + const Any& catalog, const OUString& schema, const OUString& table ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openPrimaryKeys(m_bUseCatalog ? catalog : Any(),schema,table); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::ePrimaryKeys); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getIndexInfo( + const Any& catalog, const OUString& schema, const OUString& table, + sal_Bool unique, sal_Bool approximate ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openIndexInfo(m_bUseCatalog ? catalog : Any(),schema,table,unique,approximate); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eIndexInfo); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getBestRowIdentifier( + const Any& catalog, const OUString& schema, const OUString& table, sal_Int32 scope, + sal_Bool nullable ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openBestRowIdentifier(m_bUseCatalog ? catalog : Any(),schema,table,scope,nullable); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eBestRowIdentifier); + } + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTablePrivileges( + const Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern ) +{ + if ( m_pConnection->isIgnoreDriverPrivilegesEnabled() ) + { + return new OResultSetPrivileges(this,catalog,schemaPattern,tableNamePattern); + } + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + Reference< XResultSet > xRef = pResult; + pResult->openTablePrivileges(m_bUseCatalog ? catalog : Any(),schemaPattern,tableNamePattern); + return xRef; +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCrossReference( + const Any& primaryCatalog, const OUString& primarySchema, + const OUString& primaryTable, const Any& foreignCatalog, + const OUString& foreignSchema, const OUString& foreignTable ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openForeignKeys(m_bUseCatalog ? primaryCatalog : Any(),primarySchema.toChar() == '%' ? &primarySchema : nullptr,&primaryTable, + m_bUseCatalog ? foreignCatalog : Any(), foreignSchema.toChar() == '%' ? &foreignSchema : nullptr,&foreignTable); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eCrossReference); + } + return xRef; +} + +sal_Bool SAL_CALL ODatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + OUString aVal; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_ROW_SIZE_INCLUDES_LONG,aVal,*this,m_pConnection->getTextEncoding()); + return aVal.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseQuotedIdentifiers( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_QUOTED_IDENTIFIER_CASE,nValue,*this); + return nValue == SQL_IC_LOWER; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseIdentifiers( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_IDENTIFIER_CASE,nValue,*this); + return nValue == SQL_IC_LOWER; +} + +bool ODatabaseMetaData::impl_storesMixedCaseQuotedIdentifiers_throw( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_QUOTED_IDENTIFIER_CASE,nValue,*this); + return nValue == SQL_IC_MIXED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseIdentifiers( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_IDENTIFIER_CASE,nValue,*this); + return nValue == SQL_IC_MIXED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseQuotedIdentifiers( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_QUOTED_IDENTIFIER_CASE,nValue,*this); + return nValue == SQL_IC_UPPER; +} + +sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseIdentifiers( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_IDENTIFIER_CASE,nValue,*this); + return nValue == SQL_IC_UPPER; +} + +bool ODatabaseMetaData::impl_supportsAlterTableWithAddColumn_throw( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_ALTER_TABLE,nValue,*this); + return (nValue & SQL_AT_ADD_COLUMN) == SQL_AT_ADD_COLUMN; +} + +bool ODatabaseMetaData::impl_supportsAlterTableWithDropColumn_throw( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_ALTER_TABLE,nValue,*this); + return ((nValue & SQL_AT_DROP_COLUMN) == SQL_AT_DROP_COLUMN) || + ((nValue & SQL_AT_DROP_COLUMN_CASCADE) == SQL_AT_DROP_COLUMN_CASCADE) || + ((nValue & SQL_AT_DROP_COLUMN_RESTRICT) == SQL_AT_DROP_COLUMN_RESTRICT); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxIndexLength( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_INDEX_SIZE,nValue,*this); + return nValue; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsNonNullableColumns( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_NON_NULLABLE_COLUMNS,nValue,*this); + return nValue == SQL_NNC_NON_NULL; +} + +OUString SAL_CALL ODatabaseMetaData::getCatalogTerm( ) +{ + OUString aVal; + if(m_bUseCatalog) + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CATALOG_TERM,aVal,*this,m_pConnection->getTextEncoding()); + return aVal; +} + +OUString ODatabaseMetaData::impl_getIdentifierQuoteString_throw( ) +{ + OUString aVal; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_IDENTIFIER_QUOTE_CHAR,aVal,*this,m_pConnection->getTextEncoding()); + return aVal; +} + +OUString SAL_CALL ODatabaseMetaData::getExtraNameCharacters( ) +{ + OUString aVal; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SPECIAL_CHARACTERS,aVal,*this,m_pConnection->getTextEncoding()); + return aVal; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDifferentTableCorrelationNames( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + return nValue != SQL_CN_NONE; +} + +bool ODatabaseMetaData::impl_isCatalogAtStart_throw( ) +{ + SQLUSMALLINT nValue=0; + if ( m_bUseCatalog ) + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CATALOG_LOCATION,nValue,*this); + return nValue == SQL_CL_START; +} + +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionIgnoredInTransactions( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_TXN_CAPABLE,nValue,*this); + return nValue == SQL_TC_DDL_IGNORE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionCausesTransactionCommit( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_TXN_CAPABLE,nValue,*this); + return nValue == SQL_TC_DDL_COMMIT; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDataManipulationTransactionsOnly( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_TXN_CAPABLE,nValue,*this); + return nValue == SQL_TC_DML; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsDataDefinitionAndDataManipulationTransactions( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_TXN_CAPABLE,nValue,*this); + return nValue == SQL_TC_ALL; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedDelete( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DYNAMIC_CURSOR_ATTRIBUTES1,nValue,*this); + return (nValue & SQL_CA1_POS_DELETE) == SQL_CA1_POS_DELETE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedUpdate( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DYNAMIC_CURSOR_ATTRIBUTES1,nValue,*this); + return (nValue & SQL_CA1_POS_UPDATE) == SQL_CA1_POS_UPDATE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossRollback( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CURSOR_ROLLBACK_BEHAVIOR,nValue,*this); + return nValue == SQL_CB_PRESERVE || nValue == SQL_CB_CLOSE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossCommit( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CURSOR_COMMIT_BEHAVIOR,nValue,*this); + return nValue == SQL_CB_PRESERVE || nValue == SQL_CB_CLOSE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossCommit( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CURSOR_COMMIT_BEHAVIOR,nValue,*this); + return nValue == SQL_CB_PRESERVE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossRollback( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CURSOR_ROLLBACK_BEHAVIOR,nValue,*this); + return nValue == SQL_CB_PRESERVE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactionIsolationLevel( sal_Int32 level ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_TXN_ISOLATION_OPTION,nValue,*this); + return (nValue & static_cast<SQLUINTEGER>(level)) == static_cast<SQLUINTEGER>(level); +} + +bool ODatabaseMetaData::impl_supportsSchemasInDataManipulation_throw( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SCHEMA_USAGE,nValue,*this); + return (nValue & SQL_SU_DML_STATEMENTS) == SQL_SU_DML_STATEMENTS; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92FullSQL( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SQL_CONFORMANCE,nValue,*this); + return static_cast<bool>(nValue & SQL_SC_SQL92_FULL); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92EntryLevelSQL( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SQL_CONFORMANCE,nValue,*this); + return static_cast<bool>(nValue &SQL_SC_SQL92_ENTRY); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsIntegrityEnhancementFacility( ) +{ + OUString aStr; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_INTEGRITY,aStr,*this,m_pConnection->getTextEncoding()); + return aStr.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInIndexDefinitions( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SCHEMA_USAGE,nValue,*this); + return (nValue & SQL_SU_INDEX_DEFINITION) == SQL_SU_INDEX_DEFINITION; +} + +bool ODatabaseMetaData::impl_supportsSchemasInTableDefinitions_throw( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SCHEMA_USAGE,nValue,*this); + return (nValue & SQL_SU_TABLE_DEFINITION) == SQL_SU_TABLE_DEFINITION; +} + +bool ODatabaseMetaData::impl_supportsCatalogsInTableDefinitions_throw( ) +{ + SQLUINTEGER nValue=0; + if(m_bUseCatalog) + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CATALOG_USAGE,nValue,*this); + return (nValue & SQL_CU_TABLE_DEFINITION) == SQL_CU_TABLE_DEFINITION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInIndexDefinitions( ) +{ + SQLUINTEGER nValue=0; + if(m_bUseCatalog) + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CATALOG_USAGE,nValue,*this); + return (nValue & SQL_CU_INDEX_DEFINITION) == SQL_CU_INDEX_DEFINITION; +} + +bool ODatabaseMetaData::impl_supportsCatalogsInDataManipulation_throw( ) +{ + SQLUINTEGER nValue=0; + if(m_bUseCatalog) + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CATALOG_USAGE,nValue,*this); + return (nValue & SQL_CU_DML_STATEMENTS) == SQL_CU_DML_STATEMENTS; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOuterJoins( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_OJ_CAPABILITIES,nValue,*this); + return ((nValue & (SQL_OJ_FULL|SQL_OJ_LEFT|SQL_OJ_RIGHT|SQL_OJ_NESTED|SQL_OJ_NOT_ORDERED|SQL_OJ_ALL_COMPARISON_OPS|SQL_OJ_INNER)) != 0); +} + +Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTableTypes( ) +{ + Reference< XResultSet > xRef; + try + { + ODatabaseMetaDataResultSet* pResult = new ODatabaseMetaDataResultSet(m_pConnection); + xRef = pResult; + pResult->openTablesTypes(); + } + catch(SQLException&) + { + xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTableTypes); + } + return xRef; +} + +sal_Int32 ODatabaseMetaData::impl_getMaxStatements_throw( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_CONCURRENT_ACTIVITIES,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxProcedureNameLength( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_PROCEDURE_NAME_LEN,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxSchemaNameLength( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_SCHEMA_NAME_LEN,nValue,*this); + return nValue; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactions( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_TXN_CAPABLE,nValue,*this); + return nValue != SQL_TC_NONE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::allProceduresAreCallable( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_ACCESSIBLE_PROCEDURES,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsStoredProcedures( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_PROCEDURES,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSelectForUpdate( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DYNAMIC_CURSOR_ATTRIBUTES1,nValue,*this); + return (nValue & SQL_CA1_POSITIONED_UPDATE) == SQL_CA1_POSITIONED_UPDATE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::allTablesAreSelectable( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_ACCESSIBLE_TABLES,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::isReadOnly( ) +{ + return m_pConnection->isReadOnly(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFiles( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_FILE_USAGE,nValue,*this); + return nValue == SQL_FILE_CATALOG; +} + +sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFilePerTable( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_FILE_USAGE,nValue,*this); + return nValue == SQL_FILE_TABLE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTypeConversion( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_FUNCTIONS,nValue,*this); + return (nValue & SQL_FN_CVT_CONVERT) == SQL_FN_CVT_CONVERT; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullPlusNonNullIsNull( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONCAT_NULL_BEHAVIOR,nValue,*this); + return nValue == SQL_CB_NULL; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsColumnAliasing( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_COLUMN_ALIAS,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsTableCorrelationNames( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + return nValue != SQL_CN_NONE; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsConvert( sal_Int32 fromType, sal_Int32 toType ) +{ + if(fromType == toType) + return true; + + SQLUINTEGER nValue=0; + switch(fromType) + { + case DataType::BIT: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_BIT,nValue,*this); + break; + case DataType::TINYINT: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_TINYINT,nValue,*this); + break; + case DataType::SMALLINT: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_SMALLINT,nValue,*this); + break; + case DataType::INTEGER: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_INTEGER,nValue,*this); + break; + case DataType::BIGINT: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_BIGINT,nValue,*this); + break; + case DataType::FLOAT: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_FLOAT,nValue,*this); + break; + case DataType::REAL: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_REAL,nValue,*this); + break; + case DataType::DOUBLE: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_DOUBLE,nValue,*this); + break; + case DataType::NUMERIC: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_NUMERIC,nValue,*this); + break; + case DataType::DECIMAL: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_DECIMAL,nValue,*this); + break; + case DataType::CHAR: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_CHAR,nValue,*this); + break; + case DataType::VARCHAR: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_VARCHAR,nValue,*this); + break; + case DataType::LONGVARCHAR: + case DataType::CLOB: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_LONGVARCHAR,nValue,*this); + break; + case DataType::DATE: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_DATE,nValue,*this); + break; + case DataType::TIME: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_TIME,nValue,*this); + break; + case DataType::TIMESTAMP: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_TIMESTAMP,nValue,*this); + break; + case DataType::BINARY: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_BINARY,nValue,*this); + break; + case DataType::VARBINARY: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_VARBINARY,nValue,*this); + break; + case DataType::LONGVARBINARY: + case DataType::BLOB: + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CONVERT_LONGVARBINARY,nValue,*this); + break; + case DataType::SQLNULL: + // OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + break; + case DataType::OTHER: + // OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + break; + case DataType::OBJECT: + // OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + break; + case DataType::DISTINCT: + // OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + break; + case DataType::STRUCT: + // OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + break; + case DataType::ARRAY: + // OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + break; + case DataType::REF: + // OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CORRELATION_NAME,nValue,*this); + break; + } + bool bConvert = false; + switch(toType) + { + case DataType::BIT: + bConvert = (nValue & SQL_CVT_BIT) == SQL_CVT_BIT; + break; + case DataType::TINYINT: + bConvert = (nValue & SQL_CVT_TINYINT) == SQL_CVT_TINYINT; + break; + case DataType::SMALLINT: + bConvert = (nValue & SQL_CVT_SMALLINT) == SQL_CVT_SMALLINT; + break; + case DataType::INTEGER: + bConvert = (nValue & SQL_CVT_INTEGER) == SQL_CVT_INTEGER; + break; + case DataType::BIGINT: + bConvert = (nValue & SQL_CVT_BIGINT) == SQL_CVT_BIGINT; + break; + case DataType::FLOAT: + bConvert = (nValue & SQL_CVT_FLOAT) == SQL_CVT_FLOAT; + break; + case DataType::REAL: + bConvert = (nValue & SQL_CVT_REAL) == SQL_CVT_REAL; + break; + case DataType::DOUBLE: + bConvert = (nValue & SQL_CVT_DOUBLE) == SQL_CVT_DOUBLE; + break; + case DataType::NUMERIC: + bConvert = (nValue & SQL_CVT_NUMERIC) == SQL_CVT_NUMERIC; + break; + case DataType::DECIMAL: + bConvert = (nValue & SQL_CVT_DECIMAL) == SQL_CVT_DECIMAL; + break; + case DataType::CHAR: + bConvert = (nValue & SQL_CVT_CHAR) == SQL_CVT_CHAR; + break; + case DataType::VARCHAR: + bConvert = (nValue & SQL_CVT_VARCHAR) == SQL_CVT_VARCHAR; + break; + case DataType::LONGVARCHAR: + case DataType::CLOB: + bConvert = (nValue & SQL_CVT_LONGVARCHAR) == SQL_CVT_LONGVARCHAR; + break; + case DataType::DATE: + bConvert = (nValue & SQL_CVT_DATE) == SQL_CVT_DATE; + break; + case DataType::TIME: + bConvert = (nValue & SQL_CVT_TIME) == SQL_CVT_TIME; + break; + case DataType::TIMESTAMP: + bConvert = (nValue & SQL_CVT_TIMESTAMP) == SQL_CVT_TIMESTAMP; + break; + case DataType::BINARY: + bConvert = (nValue & SQL_CVT_BINARY) == SQL_CVT_BINARY; + break; + case DataType::VARBINARY: + bConvert = (nValue & SQL_CVT_VARBINARY) == SQL_CVT_VARBINARY; + break; + case DataType::LONGVARBINARY: + case DataType::BLOB: + bConvert = (nValue & SQL_CVT_LONGVARBINARY) == SQL_CVT_LONGVARBINARY; + break; + } + + return bConvert; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExpressionsInOrderBy( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_EXPRESSIONS_IN_ORDERBY,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupBy( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_GROUP_BY,nValue,*this); + return nValue != SQL_GB_NOT_SUPPORTED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByBeyondSelect( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_GROUP_BY,nValue,*this); + return nValue != SQL_GB_GROUP_BY_CONTAINS_SELECT; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByUnrelated( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_GROUP_BY,nValue,*this); + return nValue == SQL_GB_NO_RELATION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleTransactions( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MULTIPLE_ACTIVE_TXN,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleResultSets( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MULT_RESULT_SETS,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLikeEscapeClause( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_LIKE_ESCAPE_CLAUSE,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'Y'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsOrderByUnrelated( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_ORDER_BY_COLUMNS_IN_SELECT,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.toChar() == 'N'; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnion( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_UNION,nValue,*this); + return (nValue & SQL_U_UNION) == SQL_U_UNION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsUnionAll( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_UNION,nValue,*this); + return (nValue & SQL_U_UNION_ALL) == SQL_U_UNION_ALL; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseIdentifiers( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_IDENTIFIER_CASE,nValue,*this); + return nValue == SQL_IC_MIXED; +} + +bool ODatabaseMetaData::impl_supportsMixedCaseQuotedIdentifiers_throw( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_QUOTED_IDENTIFIER_CASE,nValue,*this); + return nValue == SQL_IC_MIXED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtEnd( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_NULL_COLLATION,nValue,*this); + return nValue == SQL_NC_END; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtStart( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_NULL_COLLATION,nValue,*this); + return nValue == SQL_NC_START; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedHigh( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_NULL_COLLATION,nValue,*this); + return nValue == SQL_NC_HIGH; +} + +sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedLow( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_NULL_COLLATION,nValue,*this); + return nValue == SQL_NC_LOW; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInProcedureCalls( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SCHEMA_USAGE,nValue,*this); + return (nValue & SQL_SU_PROCEDURE_INVOCATION) == SQL_SU_PROCEDURE_INVOCATION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInPrivilegeDefinitions( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SCHEMA_USAGE,nValue,*this); + return (nValue & SQL_SU_PRIVILEGE_DEFINITION) == SQL_SU_PRIVILEGE_DEFINITION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInProcedureCalls( ) +{ + SQLUINTEGER nValue=0; + if(m_bUseCatalog) + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CATALOG_USAGE,nValue,*this); + return (nValue & SQL_CU_PROCEDURE_INVOCATION) == SQL_CU_PROCEDURE_INVOCATION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInPrivilegeDefinitions( ) +{ + SQLUINTEGER nValue=0; + if(m_bUseCatalog) + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CATALOG_USAGE,nValue,*this); + return (nValue & SQL_CU_PRIVILEGE_DEFINITION) == SQL_CU_PRIVILEGE_DEFINITION; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCorrelatedSubqueries( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SUBQUERIES,nValue,*this); + return (nValue & SQL_SQ_CORRELATED_SUBQUERIES) == SQL_SQ_CORRELATED_SUBQUERIES; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInComparisons( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SUBQUERIES,nValue,*this); + return (nValue & SQL_SQ_COMPARISON) == SQL_SQ_COMPARISON; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInExists( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SUBQUERIES,nValue,*this); + return (nValue & SQL_SQ_EXISTS) == SQL_SQ_EXISTS; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInIns( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SUBQUERIES,nValue,*this); + return (nValue & SQL_SQ_IN) == SQL_SQ_IN; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInQuantifieds( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SUBQUERIES,nValue,*this); + return (nValue & SQL_SQ_QUANTIFIED) == SQL_SQ_QUANTIFIED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92IntermediateSQL( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SQL_CONFORMANCE,nValue,*this); + return static_cast<bool>(nValue & SQL_SC_SQL92_INTERMEDIATE); +} + +OUString ODatabaseMetaData::getURLImpl() +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DATA_SOURCE_NAME,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getURL( ) +{ + OUString aValue = m_pConnection->getURL(); + if ( aValue.isEmpty() ) + { + aValue = "sdbc:odbc:" + getURLImpl(); + } + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getUserName( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_USER_NAME,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getDriverName( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DRIVER_NAME,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getDriverVersion() +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DRIVER_ODBC_VER,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductVersion( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DRIVER_VER,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getDatabaseProductName( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DBMS_NAME,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getProcedureTerm( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_PROCEDURE_TERM,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getSchemaTerm( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SCHEMA_TERM,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMajorVersion( ) try +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DRIVER_VER,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.copy(0,aValue.indexOf('.')).toInt32(); +} +catch (const SQLException &) +{ + return 0; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDefaultTransactionIsolation( ) +{ + SQLUINTEGER nValue; + sal_Int32 nValueTranslated; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DEFAULT_TXN_ISOLATION,nValue,*this); + switch(nValue) + { + case SQL_TXN_READ_UNCOMMITTED: + nValueTranslated = css::sdbc::TransactionIsolation::READ_UNCOMMITTED; + break; + case SQL_TXN_READ_COMMITTED: + nValueTranslated = css::sdbc::TransactionIsolation::READ_COMMITTED; + break; + case SQL_TXN_REPEATABLE_READ: + nValueTranslated = css::sdbc::TransactionIsolation::REPEATABLE_READ; + break; + case SQL_TXN_SERIALIZABLE: + nValueTranslated = css::sdbc::TransactionIsolation::SERIALIZABLE; + break; + default: + nValueTranslated = 0; + } + return nValueTranslated; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMinorVersion( ) try +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_DRIVER_VER,aValue,*this,m_pConnection->getTextEncoding()); + return aValue.copy(0,aValue.lastIndexOf('.')).toInt32(); +} +catch (const SQLException &) +{ + return 0; +} + +OUString SAL_CALL ODatabaseMetaData::getSQLKeywords( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_KEYWORDS,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getSearchStringEscape( ) +{ + OUString aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SEARCH_PATTERN_ESCAPE,aValue,*this,m_pConnection->getTextEncoding()); + return aValue; +} + +OUString SAL_CALL ODatabaseMetaData::getStringFunctions( ) +{ + SQLUINTEGER nValue; + OUStringBuffer aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_STRING_FUNCTIONS,nValue,*this); + if(nValue & SQL_FN_STR_ASCII) + aValue.append("ASCII,"); + if(nValue & SQL_FN_STR_BIT_LENGTH) + aValue.append("BIT_LENGTH,"); + if(nValue & SQL_FN_STR_CHAR) + aValue.append("CHAR,"); + if(nValue & SQL_FN_STR_CHAR_LENGTH) + aValue.append("CHAR_LENGTH,"); + if(nValue & SQL_FN_STR_CHARACTER_LENGTH) + aValue.append("CHARACTER_LENGTH,"); + if(nValue & SQL_FN_STR_CONCAT) + aValue.append("CONCAT,"); + if(nValue & SQL_FN_STR_DIFFERENCE) + aValue.append("DIFFERENCE,"); + if(nValue & SQL_FN_STR_INSERT) + aValue.append("INSERT,"); + if(nValue & SQL_FN_STR_LCASE) + aValue.append("LCASE,"); + if(nValue & SQL_FN_STR_LEFT) + aValue.append("LEFT,"); + if(nValue & SQL_FN_STR_LENGTH) + aValue.append("LENGTH,"); + if(nValue & SQL_FN_STR_LOCATE) + aValue.append("LOCATE,"); + if(nValue & SQL_FN_STR_LOCATE_2) + aValue.append("LOCATE_2,"); + if(nValue & SQL_FN_STR_LTRIM) + aValue.append("LTRIM,"); + if(nValue & SQL_FN_STR_OCTET_LENGTH) + aValue.append("OCTET_LENGTH,"); + if(nValue & SQL_FN_STR_POSITION) + aValue.append("POSITION,"); + if(nValue & SQL_FN_STR_REPEAT) + aValue.append("REPEAT,"); + if(nValue & SQL_FN_STR_REPLACE) + aValue.append("REPLACE,"); + if(nValue & SQL_FN_STR_RIGHT) + aValue.append("RIGHT,"); + if(nValue & SQL_FN_STR_RTRIM) + aValue.append("RTRIM,"); + if(nValue & SQL_FN_STR_SOUNDEX) + aValue.append("SOUNDEX,"); + if(nValue & SQL_FN_STR_SPACE) + aValue.append("SPACE,"); + if(nValue & SQL_FN_STR_SUBSTRING) + aValue.append("SUBSTRING,"); + if(nValue & SQL_FN_STR_UCASE) + aValue.append("UCASE,"); + + + if ( !aValue.isEmpty() ) + aValue.setLength(aValue.getLength()-1); + + return aValue.makeStringAndClear(); +} + +OUString SAL_CALL ODatabaseMetaData::getTimeDateFunctions( ) +{ + SQLUINTEGER nValue; + OUStringBuffer aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_TIMEDATE_FUNCTIONS,nValue,*this); + + if(nValue & SQL_FN_TD_CURRENT_DATE) + aValue.append("CURRENT_DATE,"); + if(nValue & SQL_FN_TD_CURRENT_TIME) + aValue.append("CURRENT_TIME,"); + if(nValue & SQL_FN_TD_CURRENT_TIMESTAMP) + aValue.append("CURRENT_TIMESTAMP,"); + if(nValue & SQL_FN_TD_CURDATE) + aValue.append("CURDATE,"); + if(nValue & SQL_FN_TD_CURTIME) + aValue.append("CURTIME,"); + if(nValue & SQL_FN_TD_DAYNAME) + aValue.append("DAYNAME,"); + if(nValue & SQL_FN_TD_DAYOFMONTH) + aValue.append("DAYOFMONTH,"); + if(nValue & SQL_FN_TD_DAYOFWEEK) + aValue.append("DAYOFWEEK,"); + if(nValue & SQL_FN_TD_DAYOFYEAR) + aValue.append("DAYOFYEAR,"); + if(nValue & SQL_FN_TD_EXTRACT) + aValue.append("EXTRACT,"); + if(nValue & SQL_FN_TD_HOUR) + aValue.append("HOUR,"); + if(nValue & SQL_FN_TD_MINUTE) + aValue.append("MINUTE,"); + if(nValue & SQL_FN_TD_MONTH) + aValue.append("MONTH,"); + if(nValue & SQL_FN_TD_MONTHNAME) + aValue.append("MONTHNAME,"); + if(nValue & SQL_FN_TD_NOW) + aValue.append("NOW,"); + if(nValue & SQL_FN_TD_QUARTER) + aValue.append("QUARTER,"); + if(nValue & SQL_FN_TD_SECOND) + aValue.append("SECOND,"); + if(nValue & SQL_FN_TD_TIMESTAMPADD) + aValue.append("TIMESTAMPADD,"); + if(nValue & SQL_FN_TD_TIMESTAMPDIFF) + aValue.append("TIMESTAMPDIFF,"); + if(nValue & SQL_FN_TD_WEEK) + aValue.append("WEEK,"); + if(nValue & SQL_FN_TD_YEAR) + aValue.append("YEAR,"); + + if ( !aValue.isEmpty() ) + aValue.setLength(aValue.getLength()-1); + + return aValue.makeStringAndClear(); +} + +OUString SAL_CALL ODatabaseMetaData::getSystemFunctions( ) +{ + SQLUINTEGER nValue; + OUStringBuffer aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_SYSTEM_FUNCTIONS,nValue,*this); + + if(nValue & SQL_FN_SYS_DBNAME) + aValue.append("DBNAME,"); + if(nValue & SQL_FN_SYS_IFNULL) + aValue.append("IFNULL,"); + if(nValue & SQL_FN_SYS_USERNAME) + aValue.append("USERNAME,"); + + if ( !aValue.isEmpty() ) + aValue.setLength(aValue.getLength()-1); + + return aValue.makeStringAndClear(); +} + +OUString SAL_CALL ODatabaseMetaData::getNumericFunctions( ) +{ + SQLUINTEGER nValue; + OUStringBuffer aValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_NUMERIC_FUNCTIONS,nValue,*this); + + if(nValue & SQL_FN_NUM_ABS) + aValue.append("ABS,"); + if(nValue & SQL_FN_NUM_ACOS) + aValue.append("ACOS,"); + if(nValue & SQL_FN_NUM_ASIN) + aValue.append("ASIN,"); + if(nValue & SQL_FN_NUM_ATAN) + aValue.append("ATAN,"); + if(nValue & SQL_FN_NUM_ATAN2) + aValue.append("ATAN2,"); + if(nValue & SQL_FN_NUM_CEILING) + aValue.append("CEILING,"); + if(nValue & SQL_FN_NUM_COS) + aValue.append("COS,"); + if(nValue & SQL_FN_NUM_COT) + aValue.append("COT,"); + if(nValue & SQL_FN_NUM_DEGREES) + aValue.append("DEGREES,"); + if(nValue & SQL_FN_NUM_EXP) + aValue.append("EXP,"); + if(nValue & SQL_FN_NUM_FLOOR) + aValue.append("FLOOR,"); + if(nValue & SQL_FN_NUM_LOG) + aValue.append("LOGF,"); + if(nValue & SQL_FN_NUM_LOG10) + aValue.append("LOG10,"); + if(nValue & SQL_FN_NUM_MOD) + aValue.append("MOD,"); + if(nValue & SQL_FN_NUM_PI) + aValue.append("PI,"); + if(nValue & SQL_FN_NUM_POWER) + aValue.append("POWER,"); + if(nValue & SQL_FN_NUM_RADIANS) + aValue.append("RADIANS,"); + if(nValue & SQL_FN_NUM_RAND) + aValue.append("RAND,"); + if(nValue & SQL_FN_NUM_ROUND) + aValue.append("ROUND,"); + if(nValue & SQL_FN_NUM_SIGN) + aValue.append("SIGN,"); + if(nValue & SQL_FN_NUM_SIN) + aValue.append("SIN,"); + if(nValue & SQL_FN_NUM_SQRT) + aValue.append("SQRT,"); + if(nValue & SQL_FN_NUM_TAN) + aValue.append("TAN,"); + if(nValue & SQL_FN_NUM_TRUNCATE) + aValue.append("TRUNCATE,"); + + if ( !aValue.isEmpty() ) + aValue.setLength(aValue.getLength()-1); + + return aValue.makeStringAndClear(); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsExtendedSQLGrammar( ) +{ + SQLUINTEGER nValue; + // SQL_ODBC_SQL_CONFORMANCE is deprecated in ODBC 3.x, but there does not seem te be any equivalent. + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_ODBC_SQL_CONFORMANCE,nValue,*this); + SAL_WARN_IF(! (nValue == SQL_OSC_MINIMUM || nValue == SQL_OSC_CORE || nValue == SQL_OSC_EXTENDED), + "connectivity.odbc", + "SQL_ODBC_SQL_CONFORMANCE is neither MINIMAL nor CORE nor EXTENDED"); + return nValue == SQL_OSC_EXTENDED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsCoreSQLGrammar( ) +{ + SQLUINTEGER nValue; + // SQL_ODBC_SQL_CONFORMANCE is deprecated in ODBC 3.x, but there does not seem te be any equivalent. + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_ODBC_SQL_CONFORMANCE,nValue,*this); + SAL_WARN_IF(! (nValue == SQL_OSC_MINIMUM || nValue == SQL_OSC_CORE || nValue == SQL_OSC_EXTENDED), + "connectivity.odbc", + "SQL_ODBC_SQL_CONFORMANCE is neither MINIMAL nor CORE nor EXTENDED"); + return nValue == SQL_OSC_CORE || nValue == SQL_OSC_EXTENDED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsMinimumSQLGrammar( ) +{ + SQLUINTEGER nValue; + // SQL_ODBC_SQL_CONFORMANCE is deprecated in ODBC 3.x, but there does not seem te be any equivalent. + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_ODBC_SQL_CONFORMANCE,nValue,*this); + SAL_WARN_IF(! (nValue == SQL_OSC_MINIMUM || nValue == SQL_OSC_CORE || nValue == SQL_OSC_EXTENDED), + "connectivity.odbc", + "SQL_ODBC_SQL_CONFORMANCE is neither MINIMAL nor CORE nor EXTENDED"); + return nValue == SQL_OSC_MINIMUM || nValue == SQL_OSC_CORE || nValue == SQL_OSC_EXTENDED; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsFullOuterJoins( ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_OJ_CAPABILITIES,nValue,*this); + return (nValue & SQL_OJ_FULL) == SQL_OJ_FULL; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsLimitedOuterJoins( ) +{ + return supportsFullOuterJoins( ); +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInGroupBy( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_COLUMNS_IN_GROUP_BY,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_COLUMNS_IN_ORDER_BY,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInSelect( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_COLUMNS_IN_SELECT,nValue,*this); + return nValue; +} + +sal_Int32 SAL_CALL ODatabaseMetaData::getMaxUserNameLength( ) +{ + SQLUSMALLINT nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_MAX_USER_NAME_LEN,nValue,*this); + return nValue; +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetType( sal_Int32 setType ) +{ + SQLUINTEGER nValue; + OTools::GetInfo(m_pConnection,m_aConnectionHandle,SQL_CURSOR_SENSITIVITY,nValue,*this); + return (nValue & static_cast<SQLUINTEGER>(setType)) == static_cast<SQLUINTEGER>(setType); +} + +sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 concurrency ) +{ + SQLUINTEGER nValue; + SQLUSMALLINT nAskFor( SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 ); + switch(setType) + { + default: + case ResultSetType::FORWARD_ONLY: + nAskFor = SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2; + break; + case ResultSetType::SCROLL_INSENSITIVE: + nAskFor = SQL_STATIC_CURSOR_ATTRIBUTES2; + break; + case ResultSetType::SCROLL_SENSITIVE: + nAskFor = SQL_DYNAMIC_CURSOR_ATTRIBUTES2; + break; + } + + OTools::GetInfo(m_pConnection,m_aConnectionHandle,nAskFor,nValue,*this); + bool bRet = false; + switch(concurrency) + { + case ResultSetConcurrency::READ_ONLY: + bRet = (nValue & SQL_CA2_READ_ONLY_CONCURRENCY) == SQL_CA2_READ_ONLY_CONCURRENCY; + break; + case ResultSetConcurrency::UPDATABLE: + bRet = (nValue & SQL_CA2_OPT_VALUES_CONCURRENCY) == SQL_CA2_OPT_VALUES_CONCURRENCY; + break; + } + return bRet; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownUpdatesAreVisible( sal_Int32 setType ) +{ + SQLUINTEGER nValue; + SQLUSMALLINT nAskFor( SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 ); + switch(setType) + { + default: + case ResultSetType::FORWARD_ONLY: + nAskFor = SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2; + break; + case ResultSetType::SCROLL_INSENSITIVE: + nAskFor = SQL_STATIC_CURSOR_ATTRIBUTES2; + break; + case ResultSetType::SCROLL_SENSITIVE: + nAskFor = SQL_DYNAMIC_CURSOR_ATTRIBUTES2; + break; + } + + OTools::GetInfo(m_pConnection,m_aConnectionHandle,nAskFor,nValue,*this); + return (nValue & SQL_CA2_SENSITIVITY_UPDATES) == SQL_CA2_SENSITIVITY_UPDATES; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownDeletesAreVisible( sal_Int32 setType ) +{ + SQLUINTEGER nValue; + SQLUSMALLINT nAskFor( SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 ); + switch(setType) + { + default: + case ResultSetType::FORWARD_ONLY: + nAskFor = SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2; + break; + case ResultSetType::SCROLL_INSENSITIVE: + nAskFor = SQL_STATIC_CURSOR_ATTRIBUTES2; + break; + case ResultSetType::SCROLL_SENSITIVE: + nAskFor = SQL_DYNAMIC_CURSOR_ATTRIBUTES2; + break; + } + + OTools::GetInfo(m_pConnection,m_aConnectionHandle,nAskFor,nValue,*this); + return (nValue & SQL_CA2_SENSITIVITY_DELETIONS) != SQL_CA2_SENSITIVITY_DELETIONS; +} + +sal_Bool SAL_CALL ODatabaseMetaData::ownInsertsAreVisible( sal_Int32 setType ) +{ + SQLUINTEGER nValue; + SQLUSMALLINT nAskFor( SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 ); + switch(setType) + { + default: + case ResultSetType::FORWARD_ONLY: + nAskFor = SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2; + break; + case ResultSetType::SCROLL_INSENSITIVE: + nAskFor = SQL_STATIC_CURSOR_ATTRIBUTES2; + break; + case ResultSetType::SCROLL_SENSITIVE: + nAskFor = SQL_DYNAMIC_CURSOR_ATTRIBUTES2; + break; + } + + OTools::GetInfo(m_pConnection,m_aConnectionHandle,nAskFor,nValue,*this); + return (nValue & SQL_CA2_SENSITIVITY_ADDITIONS) == SQL_CA2_SENSITIVITY_ADDITIONS; +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersUpdatesAreVisible( sal_Int32 setType ) +{ + return ownUpdatesAreVisible(setType); +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersDeletesAreVisible( sal_Int32 setType ) +{ + return ownDeletesAreVisible(setType); +} + +sal_Bool SAL_CALL ODatabaseMetaData::othersInsertsAreVisible( sal_Int32 setType ) +{ + return ownInsertsAreVisible(setType); +} + +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/odbc/ODatabaseMetaDataResultSet.cxx b/connectivity/source/drivers/odbc/ODatabaseMetaDataResultSet.cxx new file mode 100644 index 000000000..7afd4bed5 --- /dev/null +++ b/connectivity/source/drivers/odbc/ODatabaseMetaDataResultSet.cxx @@ -0,0 +1,1309 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <TConnection.hxx> + +#include <odbc/ODatabaseMetaDataResultSet.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <comphelper/property.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/sequence.hxx> +#include <odbc/OResultSetMetaData.hxx> +#include <odbc/OTools.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> + +using namespace ::comphelper; + + +using namespace connectivity::odbc; +using namespace cppu; + +using namespace ::com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::util; + + +ODatabaseMetaDataResultSet::ODatabaseMetaDataResultSet(OConnection* _pConnection) + :ODatabaseMetaDataResultSet_BASE(m_aMutex) + ,OPropertySetHelper(ODatabaseMetaDataResultSet_BASE::rBHelper) + + ,m_aStatementHandle(_pConnection->createStatementHandle()) + ,m_aStatement(nullptr) + ,m_pConnection(_pConnection) + ,m_nTextEncoding(_pConnection->getTextEncoding()) + ,m_nRowPos(-1) + ,m_nDriverColumnCount(0) + ,m_nCurrentFetchState(0) + ,m_bWasNull(true) + ,m_bEOF(false) +{ + OSL_ENSURE(m_pConnection.is(),"ODatabaseMetaDataResultSet::ODatabaseMetaDataResultSet: No parent set!"); + if( SQL_NULL_HANDLE == m_aStatementHandle ) + throw RuntimeException(); + + osl_atomic_increment( &m_refCount ); + m_pRowStatusArray.reset( new SQLUSMALLINT[1] ); // the default value + osl_atomic_decrement( &m_refCount ); +} + + +ODatabaseMetaDataResultSet::~ODatabaseMetaDataResultSet() +{ + OSL_ENSURE(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed,"Object wasn't disposed!"); + if(!ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed) + { + osl_atomic_increment( &m_refCount ); + dispose(); + } +} + +void ODatabaseMetaDataResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + + m_pConnection->freeStatementHandle(m_aStatementHandle); + + m_aStatement = nullptr; + m_xMetaData.clear(); + m_pConnection.clear(); +} + +Any SAL_CALL ODatabaseMetaDataResultSet::queryInterface( const Type & rType ) +{ + Any aRet = OPropertySetHelper::queryInterface(rType); + return aRet.hasValue() ? aRet : ODatabaseMetaDataResultSet_BASE::queryInterface(rType); +} + +Reference< XPropertySetInfo > SAL_CALL ODatabaseMetaDataResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +void SAL_CALL ODatabaseMetaDataResultSet::acquire() throw() +{ + ODatabaseMetaDataResultSet_BASE::acquire(); +} + +void SAL_CALL ODatabaseMetaDataResultSet::release() throw() +{ + ODatabaseMetaDataResultSet_BASE::release(); +} + +Sequence< Type > SAL_CALL ODatabaseMetaDataResultSet::getTypes( ) +{ + ::cppu::OTypeCollection aTypes( cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get()); + + return ::comphelper::concatSequences(aTypes.getTypes(),ODatabaseMetaDataResultSet_BASE::getTypes()); +} + +sal_Int32 ODatabaseMetaDataResultSet::mapColumn (sal_Int32 column) +{ + sal_Int32 map = column; + + if (!m_aColMapping.empty()) + { + // Validate column number + map = m_aColMapping[column]; + } + + return map; +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::findColumn( const OUString& columnName ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + 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 +} + +template < typename T, SQLSMALLINT sqlTypeId > T ODatabaseMetaDataResultSet::getInteger ( sal_Int32 columnIndex ) +{ + ::cppu::OBroadcastHelper& rBHelper(ODatabaseMetaDataResultSet_BASE::rBHelper); + checkDisposed(rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + columnIndex = mapColumn(columnIndex); + T nVal = 0; + if(columnIndex <= m_nDriverColumnCount) + { + getValue<T>(m_pConnection.get(), m_aStatementHandle, columnIndex, sqlTypeId, m_bWasNull, **this, nVal); + + if ( !m_aValueRange.empty() ) + { + auto aValueRangeIter = m_aValueRange.find(columnIndex); + if ( aValueRangeIter != m_aValueRange.end() ) + return static_cast<T>(aValueRangeIter->second[nVal]); + } + } + else + m_bWasNull = true; + return nVal; +} + + +Reference< css::io::XInputStream > SAL_CALL ODatabaseMetaDataResultSet::getBinaryStream( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBinaryStream", *this ); + return nullptr; +} + +Reference< css::io::XInputStream > SAL_CALL ODatabaseMetaDataResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getCharacterStream", *this ); + return nullptr; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::getBoolean( sal_Int32 columnIndex ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + columnIndex = mapColumn(columnIndex); + + bool bRet = false; + if(columnIndex <= m_nDriverColumnCount) + { + sal_Int32 nType = getMetaData()->getColumnType(columnIndex); + switch(nType) + { + case DataType::BIT: + { + sal_Int8 nValue = 0; + OTools::getValue(m_pConnection.get(),m_aStatementHandle,columnIndex,SQL_C_BIT,m_bWasNull,**this,&nValue,sizeof nValue); + bRet = nValue != 0; + } + break; + default: + bRet = getInt(columnIndex) != 0; + } + } + return bRet; +} + + +sal_Int8 SAL_CALL ODatabaseMetaDataResultSet::getByte( sal_Int32 columnIndex ) +{ + return getInteger<sal_Int8, SQL_C_STINYINT>( columnIndex ); +} + + +Sequence< sal_Int8 > SAL_CALL ODatabaseMetaDataResultSet::getBytes( sal_Int32 columnIndex ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + columnIndex = mapColumn(columnIndex); + if(columnIndex <= m_nDriverColumnCount) + { + sal_Int32 nType = getMetaData()->getColumnType(columnIndex); + switch(nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + { + OUString const & aRet = OTools::getStringValue(m_pConnection.get(),m_aStatementHandle,columnIndex,SQL_C_BINARY,m_bWasNull,**this,m_nTextEncoding); + return Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),sizeof(sal_Unicode)*aRet.getLength()); + } + } + return OTools::getBytesValue(m_pConnection.get(),m_aStatementHandle,columnIndex,SQL_C_BINARY,m_bWasNull,**this); + } + else + m_bWasNull = true; + return Sequence<sal_Int8>(); +} + + +css::util::Date SAL_CALL ODatabaseMetaDataResultSet::getDate( sal_Int32 columnIndex ) +{ + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + columnIndex = mapColumn(columnIndex); + if(columnIndex <= m_nDriverColumnCount) + { + DATE_STRUCT aDate; + aDate.day = 0; + aDate.month = 0; + aDate.year = 0; + OTools::getValue(m_pConnection.get(),m_aStatementHandle,columnIndex,m_pConnection->useOldDateFormat() ? SQL_C_DATE : SQL_C_TYPE_DATE,m_bWasNull,**this,&aDate,sizeof aDate); + return Date(aDate.day,aDate.month,aDate.year); + } + else + m_bWasNull = true; + return Date(); +} + + +double SAL_CALL ODatabaseMetaDataResultSet::getDouble( sal_Int32 columnIndex ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + columnIndex = mapColumn(columnIndex); + double nValue(0.0); + if(columnIndex <= m_nDriverColumnCount) + OTools::getValue(m_pConnection.get(),m_aStatementHandle,columnIndex,SQL_C_DOUBLE,m_bWasNull,**this,&nValue,sizeof nValue); + else + m_bWasNull = true; + return nValue; +} + + +float SAL_CALL ODatabaseMetaDataResultSet::getFloat( sal_Int32 columnIndex ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + columnIndex = mapColumn(columnIndex); + float nVal(0); + if(columnIndex <= m_nDriverColumnCount) + OTools::getValue(m_pConnection.get(),m_aStatementHandle,columnIndex,SQL_C_FLOAT,m_bWasNull,**this,&nVal,sizeof nVal); + else + m_bWasNull = true; + return nVal; +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::getInt( sal_Int32 columnIndex ) +{ + return getInteger<sal_Int32, SQL_C_SLONG>( columnIndex ); +} + + +sal_Int32 SAL_CALL ODatabaseMetaDataResultSet::getRow( ) +{ + return 0; +} + + +sal_Int64 SAL_CALL ODatabaseMetaDataResultSet::getLong( sal_Int32 columnIndex ) +{ + return getInteger<sal_Int64, SQL_C_SBIGINT>( columnIndex ); +} + + +Reference< XResultSetMetaData > SAL_CALL ODatabaseMetaDataResultSet::getMetaData( ) +{ + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + if (!m_xMetaData.is()) + m_xMetaData = new OResultSetMetaData(m_pConnection.get(),m_aStatementHandle); + return m_xMetaData; +} + +Reference< XArray > SAL_CALL ODatabaseMetaDataResultSet::getArray( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getArray", *this ); + return nullptr; +} + +Reference< XClob > SAL_CALL ODatabaseMetaDataResultSet::getClob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getClob", *this ); + return nullptr; +} + +Reference< XBlob > SAL_CALL ODatabaseMetaDataResultSet::getBlob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBlob", *this ); + return nullptr; +} + + +Reference< XRef > SAL_CALL ODatabaseMetaDataResultSet::getRef( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getRef", *this ); + return nullptr; +} + + +Any SAL_CALL ODatabaseMetaDataResultSet::getObject( sal_Int32 /*columnIndex*/, const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getObject", *this ); + return Any(); +} + + +sal_Int16 SAL_CALL ODatabaseMetaDataResultSet::getShort( sal_Int32 columnIndex ) +{ + return getInteger<sal_Int16, SQL_C_SSHORT>( columnIndex ); +} + + +OUString SAL_CALL ODatabaseMetaDataResultSet::getString( sal_Int32 columnIndex ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + columnIndex = mapColumn(columnIndex); + OUString aVal; + if(columnIndex <= m_nDriverColumnCount) + aVal = OTools::getStringValue(m_pConnection.get(),m_aStatementHandle,columnIndex,impl_getColumnType_nothrow(columnIndex),m_bWasNull,**this,m_nTextEncoding); + else + m_bWasNull = true; + + return aVal; +} + + +css::util::Time SAL_CALL ODatabaseMetaDataResultSet::getTime( sal_Int32 columnIndex ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + columnIndex = mapColumn(columnIndex); + TIME_STRUCT aTime={0,0,0}; + if(columnIndex <= m_nDriverColumnCount) + OTools::getValue(m_pConnection.get(),m_aStatementHandle,columnIndex,m_pConnection->useOldDateFormat() ? SQL_C_TIME : SQL_C_TYPE_TIME,m_bWasNull,**this,&aTime,sizeof aTime); + else + m_bWasNull = true; + return Time(0, aTime.second,aTime.minute,aTime.hour, false); +} + + +css::util::DateTime SAL_CALL ODatabaseMetaDataResultSet::getTimestamp( sal_Int32 columnIndex ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + columnIndex = mapColumn(columnIndex); + TIMESTAMP_STRUCT aTime={0,0,0,0,0,0,0}; + if(columnIndex <= m_nDriverColumnCount) + OTools::getValue(m_pConnection.get(),m_aStatementHandle,columnIndex,m_pConnection->useOldDateFormat() ? SQL_C_TIMESTAMP : SQL_C_TYPE_TIMESTAMP, m_bWasNull, **this, &aTime, sizeof aTime); + else + m_bWasNull = true; + return DateTime(aTime.fraction, aTime.second, aTime.minute, aTime.hour, + aTime.day, aTime.month, aTime.year, false); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isAfterLast( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + return m_nCurrentFetchState == SQL_NO_DATA; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isFirst( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + return m_nRowPos == 1; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isLast( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + return m_bEOF && m_nCurrentFetchState != SQL_NO_DATA; +} + +void SAL_CALL ODatabaseMetaDataResultSet::beforeFirst( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + if(first()) + previous(); + m_nCurrentFetchState = SQL_SUCCESS; +} + +void SAL_CALL ODatabaseMetaDataResultSet::afterLast( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + if(last()) + next(); +} + + +void SAL_CALL ODatabaseMetaDataResultSet::close( ) +{ + { + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + } + dispose(); +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::first( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + m_bEOF = false; + + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_FIRST,0); + OTools::ThrowException(m_pConnection.get(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + bool bRet = ( m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO ); + if( bRet ) + m_nRowPos = 1; + return bRet; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::last( ) +{ + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed ); + ::osl::MutexGuard aGuard( m_aMutex ); + + + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_LAST,0); + OTools::ThrowException(m_pConnection.get(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + // here I know definitely that I stand on the last record + bool bRet = ( m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO ); + if( bRet ) + m_bEOF = true; + return bRet; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::absolute( sal_Int32 row ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + m_bEOF = false; + + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_ABSOLUTE,row); + OTools::ThrowException(m_pConnection.get(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + bool bRet = m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO; + if(bRet) + m_nRowPos = row; + return bRet; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::relative( sal_Int32 row ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + m_bEOF = false; + + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_RELATIVE,row); + OTools::ThrowException(m_pConnection.get(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + bool bRet = m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO; + if(bRet) + m_nRowPos += row; + return bRet; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::previous( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + m_bEOF = false; + + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_PRIOR,0); + OTools::ThrowException(m_pConnection.get(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + bool bRet = m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO; + if(bRet) + --m_nRowPos; + else if ( m_nCurrentFetchState == SQL_NO_DATA ) + m_nRowPos = 0; + return bRet; +} + +Reference< XInterface > SAL_CALL ODatabaseMetaDataResultSet::getStatement( ) +{ + return nullptr; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowDeleted( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + return m_pRowStatusArray[0] == SQL_ROW_DELETED; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowInserted( ) +{ + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + return m_pRowStatusArray[0] == SQL_ROW_ADDED; +} + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::rowUpdated( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + return m_pRowStatusArray[0] == SQL_ROW_UPDATED; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::isBeforeFirst( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + return m_nRowPos == 0; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::next( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + m_bEOF = false; + + SQLRETURN nOldFetchStatus = m_nCurrentFetchState; + // m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_NEXT,0); + m_nCurrentFetchState = N3SQLFetch(m_aStatementHandle); + OTools::ThrowException(m_pConnection.get(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + bool bRet = m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO; + if(bRet || ( m_nCurrentFetchState == SQL_NO_DATA && nOldFetchStatus != SQL_NO_DATA ) ) + ++m_nRowPos; + return bRet; +} + + +sal_Bool SAL_CALL ODatabaseMetaDataResultSet::wasNull( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + return m_bWasNull; +} + +void SAL_CALL ODatabaseMetaDataResultSet::refreshRow( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + +} + + +void SAL_CALL ODatabaseMetaDataResultSet::cancel( ) +{ + + checkDisposed(ODatabaseMetaDataResultSet_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + + N3SQLCancel(m_aStatementHandle); +} + +void SAL_CALL ODatabaseMetaDataResultSet::clearWarnings( ) +{ +} + +Any SAL_CALL ODatabaseMetaDataResultSet::getWarnings( ) +{ + return Any(); +} + +sal_Int32 ODatabaseMetaDataResultSet::getFetchSize() +{ + return 1; +} + +OUString ODatabaseMetaDataResultSet::getCursorName() +{ + return OUString(); +} + + +::cppu::IPropertyArrayHelper* ODatabaseMetaDataResultSet::createArrayHelper( ) const +{ + + Sequence< css::beans::Property > aProps(5); + css::beans::Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + +::cppu::IPropertyArrayHelper & ODatabaseMetaDataResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool ODatabaseMetaDataResultSet::convertFastPropertyValue( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + case PROPERTY_ID_FETCHDIRECTION: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection()); + case PROPERTY_ID_FETCHSIZE: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize()); + default: + ; + } + return false; +} + +void ODatabaseMetaDataResultSet::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& /*rValue*/ ) +{ + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + case PROPERTY_ID_FETCHDIRECTION: + case PROPERTY_ID_FETCHSIZE: + throw Exception("cannot set prop " + OUString::number(nHandle), nullptr); + default: + OSL_FAIL("setFastPropertyValue_NoBroadcast: Illegal handle value!"); + } +} + +void ODatabaseMetaDataResultSet::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + switch(nHandle) + { + case PROPERTY_ID_CURSORNAME: + rValue <<= getCursorName(); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= sal_Int32(css::sdbc::ResultSetConcurrency::READ_ONLY); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= sal_Int32(css::sdbc::ResultSetType::FORWARD_ONLY); + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= getFetchDirection(); + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= getFetchSize(); + break; + } +} + +void ODatabaseMetaDataResultSet::openTypeInfo() +{ + ::std::map<sal_Int32,sal_Int32> aMap; + aMap[SQL_BIT] = DataType::BIT; + aMap[SQL_TINYINT] = DataType::TINYINT; + aMap[SQL_SMALLINT] = DataType::SMALLINT; + aMap[SQL_INTEGER] = DataType::INTEGER; + aMap[SQL_FLOAT] = DataType::FLOAT; + aMap[SQL_REAL] = DataType::REAL; + aMap[SQL_DOUBLE] = DataType::DOUBLE; + aMap[SQL_BIGINT] = DataType::BIGINT; + + aMap[SQL_CHAR] = DataType::CHAR; + aMap[SQL_WCHAR] = DataType::CHAR; + aMap[SQL_VARCHAR] = DataType::VARCHAR; + aMap[SQL_WVARCHAR] = DataType::VARCHAR; + aMap[SQL_LONGVARCHAR] = DataType::LONGVARCHAR; + aMap[SQL_WLONGVARCHAR] = DataType::LONGVARCHAR; + + aMap[SQL_TYPE_DATE] = DataType::DATE; + aMap[SQL_DATE] = DataType::DATE; + aMap[SQL_TYPE_TIME] = DataType::TIME; + aMap[SQL_TIME] = DataType::TIME; + aMap[SQL_TYPE_TIMESTAMP] = DataType::TIMESTAMP; + aMap[SQL_TIMESTAMP] = DataType::TIMESTAMP; + + aMap[SQL_DECIMAL] = DataType::DECIMAL; + aMap[SQL_NUMERIC] = DataType::NUMERIC; + + aMap[SQL_BINARY] = DataType::BINARY; + aMap[SQL_VARBINARY] = DataType::VARBINARY; + aMap[SQL_LONGVARBINARY] = DataType::LONGVARBINARY; + + aMap[SQL_GUID] = DataType::VARBINARY; + + + m_aValueRange[2] = aMap; + + OTools::ThrowException(m_pConnection.get(),N3SQLGetTypeInfo(m_aStatementHandle, SQL_ALL_TYPES),m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openTables(const Any& catalog, const OUString& schemaPattern, + const OUString& tableNamePattern, + const Sequence< OUString >& types ) +{ + OString aPKQ,aPKO,aPKN,aCOL; + const OUString *pSchemaPat = nullptr; + + if(schemaPattern != "%") + pSchemaPat = &schemaPattern; + else + pSchemaPat = nullptr; + + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schemaPattern,m_nTextEncoding); + aPKN = OUStringToOString(tableNamePattern,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(); + + + const char *pCOL = nullptr; + const char* const pComma = ","; + const OUString* pBegin = types.getConstArray(); + const OUString* pEnd = pBegin + types.getLength(); + for(;pBegin != pEnd;++pBegin) + { + aCOL += OUStringToOString(*pBegin,m_nTextEncoding) + pComma; + } + if ( !aCOL.isEmpty() ) + { + aCOL = aCOL.replaceAt(aCOL.getLength()-1,1,pComma); + pCOL = aCOL.getStr(); + } + else + pCOL = SQL_ALL_TABLE_TYPES; + + SQLRETURN nRetcode = N3SQLTables(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pCOL)), pCOL ? SQL_NTS : 0); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); + +} + +void ODatabaseMetaDataResultSet::openTablesTypes( ) +{ + SQLRETURN nRetcode = N3SQLTables(m_aStatementHandle, + nullptr,0, + nullptr,0, + nullptr,0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(SQL_ALL_TABLE_TYPES)),SQL_NTS); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + + m_aColMapping.clear(); + m_aColMapping.push_back(-1); + m_aColMapping.push_back(4); + m_xMetaData = new OResultSetMetaData(m_pConnection.get(),m_aStatementHandle,m_aColMapping); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openCatalogs() +{ + SQLRETURN nRetcode = N3SQLTables(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(SQL_ALL_CATALOGS)),SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>("")),SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>("")),SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>("")),SQL_NTS); + + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + + m_aColMapping.clear(); + m_aColMapping.push_back(-1); + m_aColMapping.push_back(1); + m_xMetaData = new OResultSetMetaData(m_pConnection.get(),m_aStatementHandle,m_aColMapping); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openSchemas() +{ + SQLRETURN nRetcode = N3SQLTables(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>("")),SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(SQL_ALL_SCHEMAS)),SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>("")),SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>("")),SQL_NTS); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + + m_aColMapping.clear(); + m_aColMapping.push_back(-1); + m_aColMapping.push_back(2); + m_xMetaData = new OResultSetMetaData(m_pConnection.get(),m_aStatementHandle,m_aColMapping); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openColumnPrivileges( const Any& catalog, const OUString& schema, + const OUString& table, const OUString& columnNamePattern ) +{ + const OUString *pSchemaPat = nullptr; + + if(schema != "%") + pSchemaPat = &schema; + else + pSchemaPat = nullptr; + + OString aPKQ,aPKO,aPKN,aCOL; + + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schema,m_nTextEncoding); + aPKN = OUStringToOString(table,m_nTextEncoding); + aCOL = OUStringToOString(columnNamePattern,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(), + *pCOL = aCOL.getStr(); + + + SQLRETURN nRetcode = N3SQLColumnPrivileges(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0 , + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pCOL)), SQL_NTS); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openColumns( const Any& catalog, const OUString& schemaPattern, + const OUString& tableNamePattern, const OUString& columnNamePattern ) +{ + const OUString *pSchemaPat = nullptr; + + if(schemaPattern != "%") + pSchemaPat = &schemaPattern; + else + pSchemaPat = nullptr; + + OString aPKQ,aPKO,aPKN,aCOL; + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schemaPattern,m_nTextEncoding); + aPKN = OUStringToOString(tableNamePattern,m_nTextEncoding); + aCOL = OUStringToOString(columnNamePattern,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(), + *pCOL = aCOL.getStr(); + + + SQLRETURN nRetcode = N3SQLColumns(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pCOL)), SQL_NTS); + + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + ::std::map<sal_Int32,sal_Int32> aMap; + aMap[SQL_BIT] = DataType::BIT; + aMap[SQL_TINYINT] = DataType::TINYINT; + aMap[SQL_SMALLINT] = DataType::SMALLINT; + aMap[SQL_INTEGER] = DataType::INTEGER; + aMap[SQL_FLOAT] = DataType::FLOAT; + aMap[SQL_REAL] = DataType::REAL; + aMap[SQL_DOUBLE] = DataType::DOUBLE; + aMap[SQL_BIGINT] = DataType::BIGINT; + + aMap[SQL_CHAR] = DataType::CHAR; + aMap[SQL_WCHAR] = DataType::CHAR; + aMap[SQL_VARCHAR] = DataType::VARCHAR; + aMap[SQL_WVARCHAR] = DataType::VARCHAR; + aMap[SQL_LONGVARCHAR] = DataType::LONGVARCHAR; + aMap[SQL_WLONGVARCHAR] = DataType::LONGVARCHAR; + + aMap[SQL_TYPE_DATE] = DataType::DATE; + aMap[SQL_DATE] = DataType::DATE; + aMap[SQL_TYPE_TIME] = DataType::TIME; + aMap[SQL_TIME] = DataType::TIME; + aMap[SQL_TYPE_TIMESTAMP] = DataType::TIMESTAMP; + aMap[SQL_TIMESTAMP] = DataType::TIMESTAMP; + + aMap[SQL_DECIMAL] = DataType::DECIMAL; + aMap[SQL_NUMERIC] = DataType::NUMERIC; + + aMap[SQL_BINARY] = DataType::BINARY; + aMap[SQL_VARBINARY] = DataType::VARBINARY; + aMap[SQL_LONGVARBINARY] = DataType::LONGVARBINARY; + + aMap[SQL_GUID] = DataType::VARBINARY; + + m_aValueRange[5] = aMap; + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openProcedureColumns( const Any& catalog, const OUString& schemaPattern, + const OUString& procedureNamePattern,const OUString& columnNamePattern ) +{ + const OUString *pSchemaPat = nullptr; + + if(schemaPattern != "%") + pSchemaPat = &schemaPattern; + else + pSchemaPat = nullptr; + + OString aPKQ,aPKO,aPKN,aCOL; + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schemaPattern,m_nTextEncoding); + aPKN = OUStringToOString(procedureNamePattern,m_nTextEncoding); + aCOL = OUStringToOString(columnNamePattern,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(), + *pCOL = aCOL.getStr(); + + + SQLRETURN nRetcode = N3SQLProcedureColumns(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0 , + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pCOL)), SQL_NTS); + + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openProcedures(const Any& catalog, const OUString& schemaPattern, + const OUString& procedureNamePattern) +{ + const OUString *pSchemaPat = nullptr; + + if(schemaPattern != "%") + pSchemaPat = &schemaPattern; + else + pSchemaPat = nullptr; + + OString aPKQ,aPKO,aPKN; + + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schemaPattern,m_nTextEncoding); + aPKN = OUStringToOString(procedureNamePattern,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(); + + + SQLRETURN nRetcode = N3SQLProcedures(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0 , + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openSpecialColumns(bool _bRowVer,const Any& catalog, const OUString& schema, + const OUString& table,sal_Int32 scope, bool nullable ) +{ + // Some ODBC drivers really don't like getting an empty string as tableName + // E.g. psqlodbc up to at least version 09.01.0100 segfaults + if (table.isEmpty()) + { + const char errMsg[] = "ODBC: Trying to get special columns of empty table name"; + const char SQLState[] = "HY009"; + throw SQLException( errMsg, *this, SQLState, -1, Any() ); + } + + const OUString *pSchemaPat = nullptr; + + if(schema != "%") + pSchemaPat = &schema; + else + pSchemaPat = nullptr; + + OString aPKQ,aPKO,aPKN; + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schema,m_nTextEncoding); + aPKN = OUStringToOString(table,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(); + + + SQLRETURN nRetcode = N3SQLSpecialColumns(m_aStatementHandle,_bRowVer ? SQL_ROWVER : SQL_BEST_ROWID, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0 , + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS, + static_cast<SQLSMALLINT>(scope), + nullable ? SQL_NULLABLE : SQL_NO_NULLS); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openVersionColumns(const Any& catalog, const OUString& schema, + const OUString& table) +{ + openSpecialColumns(true,catalog,schema,table,SQL_SCOPE_TRANSACTION,false); +} + +void ODatabaseMetaDataResultSet::openBestRowIdentifier( const Any& catalog, const OUString& schema, + const OUString& table,sal_Int32 scope,bool nullable ) +{ + openSpecialColumns(false,catalog,schema,table,scope,nullable); +} + +void ODatabaseMetaDataResultSet::openForeignKeys( const Any& catalog, const OUString* schema, + const OUString* table, + const Any& catalog2, const OUString* schema2, + const OUString* table2) +{ + OString aPKQ, aPKO, aPKN, aFKQ, aFKO, aFKN; + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + if ( catalog2.hasValue() ) + aFKQ = OUStringToOString(comphelper::getString(catalog2),m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr; + const char *pPKO = nullptr; + if (schema && !schema->isEmpty()) + { + aPKO = OUStringToOString(*schema,m_nTextEncoding); + pPKO = aPKO.getStr(); + } + const char *pPKN = nullptr; + if (table) + { + aPKN = OUStringToOString(*table,m_nTextEncoding); + pPKN = aPKN.getStr(); + } + const char *pFKQ = catalog2.hasValue() && !aFKQ.isEmpty() ? aFKQ.getStr() : nullptr; + const char *pFKO = nullptr; + if (schema2 && !schema2->isEmpty()) + { + aFKO = OUStringToOString(*schema2,m_nTextEncoding); + pFKO = aFKO.getStr(); + } + const char *pFKN = nullptr; + if (table2) + { + aFKN = OUStringToOString(*table2,m_nTextEncoding); + pFKN = aFKN.getStr(); + } + + SQLRETURN nRetcode = N3SQLForeignKeys(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), pPKN ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pFKQ)), (catalog2.hasValue() && !aFKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pFKO)), pFKO ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pFKN)), SQL_NTS + ); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openImportedKeys(const Any& catalog, const OUString& schema, + const OUString& table) +{ + + openForeignKeys(Any(),nullptr,nullptr,catalog, schema == "%" ? &schema : nullptr, &table); +} + +void ODatabaseMetaDataResultSet::openExportedKeys(const Any& catalog, const OUString& schema, + const OUString& table) +{ + openForeignKeys(catalog, schema == "%" ? &schema : nullptr, &table,Any(),nullptr,nullptr); +} + +void ODatabaseMetaDataResultSet::openPrimaryKeys(const Any& catalog, const OUString& schema, + const OUString& table) +{ + const OUString *pSchemaPat = nullptr; + + if(schema != "%") + pSchemaPat = &schema; + else + pSchemaPat = nullptr; + + OString aPKQ,aPKO,aPKN; + + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schema,m_nTextEncoding); + aPKN = OUStringToOString(table,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(); + + + SQLRETURN nRetcode = N3SQLPrimaryKeys(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0 , + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openTablePrivileges(const Any& catalog, const OUString& schemaPattern, + const OUString& tableNamePattern) +{ + const OUString *pSchemaPat = nullptr; + + if(schemaPattern != "%") + pSchemaPat = &schemaPattern; + else + pSchemaPat = nullptr; + + OString aPKQ,aPKO,aPKN; + + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schemaPattern,m_nTextEncoding); + aPKN = OUStringToOString(tableNamePattern,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(); + + SQLRETURN nRetcode = N3SQLTablePrivileges(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0 , + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::openIndexInfo( const Any& catalog, const OUString& schema, + const OUString& table, bool unique, bool approximate ) +{ + const OUString *pSchemaPat = nullptr; + + if(schema != "%") + pSchemaPat = &schema; + else + pSchemaPat = nullptr; + + OString aPKQ,aPKO,aPKN; + + if ( catalog.hasValue() ) + aPKQ = OUStringToOString(comphelper::getString(catalog),m_nTextEncoding); + aPKO = OUStringToOString(schema,m_nTextEncoding); + aPKN = OUStringToOString(table,m_nTextEncoding); + + const char *pPKQ = catalog.hasValue() && !aPKQ.isEmpty() ? aPKQ.getStr() : nullptr, + *pPKO = pSchemaPat && !pSchemaPat->isEmpty() && !aPKO.isEmpty() ? aPKO.getStr() : nullptr, + *pPKN = aPKN.getStr(); + + SQLRETURN nRetcode = N3SQLStatistics(m_aStatementHandle, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKQ)), (catalog.hasValue() && !aPKQ.isEmpty()) ? SQL_NTS : 0, + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKO)), pPKO ? SQL_NTS : 0 , + reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(pPKN)), SQL_NTS, + unique ? SQL_INDEX_UNIQUE : SQL_INDEX_ALL, + approximate ? 1 : 0); + OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this); + checkColumnCount(); +} + +void ODatabaseMetaDataResultSet::checkColumnCount() +{ + sal_Int16 nNumResultCols=0; + OTools::ThrowException(m_pConnection.get(),N3SQLNumResultCols(m_aStatementHandle,&nNumResultCols),m_aStatementHandle,SQL_HANDLE_STMT,*this); + m_nDriverColumnCount = nNumResultCols; +} + + +SWORD ODatabaseMetaDataResultSet::impl_getColumnType_nothrow(sal_Int32 columnIndex) +{ + std::map<sal_Int32,SWORD>::iterator aFind = m_aODBCColumnTypes.find(columnIndex); + if ( aFind == m_aODBCColumnTypes.end() ) + aFind = m_aODBCColumnTypes.emplace( + columnIndex, + OResultSetMetaData::getColumnODBCType(m_pConnection.get(),m_aStatementHandle,*this,columnIndex) + ).first; + return aFind->second; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/ODriver.cxx b/connectivity/source/drivers/odbc/ODriver.cxx new file mode 100644 index 000000000..376946d02 --- /dev/null +++ b/connectivity/source/drivers/odbc/ODriver.cxx @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <odbc/ODriver.hxx> +#include <odbc/OConnection.hxx> +#include <odbc/OTools.hxx> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <strings.hrc> +#include <resource/sharedresources.hxx> + +using namespace connectivity::odbc; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::sdbc; + +ODBCDriver::ODBCDriver(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory) + :ODriver_BASE(m_aMutex) + ,m_xORB(_rxFactory) + ,m_pDriverHandle(SQL_NULL_HANDLE) +{ +} + +void ODBCDriver::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(); +} + +// static ServiceInfo + +OUString ODBCDriver::getImplementationName_Static( ) +{ + return "com.sun.star.comp.sdbc.ODBCDriver"; + // this name is referenced in the configuration and in the odbc.xml + // Please take care when changing it. +} + + +Sequence< OUString > ODBCDriver::getSupportedServiceNames_Static( ) +{ + Sequence<OUString> aSNS { "com.sun.star.sdbc.Driver" }; + return aSNS; +} + + +OUString SAL_CALL ODBCDriver::getImplementationName( ) +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL ODBCDriver::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + + +Sequence< OUString > SAL_CALL ODBCDriver::getSupportedServiceNames( ) +{ + return getSupportedServiceNames_Static(); +} + + +Reference< XConnection > SAL_CALL ODBCDriver::connect( const OUString& url, const Sequence< PropertyValue >& info ) +{ + if ( ! acceptsURL(url) ) + return nullptr; + + if(!m_pDriverHandle) + { + OUString aPath; + if(!EnvironmentHandle(aPath)) + throw SQLException(aPath,*this,OUString(),1000,Any()); + } + OConnection* pCon = new OConnection(m_pDriverHandle,this); + Reference< XConnection > xCon = pCon; + pCon->Construct(url,info); + m_xConnections.push_back(WeakReferenceHelper(*pCon)); + + return xCon; +} + +sal_Bool SAL_CALL ODBCDriver::acceptsURL( const OUString& url ) +{ + return url.startsWith("sdbc:odbc:"); +} + +Sequence< DriverPropertyInfo > SAL_CALL ODBCDriver::getPropertyInfo( const OUString& url, const Sequence< PropertyValue >& /*info*/ ) +{ + if ( acceptsURL(url) ) + { + std::vector< DriverPropertyInfo > aDriverInfo; + + Sequence< OUString > aBooleanValues(2); + aBooleanValues[0] = "false"; + aBooleanValues[1] = "true"; + + aDriverInfo.push_back(DriverPropertyInfo( + "CharSet" + ,"CharSet of the database." + ,false + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "UseCatalog" + ,"Use catalog for file-based databases." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "SystemDriverSettings" + ,"Driver settings." + ,false + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "ParameterNameSubstitution" + ,"Change named parameters with '?'." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "IgnoreDriverPrivileges" + ,"Ignore the privileges from the database driver." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "IsAutoRetrievingEnabled" + ,"Retrieve generated values." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "AutoRetrievingStatement" + ,"Auto-increment statement." + ,false + ,OUString() + ,Sequence< OUString >()) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "GenerateASBeforeCorrelationName" + ,"Generate AS before table correlation names." + ,false + ,"false" + ,aBooleanValues) + ); + aDriverInfo.push_back(DriverPropertyInfo( + "EscapeDateTime" + ,"Escape date time format." + ,false + ,"true" + ,aBooleanValues) + ); + + return Sequence< DriverPropertyInfo >(aDriverInfo.data(),aDriverInfo.size()); + } + ::connectivity::SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage ,*this); + return Sequence< DriverPropertyInfo >(); +} + +sal_Int32 SAL_CALL ODBCDriver::getMajorVersion( ) +{ + return 1; +} + +sal_Int32 SAL_CALL ODBCDriver::getMinorVersion( ) +{ + return 0; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/OFunctions.cxx b/connectivity/source/drivers/odbc/OFunctions.cxx new file mode 100644 index 000000000..ae8953176 --- /dev/null +++ b/connectivity/source/drivers/odbc/OFunctions.cxx @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <odbc/OFunctions.hxx> + +// Implib definitions for ODBC-DLL/shared library: + +namespace connectivity +{ + T3SQLAllocHandle pODBC3SQLAllocHandle; +T3SQLConnect pODBC3SQLConnect; +T3SQLDriverConnect pODBC3SQLDriverConnect; +T3SQLBrowseConnect pODBC3SQLBrowseConnect; +T3SQLDataSources pODBC3SQLDataSources; +T3SQLDrivers pODBC3SQLDrivers; +T3SQLGetInfo pODBC3SQLGetInfo; +T3SQLGetFunctions pODBC3SQLGetFunctions; +T3SQLGetTypeInfo pODBC3SQLGetTypeInfo; +T3SQLSetConnectAttr pODBC3SQLSetConnectAttr; +T3SQLGetConnectAttr pODBC3SQLGetConnectAttr; +T3SQLSetEnvAttr pODBC3SQLSetEnvAttr; +T3SQLGetEnvAttr pODBC3SQLGetEnvAttr; +T3SQLSetStmtAttr pODBC3SQLSetStmtAttr; +T3SQLGetStmtAttr pODBC3SQLGetStmtAttr; +T3SQLPrepare pODBC3SQLPrepare; +T3SQLBindParameter pODBC3SQLBindParameter; +T3SQLSetCursorName pODBC3SQLSetCursorName; +T3SQLExecute pODBC3SQLExecute; +T3SQLExecDirect pODBC3SQLExecDirect; +T3SQLDescribeParam pODBC3SQLDescribeParam; +T3SQLNumParams pODBC3SQLNumParams; +T3SQLParamData pODBC3SQLParamData; +T3SQLPutData pODBC3SQLPutData; +T3SQLRowCount pODBC3SQLRowCount; +T3SQLNumResultCols pODBC3SQLNumResultCols; +T3SQLDescribeCol pODBC3SQLDescribeCol; +T3SQLColAttribute pODBC3SQLColAttribute; +T3SQLBindCol pODBC3SQLBindCol; +T3SQLFetch pODBC3SQLFetch; +T3SQLFetchScroll pODBC3SQLFetchScroll; +T3SQLGetData pODBC3SQLGetData; +T3SQLSetPos pODBC3SQLSetPos; +T3SQLBulkOperations pODBC3SQLBulkOperations; +T3SQLMoreResults pODBC3SQLMoreResults; +T3SQLGetDiagRec pODBC3SQLGetDiagRec; +T3SQLColumnPrivileges pODBC3SQLColumnPrivileges; +T3SQLColumns pODBC3SQLColumns; +T3SQLForeignKeys pODBC3SQLForeignKeys; +T3SQLPrimaryKeys pODBC3SQLPrimaryKeys; +T3SQLProcedureColumns pODBC3SQLProcedureColumns; +T3SQLProcedures pODBC3SQLProcedures; +T3SQLSpecialColumns pODBC3SQLSpecialColumns; +T3SQLStatistics pODBC3SQLStatistics; +T3SQLTablePrivileges pODBC3SQLTablePrivileges; +T3SQLTables pODBC3SQLTables; +T3SQLFreeStmt pODBC3SQLFreeStmt; +T3SQLCloseCursor pODBC3SQLCloseCursor; +T3SQLCancel pODBC3SQLCancel; +T3SQLEndTran pODBC3SQLEndTran; +T3SQLDisconnect pODBC3SQLDisconnect; +T3SQLFreeHandle pODBC3SQLFreeHandle; +T3SQLGetCursorName pODBC3SQLGetCursorName; +T3SQLNativeSql pODBC3SQLNativeSql; + +static bool LoadFunctions(oslModule pODBCso); + +// Take care of Dynamically loading of the DLL/shared lib and Addresses: +// Returns sal_True at success +bool LoadLibrary_ODBC3(OUString &_rPath) +{ + static bool bLoaded = false; + static oslModule pODBCso = nullptr; + + if (bLoaded) + return true; +#ifndef DISABLE_DYNLOADING +#ifdef _WIN32 + _rPath = "ODBC32.DLL"; +#endif +#ifdef UNX + #ifdef MACOSX + _rPath = "libiodbc.dylib"; + #else + _rPath = "libodbc.so.2"; + pODBCso = osl_loadModule( _rPath.pData,SAL_LOADMODULE_NOW ); + if ( !pODBCso ) + { + _rPath = "libodbc.so.1"; + pODBCso = osl_loadModule( _rPath.pData,SAL_LOADMODULE_NOW ); + } + if ( !pODBCso ) + _rPath = "libodbc.so"; + + #endif /* MACOSX */ +#endif + + if ( !pODBCso ) + pODBCso = osl_loadModule( _rPath.pData,SAL_LOADMODULE_NOW ); +#endif + if( !pODBCso) + return false; + + bLoaded = LoadFunctions(pODBCso); + return bLoaded; +} + + +bool LoadFunctions(oslModule pODBCso) +{ + + if( ( pODBC3SQLAllocHandle = reinterpret_cast<T3SQLAllocHandle>(osl_getFunctionSymbol(pODBCso, OUString("SQLAllocHandle").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLConnect = reinterpret_cast<T3SQLConnect>(osl_getFunctionSymbol(pODBCso, OUString("SQLConnect").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLDriverConnect = reinterpret_cast<T3SQLDriverConnect>(osl_getFunctionSymbol(pODBCso, OUString("SQLDriverConnect").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLBrowseConnect = reinterpret_cast<T3SQLBrowseConnect>(osl_getFunctionSymbol(pODBCso, OUString("SQLBrowseConnect").pData ))) == nullptr ) + return false; + if(( pODBC3SQLDataSources = reinterpret_cast<T3SQLDataSources>(osl_getFunctionSymbol(pODBCso, OUString("SQLDataSources").pData ))) == nullptr ) + return false; + if(( pODBC3SQLDrivers = reinterpret_cast<T3SQLDrivers>(osl_getFunctionSymbol(pODBCso, OUString("SQLDrivers").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLGetInfo = reinterpret_cast<T3SQLGetInfo>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetInfo").pData ))) == nullptr ) + return false; + if(( pODBC3SQLGetFunctions = reinterpret_cast<T3SQLGetFunctions>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetFunctions").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLGetTypeInfo = reinterpret_cast<T3SQLGetTypeInfo>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetTypeInfo").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLSetConnectAttr = reinterpret_cast<T3SQLSetConnectAttr>(osl_getFunctionSymbol(pODBCso, OUString("SQLSetConnectAttr").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLGetConnectAttr = reinterpret_cast<T3SQLGetConnectAttr>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetConnectAttr").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLSetEnvAttr = reinterpret_cast<T3SQLSetEnvAttr>(osl_getFunctionSymbol(pODBCso, OUString("SQLSetEnvAttr").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLGetEnvAttr = reinterpret_cast<T3SQLGetEnvAttr>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetEnvAttr").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLSetStmtAttr = reinterpret_cast<T3SQLSetStmtAttr>(osl_getFunctionSymbol(pODBCso, OUString("SQLSetStmtAttr").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLGetStmtAttr = reinterpret_cast<T3SQLGetStmtAttr>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetStmtAttr").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLPrepare = reinterpret_cast<T3SQLPrepare>(osl_getFunctionSymbol(pODBCso, OUString("SQLPrepare").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLBindParameter = reinterpret_cast<T3SQLBindParameter>(osl_getFunctionSymbol(pODBCso, OUString("SQLBindParameter").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLSetCursorName = reinterpret_cast<T3SQLSetCursorName>(osl_getFunctionSymbol(pODBCso, OUString("SQLSetCursorName").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLExecute = reinterpret_cast<T3SQLExecute>(osl_getFunctionSymbol(pODBCso, OUString("SQLExecute").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLExecDirect = reinterpret_cast<T3SQLExecDirect>(osl_getFunctionSymbol(pODBCso, OUString("SQLExecDirect").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLDescribeParam = reinterpret_cast<T3SQLDescribeParam>(osl_getFunctionSymbol(pODBCso, OUString("SQLDescribeParam").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLNumParams = reinterpret_cast<T3SQLNumParams>(osl_getFunctionSymbol(pODBCso, OUString("SQLNumParams").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLParamData = reinterpret_cast<T3SQLParamData>(osl_getFunctionSymbol(pODBCso, OUString("SQLParamData").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLPutData = reinterpret_cast<T3SQLPutData>(osl_getFunctionSymbol(pODBCso, OUString("SQLPutData").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLRowCount = reinterpret_cast<T3SQLRowCount>(osl_getFunctionSymbol(pODBCso, OUString("SQLRowCount").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLNumResultCols = reinterpret_cast<T3SQLNumResultCols>(osl_getFunctionSymbol(pODBCso, OUString("SQLNumResultCols").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLDescribeCol = reinterpret_cast<T3SQLDescribeCol>(osl_getFunctionSymbol(pODBCso, OUString("SQLDescribeCol").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLColAttribute = reinterpret_cast<T3SQLColAttribute>(osl_getFunctionSymbol(pODBCso, OUString("SQLColAttribute").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLBindCol = reinterpret_cast<T3SQLBindCol>(osl_getFunctionSymbol(pODBCso, OUString("SQLBindCol").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLFetch = reinterpret_cast<T3SQLFetch>(osl_getFunctionSymbol(pODBCso, OUString("SQLFetch").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLFetchScroll = reinterpret_cast<T3SQLFetchScroll>(osl_getFunctionSymbol(pODBCso, OUString("SQLFetchScroll").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLGetData = reinterpret_cast<T3SQLGetData>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetData").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLSetPos = reinterpret_cast<T3SQLSetPos>(osl_getFunctionSymbol(pODBCso, OUString("SQLSetPos").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLBulkOperations = reinterpret_cast<T3SQLBulkOperations>(osl_getFunctionSymbol(pODBCso, OUString("SQLBulkOperations").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLMoreResults = reinterpret_cast<T3SQLMoreResults>(osl_getFunctionSymbol(pODBCso, OUString("SQLMoreResults").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLGetDiagRec = reinterpret_cast<T3SQLGetDiagRec>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetDiagRec").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLColumnPrivileges = reinterpret_cast<T3SQLColumnPrivileges>(osl_getFunctionSymbol(pODBCso, OUString("SQLColumnPrivileges").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLColumns = reinterpret_cast<T3SQLColumns>(osl_getFunctionSymbol(pODBCso, OUString("SQLColumns").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLForeignKeys = reinterpret_cast<T3SQLForeignKeys>(osl_getFunctionSymbol(pODBCso, OUString("SQLForeignKeys").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLPrimaryKeys = reinterpret_cast<T3SQLPrimaryKeys>(osl_getFunctionSymbol(pODBCso, OUString("SQLPrimaryKeys").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLProcedureColumns = reinterpret_cast<T3SQLProcedureColumns>(osl_getFunctionSymbol(pODBCso, OUString("SQLProcedureColumns").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLProcedures = reinterpret_cast<T3SQLProcedures>(osl_getFunctionSymbol(pODBCso, OUString("SQLProcedures").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLSpecialColumns = reinterpret_cast<T3SQLSpecialColumns>(osl_getFunctionSymbol(pODBCso, OUString("SQLSpecialColumns").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLStatistics = reinterpret_cast<T3SQLStatistics>(osl_getFunctionSymbol(pODBCso, OUString("SQLStatistics").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLTablePrivileges = reinterpret_cast<T3SQLTablePrivileges>(osl_getFunctionSymbol(pODBCso, OUString("SQLTablePrivileges").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLTables = reinterpret_cast<T3SQLTables>(osl_getFunctionSymbol(pODBCso, OUString("SQLTables").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLFreeStmt = reinterpret_cast<T3SQLFreeStmt>(osl_getFunctionSymbol(pODBCso, OUString("SQLFreeStmt").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLCloseCursor = reinterpret_cast<T3SQLCloseCursor>(osl_getFunctionSymbol(pODBCso, OUString("SQLCloseCursor").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLCancel = reinterpret_cast<T3SQLCancel>(osl_getFunctionSymbol(pODBCso, OUString("SQLCancel").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLEndTran = reinterpret_cast<T3SQLEndTran>(osl_getFunctionSymbol(pODBCso, OUString("SQLEndTran").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLDisconnect = reinterpret_cast<T3SQLDisconnect>(osl_getFunctionSymbol(pODBCso, OUString("SQLDisconnect").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLFreeHandle = reinterpret_cast<T3SQLFreeHandle>(osl_getFunctionSymbol(pODBCso, OUString("SQLFreeHandle").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLGetCursorName = reinterpret_cast<T3SQLGetCursorName>(osl_getFunctionSymbol(pODBCso, OUString("SQLGetCursorName").pData ))) == nullptr ) + return false; + if( ( pODBC3SQLNativeSql = reinterpret_cast<T3SQLNativeSql>(osl_getFunctionSymbol(pODBCso, OUString("SQLNativeSql").pData ))) == nullptr ) + return false; + + return true; +} + + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/OPreparedStatement.cxx b/connectivity/source/drivers/odbc/OPreparedStatement.cxx new file mode 100644 index 000000000..65cfbf5e7 --- /dev/null +++ b/connectivity/source/drivers/odbc/OPreparedStatement.cxx @@ -0,0 +1,925 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <string.h> +#include <osl/diagnose.h> +#include <odbc/OPreparedStatement.hxx> +#include <odbc/OBoundParam.hxx> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <odbc/OTools.hxx> +#include <odbc/OResultSet.hxx> +#include <odbc/OResultSetMetaData.hxx> +#include <comphelper/sequence.hxx> +#include <connectivity/dbtools.hxx> +#include <comphelper/types.hxx> +#include <connectivity/FValue.hxx> +#include <strings.hrc> +#include <memory> +#include <type_traits> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::odbc; +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::io; +using namespace com::sun::star::util; + +IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.OPreparedStatement","com.sun.star.sdbc.PreparedStatement"); + +namespace +{ + // for now, never use wchar, + // but most of code is prepared to handle it + // in case we make this configurable + const bool bUseWChar = false; +} + +OPreparedStatement::OPreparedStatement( OConnection* _pConnection,const OUString& sql) + :OStatement_BASE2(_pConnection) + ,numParams(0) + ,m_bPrepared(false) +{ + m_sSqlStatement = sql; +} + +OPreparedStatement::~OPreparedStatement() +{ +} + +void SAL_CALL OPreparedStatement::acquire() throw() +{ + OStatement_BASE2::acquire(); +} + +void SAL_CALL OPreparedStatement::release() throw() +{ + OStatement_BASE2::release(); +} + +Any SAL_CALL OPreparedStatement::queryInterface( const Type & rType ) +{ + Any aRet = OStatement_BASE2::queryInterface(rType); + return aRet.hasValue() ? aRet : OPreparedStatement_BASE::queryInterface(rType); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL OPreparedStatement::getTypes( ) +{ + return ::comphelper::concatSequences(OPreparedStatement_BASE::getTypes(),OStatement_BASE2::getTypes()); +} + + +Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + prepareStatement(); + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + if(!m_xMetaData.is()) + m_xMetaData = new OResultSetMetaData(getOwnConnection(),m_aStatementHandle); + return m_xMetaData; +} + + +void SAL_CALL OPreparedStatement::close( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + // Close/clear our result set + clearMyResultSet (); + + // Reset last warning message + + try { + clearWarnings (); + OStatement_BASE2::close(); + FreeParams(); + } + catch (SQLException &) { + // If we get an error, ignore + } + + // Remove this Statement object from the Connection object's + // list +} + + +sal_Bool SAL_CALL OPreparedStatement::execute( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + // Reset warnings + + clearWarnings (); + + // Reset the statement handle, warning and saved Resultset + + reset(); + + // Call SQLExecute + prepareStatement(); + + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + try + { + SQLRETURN nReturn = N3SQLExecute(m_aStatementHandle); + + OTools::ThrowException(m_pConnection.get(),nReturn,m_aStatementHandle,SQL_HANDLE_STMT,*this); + bool needData = nReturn == SQL_NEED_DATA; + + // Now loop while more data is needed (i.e. a data-at- + // execution parameter was given). For each parameter + // that needs data, put the data from the input stream. + + while (needData) { + + // Get the parameter number that requires data + + sal_Int32* paramIndex = nullptr; + N3SQLParamData(m_aStatementHandle, reinterpret_cast<SQLPOINTER*>(¶mIndex)); + + // If the parameter index is -1, there is no + // more data required + + if ( !paramIndex || ( *paramIndex == -1 ) ) + needData = false; + else + { + // Now we have the proper parameter + // index, get the data from the input + // stream and do a SQLPutData + putParamData (*paramIndex); + } + } + + } + catch (const SQLWarning&) + { + } + + // Now determine if there is a result set associated with + // the SQL statement that was executed. Get the column + // count, and if it is not zero, there is a result set. + + + return getColumnCount() > 0; +} + + +sal_Int32 SAL_CALL OPreparedStatement::executeUpdate( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + sal_Int32 numRows = -1; + + prepareStatement(); + // Execute the statement. If execute returns sal_False, a + // row count exists. + + if (!execute()) + numRows = getUpdateCount (); + else + { + // No update count was produced (a ResultSet was). Raise + // an exception + m_pConnection->throwGenericSQLException(STR_NO_ROWCOUNT,*this); + } + return numRows; +} + + +void SAL_CALL OPreparedStatement::setString( sal_Int32 parameterIndex, const OUString& x ) +{ + setParameter(parameterIndex, DataType::CHAR, invalid_scale, x); +} + + +Reference< XConnection > SAL_CALL OPreparedStatement::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + return Reference< XConnection >(m_pConnection.get()); +} + + +Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + Reference< XResultSet > rs; + + prepareStatement(); + + if (execute()) + rs = getResultSet(false); + else + { + // No ResultSet was produced. Raise an exception + m_pConnection->throwGenericSQLException(STR_NO_RESULTSET,*this); + } + return rs; +} + + +void SAL_CALL OPreparedStatement::setBoolean( sal_Int32 parameterIndex, sal_Bool x ) +{ + // Set the parameter as if it were an integer + setInt (parameterIndex, x ? 1 : 0 ); +} + +// The MutexGuard must _already_ be taken! +void OPreparedStatement::setParameterPre(sal_Int32 parameterIndex) +{ + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + prepareStatement(); + checkParameterIndex(parameterIndex); + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); +} + + +template <typename T> void OPreparedStatement::setScalarParameter(const sal_Int32 parameterIndex, const sal_Int32 i_nType, const SQLULEN i_nColSize, const T i_Value) +{ + setScalarParameter(parameterIndex, i_nType, i_nColSize, invalid_scale, i_Value); +} + + +template <typename T> void OPreparedStatement::setScalarParameter(const sal_Int32 parameterIndex, const sal_Int32 i_nType, const SQLULEN i_nColSize, sal_Int32 i_nScale, const T i_Value) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + setParameterPre(parameterIndex); + + typedef typename std::remove_reference<T>::type TnoRef; + + TnoRef *bindBuf = static_cast< TnoRef* >( allocBindBuf(parameterIndex, sizeof(i_Value)) ); + *bindBuf = i_Value; + + setParameter(parameterIndex, i_nType, i_nColSize, i_nScale, bindBuf, sizeof(i_Value), sizeof(i_Value)); +} + + +void OPreparedStatement::setParameter(const sal_Int32 parameterIndex, const sal_Int32 _nType, const sal_Int16 _nScale, const OUString &_sData) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + setParameterPre(parameterIndex); + + assert (_nType == DataType::VARCHAR || _nType == DataType::CHAR || _nType == DataType::DECIMAL || _nType == DataType::NUMERIC); + + sal_Int32 nCharLen; + sal_Int32 nByteLen; + void *pData; + if (bUseWChar) + { + /* + * On Windows, wchar is 16 bits (UTF-16 encoding), the ODBC "W" variants functions take UTF-16 encoded strings + * and character lengths are number of UTF-16 codepoints. + * Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms716246%28v=vs.85%29.aspx + * ODBC Programmer's reference > Developing Applications > Programming Considerations > Unicode > Unicode Function Arguments + * http://support.microsoft.com/kb/294169 + * + * UnixODBC can be configured at compile-time so that the "W" variants expect + * UTF-16 or UTF-32 encoded strings, and character lengths are number of codepoints. + * However, UTF-16 is the default, what all/most distributions do + * and the established API that most drivers implement. + * As wchar is often 32 bits, this differs from C-style strings of wchar! + * + * On MacOS X, the "W" variants use wchar_t, which is UCS4 + * + * Our internal OUString storage is always UTF-16, so no conversion to do here. + */ + static_assert(sizeof (SQLWCHAR) == 2 || sizeof (SQLWCHAR) == 4, "must be 2 or 4"); + if (sizeof (SQLWCHAR) == 2) + { + nCharLen = _sData.getLength(); + nByteLen = 2 * nCharLen; + pData = allocBindBuf(parameterIndex, nByteLen); + memcpy(pData, _sData.getStr(), nByteLen); + } + else + { + pData = allocBindBuf(parameterIndex, _sData.getLength() * 4); + sal_uInt32* pCursor = static_cast<sal_uInt32*>(pData); + nCharLen = 0; + for (sal_Int32 i = 0; i != _sData.getLength();) + { + *pCursor++ = _sData.iterateCodePoints(&i); + nCharLen += 1; + } + nByteLen = 4 * nCharLen; + } + } + else + { + assert(getOwnConnection()->getTextEncoding() != RTL_TEXTENCODING_UCS2 && + getOwnConnection()->getTextEncoding() != RTL_TEXTENCODING_UCS4); + OString sOData( + OUStringToOString(_sData, getOwnConnection()->getTextEncoding())); + nCharLen = nByteLen = sOData.getLength(); + pData = allocBindBuf(parameterIndex, nByteLen); + memcpy(pData, sOData.getStr(), nByteLen); + } + + setParameter( parameterIndex, _nType, nCharLen, _nScale, pData, nByteLen, nByteLen ); +} + +void OPreparedStatement::setParameter(const sal_Int32 parameterIndex, const sal_Int32 _nType, const Sequence< sal_Int8 > &x) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + setParameterPre(parameterIndex); + + assert(_nType == DataType::BINARY || _nType == DataType::VARBINARY); + + // don't copy the sequence, just point the ODBC directly at the sequence's storage array + // Why BINARY/Sequence is treated differently than strings (which are copied), I'm not sure + OSL_VERIFY(allocBindBuf(parameterIndex, 0) == nullptr); + boundParams[parameterIndex-1].setSequence(x); // this ensures that the sequence stays alive + + setParameter( parameterIndex, _nType, x.getLength(), invalid_scale, x.getConstArray(), x.getLength(), x.getLength() ); +} + +void OPreparedStatement::setParameter(const sal_Int32 parameterIndex, const sal_Int32 _nType, const SQLULEN _nColumnSize, const sal_Int32 _nScale, const void* const _pData, const SQLULEN _nDataLen, const SQLLEN _nDataAllocLen) +{ + SQLSMALLINT fCType, fSqlType; + OTools::getBindTypes(bUseWChar, m_pConnection->useOldDateFormat(), OTools::jdbcTypeToOdbc(_nType), fCType, fSqlType); + + SQLLEN& rDataLen = boundParams[parameterIndex-1].getBindLengthBuffer(); + rDataLen = _nDataLen; + + SQLRETURN nRetcode; + nRetcode = (*reinterpret_cast<T3SQLBindParameter>(m_pConnection->getOdbcFunction(ODBC3SQLFunctionId::BindParameter)))( + m_aStatementHandle, + // checkParameterIndex guarantees this is safe + static_cast<SQLUSMALLINT>(parameterIndex), + SQL_PARAM_INPUT, + fCType, + fSqlType, + _nColumnSize, + _nScale, + // we trust the ODBC driver not to touch it because SQL_PARAM_INPUT + const_cast<void*>(_pData), + _nDataAllocLen, + &rDataLen); + + OTools::ThrowException(m_pConnection.get(), nRetcode, m_aStatementHandle, SQL_HANDLE_STMT, *this); +} + +void SAL_CALL OPreparedStatement::setByte( const sal_Int32 parameterIndex, const sal_Int8 x ) +{ + setScalarParameter(parameterIndex, DataType::TINYINT, 3, 0, x); +} + +void SAL_CALL OPreparedStatement::setDate( sal_Int32 parameterIndex, const Date& aData ) +{ + DATE_STRUCT x(OTools::DateToOdbcDate(aData)); + setScalarParameter<DATE_STRUCT&>(parameterIndex, DataType::DATE, 10, x); +} + +void SAL_CALL OPreparedStatement::setTime( sal_Int32 parameterIndex, const css::util::Time& aVal ) +{ + SQLULEN nColSize; + if(aVal.NanoSeconds == 0) + nColSize = 8; + else if(aVal.NanoSeconds % 100000000 == 0) + nColSize = 10; + else if(aVal.NanoSeconds % 10000000 == 0) + nColSize = 11; + else if(aVal.NanoSeconds % 1000000 == 0) + nColSize = 12; + else if(aVal.NanoSeconds % 100000 == 0) + nColSize = 13; + else if(aVal.NanoSeconds % 10000 == 0) + nColSize = 14; + else if(aVal.NanoSeconds % 1000 == 0) + nColSize = 15; + else if(aVal.NanoSeconds % 100 == 0) + nColSize = 16; + else if(aVal.NanoSeconds % 10 == 0) + nColSize = 17; + else + nColSize = 18; + TIME_STRUCT x(OTools::TimeToOdbcTime(aVal)); + setScalarParameter<TIME_STRUCT&>(parameterIndex, DataType::TIME, nColSize, (nColSize == 8)? 0 : nColSize-9, x); +} + + +void SAL_CALL OPreparedStatement::setTimestamp( sal_Int32 parameterIndex, const DateTime& aVal ) +{ + SQLULEN nColSize; + if(aVal.NanoSeconds == 0) + { + if (aVal.Seconds == 0) + nColSize=16; + else + nColSize=19; + } + else if(aVal.NanoSeconds % 100000000 == 0) + nColSize = 21; + else if(aVal.NanoSeconds % 10000000 == 0) + nColSize = 22; + else if(aVal.NanoSeconds % 1000000 == 0) + nColSize = 23; + else if(aVal.NanoSeconds % 100000 == 0) + nColSize = 24; + else if(aVal.NanoSeconds % 10000 == 0) + nColSize = 25; + else if(aVal.NanoSeconds % 1000 == 0) + nColSize = 26; + else if(aVal.NanoSeconds % 100 == 0) + nColSize = 27; + else if(aVal.NanoSeconds % 10 == 0) + nColSize = 28; + else + nColSize = 29; + + TIMESTAMP_STRUCT x(OTools::DateTimeToTimestamp(aVal)); + setScalarParameter<TIMESTAMP_STRUCT&>(parameterIndex, DataType::TIMESTAMP, nColSize, (nColSize <= 19)? 0 : nColSize-20, x); +} + + +void SAL_CALL OPreparedStatement::setDouble( sal_Int32 parameterIndex, double x ) +{ + setScalarParameter(parameterIndex, DataType::DOUBLE, 15, x); +} + + +void SAL_CALL OPreparedStatement::setFloat( sal_Int32 parameterIndex, float x ) +{ + setScalarParameter(parameterIndex, DataType::FLOAT, 15, x); +} + + +void SAL_CALL OPreparedStatement::setInt( sal_Int32 parameterIndex, sal_Int32 x ) +{ + setScalarParameter(parameterIndex, DataType::INTEGER, 10, 0, x); +} + + +void SAL_CALL OPreparedStatement::setLong( sal_Int32 parameterIndex, sal_Int64 x ) +{ + try + { + setScalarParameter(parameterIndex, DataType::BIGINT, 19, 0, x); + } + catch(SQLException&) + { + setString(parameterIndex, ORowSetValue(x)); + } +} + + +void SAL_CALL OPreparedStatement::setNull( sal_Int32 parameterIndex, const sal_Int32 _nType ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + setParameterPre(parameterIndex); + + OSL_VERIFY(allocBindBuf(parameterIndex, 0) == nullptr); + SQLLEN * const lenBuf = getLengthBuf (parameterIndex); + *lenBuf = SQL_NULL_DATA; + + + SQLSMALLINT fCType; + SQLSMALLINT fSqlType; + + OTools::getBindTypes( bUseWChar, + m_pConnection->useOldDateFormat(), + OTools::jdbcTypeToOdbc(_nType), + fCType, + fSqlType); + + SQLRETURN nReturn = N3SQLBindParameter( m_aStatementHandle, + static_cast<SQLUSMALLINT>(parameterIndex), + SQL_PARAM_INPUT, + fCType, + fSqlType, + 0, + 0, + nullptr, + 0, + lenBuf + ); + OTools::ThrowException(m_pConnection.get(),nReturn,m_aStatementHandle,SQL_HANDLE_STMT,*this); +} + + +void SAL_CALL OPreparedStatement::setClob( sal_Int32 parameterIndex, const Reference< XClob >& x ) +{ + if ( x.is() ) + setStream(parameterIndex, x->getCharacterStream(), x->length(), DataType::LONGVARCHAR); +} + + +void SAL_CALL OPreparedStatement::setBlob( sal_Int32 parameterIndex, const Reference< XBlob >& x ) +{ + if ( x.is() ) + setStream(parameterIndex, x->getBinaryStream(), x->length(), DataType::LONGVARBINARY); +} + + +void SAL_CALL OPreparedStatement::setArray( sal_Int32 /*parameterIndex*/, const Reference< XArray >& /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setArray", *this ); +} + + +void SAL_CALL OPreparedStatement::setRef( sal_Int32 /*parameterIndex*/, const Reference< XRef >& /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XParameters::setRef", *this ); +} + +void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale ) +{ + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + prepareStatement(); + // For each known SQL Type, call the appropriate + // set routine + + switch (sqlType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + if(x.hasValue()) + { + OUString sStr; + x >>= sStr; + setParameter(parameterIndex, sqlType, scale, sStr); + } + else + setNull(parameterIndex,sqlType); + break; + case DataType::DECIMAL: + case DataType::NUMERIC: + if(x.hasValue()) + { + ORowSetValue aValue; + aValue.fill(x); + // TODO: make sure that this calls the string overload + setParameter(parameterIndex, sqlType, scale, aValue); + } + else + setNull(parameterIndex,sqlType); + break; + default: + ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale); + } +} + + +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)) + { // there is no other setXXX call which can handle the value in x + throw SQLException(); + } +} + + +void SAL_CALL OPreparedStatement::setShort( sal_Int32 parameterIndex, sal_Int16 x ) +{ + setScalarParameter(parameterIndex, DataType::SMALLINT, 5, 0, x); +} + + +void SAL_CALL OPreparedStatement::setBytes( sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x ) +{ + setParameter(parameterIndex, DataType::BINARY, x); +} + + +void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + // LEM: It is quite unclear to me what the interface here is. + // The XInputStream provides *bytes*, not characters. + setStream(parameterIndex, x, length, DataType::LONGVARCHAR); +} + + +void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + setStream(parameterIndex, x, length, DataType::LONGVARBINARY); +} + + +void SAL_CALL OPreparedStatement::clearParameters( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + prepareStatement(); + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + N3SQLFreeStmt (m_aStatementHandle, SQL_RESET_PARAMS); + N3SQLFreeStmt (m_aStatementHandle, SQL_UNBIND); +} + +void SAL_CALL OPreparedStatement::clearBatch( ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XPreparedBatchExecution::clearBatch", *this ); + // clearParameters( ); + // m_aBatchVector.erase(); +} + + +void SAL_CALL OPreparedStatement::addBatch( ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XPreparedBatchExecution::addBatch", *this ); +} + + +Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch( ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XPreparedBatchExecution::executeBatch", *this ); + // not reached, but keep -Werror happy + return Sequence< sal_Int32 > (); +} + + +// methods + + +// initBoundParam +// Initialize the bound parameter objects + + +void OPreparedStatement::initBoundParam () +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + // Get the number of parameters + numParams = 0; + N3SQLNumParams (m_aStatementHandle,&numParams); + + // There are parameter markers, allocate the bound + // parameter objects + + if (numParams > 0) + { + boundParams.reset(new OBoundParam[numParams]); + } +} + + +// allocBindBuf +// Allocate storage for the permanent data buffer for the bound +// parameter. + + +void* OPreparedStatement::allocBindBuf( sal_Int32 index,sal_Int32 bufLen) +{ + void* b = nullptr; + + // Sanity check the parameter number + + if ((index >= 1) && (index <= numParams)) + { + b = boundParams[index - 1].allocBindDataBuffer(bufLen); + } + + return b; +} + + +// getLengthBuf +// Gets the length buffer for the given parameter index + + +SQLLEN* OPreparedStatement::getLengthBuf (sal_Int32 index) +{ + SQLLEN* b = nullptr; + + // Sanity check the parameter number + + if ((index >= 1) && + (index <= numParams)) + { + b = &boundParams[index - 1].getBindLengthBuffer (); + } + + return b; +} + + +// putParamData +// Puts parameter data from a previously bound input stream. The +// input stream was bound using SQL_LEN_DATA_AT_EXEC. +void OPreparedStatement::putParamData (sal_Int32 index) +{ + // Sanity check the parameter index + if ((index < 1) || + (index > numParams)) + { + return; + } + + // We'll transfer up to MAX_PUT_DATA_LENGTH at a time + Sequence< sal_Int8 > buf( MAX_PUT_DATA_LENGTH ); + + // Get the information about the input stream + + Reference< XInputStream> inputStream = boundParams[index - 1].getInputStream (); + if ( !inputStream.is() ) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_NO_INPUTSTREAM)); + throw SQLException (sError, *this,OUString(),0,Any()); + } + + sal_Int32 maxBytesLeft = boundParams[index - 1].getInputStreamLen (); + + // Loop while more data from the input stream + sal_Int32 haveRead = 0; + try + { + + do + { + sal_Int32 toReadThisRound = std::min( MAX_PUT_DATA_LENGTH, maxBytesLeft ); + + // Read some data from the input stream + haveRead = inputStream->readBytes( buf, toReadThisRound ); + OSL_ENSURE( haveRead == buf.getLength(), "OPreparedStatement::putParamData: inconsistency!" ); + + if ( !haveRead ) + // no more data in the stream - the given stream length was a maximum which could not be + // fulfilled by the stream + break; + + // Put the data + OSL_ENSURE( m_aStatementHandle, "OPreparedStatement::putParamData: StatementHandle is null!" ); + N3SQLPutData ( m_aStatementHandle, buf.getArray(), buf.getLength() ); + + // decrement the number of bytes still needed + maxBytesLeft -= haveRead; + } + while ( maxBytesLeft > 0 ); + } + catch (const IOException& ex) + { + + // If an I/O exception was generated, turn + // it into a SQLException + + throw SQLException(ex.Message,*this,OUString(),0,Any()); + } +} + +// setStream +// Sets an input stream as a parameter, using the given SQL type +void OPreparedStatement::setStream( + sal_Int32 ParameterIndex, + const Reference< XInputStream>& x, + SQLLEN length, + sal_Int32 _nType) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + prepareStatement(); + + checkParameterIndex(ParameterIndex); + // Get the buffer needed for the length + + SQLLEN * const lenBuf = getLengthBuf(ParameterIndex); + + // Allocate a new buffer for the parameter data. This buffer + // will be returned by SQLParamData (it is set to the parameter + // number, a sal_Int32) + + sal_Int32* dataBuf = static_cast<sal_Int32*>( allocBindBuf(ParameterIndex, sizeof(ParameterIndex)) ); + *dataBuf = ParameterIndex; + + // Bind the parameter with SQL_LEN_DATA_AT_EXEC + *lenBuf = SQL_LEN_DATA_AT_EXEC (length); + + SQLSMALLINT fCType, fSqlType; + OTools::getBindTypes(bUseWChar, m_pConnection->useOldDateFormat(), OTools::jdbcTypeToOdbc(_nType), fCType, fSqlType); + + + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + N3SQLBindParameter(m_aStatementHandle, + static_cast<SQLUSMALLINT>(ParameterIndex), + SQL_PARAM_INPUT, + fCType, + fSqlType, + length, + invalid_scale, + dataBuf, + sizeof(ParameterIndex), + lenBuf); + + // Save the input stream + boundParams[ParameterIndex - 1].setInputStream (x, length); +} + + +void OPreparedStatement::FreeParams() +{ + numParams = 0; + boundParams.reset(); +} + +void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + try + { + switch(nHandle) + { + case PROPERTY_ID_RESULTSETCONCURRENCY: + if(!isPrepared()) + setResultSetConcurrency(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_RESULTSETTYPE: + if(!isPrepared()) + setResultSetType(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_FETCHDIRECTION: + if(!isPrepared()) + setFetchDirection(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_USEBOOKMARKS: + if(!isPrepared()) + setUsingBookmarks(comphelper::getBOOL(rValue)); + break; + default: + OStatement_Base::setFastPropertyValue_NoBroadcast(nHandle,rValue); + } + } + catch(const SQLException&) + { + // throw Exception(e.Message,*this); + } +} + +void OPreparedStatement::prepareStatement() +{ + if(!isPrepared()) + { + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + OString aSql(OUStringToOString(m_sSqlStatement,getOwnConnection()->getTextEncoding())); + SQLRETURN nReturn = N3SQLPrepare(m_aStatementHandle, reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(aSql.getStr())), aSql.getLength()); + OTools::ThrowException(m_pConnection.get(),nReturn,m_aStatementHandle,SQL_HANDLE_STMT,*this); + m_bPrepared = true; + initBoundParam(); + } +} + +void OPreparedStatement::checkParameterIndex(sal_Int32 _parameterIndex) +{ + if( _parameterIndex > numParams || + _parameterIndex < 1 || + _parameterIndex > std::numeric_limits<SQLUSMALLINT>::max() ) + { + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceStringWithSubstitution(STR_WRONG_PARAM_INDEX, + "$pos$", OUString::number(_parameterIndex), + "$count$", OUString::number(numParams) + )); + SQLException aNext(sError,*this, OUString(),0,Any()); + + ::dbtools::throwInvalidIndexException(*this,makeAny(aNext)); + } +} + +OResultSet* OPreparedStatement::createResulSet() +{ + OResultSet* pReturn = new OResultSet(m_aStatementHandle,this); + pReturn->setMetaData(getMetaData()); + return pReturn; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/ORealDriver.cxx b/connectivity/source/drivers/odbc/ORealDriver.cxx new file mode 100644 index 000000000..47576954a --- /dev/null +++ b/connectivity/source/drivers/odbc/ORealDriver.cxx @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "ORealDriver.hxx" +#include <odbc/ODriver.hxx> +#include <odbc/OTools.hxx> +#include <odbc/OFunctions.hxx> + +namespace connectivity::odbc +{ + namespace { + + class ORealOdbcDriver : public ODBCDriver + { + protected: + virtual oslGenericFunction getOdbcFunction(ODBC3SQLFunctionId _nIndex) const override; + virtual SQLHANDLE EnvironmentHandle(OUString &_rPath) override; + public: + explicit ORealOdbcDriver(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory) : ODBCDriver(_rxFactory) {} + }; + + } + +oslGenericFunction ORealOdbcDriver::getOdbcFunction(ODBC3SQLFunctionId _nIndex) const +{ + oslGenericFunction pFunction = nullptr; + switch(_nIndex) + { + case ODBC3SQLFunctionId::AllocHandle: + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLAllocHandle); + break; + case ODBC3SQLFunctionId::Connect: + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLConnect); + break; + case ODBC3SQLFunctionId::DriverConnect: + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLDriverConnect); + break; + case ODBC3SQLFunctionId::BrowseConnect: + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLBrowseConnect); + break; + case ODBC3SQLFunctionId::DataSources: + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLDataSources); + break; + case ODBC3SQLFunctionId::Drivers: + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLDrivers); + break; + case ODBC3SQLFunctionId::GetInfo: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetInfo); + break; + case ODBC3SQLFunctionId::GetFunctions: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetFunctions); + break; + case ODBC3SQLFunctionId::GetTypeInfo: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetTypeInfo); + break; + case ODBC3SQLFunctionId::SetConnectAttr: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLSetConnectAttr); + break; + case ODBC3SQLFunctionId::GetConnectAttr: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetConnectAttr); + break; + case ODBC3SQLFunctionId::SetEnvAttr: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLSetEnvAttr); + break; + case ODBC3SQLFunctionId::GetEnvAttr: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetEnvAttr); + break; + case ODBC3SQLFunctionId::SetStmtAttr: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLSetStmtAttr); + break; + case ODBC3SQLFunctionId::GetStmtAttr: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetStmtAttr); + break; + case ODBC3SQLFunctionId::Prepare: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLPrepare); + break; + case ODBC3SQLFunctionId::BindParameter: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLBindParameter); + break; + case ODBC3SQLFunctionId::SetCursorName: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLSetCursorName); + break; + case ODBC3SQLFunctionId::Execute: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLExecute); + break; + case ODBC3SQLFunctionId::ExecDirect: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLExecDirect); + break; + case ODBC3SQLFunctionId::DescribeParam: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLDescribeParam); + break; + case ODBC3SQLFunctionId::NumParams: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLNumParams); + break; + case ODBC3SQLFunctionId::ParamData: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLParamData); + break; + case ODBC3SQLFunctionId::PutData: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLPutData); + break; + case ODBC3SQLFunctionId::RowCount: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLRowCount); + break; + case ODBC3SQLFunctionId::NumResultCols: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLNumResultCols); + break; + case ODBC3SQLFunctionId::DescribeCol: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLDescribeCol); + break; + case ODBC3SQLFunctionId::ColAttribute: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLColAttribute); + break; + case ODBC3SQLFunctionId::BindCol: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLBindCol); + break; + case ODBC3SQLFunctionId::Fetch: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLFetch); + break; + case ODBC3SQLFunctionId::FetchScroll: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLFetchScroll); + break; + case ODBC3SQLFunctionId::GetData: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetData); + break; + case ODBC3SQLFunctionId::SetPos: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLSetPos); + break; + case ODBC3SQLFunctionId::BulkOperations: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLBulkOperations); + break; + case ODBC3SQLFunctionId::MoreResults: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLMoreResults); + break; + case ODBC3SQLFunctionId::GetDiagRec: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetDiagRec); + break; + case ODBC3SQLFunctionId::ColumnPrivileges: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLColumnPrivileges); + break; + case ODBC3SQLFunctionId::Columns: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLColumns); + break; + case ODBC3SQLFunctionId::ForeignKeys: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLForeignKeys); + break; + case ODBC3SQLFunctionId::PrimaryKeys: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLPrimaryKeys); + break; + case ODBC3SQLFunctionId::ProcedureColumns: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLProcedureColumns); + break; + case ODBC3SQLFunctionId::Procedures: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLProcedures); + break; + case ODBC3SQLFunctionId::SpecialColumns: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLSpecialColumns); + break; + case ODBC3SQLFunctionId::Statistics: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLStatistics); + break; + case ODBC3SQLFunctionId::TablePrivileges: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLTablePrivileges); + break; + case ODBC3SQLFunctionId::Tables: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLTables); + break; + case ODBC3SQLFunctionId::FreeStmt: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLFreeStmt); + break; + case ODBC3SQLFunctionId::CloseCursor: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLCloseCursor); + break; + case ODBC3SQLFunctionId::Cancel: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLCancel); + break; + case ODBC3SQLFunctionId::EndTran: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLEndTran); + break; + case ODBC3SQLFunctionId::Disconnect: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLDisconnect); + break; + case ODBC3SQLFunctionId::FreeHandle: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLFreeHandle); + break; + case ODBC3SQLFunctionId::GetCursorName: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLGetCursorName); + break; + case ODBC3SQLFunctionId::NativeSql: + + pFunction = reinterpret_cast<oslGenericFunction>(pODBC3SQLNativeSql); + break; + default: + OSL_FAIL("Function unknown!"); + } + return pFunction; +} + + +css::uno::Reference< css::uno::XInterface > ODBCDriver_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxFactory) +{ + return *(new ORealOdbcDriver(_rxFactory)); +} + +// ODBC Environment (common for all Connections): +SQLHANDLE ORealOdbcDriver::EnvironmentHandle(OUString &_rPath) +{ + // Is (for this instance) already an Environment made? + if (!m_pDriverHandle) + { + SQLHANDLE h = SQL_NULL_HANDLE; + // allocate Environment + + // load ODBC-DLL now: + if (!LoadLibrary_ODBC3(_rPath) || N3SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&h) != SQL_SUCCESS) + return SQL_NULL_HANDLE; + + // Save in global Structure + m_pDriverHandle = h; + N3SQLSetEnvAttr(h, SQL_ATTR_ODBC_VERSION, reinterpret_cast<SQLPOINTER>(SQL_OV_ODBC3), SQL_IS_UINTEGER); + //N3SQLSetEnvAttr(h, SQL_ATTR_CONNECTION_POOLING,(SQLPOINTER) SQL_CP_ONE_PER_HENV, SQL_IS_INTEGER); + } + + return m_pDriverHandle; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/ORealDriver.hxx b/connectivity/source/drivers/odbc/ORealDriver.hxx new file mode 100644 index 000000000..cfecb3893 --- /dev/null +++ b/connectivity/source/drivers/odbc/ORealDriver.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_ODBC_OREALDRIVER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_ODBC_OREALDRIVER_HXX + +#include <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> + +namespace com::sun::star { + namespace lang { class XMultiServiceFactory; } + namespace uno { class XInterface; } +} + +namespace connectivity::odbc { + +/// @throws css::uno::Exception +css::uno::Reference< css::uno::XInterface > +ODBCDriver_CreateInstance( css::uno::Reference< css::lang::XMultiServiceFactory > const & factory); + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/OResultSet.cxx b/connectivity/source/drivers/odbc/OResultSet.cxx new file mode 100644 index 000000000..61e5caecf --- /dev/null +++ b/connectivity/source/drivers/odbc/OResultSet.cxx @@ -0,0 +1,1803 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <odbc/OResultSet.hxx> +#include <odbc/OTools.hxx> +#include <odbc/OResultSetMetaData.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyVetoException.hpp> +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <comphelper/property.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> + +using namespace ::comphelper; +using namespace connectivity; +using namespace connectivity::odbc; +using namespace cppu; +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::io; +using namespace com::sun::star::util; + +#define ODBC_SQL_NOT_DEFINED 99UL +static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_OFF, "ODBC_SQL_NOT_DEFINED must be unique"); +static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_ON, "ODBC_SQL_NOT_DEFINED must be unique"); +static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_FIXED, "ODBC_SQL_NOT_DEFINED must be unique"); +static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_VARIABLE, "ODBC_SQL_NOT_DEFINED must be unique"); + +namespace +{ + const SQLLEN nMaxBookmarkLen = 20; +} + + +// IMPLEMENT_SERVICE_INFO(OResultSet,"com.sun.star.sdbcx.OResultSet","com.sun.star.sdbc.ResultSet"); +OUString SAL_CALL OResultSet::getImplementationName( ) +{ + return "com.sun.star.sdbcx.odbc.ResultSet"; +} + + Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdbc.ResultSet", "com.sun.star.sdbcx.ResultSet" }; +} + +sal_Bool SAL_CALL OResultSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + + +OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) : OResultSet_BASE(m_aMutex) + ,OPropertySetHelper(OResultSet_BASE::rBHelper) + ,m_bFetchDataInOrder(true) + ,m_aStatementHandle(_pStatementHandle) + ,m_aConnectionHandle(pStmt->getConnectionHandle()) + ,m_pStatement(pStmt) + ,m_xStatement(*pStmt) + ,m_nTextEncoding(pStmt->getOwnConnection()->getTextEncoding()) + ,m_nRowPos(0) + ,m_nUseBookmarks(ODBC_SQL_NOT_DEFINED) + ,m_nCurrentFetchState(0) + ,m_bWasNull(true) + ,m_bEOF(true) + ,m_bRowInserted(false) + ,m_bRowDeleted(false) + ,m_bUseFetchScroll(false) +{ + osl_atomic_increment( &m_refCount ); + try + { + m_pRowStatusArray.reset( new SQLUSMALLINT[1] ); // the default value + setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, m_pRowStatusArray.get()); + } + catch(const Exception&) + { // we don't want our result destroy here + } + SQLULEN nCurType = 0; + try + { + nCurType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE); + SQLUINTEGER nValueLen = m_pStatement->getCursorProperties(nCurType,false); + if( (nValueLen & SQL_CA2_SENSITIVITY_DELETIONS) != SQL_CA2_SENSITIVITY_DELETIONS || + (nValueLen & SQL_CA2_CRC_EXACT) != SQL_CA2_CRC_EXACT) + m_pSkipDeletedSet.reset( new OSkipDeletedSet(this) ); + } + catch(const Exception&) + { // we don't want our result destroy here + } + try + { + SQLUINTEGER nValueLen = 0; + // Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441%28v=vs.85%29.aspx + // LibreOffice ODBC binds columns only on update, so we don't care about SQL_GD_ANY_COLUMN / SQL_GD_BOUND + // TODO: maybe a problem if a column is updated, then an earlier column fetched? + // an updated column is bound... + // TODO: aren't we assuming SQL_GD_OUTPUT_PARAMS? + // If yes, we should at least OSL_ENSURE it, + // even better throw an exception any OUT parameter registration if !SQL_GD_OUTPUT_PARAMS. + // If !SQL_GD_ANY_ORDER, cache the whole row so that callers can access columns in any order. + // In other words, isolate them from ODBC restrictions. + // TODO: we assume SQL_GD_BLOCK, unless fetchSize is 1 + OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_GETDATA_EXTENSIONS,nValueLen,nullptr); + m_bFetchDataInOrder = ((SQL_GD_ANY_ORDER & nValueLen) != SQL_GD_ANY_ORDER); + } + catch(const Exception&) + { + m_bFetchDataInOrder = true; + } + try + { + // TODO: this does *not* do what it appears. + // We use SQLFetchScroll unconditionally in several places + // the *only* difference this makes is whether ::next() uses SQLFetchScroll or SQLFetch + // so this test seems pointless + if ( getOdbcFunction(ODBC3SQLFunctionId::GetFunctions) ) + { + SQLUSMALLINT nSupported = 0; + m_bUseFetchScroll = ( N3SQLGetFunctions(m_aConnectionHandle,SQL_API_SQLFETCHSCROLL,&nSupported) == SQL_SUCCESS && nSupported == 1 ); + } + } + catch(const Exception&) + { + m_bUseFetchScroll = false; + } + + osl_atomic_decrement( &m_refCount ); +} + +OResultSet::~OResultSet() +{ +} + +void OResultSet::construct() +{ + osl_atomic_increment( &m_refCount ); + allocBuffer(); + osl_atomic_decrement( &m_refCount ); +} + +void OResultSet::disposing() +{ + N3SQLCloseCursor(m_aStatementHandle); + OPropertySetHelper::disposing(); + + ::osl::MutexGuard aGuard(m_aMutex); + releaseBuffer(); + + setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, nullptr); + m_xStatement.clear(); + m_xMetaData.clear(); +} + +SQLRETURN OResultSet::unbind(bool _bUnbindHandle) +{ + SQLRETURN nRet = 0; + if ( _bUnbindHandle ) + nRet = N3SQLFreeStmt(m_aStatementHandle,SQL_UNBIND); + + if ( !m_aBindVector.empty() ) + { + for(auto& [rPtrAddr, rType] : m_aBindVector) + { + switch (rType) + { + case DataType::CHAR: + case DataType::VARCHAR: + delete static_cast< OString* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::BIGINT: + delete static_cast< sal_Int64* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::DECIMAL: + case DataType::NUMERIC: + delete static_cast< OString* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::REAL: + case DataType::DOUBLE: + delete static_cast< double* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::LONGVARCHAR: + case DataType::CLOB: + delete [] static_cast< char* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::LONGVARBINARY: + case DataType::BLOB: + delete [] static_cast< char* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::DATE: + delete static_cast< DATE_STRUCT* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::TIME: + delete static_cast< TIME_STRUCT* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::TIMESTAMP: + delete static_cast< TIMESTAMP_STRUCT* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::BIT: + case DataType::TINYINT: + delete static_cast< sal_Int8* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::SMALLINT: + delete static_cast< sal_Int16* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::INTEGER: + delete static_cast< sal_Int32* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::FLOAT: + delete static_cast< float* >(reinterpret_cast< void * >(rPtrAddr)); + break; + case DataType::BINARY: + case DataType::VARBINARY: + delete static_cast< sal_Int8* >(reinterpret_cast< void * >(rPtrAddr)); + break; + } + } + m_aBindVector.clear(); + } + return nRet; +} + +TVoidPtr OResultSet::allocBindColumn(sal_Int32 _nType,sal_Int32 _nColumnIndex) +{ + TVoidPtr aPair; + switch (_nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new OString()),_nType); + break; + case DataType::BIGINT: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int64(0)),_nType); + break; + case DataType::DECIMAL: + case DataType::NUMERIC: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new OString()),_nType); + break; + case DataType::REAL: + case DataType::DOUBLE: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new double(0.0)),_nType); + break; + case DataType::LONGVARCHAR: + case DataType::CLOB: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new char[2]),_nType); // only for finding + break; + case DataType::LONGVARBINARY: + case DataType::BLOB: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new char[2]),_nType); // only for finding + break; + case DataType::DATE: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new DATE_STRUCT),_nType); + break; + case DataType::TIME: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new TIME_STRUCT),_nType); + break; + case DataType::TIMESTAMP: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new TIMESTAMP_STRUCT),_nType); + break; + case DataType::BIT: + case DataType::TINYINT: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int8(0)),_nType); + break; + case DataType::SMALLINT: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int16(0)),_nType); + break; + case DataType::INTEGER: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int32(0)),_nType); + break; + case DataType::FLOAT: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new float(0)),_nType); + break; + case DataType::BINARY: + case DataType::VARBINARY: + aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int8[m_aRow[_nColumnIndex].getSequence().getLength()]),_nType); + break; + default: + SAL_WARN( "connectivity.odbc", "Unknown type"); + aPair = TVoidPtr(0,_nType); + } + return aPair; +} + +void OResultSet::allocBuffer() +{ + Reference< XResultSetMetaData > xMeta = getMetaData(); + sal_Int32 nLen = xMeta->getColumnCount(); + + m_aBindVector.reserve(nLen); + m_aRow.resize(nLen+1); + + m_aRow[0].setTypeKind(DataType::VARBINARY); + m_aRow[0].setBound( false ); + + for(sal_Int32 i = 1;i<=nLen;++i) + { + sal_Int32 nType = xMeta->getColumnType(i); + m_aRow[i].setTypeKind( nType ); + m_aRow[i].setBound( false ); + } + m_aLengthVector.resize(nLen + 1); +} + +void OResultSet::releaseBuffer() +{ + unbind(false); + m_aLengthVector.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( ) +{ + 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(),OResultSet_BASE::getTypes()); +} + + +sal_Int32 SAL_CALL OResultSet::findColumn( const OUString& columnName ) +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + ::osl::MutexGuard aGuard( m_aMutex ); + + 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 +} + +void OResultSet::ensureCacheForColumn(sal_Int32 columnIndex) +{ + SAL_INFO( "connectivity.odbc", "odbc lionel@mamane.lu OResultSet::ensureCacheForColumn" ); + + assert(columnIndex >= 0); + + const TDataRow::size_type oldCacheSize = m_aRow.size(); + const TDataRow::size_type uColumnIndex = static_cast<TDataRow::size_type>(columnIndex); + + if (oldCacheSize > uColumnIndex) + // nothing to do + return; + + m_aRow.resize(columnIndex + 1); + TDataRow::iterator i (m_aRow.begin() + oldCacheSize); + const TDataRow::const_iterator end(m_aRow.end()); + for (; i != end; ++i) + { + i->setBound(false); + } +} +void OResultSet::invalidateCache() +{ + for(auto& rItem : m_aRow) + { + rItem.setBound(false); + } +} + +Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 /*columnIndex*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBinaryStream", *this ); + + return nullptr; +} + +Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBinaryStream", *this ); + + return nullptr; +} + +template < typename T > T OResultSet::impl_getValue( const sal_Int32 _nColumnIndex, SQLSMALLINT nType ) +{ + T val; + + OTools::getValue(m_pStatement->getOwnConnection(), m_aStatementHandle, _nColumnIndex, nType, m_bWasNull, **this, &val, sizeof(val)); + + return val; +} + +// this function exists for the implicit conversion to sal_Bool (compared to a direct call to impl_getValue) +bool OResultSet::impl_getBoolean( sal_Int32 columnIndex ) +{ + return impl_getValue<sal_Int8>(columnIndex, SQL_C_BIT); +} + +template < typename T > T OResultSet::getValue( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + fillColumn(columnIndex); + m_bWasNull = m_aRow[columnIndex].isNull(); + return m_aRow[columnIndex]; +} +sal_Bool SAL_CALL OResultSet::getBoolean( sal_Int32 columnIndex ) +{ + return getValue<bool>( columnIndex ); +} + +sal_Int8 SAL_CALL OResultSet::getByte( sal_Int32 columnIndex ) +{ + return getValue<sal_Int8>( columnIndex ); +} + + +Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + fillColumn(columnIndex); + m_bWasNull = m_aRow[columnIndex].isNull(); + + Sequence< sal_Int8 > nRet; + switch(m_aRow[columnIndex].getTypeKind()) + { + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + nRet = m_aRow[columnIndex]; + break; + default: + { + OUString const & sRet = m_aRow[columnIndex].getString(); + nRet = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(sRet.getStr()),sizeof(sal_Unicode)*sRet.getLength()); + } + } + return nRet; +} +Sequence< sal_Int8 > OResultSet::impl_getBytes( sal_Int32 columnIndex ) +{ + const SWORD nColumnType = impl_getColumnType_nothrow(columnIndex); + + switch(nColumnType) + { + case SQL_WVARCHAR: + case SQL_WCHAR: + case SQL_WLONGVARCHAR: + case SQL_VARCHAR: + case SQL_CHAR: + case SQL_LONGVARCHAR: + { + OUString const & aRet = OTools::getStringValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,nColumnType,m_bWasNull,**this,m_nTextEncoding); + return Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),sizeof(sal_Unicode)*aRet.getLength()); + } + default: + return OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,SQL_C_BINARY,m_bWasNull,**this); + } +} + +Date OResultSet::impl_getDate( sal_Int32 columnIndex ) +{ + DATE_STRUCT aDate = impl_getValue< DATE_STRUCT> ( columnIndex, + m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_DATE : SQL_C_TYPE_DATE ); + + return Date(aDate.day, aDate.month, aDate.year); +} + +Date SAL_CALL OResultSet::getDate( sal_Int32 columnIndex ) +{ + return getValue<Date>( columnIndex ); +} + + +double SAL_CALL OResultSet::getDouble( sal_Int32 columnIndex ) +{ + return getValue<double>( columnIndex ); +} + + +float SAL_CALL OResultSet::getFloat( sal_Int32 columnIndex ) +{ + return getValue<float>( columnIndex ); +} + +sal_Int16 SAL_CALL OResultSet::getShort( sal_Int32 columnIndex ) +{ + return getValue<sal_Int16>( columnIndex ); +} + +sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 columnIndex ) +{ + return getValue<sal_Int32>( columnIndex ); +} + +sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 columnIndex ) +{ + return getValue<sal_Int64>( columnIndex ); +} +sal_Int64 OResultSet::impl_getLong( sal_Int32 columnIndex ) +{ + try + { + return impl_getValue<sal_Int64>(columnIndex, SQL_C_SBIGINT); + } + catch(const SQLException&) + { + return getString(columnIndex).toInt64(); + } +} + +sal_Int32 SAL_CALL OResultSet::getRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_pSkipDeletedSet ? m_pSkipDeletedSet->getMappedPosition(getDriverPos()) : getDriverPos(); +} + +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_pStatement->getOwnConnection(),m_aStatementHandle); + return m_xMetaData; +} + +Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getArray", *this ); + return nullptr; +} + + +Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getClob", *this ); + return nullptr; +} + +Reference< XBlob > SAL_CALL OResultSet::getBlob( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBlob", *this ); + return nullptr; +} + + +Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getRef", *this ); + return nullptr; +} + + +Any SAL_CALL OResultSet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& /*typeMap*/ ) +{ + return getValue<ORowSetValue>( columnIndex ).makeAny(); +} + +OUString OResultSet::impl_getString( sal_Int32 columnIndex ) +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + const SWORD nColumnType = impl_getColumnType_nothrow(columnIndex); + return OTools::getStringValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,nColumnType,m_bWasNull,**this,m_nTextEncoding); +} +OUString OResultSet::getString( sal_Int32 columnIndex ) +{ + return getValue<OUString>( columnIndex ); +} + +Time OResultSet::impl_getTime( sal_Int32 columnIndex ) +{ + TIME_STRUCT aTime = impl_getValue< TIME_STRUCT > ( columnIndex, + m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIME : SQL_C_TYPE_TIME ); + + return Time(0, aTime.second,aTime.minute,aTime.hour, false); +} +Time SAL_CALL OResultSet::getTime( sal_Int32 columnIndex ) +{ + return getValue<Time>( columnIndex ); +} + +DateTime OResultSet::impl_getTimestamp( sal_Int32 columnIndex ) +{ + TIMESTAMP_STRUCT aTime = impl_getValue< TIMESTAMP_STRUCT > ( columnIndex, + m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIMESTAMP : SQL_C_TYPE_TIMESTAMP ); + + return DateTime(aTime.fraction, + aTime.second, + aTime.minute, + aTime.hour, + aTime.day, + aTime.month, + aTime.year, + false); +} +DateTime SAL_CALL OResultSet::getTimestamp( sal_Int32 columnIndex ) +{ + return getValue<DateTime>( columnIndex ); +} + +sal_Bool SAL_CALL OResultSet::isBeforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + return m_nRowPos == 0; +} + +sal_Bool SAL_CALL OResultSet::isAfterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nRowPos != 0 && m_nCurrentFetchState == SQL_NO_DATA; +} + +sal_Bool SAL_CALL OResultSet::isFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_nRowPos == 1; +} + +sal_Bool SAL_CALL OResultSet::isLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_bEOF && m_nCurrentFetchState != SQL_NO_DATA; +} + +void SAL_CALL OResultSet::beforeFirst( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + if(first()) + previous(); + m_nCurrentFetchState = SQL_SUCCESS; +} + +void SAL_CALL OResultSet::afterLast( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + if(last()) + next(); + m_bEOF = true; +} + + +void SAL_CALL OResultSet::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + } + dispose(); +} + + +sal_Bool SAL_CALL OResultSet::first( ) +{ + return moveImpl(IResultSetHelper::FIRST,0); +} + + +sal_Bool SAL_CALL OResultSet::last( ) +{ + return moveImpl(IResultSetHelper::LAST,0); +} + +sal_Bool SAL_CALL OResultSet::absolute( sal_Int32 row ) +{ + return moveImpl(IResultSetHelper::ABSOLUTE1,row); +} + +sal_Bool SAL_CALL OResultSet::relative( sal_Int32 row ) +{ + return moveImpl(IResultSetHelper::RELATIVE1,row); +} + +sal_Bool SAL_CALL OResultSet::previous( ) +{ + return moveImpl(IResultSetHelper::PRIOR,0); +} + +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); + + bool bRet = m_bRowDeleted; + m_bRowDeleted = false; + + return bRet; +} + +sal_Bool SAL_CALL OResultSet::rowInserted( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + bool bInserted = m_bRowInserted; + m_bRowInserted = false; + + return bInserted; +} + +sal_Bool SAL_CALL OResultSet::rowUpdated( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + return m_pRowStatusArray[0] == SQL_ROW_UPDATED; +} + + +sal_Bool SAL_CALL OResultSet::next( ) +{ + return moveImpl(IResultSetHelper::NEXT,1); +} + + +sal_Bool SAL_CALL OResultSet::wasNull( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return m_bWasNull; +} + + +void SAL_CALL OResultSet::cancel( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + N3SQLCancel(m_aStatementHandle); +} + +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); + + + SQLLEN nRealLen = 0; + Sequence<sal_Int8> aBookmark(nMaxBookmarkLen); + static_assert(o3tl::make_unsigned(nMaxBookmarkLen) >= sizeof(SQLLEN), "must be larger"); + + SQLRETURN nRet = N3SQLBindCol(m_aStatementHandle, + 0, + SQL_C_VARBOOKMARK, + aBookmark.getArray(), + nMaxBookmarkLen, + &nRealLen + ); + + bool bPositionByBookmark = ( nullptr != getOdbcFunction( ODBC3SQLFunctionId::BulkOperations ) ); + if ( bPositionByBookmark ) + { + nRet = N3SQLBulkOperations( m_aStatementHandle, SQL_ADD ); + fillNeededData( nRet ); + } + else + { + if(isBeforeFirst()) + next(); // must be done + nRet = N3SQLSetPos( m_aStatementHandle, 1, SQL_ADD, SQL_LOCK_NO_CHANGE ); + fillNeededData( nRet ); + } + aBookmark.realloc(nRealLen); + try + { + OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this); + } + catch(const SQLException&) + { + nRet = unbind(); + throw; + } + + nRet = unbind(); + OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this); + + if ( bPositionByBookmark ) + { + setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray())); + + nRet = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,0); + } + else + nRet = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_RELATIVE,0); // OJ 06.03.2004 + // sometimes we got an error but we are not interested in anymore #106047# OJ + // OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this); + + if(m_pSkipDeletedSet) + { + if(moveToBookmark(makeAny(aBookmark))) + { + sal_Int32 nRowPos = getDriverPos(); + if ( -1 == m_nRowPos ) + { + nRowPos = m_aPosToBookmarks.size() + 1; + } + if ( nRowPos == m_nRowPos ) + ++nRowPos; + m_nRowPos = nRowPos; + m_pSkipDeletedSet->insertNewPosition(nRowPos); + m_aPosToBookmarks[aBookmark] = nRowPos; + } + } + m_bRowInserted = true; + +} + +void SAL_CALL OResultSet::updateRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + SQLRETURN nRet; + + try + { + bool bPositionByBookmark = ( nullptr != getOdbcFunction( ODBC3SQLFunctionId::BulkOperations ) ); + if ( bPositionByBookmark ) + { + getBookmark(); + assert(m_aRow[0].isBound()); + Sequence<sal_Int8> aBookmark(m_aRow[0].getSequence()); + SQLLEN nRealLen = aBookmark.getLength(); + nRet = N3SQLBindCol(m_aStatementHandle, + 0, + SQL_C_VARBOOKMARK, + aBookmark.getArray(), + aBookmark.getLength(), + &nRealLen + ); + OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this); + nRet = N3SQLBulkOperations(m_aStatementHandle, SQL_UPDATE_BY_BOOKMARK); + fillNeededData(nRet); + // the driver should not have touched this + // (neither the contents of aBookmark FWIW) + assert(nRealLen == aBookmark.getLength()); + } + else + { + nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE); + fillNeededData(nRet); + } + OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this); + // unbind all columns so we can fetch all columns again with SQLGetData + // (and also so that our buffers don't clobber anything, and + // so that a subsequent fetch does not overwrite m_aRow[0]) + invalidateCache(); + nRet = unbind(); + OSL_ENSURE(nRet == SQL_SUCCESS,"ODBC insert could not unbind the columns after success"); + } + catch(...) + { + // unbind all columns so that a subsequent fetch does not overwrite m_aRow[0] + nRet = unbind(); + OSL_ENSURE(nRet == SQL_SUCCESS,"ODBC insert could not unbind the columns after failure"); + throw; + } +} + +void SAL_CALL OResultSet::deleteRow( ) +{ + SQLRETURN nRet = SQL_SUCCESS; + sal_Int32 nPos = getDriverPos(); + nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_DELETE,SQL_LOCK_NO_CHANGE); + OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this); + + m_bRowDeleted = ( m_pRowStatusArray[0] == SQL_ROW_DELETED ); + if ( m_bRowDeleted ) + { + TBookmarkPosMap::iterator aIter = std::find_if(m_aPosToBookmarks.begin(), m_aPosToBookmarks.end(), + [&nPos](const TBookmarkPosMap::value_type& rEntry) { return rEntry.second == nPos; }); + if (aIter != m_aPosToBookmarks.end()) + m_aPosToBookmarks.erase(aIter); + } + if ( m_pSkipDeletedSet ) + m_pSkipDeletedSet->deletePosition(nPos); +} + + +void SAL_CALL OResultSet::cancelRowUpdates( ) +{ +} + + +void SAL_CALL OResultSet::moveToInsertRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + invalidateCache(); + // first unbound all columns + OSL_VERIFY( unbind() == SQL_SUCCESS ); + // SQLRETURN nRet = N3SQLSetStmtAttr(m_aStatementHandle,SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1,SQL_IS_INTEGER); +} + + +void SAL_CALL OResultSet::moveToCurrentRow( ) +{ + invalidateCache(); +} + +void OResultSet::updateValue(sal_Int32 columnIndex, SQLSMALLINT _nType, void const * _pValue) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + m_aBindVector.push_back(allocBindColumn(OTools::MapOdbcType2Jdbc(_nType),columnIndex)); + void* pData = reinterpret_cast<void*>(m_aBindVector.rbegin()->first); + OSL_ENSURE(pData != nullptr,"Data for update is NULL!"); + OTools::bindValue( m_pStatement->getOwnConnection(), + m_aStatementHandle, + columnIndex, + _nType, + 0, + _pValue, + pData, + &m_aLengthVector[columnIndex], + **this, + m_nTextEncoding, + m_pStatement->getOwnConnection()->useOldDateFormat()); +} + +void SAL_CALL OResultSet::updateNull( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + m_aBindVector.push_back(allocBindColumn(DataType::CHAR,columnIndex)); + void* pData = reinterpret_cast<void*>(m_aBindVector.rbegin()->first); + OTools::bindValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,SQL_CHAR,0,nullptr,pData,&m_aLengthVector[columnIndex],**this,m_nTextEncoding,m_pStatement->getOwnConnection()->useOldDateFormat()); +} + + +void SAL_CALL OResultSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x ) +{ + updateValue(columnIndex,SQL_BIT,&x); +} + +void SAL_CALL OResultSet::updateByte( sal_Int32 columnIndex, sal_Int8 x ) +{ + updateValue(columnIndex,SQL_CHAR,&x); +} + + +void SAL_CALL OResultSet::updateShort( sal_Int32 columnIndex, sal_Int16 x ) +{ + updateValue(columnIndex,SQL_TINYINT,&x); +} + +void SAL_CALL OResultSet::updateInt( sal_Int32 columnIndex, sal_Int32 x ) +{ + updateValue(columnIndex,SQL_INTEGER,&x); +} + +void SAL_CALL OResultSet::updateLong( sal_Int32 /*columnIndex*/, sal_Int64 /*x*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRowUpdate::updateLong", *this ); +} + +void SAL_CALL OResultSet::updateFloat( sal_Int32 columnIndex, float x ) +{ + updateValue(columnIndex,SQL_REAL,&x); +} + + +void SAL_CALL OResultSet::updateDouble( sal_Int32 columnIndex, double x ) +{ + updateValue(columnIndex,SQL_DOUBLE,&x); +} + +void SAL_CALL OResultSet::updateString( sal_Int32 columnIndex, const OUString& x ) +{ + sal_Int32 nType = m_aRow[columnIndex].getTypeKind(); + SQLSMALLINT nOdbcType = OTools::jdbcTypeToOdbc(nType); + m_aRow[columnIndex] = x; + m_aRow[columnIndex].setTypeKind(nType); // OJ: otherwise longvarchar will be recognized by fillNeededData + m_aRow[columnIndex].setBound(true); + updateValue(columnIndex,nOdbcType, &x); +} + +void SAL_CALL OResultSet::updateBytes( sal_Int32 columnIndex, const Sequence< sal_Int8 >& x ) +{ + sal_Int32 nType = m_aRow[columnIndex].getTypeKind(); + SQLSMALLINT nOdbcType = OTools::jdbcTypeToOdbc(nType); + m_aRow[columnIndex] = x; + m_aRow[columnIndex].setTypeKind(nType); // OJ: otherwise longvarbinary will be recognized by fillNeededData + m_aRow[columnIndex].setBound(true); + updateValue(columnIndex,nOdbcType, &x); +} + +void SAL_CALL OResultSet::updateDate( sal_Int32 columnIndex, const Date& x ) +{ + DATE_STRUCT aVal = OTools::DateToOdbcDate(x); + updateValue(columnIndex,SQL_DATE,&aVal); +} + + +void SAL_CALL OResultSet::updateTime( sal_Int32 columnIndex, const css::util::Time& x ) +{ + TIME_STRUCT aVal = OTools::TimeToOdbcTime(x); + updateValue(columnIndex,SQL_TIME,&aVal); +} + + +void SAL_CALL OResultSet::updateTimestamp( sal_Int32 columnIndex, const DateTime& x ) +{ + TIMESTAMP_STRUCT aVal = OTools::DateTimeToTimestamp(x); + updateValue(columnIndex,SQL_TIMESTAMP,&aVal); +} + + +void SAL_CALL OResultSet::updateBinaryStream( sal_Int32 columnIndex, const Reference< XInputStream >& x, sal_Int32 length ) +{ + if(!x.is()) + ::dbtools::throwFunctionSequenceException(*this); + + Sequence<sal_Int8> aSeq; + x->readBytes(aSeq,length); + updateBytes(columnIndex,aSeq); +} + +void SAL_CALL OResultSet::updateCharacterStream( sal_Int32 columnIndex, const Reference< XInputStream >& x, sal_Int32 length ) +{ + updateBinaryStream(columnIndex,x,length); +} + +void SAL_CALL OResultSet::refreshRow( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + // SQLRETURN nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_REFRESH,SQL_LOCK_NO_CHANGE); + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_RELATIVE,0); + OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); +} + +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(); +} + +// XRowLocate +Any SAL_CALL OResultSet::getBookmark( ) +{ + fillColumn(0); + if(m_aRow[0].isNull()) + throw SQLException(); + return m_aRow[0].makeAny(); +} +Sequence<sal_Int8> OResultSet::impl_getBookmark( ) +{ + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + TBookmarkPosMap::const_iterator aFind = std::find_if(m_aPosToBookmarks.begin(),m_aPosToBookmarks.end(), + [this] (const TBookmarkPosMap::value_type& bookmarkPos) { + return bookmarkPos.second == m_nRowPos; + }); + + if ( aFind == m_aPosToBookmarks.end() ) + { + if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED ) + { + m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS); + } + if(m_nUseBookmarks == SQL_UB_OFF) + throw SQLException(); + + Sequence<sal_Int8> bookmark = OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,0,SQL_C_VARBOOKMARK,m_bWasNull,**this); + m_aPosToBookmarks[bookmark] = m_nRowPos; + OSL_ENSURE(bookmark.hasElements(),"Invalid bookmark from length 0!"); + return bookmark; + } + else + { + return aFind->first; + } +} + +sal_Bool SAL_CALL OResultSet::moveToBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + invalidateCache(); + Sequence<sal_Int8> aBookmark; + bookmark >>= aBookmark; + OSL_ENSURE(aBookmark.hasElements(),"Invalid bookmark from length 0!"); + if(aBookmark.hasElements()) + { + SQLRETURN nReturn = setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray())); + + if ( SQL_INVALID_HANDLE != nReturn && SQL_ERROR != nReturn ) + { + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,0); + OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + TBookmarkPosMap::const_iterator aFind = m_aPosToBookmarks.find(aBookmark); + if(aFind != m_aPosToBookmarks.end()) + m_nRowPos = aFind->second; + else + m_nRowPos = -1; + return m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO; + } + } + return false; +} + +sal_Bool SAL_CALL OResultSet::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + + invalidateCache(); + Sequence<sal_Int8> aBookmark; + bookmark >>= aBookmark; + setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray())); + + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,rows); + OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + return m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO; +} + +sal_Int32 SAL_CALL OResultSet::compareBookmarks( const Any& lhs, const Any& rhs ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + + return (lhs == rhs) ? CompareBookmark::EQUAL : CompareBookmark::NOT_EQUAL; +} + +sal_Bool SAL_CALL OResultSet::hasOrderedBookmarks( ) +{ + return false; +} + +sal_Int32 SAL_CALL OResultSet::hashBookmark( const Any& /*bookmark*/ ) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "XRowLocate::hashBookmark", *this ); + return 0; +} + +// XDeleteRows +Sequence< sal_Int32 > SAL_CALL OResultSet::deleteRows( const Sequence< Any >& rows ) +{ + Sequence< sal_Int32 > aRet(rows.getLength()); + sal_Int32 *pRet = aRet.getArray(); + + const Any *pBegin = rows.getConstArray(); + const Any *pEnd = pBegin + rows.getLength(); + + for(;pBegin != pEnd;++pBegin,++pRet) + { + try + { + if(moveToBookmark(*pBegin)) + { + deleteRow(); + *pRet = 1; + } + } + catch(const SQLException&) + { + *pRet = 0; + } + } + return aRet; +} + +template < typename T, SQLINTEGER BufferLength > T OResultSet::getStmtOption (SQLINTEGER fOption) const +{ + T result (0); + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + N3SQLGetStmtAttr(m_aStatementHandle, fOption, &result, BufferLength, nullptr); + return result; +} +template < typename T, SQLINTEGER BufferLength > SQLRETURN OResultSet::setStmtOption (SQLINTEGER fOption, T value) const +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + SQLPOINTER sv = reinterpret_cast<SQLPOINTER>(value); + return N3SQLSetStmtAttr(m_aStatementHandle, fOption, sv, BufferLength); +} + +sal_Int32 OResultSet::getResultSetConcurrency() const +{ + sal_uInt32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CONCURRENCY); + if(SQL_CONCUR_READ_ONLY == nValue) + nValue = ResultSetConcurrency::READ_ONLY; + else + nValue = ResultSetConcurrency::UPDATABLE; + + return nValue; +} + +sal_Int32 OResultSet::getResultSetType() const +{ + sal_uInt32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_SENSITIVITY); + if(SQL_SENSITIVE == nValue) + nValue = ResultSetType::SCROLL_SENSITIVE; + else if(SQL_INSENSITIVE == nValue) + nValue = ResultSetType::SCROLL_INSENSITIVE; + else + { + SQLULEN nCurType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE); + if(SQL_CURSOR_KEYSET_DRIVEN == nCurType) + nValue = ResultSetType::SCROLL_SENSITIVE; + else if(SQL_CURSOR_STATIC == nCurType) + nValue = ResultSetType::SCROLL_INSENSITIVE; + else if(SQL_CURSOR_FORWARD_ONLY == nCurType) + nValue = ResultSetType::FORWARD_ONLY; + else if(SQL_CURSOR_DYNAMIC == nCurType) + nValue = ResultSetType::SCROLL_SENSITIVE; + } + return nValue; +} + +sal_Int32 OResultSet::getFetchSize() const +{ + return getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_ARRAY_SIZE); +} + +OUString OResultSet::getCursorName() const +{ + SQLCHAR pName[258]; + SQLSMALLINT nRealLen = 0; + N3SQLGetCursorName(m_aStatementHandle,pName,256,&nRealLen); + return OUString::createFromAscii(reinterpret_cast<char*>(pName)); +} + +bool OResultSet::isBookmarkable() const +{ + if(!m_aConnectionHandle) + return false; + + const SQLULEN nCursorType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE); + + sal_Int32 nAttr = 0; + try + { + switch(nCursorType) + { + case SQL_CURSOR_FORWARD_ONLY: + return false; + case SQL_CURSOR_STATIC: + OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_STATIC_CURSOR_ATTRIBUTES1,nAttr,nullptr); + break; + case SQL_CURSOR_KEYSET_DRIVEN: + OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_KEYSET_CURSOR_ATTRIBUTES1,nAttr,nullptr); + break; + case SQL_CURSOR_DYNAMIC: + OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_DYNAMIC_CURSOR_ATTRIBUTES1,nAttr,nullptr); + break; + } + } + catch(const Exception&) + { + return false; + } + + if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED ) + { + m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS); + } + + return (m_nUseBookmarks != SQL_UB_OFF) && (nAttr & SQL_CA1_BOOKMARK) == SQL_CA1_BOOKMARK; +} + +void OResultSet::setFetchDirection(sal_Int32 _par0) +{ + ::dbtools::throwFunctionNotSupportedSQLException( "setFetchDirection", *this ); + + OSL_ENSURE(_par0>0,"Illegal fetch direction!"); + if ( _par0 > 0 ) + { + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE, _par0); + } +} + +void OResultSet::setFetchSize(sal_Int32 _par0) +{ + OSL_ENSURE(_par0>0,"Illegal fetch size!"); + if ( _par0 != 1 ) + { + throw css::beans::PropertyVetoException("SDBC/ODBC layer not prepared for fetchSize > 1", *this); + } + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_ARRAY_SIZE, _par0); + m_pRowStatusArray.reset( new SQLUSMALLINT[_par0] ); + setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, m_pRowStatusArray.get()); +} + +IPropertyArrayHelper* OResultSet::createArrayHelper( ) const +{ + Sequence< Property > aProps(6); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE), + PROPERTY_ID_ISBOOKMARKABLE, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY); + + return new OPropertyArrayHelper(aProps); +} + +IPropertyArrayHelper & OResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OResultSet::convertFastPropertyValue( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + switch(nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw css::lang::IllegalArgumentException(); + case PROPERTY_ID_FETCHDIRECTION: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection()); + case PROPERTY_ID_FETCHSIZE: + return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize()); + default: + ; + } + return false; +} + +void OResultSet::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const Any& rValue + ) +{ + switch(nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + case PROPERTY_ID_CURSORNAME: + case PROPERTY_ID_RESULTSETCONCURRENCY: + case PROPERTY_ID_RESULTSETTYPE: + throw Exception("cannot set prop " + OUString::number(nHandle), nullptr); + case PROPERTY_ID_FETCHDIRECTION: + setFetchDirection(getINT32(rValue)); + break; + case PROPERTY_ID_FETCHSIZE: + setFetchSize(getINT32(rValue)); + break; + default: + ; + } +} + +void OResultSet::getFastPropertyValue( + Any& rValue, + sal_Int32 nHandle + ) const +{ + switch(nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + rValue <<= isBookmarkable(); + break; + case PROPERTY_ID_CURSORNAME: + rValue <<= getCursorName(); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= getResultSetConcurrency(); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= getResultSetType(); + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= getFetchDirection(); + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= getFetchSize(); + break; + } +} + +void OResultSet::fillColumn(const sal_Int32 _nColumn) +{ + ensureCacheForColumn(_nColumn); + + if (m_aRow[_nColumn].isBound()) + return; + + sal_Int32 curCol; + if(m_bFetchDataInOrder) + { + // m_aRow necessarily has a prefix of bound values, then all unbound values + // EXCEPT for column 0 + // so use binary search to find the earliest unbound value before or at _nColumn + sal_Int32 lower=0; + sal_Int32 upper=_nColumn; + + while (lower < upper) + { + const sal_Int32 middle=(upper-lower)/2 + lower; + if(m_aRow[middle].isBound()) + { + lower=middle+1; + } + else + { + upper=middle; + } + } + + curCol = upper; + } + else + { + curCol = _nColumn; + } + + TDataRow::iterator pColumn = m_aRow.begin() + curCol; + const TDataRow::const_iterator pColumnEnd = m_aRow.begin() + _nColumn + 1; + + if(curCol==0) + { + try + { + *pColumn=impl_getBookmark(); + } + catch (SQLException &) + { + pColumn->setNull(); + } + pColumn->setBound(true); + ++curCol; + ++pColumn; + } + + for (; pColumn != pColumnEnd; ++curCol, ++pColumn) + { + const sal_Int32 nType = pColumn->getTypeKind(); + switch (nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::LONGVARCHAR: + case DataType::CLOB: + *pColumn=impl_getString(curCol); + break; + case DataType::FLOAT: + *pColumn = impl_getValue<float>(curCol, SQL_C_FLOAT); + break; + case DataType::REAL: + case DataType::DOUBLE: + *pColumn = impl_getValue<double>(curCol, SQL_C_DOUBLE); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + *pColumn = impl_getBytes(curCol); + break; + case DataType::DATE: + *pColumn = impl_getDate(curCol); + break; + case DataType::TIME: + *pColumn = impl_getTime(curCol); + break; + case DataType::TIMESTAMP: + *pColumn = impl_getTimestamp(curCol); + break; + case DataType::BIT: + *pColumn = impl_getBoolean(curCol); + break; + case DataType::TINYINT: + *pColumn = impl_getValue<sal_Int8>(curCol, SQL_C_TINYINT); + break; + case DataType::SMALLINT: + *pColumn = impl_getValue<sal_Int16>(curCol, SQL_C_SHORT); + break; + case DataType::INTEGER: + *pColumn = impl_getValue<sal_Int32>(curCol, SQL_C_LONG); + break; + case DataType::BIGINT: + *pColumn = impl_getLong(curCol); + break; + default: + SAL_WARN( "connectivity.odbc","Unknown DataType"); + } + + if ( m_bWasNull ) + pColumn->setNull(); + pColumn->setBound(true); + if(nType != pColumn->getTypeKind()) + { + pColumn->setTypeKind(nType); + } + } +} + +void SAL_CALL OResultSet::acquire() throw() +{ + OResultSet_BASE::acquire(); +} + +void SAL_CALL OResultSet::release() throw() +{ + OResultSet_BASE::release(); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +bool OResultSet::move(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset, bool /*_bRetrieveData*/) +{ + SQLSMALLINT nFetchOrientation = SQL_FETCH_NEXT; + switch(_eCursorPosition) + { + case IResultSetHelper::NEXT: + nFetchOrientation = SQL_FETCH_NEXT; + break; + case IResultSetHelper::PRIOR: + nFetchOrientation = SQL_FETCH_PRIOR; + break; + case IResultSetHelper::FIRST: + nFetchOrientation = SQL_FETCH_FIRST; + break; + case IResultSetHelper::LAST: + nFetchOrientation = SQL_FETCH_LAST; + break; + case IResultSetHelper::RELATIVE1: + nFetchOrientation = SQL_FETCH_RELATIVE; + break; + case IResultSetHelper::ABSOLUTE1: + nFetchOrientation = SQL_FETCH_ABSOLUTE; + break; + case IResultSetHelper::BOOKMARK: // special case here because we are only called with position numbers + { + TBookmarkPosMap::const_iterator aIter = std::find_if(m_aPosToBookmarks.begin(), m_aPosToBookmarks.end(), + [&_nOffset](const TBookmarkPosMap::value_type& rEntry) { return rEntry.second == _nOffset; }); + if (aIter != m_aPosToBookmarks.end()) + return moveToBookmark(makeAny(aIter->first)); + SAL_WARN( "connectivity.odbc", "Bookmark not found!"); + } + return false; + } + + m_bEOF = false; + invalidateCache(); + + SQLRETURN nOldFetchStatus = m_nCurrentFetchState; + // TODO FIXME: both of these will misbehave for + // _eCursorPosition == IResultSetHelper::NEXT/PREVIOUS + // when fetchSize > 1 + if ( !m_bUseFetchScroll && _eCursorPosition == IResultSetHelper::NEXT ) + m_nCurrentFetchState = N3SQLFetch(m_aStatementHandle); + else + m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,nFetchOrientation,_nOffset); + + SAL_INFO( + "connectivity.odbc", + "move(" << nFetchOrientation << "," << _nOffset << "), FetchState = " + << m_nCurrentFetchState); + OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this); + + const bool bSuccess = m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO; + if ( bSuccess ) + { + switch(_eCursorPosition) + { + case IResultSetHelper::NEXT: + ++m_nRowPos; + break; + case IResultSetHelper::PRIOR: + --m_nRowPos; + break; + case IResultSetHelper::FIRST: + m_nRowPos = 1; + break; + case IResultSetHelper::LAST: + m_bEOF = true; + break; + case IResultSetHelper::RELATIVE1: + m_nRowPos += _nOffset; + break; + case IResultSetHelper::ABSOLUTE1: + case IResultSetHelper::BOOKMARK: // special case here because we are only called with position numbers + m_nRowPos = _nOffset; + break; + } // switch(_eCursorPosition) + if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED ) + { + m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS); + } + if ( m_nUseBookmarks == SQL_UB_OFF ) + { + m_aRow[0].setNull(); + } + else + { + ensureCacheForColumn(0); + Sequence<sal_Int8> bookmark = OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,0,SQL_C_VARBOOKMARK,m_bWasNull,**this); + m_aPosToBookmarks[bookmark] = m_nRowPos; + OSL_ENSURE(bookmark.hasElements(),"Invalid bookmark from length 0!"); + m_aRow[0] = bookmark; + } + m_aRow[0].setBound(true); + } + else if ( IResultSetHelper::PRIOR == _eCursorPosition && m_nCurrentFetchState == SQL_NO_DATA ) + // we went beforeFirst + m_nRowPos = 0; + else if(IResultSetHelper::NEXT == _eCursorPosition && m_nCurrentFetchState == SQL_NO_DATA && nOldFetchStatus != SQL_NO_DATA) + // we went afterLast + ++m_nRowPos; + + return bSuccess; +} + +sal_Int32 OResultSet::getDriverPos() const +{ + sal_Int32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_NUMBER); + SAL_INFO( + "connectivity.odbc", + "RowNum = " << nValue << ", RowPos = " << m_nRowPos); + return nValue ? nValue : m_nRowPos; +} + +bool OResultSet::isRowDeleted() const +{ + return m_pRowStatusArray[0] == SQL_ROW_DELETED; +} + +bool OResultSet::moveImpl(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OResultSet_BASE::rBHelper.bDisposed); + return (m_pSkipDeletedSet != nullptr) + ? m_pSkipDeletedSet->skipDeleted(_eCursorPosition,_nOffset,true/*_bRetrieveData*/) + : move(_eCursorPosition,_nOffset,true/*_bRetrieveData*/); +} + +void OResultSet::fillNeededData(SQLRETURN _nRet) +{ + SQLRETURN nRet = _nRet; + if( nRet != SQL_NEED_DATA) + return; + + void* pColumnIndex = nullptr; + nRet = N3SQLParamData(m_aStatementHandle,&pColumnIndex); + + do + { + if (nRet != SQL_SUCCESS && nRet != SQL_SUCCESS_WITH_INFO && nRet != SQL_NEED_DATA) + break; + + sal_IntPtr nColumnIndex ( reinterpret_cast<sal_IntPtr>(pColumnIndex)); + Sequence< sal_Int8 > aSeq; + switch(m_aRow[nColumnIndex].getTypeKind()) + { + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::BLOB: + aSeq = m_aRow[nColumnIndex]; + N3SQLPutData (m_aStatementHandle, aSeq.getArray(), aSeq.getLength()); + break; + case SQL_WLONGVARCHAR: + { + OUString const & sRet = m_aRow[nColumnIndex].getString(); + N3SQLPutData (m_aStatementHandle, static_cast<SQLPOINTER>(const_cast<sal_Unicode *>(sRet.getStr())), sizeof(sal_Unicode)*sRet.getLength()); + break; + } + case DataType::LONGVARCHAR: + case DataType::CLOB: + { + OUString sRet = m_aRow[nColumnIndex].getString(); + OString aString(OUStringToOString(sRet,m_nTextEncoding)); + N3SQLPutData (m_aStatementHandle, static_cast<SQLPOINTER>(const_cast<char *>(aString.getStr())), aString.getLength()); + break; + } + default: + SAL_WARN( "connectivity.odbc", "Not supported at the moment!"); + } + nRet = N3SQLParamData(m_aStatementHandle,&pColumnIndex); + } + while (nRet == SQL_NEED_DATA); +} + +SWORD OResultSet::impl_getColumnType_nothrow(sal_Int32 columnIndex) +{ + std::map<sal_Int32,SWORD>::const_iterator aFind = m_aODBCColumnTypes.find(columnIndex); + if ( aFind == m_aODBCColumnTypes.end() ) + aFind = m_aODBCColumnTypes.emplace( + columnIndex, + OResultSetMetaData::getColumnODBCType(m_pStatement->getOwnConnection(),m_aStatementHandle,*this,columnIndex) + ).first; + return aFind->second; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/OResultSetMetaData.cxx b/connectivity/source/drivers/odbc/OResultSetMetaData.cxx new file mode 100644 index 000000000..21b95c6a7 --- /dev/null +++ b/connectivity/source/drivers/odbc/OResultSetMetaData.cxx @@ -0,0 +1,291 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <odbc/OResultSetMetaData.hxx> +#include <odbc/OTools.hxx> + +using namespace connectivity::odbc; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; + + +OResultSetMetaData::~OResultSetMetaData() +{ +} + +OUString OResultSetMetaData::getCharColAttrib(sal_Int32 _column,sal_Int32 ident) +{ + sal_Int32 column = _column; + if(_column <static_cast<sal_Int32>(m_vMapping.size())) // use mapping + column = m_vMapping[_column]; + + SQLSMALLINT BUFFER_LEN = 128; + std::unique_ptr<char[]> pName(new char[BUFFER_LEN+1]); + SQLSMALLINT nRealLen=0; + SQLRETURN nRet = N3SQLColAttribute(m_aStatementHandle, + static_cast<SQLUSMALLINT>(column), + static_cast<SQLUSMALLINT>(ident), + static_cast<SQLPOINTER>(pName.get()), + BUFFER_LEN, + &nRealLen, + nullptr + ); + OUString sValue; + if ( nRet == SQL_SUCCESS ) + { + if ( nRealLen < 0 ) + nRealLen = BUFFER_LEN; + sValue = OUString(pName.get(),nRealLen,m_pConnection->getTextEncoding()); + } + pName.reset(); + OTools::ThrowException(m_pConnection,nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this); + if(nRealLen > BUFFER_LEN) + { + pName.reset(new char[nRealLen+1]); + nRet = N3SQLColAttribute(m_aStatementHandle, + static_cast<SQLUSMALLINT>(column), + static_cast<SQLUSMALLINT>(ident), + static_cast<SQLPOINTER>(pName.get()), + nRealLen, + &nRealLen, + nullptr + ); + if ( nRet == SQL_SUCCESS && nRealLen > 0) + sValue = OUString(pName.get(),nRealLen,m_pConnection->getTextEncoding()); + OTools::ThrowException(m_pConnection,nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this); + } + + return sValue; +} + +SQLLEN OResultSetMetaData::getNumColAttrib(OConnection const * _pConnection + ,SQLHANDLE _aStatementHandle + ,const css::uno::Reference< css::uno::XInterface >& _xInterface + ,sal_Int32 _column + ,sal_Int32 _ident) +{ + SQLLEN nValue=0; + OTools::ThrowException(_pConnection,(*reinterpret_cast<T3SQLColAttribute>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::ColAttribute)))(_aStatementHandle, + static_cast<SQLUSMALLINT>(_column), + static_cast<SQLUSMALLINT>(_ident), + nullptr, + 0, + nullptr, + &nValue),_aStatementHandle,SQL_HANDLE_STMT,_xInterface); + return nValue; +} + +sal_Int32 OResultSetMetaData::getNumColAttrib(sal_Int32 _column,sal_Int32 ident) +{ + sal_Int32 column = _column; + if(_column < static_cast<sal_Int32>(m_vMapping.size())) // use mapping + column = m_vMapping[_column]; + + return getNumColAttrib(m_pConnection,m_aStatementHandle,*this,column,ident); +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnDisplaySize( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_DISPLAY_SIZE); +} + +SQLSMALLINT OResultSetMetaData::getColumnODBCType(OConnection const * _pConnection + ,SQLHANDLE _aStatementHandle + ,const css::uno::Reference< css::uno::XInterface >& _xInterface + ,sal_Int32 column) +{ + SQLSMALLINT nType = 0; + try + { + nType = static_cast<SQLSMALLINT>(getNumColAttrib(_pConnection,_aStatementHandle,_xInterface,column,SQL_DESC_CONCISE_TYPE)); + if(nType == SQL_UNKNOWN_TYPE) + nType = static_cast<SQLSMALLINT>(getNumColAttrib(_pConnection,_aStatementHandle,_xInterface,column, SQL_DESC_TYPE)); + } + catch(SQLException& ) // in this case we have an odbc 2.0 driver + { + nType = static_cast<SQLSMALLINT>(getNumColAttrib(_pConnection,_aStatementHandle,_xInterface,column,SQL_DESC_CONCISE_TYPE )); + } + + return nType; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnType( sal_Int32 column ) +{ + std::map<sal_Int32,sal_Int32>::iterator aFind = m_aColumnTypes.find(column); + if ( aFind == m_aColumnTypes.end() ) + { + sal_Int32 nType = 0; + if(!m_bUseODBC2Types) + { + try + { + nType = getNumColAttrib(column,SQL_DESC_CONCISE_TYPE); + if(nType == SQL_UNKNOWN_TYPE) + nType = getNumColAttrib(column, SQL_DESC_TYPE); + nType = OTools::MapOdbcType2Jdbc(nType); + } + catch(SQLException& ) // in this case we have an odbc 2.0 driver + { + m_bUseODBC2Types = true; + nType = OTools::MapOdbcType2Jdbc(getNumColAttrib(column,SQL_DESC_CONCISE_TYPE )); + } + } + else + nType = OTools::MapOdbcType2Jdbc(getNumColAttrib(column,SQL_DESC_CONCISE_TYPE )); + aFind = m_aColumnTypes.emplace(column,nType).first; + } + + + return aFind->second; +} + + +sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount( ) +{ + if(m_nColCount != -1) + return m_nColCount; + sal_Int16 nNumResultCols=0; + OTools::ThrowException(m_pConnection,N3SQLNumResultCols(m_aStatementHandle,&nNumResultCols),m_aStatementHandle,SQL_HANDLE_STMT,*this); + m_nColCount = nNumResultCols; + return m_nColCount; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_CASE_SENSITIVE) == SQL_TRUE; +} + + +OUString SAL_CALL OResultSetMetaData::getSchemaName( sal_Int32 column ) +{ + return getCharColAttrib(column,SQL_DESC_SCHEMA_NAME); +} + + +OUString SAL_CALL OResultSetMetaData::getColumnName( sal_Int32 column ) +{ + return getCharColAttrib(column,SQL_DESC_NAME); +} + +OUString SAL_CALL OResultSetMetaData::getTableName( sal_Int32 column ) +{ + return getCharColAttrib(column,SQL_DESC_TABLE_NAME); +} + +OUString SAL_CALL OResultSetMetaData::getCatalogName( sal_Int32 column ) +{ + return getCharColAttrib(column,SQL_DESC_CATALOG_NAME); +} + +OUString SAL_CALL OResultSetMetaData::getColumnTypeName( sal_Int32 column ) +{ + return getCharColAttrib(column,SQL_DESC_TYPE_NAME); +} + +OUString SAL_CALL OResultSetMetaData::getColumnLabel( sal_Int32 column ) +{ + return getCharColAttrib(column,SQL_DESC_LABEL); +} + +OUString SAL_CALL OResultSetMetaData::getColumnServiceName( sal_Int32 /*column*/ ) +{ + return OUString(); +} + + +sal_Bool SAL_CALL OResultSetMetaData::isCurrency( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_FIXED_PREC_SCALE) == SQL_TRUE; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isAutoIncrement( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_AUTO_UNIQUE_VALUE) == SQL_TRUE; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isSigned( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_UNSIGNED) == SQL_FALSE; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getPrecision( sal_Int32 column ) +{ + sal_Int32 nType = 0; + try + { + nType = getNumColAttrib(column,SQL_DESC_PRECISION); + } + catch(const SQLException& ) // in this case we have an odbc 2.0 driver + { + m_bUseODBC2Types = true; + nType = getNumColAttrib(column,SQL_COLUMN_PRECISION ); + } + return nType; +} + +sal_Int32 SAL_CALL OResultSetMetaData::getScale( sal_Int32 column ) +{ + sal_Int32 nType = 0; + try + { + nType = getNumColAttrib(column,SQL_DESC_SCALE); + } + catch(const SQLException& ) // in this case we have an odbc 2.0 driver + { + m_bUseODBC2Types = true; + nType = getNumColAttrib(column,SQL_COLUMN_SCALE ); + } + return nType; +} + + +sal_Int32 SAL_CALL OResultSetMetaData::isNullable( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_NULLABLE); +} + + +sal_Bool SAL_CALL OResultSetMetaData::isSearchable( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_SEARCHABLE) != SQL_PRED_NONE; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isReadOnly( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_UPDATABLE) == SQL_ATTR_READONLY; +} + + +sal_Bool SAL_CALL OResultSetMetaData::isDefinitelyWritable( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_UPDATABLE) == SQL_ATTR_WRITE; +} + +sal_Bool SAL_CALL OResultSetMetaData::isWritable( sal_Int32 column ) +{ + return getNumColAttrib(column,SQL_DESC_UPDATABLE) == SQL_ATTR_WRITE; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/OStatement.cxx b/connectivity/source/drivers/odbc/OStatement.cxx new file mode 100644 index 000000000..6e0a37169 --- /dev/null +++ b/connectivity/source/drivers/odbc/OStatement.cxx @@ -0,0 +1,1102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <osl/diagnose.h> +#include <odbc/OStatement.hxx> +#include <odbc/OConnection.hxx> +#include <odbc/OResultSet.hxx> +#include <comphelper/property.hxx> +#include <odbc/OTools.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 <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/types.hxx> +#include <rtl/strbuf.hxx> +#include <algorithm> +#include <strings.hrc> +#include <connectivity/dbexception.hxx> + +using namespace ::comphelper; + +#define THROW_SQL(x) \ + OTools::ThrowException(m_pConnection.get(),x,m_aStatementHandle,SQL_HANDLE_STMT,*this) + + +using namespace connectivity::odbc; + +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::io; +using namespace com::sun::star::util; + +OStatement_Base::OStatement_Base(OConnection* _pConnection ) + :OStatement_BASE(m_aMutex) + ,OPropertySetHelper(OStatement_BASE::rBHelper) + ,m_pConnection(_pConnection) + ,m_aStatementHandle(SQL_NULL_HANDLE) + ,m_pRowStatusArray(nullptr) +{ + osl_atomic_increment( &m_refCount ); + m_aStatementHandle = m_pConnection->createStatementHandle(); + + //setMaxFieldSize(0); + // Don't do this. By ODBC spec, "0" is the default for the SQL_ATTR_MAX_LENGTH attribute. We once introduced + // this line since a PostgreSQL ODBC driver had a default other than 0. However, current drivers (at least 8.3 + // and later) have a proper default of 0, so there should be no need anymore. + // On the other hand, the NotesSQL driver (IBM's ODBC driver for the Lotus Notes series) wrongly interprets + // "0" as "0", whereas the ODBC spec says it should in fact mean "unlimited". + // So, removing this line seems to be the best option for now. + // If we ever again encounter an ODBC driver which needs this option, then we should introduce a data source + // setting for it, instead of unconditionally doing it. + + osl_atomic_decrement( &m_refCount ); +} + +OStatement_Base::~OStatement_Base() +{ + OSL_ENSURE(!m_aStatementHandle,"Sohould ne null here!"); +} + +void OStatement_Base::disposeResultSet() +{ + // free the cursor if alive + Reference< XComponent > xComp(m_xResultSet.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + m_xResultSet.clear(); +} + +void SAL_CALL OStatement_Base::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + disposeResultSet(); + ::comphelper::disposeComponent(m_xGeneratedStatement); + + OSL_ENSURE(m_aStatementHandle,"OStatement_BASE2::disposing: StatementHandle is null!"); + if (m_pConnection.is()) + { + m_pConnection->freeStatementHandle(m_aStatementHandle); + m_pConnection.clear(); + } + OSL_ENSURE(!m_aStatementHandle,"Sohould ne null here!"); + + OStatement_BASE::disposing(); +} + +void OStatement_BASE2::disposing() +{ + ::osl::MutexGuard aGuard1(m_aMutex); + OStatement_Base::disposing(); +} + +Any SAL_CALL OStatement_Base::queryInterface( const Type & rType ) +{ + if ( m_pConnection.is() && !m_pConnection->isAutoRetrievingEnabled() && rType == cppu::UnoType<XGeneratedResultSet>::get()) + return Any(); + 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<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get()); + Sequence< Type > aOldTypes = OStatement_BASE::getTypes(); + if ( m_pConnection.is() && !m_pConnection->isAutoRetrievingEnabled() ) + { + auto newEnd = std::remove(aOldTypes.begin(), aOldTypes.end(), + cppu::UnoType<XGeneratedResultSet>::get()); + aOldTypes.realloc(std::distance(aOldTypes.begin(), newEnd)); + } + + return ::comphelper::concatSequences(aTypes.getTypes(),aOldTypes); +} + +Reference< XResultSet > SAL_CALL OStatement_Base::getGeneratedValues( ) +{ + OSL_ENSURE( m_pConnection.is() && m_pConnection->isAutoRetrievingEnabled(),"Illegal call here. isAutoRetrievingEnabled is false!"); + Reference< XResultSet > xRes; + if ( m_pConnection.is() ) + { + OUString sStmt = m_pConnection->getTransformedGeneratedStatement(m_sSqlStatement); + if ( !sStmt.isEmpty() ) + { + ::comphelper::disposeComponent(m_xGeneratedStatement); + m_xGeneratedStatement = m_pConnection->createStatement(); + xRes = m_xGeneratedStatement->executeQuery(sStmt); + } + } + return xRes; +} + +void SAL_CALL OStatement_Base::cancel( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + N3SQLCancel(m_aStatementHandle); +} + + +void SAL_CALL OStatement_Base::close( ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + } + dispose(); +} + + +void SAL_CALL OStatement::clearBatch( ) +{ + +} + +void OStatement_Base::reset() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + clearWarnings (); + + if (m_xResultSet.get().is()) + { + clearMyResultSet(); + } + if(m_aStatementHandle) + { + THROW_SQL(N3SQLFreeStmt(m_aStatementHandle, SQL_CLOSE)); + } +} + +// clearMyResultSet +// If a ResultSet was created for this Statement, close it +void OStatement_Base::clearMyResultSet() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + try + { + Reference<XCloseable> xCloseable( + m_xResultSet.get(), css::uno::UNO_QUERY); + if ( xCloseable.is() ) + xCloseable->close(); + } + catch( const DisposedException& ) { } + + m_xResultSet.clear(); +} + +SQLLEN OStatement_Base::getRowCount() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + SQLLEN numRows = 0; + + try { + THROW_SQL(N3SQLRowCount(m_aStatementHandle,&numRows)); + } + catch (const SQLException&) + { + } + return numRows; +} + +// lockIfNecessary +// If the given SQL statement contains a 'FOR UPDATE' clause, change +// the concurrency to lock so that the row can then be updated. Returns +// true if the concurrency has been changed +bool OStatement_Base::lockIfNecessary (const OUString& sql) +{ + bool rc = false; + + // First, convert the statement to upper case + + OUString sqlStatement = sql.toAsciiUpperCase (); + + // Now, look for the FOR UPDATE keywords. If there is any extra white + // space between the FOR and UPDATE, this will fail. + + sal_Int32 index = sqlStatement.indexOf(" FOR UPDATE"); + + // We found it. Change our concurrency level to ensure that the + // row can be updated. + + if (index > 0) + { + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + try + { + THROW_SQL((setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CONCURRENCY, SQL_CONCUR_LOCK))); + } + catch (const SQLWarning& warn) + { + // Catch any warnings and place on the warning stack + setWarning (warn); + } + rc = true; + } + + return rc; +} + +// setWarning +// Sets the warning + + +void OStatement_Base::setWarning (const SQLWarning &ex) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_aLastWarning = ex; +} + + +// getColumnCount +// Return the number of columns in the ResultSet +sal_Int32 OStatement_Base::getColumnCount() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + sal_Int16 numCols = 0; + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + + try { + THROW_SQL(N3SQLNumResultCols(m_aStatementHandle,&numCols)); + } + catch (const SQLException&) + { + } + return numCols; +} + + +sal_Bool SAL_CALL OStatement_Base::execute( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + m_sSqlStatement = sql; + + + OString aSql(OUStringToOString(sql,getOwnConnection()->getTextEncoding())); + + bool hasResultSet = false; + SQLWarning aWarning; + + // Reset the statement handle and warning + + reset(); + + // Check for a 'FOR UPDATE' statement. If present, change + // the concurrency to lock + + lockIfNecessary (sql); + + // Call SQLExecDirect + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + + try { + THROW_SQL(N3SQLExecDirect(m_aStatementHandle, reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(aSql.getStr())), aSql.getLength())); + } + catch (const SQLWarning& ex) { + + // Save pointer to warning and save with ResultSet + // object once it is created. + + aWarning = ex; + } + + // Now determine if there is a result set associated with + // the SQL statement that was executed. Get the column + // count, and if it is not zero, there is a result set. + + if (getColumnCount () > 0) + { + hasResultSet = true; + } + + return hasResultSet; +} + +// getResultSet +// getResultSet returns the current result as a ResultSet. It +// returns NULL if the current result is not a ResultSet. + +Reference< XResultSet > OStatement_Base::getResultSet(bool checkCount) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + if (m_xResultSet.get().is()) // if resultset already retrieved, + { + // throw exception to avoid sequence error + ::dbtools::throwFunctionSequenceException(*this); + } + + OResultSet* pRs = nullptr; + sal_Int32 numCols = 1; + + // If we already know we have result columns, checkCount + // is false. This is an optimization to prevent unneeded + // calls to getColumnCount + + if (checkCount) + numCols = getColumnCount (); + + // Only return a result set if there are result columns + + if (numCols > 0) + { + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + pRs = createResulSet(); + pRs->construct(); + + // Save a copy of our last result set + // Changed to save copy at getResultSet. + //m_xResultSet = rs; + } + else + clearMyResultSet (); + + return pRs; +} + +// getStmtOption +// Invoke SQLGetStmtOption with the given option. + + +template < typename T, SQLINTEGER BufferLength > T OStatement_Base::getStmtOption (SQLINTEGER fOption) const +{ + T result (0); + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + N3SQLGetStmtAttr(m_aStatementHandle, fOption, &result, BufferLength, nullptr); + return result; +} +template < typename T, SQLINTEGER BufferLength > SQLRETURN OStatement_Base::setStmtOption (SQLINTEGER fOption, T value) const +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + SQLPOINTER sv = reinterpret_cast<SQLPOINTER>(value); + return N3SQLSetStmtAttr(m_aStatementHandle, fOption, sv, BufferLength); +} + + +Reference< XResultSet > SAL_CALL OStatement_Base::executeQuery( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + Reference< XResultSet > xRS; + + // Execute the statement. If execute returns true, a result + // set exists. + + if (execute (sql)) + { + xRS = getResultSet (false); + m_xResultSet = xRS; + } + else + { + // No ResultSet was produced. Raise an exception + m_pConnection->throwGenericSQLException(STR_NO_RESULTSET,*this); + } + return xRS; +} + + +Reference< XConnection > SAL_CALL OStatement_Base::getConnection( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + return Reference< XConnection >(m_pConnection.get()); +} + + +Any SAL_CALL OStatement::queryInterface( const Type & rType ) +{ + Any aRet = ::cppu::queryInterface(rType,static_cast< XBatchExecution*> (this)); + return aRet.hasValue() ? aRet : OStatement_Base::queryInterface(rType); +} + + +void SAL_CALL OStatement::addBatch( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_aBatchVector.push_back(sql); +} + +Sequence< sal_Int32 > SAL_CALL OStatement::executeBatch( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + OStringBuffer aBatchSql; + sal_Int32 nLen = m_aBatchVector.size(); + + for (auto const& elem : m_aBatchVector) + { + aBatchSql.append(OUStringToOString(elem,getOwnConnection()->getTextEncoding())); + aBatchSql.append(";"); + } + + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + auto s = aBatchSql.makeStringAndClear(); + THROW_SQL(N3SQLExecDirect(m_aStatementHandle, reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(s.getStr())), s.getLength())); + + Sequence< sal_Int32 > aRet(nLen); + sal_Int32* pArray = aRet.getArray(); + for(sal_Int32 j=0;j<nLen;++j) + { + SQLRETURN nError = N3SQLMoreResults(m_aStatementHandle); + if(nError == SQL_SUCCESS) + { + SQLLEN nRowCount=0; + N3SQLRowCount(m_aStatementHandle,&nRowCount); + pArray[j] = nRowCount; + } + } + return aRet; +} + + +sal_Int32 SAL_CALL OStatement_Base::executeUpdate( const OUString& sql ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + sal_Int32 numRows = -1; + + // Execute the statement. If execute returns false, a + // row count exists. + + if (!execute (sql)) { + numRows = getUpdateCount(); + } + else { + + // No update count was produced (a ResultSet was). Raise + // an exception + + ::connectivity::SharedResources aResources; + const OUString sError( aResources.getResourceString(STR_NO_ROWCOUNT)); + throw SQLException (sError, *this,OUString(),0,Any()); + } + return numRows; + +} + + +Reference< XResultSet > SAL_CALL OStatement_Base::getResultSet( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_xResultSet = getResultSet(true); + return m_xResultSet; +} + + +sal_Int32 SAL_CALL OStatement_Base::getUpdateCount( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + sal_Int32 rowCount = -1; + + // Only return a row count for SQL statements that did not + // return a result set. + + if (getColumnCount () == 0) + rowCount = getRowCount (); + + return rowCount; +} + + +sal_Bool SAL_CALL OStatement_Base::getMoreResults( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + SQLWarning warning; + bool hasResultSet = false; + + // clear previous warnings + + clearWarnings (); + + // Call SQLMoreResults + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + + try { + hasResultSet = N3SQLMoreResults(m_aStatementHandle) == SQL_SUCCESS; + } + catch (const SQLWarning &ex) { + + // Save pointer to warning and save with ResultSet + // object once it is created. + + warning = ex; + } + + // There are more results (it may not be a result set, though) + + if (hasResultSet) + { + + // Now determine if there is a result set associated + // with the SQL statement that was executed. Get the + // column count, and if it is zero, there is not a + // result set. + + if (getColumnCount () == 0) + hasResultSet = false; + } + + // Set the warning for the statement, if one was generated + + setWarning (warning); + + // Return the result set indicator + + return hasResultSet; +} + + +Any SAL_CALL OStatement_Base::getWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + return makeAny(m_aLastWarning); +} + + +void SAL_CALL OStatement_Base::clearWarnings( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(OStatement_BASE::rBHelper.bDisposed); + + + m_aLastWarning = SQLWarning(); +} + + +sal_Int64 OStatement_Base::getQueryTimeOut() const +{ + return getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_QUERY_TIMEOUT); +} + +sal_Int64 OStatement_Base::getMaxRows() const +{ + return getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_MAX_ROWS); +} + +sal_Int32 OStatement_Base::getResultSetConcurrency() const +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + SQLULEN nValue (getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CONCURRENCY)); + if(nValue == SQL_CONCUR_READ_ONLY) + nValue = ResultSetConcurrency::READ_ONLY; + else + nValue = ResultSetConcurrency::UPDATABLE; + return nValue; +} + +sal_Int32 OStatement_Base::getResultSetType() const +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + SQLULEN nValue (getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE)); + switch(nValue) + { + case SQL_CURSOR_FORWARD_ONLY: + nValue = ResultSetType::FORWARD_ONLY; + break; + case SQL_CURSOR_KEYSET_DRIVEN: + case SQL_CURSOR_STATIC: + nValue = ResultSetType::SCROLL_INSENSITIVE; + break; + case SQL_CURSOR_DYNAMIC: + nValue = ResultSetType::SCROLL_SENSITIVE; + break; + default: + OSL_FAIL("Unknown ODBC Cursor Type"); + } + + return nValue; +} + +sal_Int32 OStatement_Base::getFetchDirection() const +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + SQLULEN nValue (getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_SCROLLABLE)); + switch(nValue) + { + case SQL_SCROLLABLE: + nValue = FetchDirection::REVERSE; + break; + default: + nValue = FetchDirection::FORWARD; + break; + } + + return nValue; +} + +sal_Int32 OStatement_Base::getFetchSize() const +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + return getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_ARRAY_SIZE); +} + +sal_Int64 OStatement_Base::getMaxFieldSize() const +{ + return getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_MAX_LENGTH); +} + +OUString OStatement_Base::getCursorName() const +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + SQLCHAR pName[258]; + SQLSMALLINT nRealLen = 0; + N3SQLGetCursorName(m_aStatementHandle,pName,256,&nRealLen); + return OUString::createFromAscii(reinterpret_cast<char*>(pName)); +} + +void OStatement_Base::setQueryTimeOut(sal_Int64 seconds) +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_QUERY_TIMEOUT,seconds); +} + +void OStatement_Base::setMaxRows(sal_Int64 _par0) +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_MAX_ROWS, _par0); +} + +void OStatement_Base::setResultSetConcurrency(sal_Int32 _par0) +{ + SQLULEN nSet; + if(_par0 == ResultSetConcurrency::READ_ONLY) + nSet = SQL_CONCUR_READ_ONLY; + else + nSet = SQL_CONCUR_VALUES; + + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CONCURRENCY, nSet); +} + +void OStatement_Base::setResultSetType(sal_Int32 _par0) +{ + + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN); + + bool bUseBookmark = isUsingBookmarks(); + SQLULEN nSet( SQL_UNSPECIFIED ); + switch(_par0) + { + case ResultSetType::FORWARD_ONLY: + nSet = SQL_UNSPECIFIED; + break; + case ResultSetType::SCROLL_INSENSITIVE: + nSet = SQL_INSENSITIVE; + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_KEYSET_DRIVEN); + break; + case ResultSetType::SCROLL_SENSITIVE: + if(bUseBookmark) + { + SQLUINTEGER nCurProp = getCursorProperties(SQL_CURSOR_DYNAMIC,true); + if((nCurProp & SQL_CA1_BOOKMARK) != SQL_CA1_BOOKMARK) // check if bookmark for this type isn't supported + { // we have to test the next one + nCurProp = getCursorProperties(SQL_CURSOR_KEYSET_DRIVEN,true); + bool bNotBookmarks = ((nCurProp & SQL_CA1_BOOKMARK) != SQL_CA1_BOOKMARK); + nCurProp = getCursorProperties(SQL_CURSOR_KEYSET_DRIVEN,false); + nSet = SQL_CURSOR_KEYSET_DRIVEN; + if( bNotBookmarks || + ((nCurProp & SQL_CA2_SENSITIVITY_DELETIONS) != SQL_CA2_SENSITIVITY_DELETIONS) || + ((nCurProp & SQL_CA2_SENSITIVITY_ADDITIONS) != SQL_CA2_SENSITIVITY_ADDITIONS)) + { + // bookmarks for keyset isn't supported so reset bookmark setting + setUsingBookmarks(false); + nSet = SQL_CURSOR_DYNAMIC; + } + } + else + nSet = SQL_CURSOR_DYNAMIC; + } + else + nSet = SQL_CURSOR_DYNAMIC; + if( setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE, nSet) != SQL_SUCCESS ) + { + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_KEYSET_DRIVEN); + } + nSet = SQL_SENSITIVE; + break; + default: + OSL_FAIL( "OStatement_Base::setResultSetType: invalid result set type!" ); + break; + } + + + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_SENSITIVITY, nSet); +} + +void OStatement_Base::setEscapeProcessing( const bool _bEscapeProc ) +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + SQLULEN nEscapeProc( _bEscapeProc ? SQL_NOSCAN_OFF : SQL_NOSCAN_ON ); + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_NOSCAN, nEscapeProc); +} + + +void OStatement_Base::setFetchDirection(sal_Int32 _par0) +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + if(_par0 == FetchDirection::FORWARD) + { + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_SCROLLABLE, SQL_NONSCROLLABLE); + } + else if(_par0 == FetchDirection::REVERSE) + { + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_SCROLLABLE, SQL_SCROLLABLE); + } +} + +void OStatement_Base::setFetchSize(sal_Int32 _par0) +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + OSL_ENSURE(_par0>0,"Illegal fetch size!"); + if ( _par0 > 0 ) + { + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_ARRAY_SIZE, _par0); + + delete[] m_pRowStatusArray; + m_pRowStatusArray = new SQLUSMALLINT[_par0]; + setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, m_pRowStatusArray); + } +} + +void OStatement_Base::setMaxFieldSize(sal_Int64 _par0) +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_MAX_LENGTH, _par0); +} + +void OStatement_Base::setCursorName(const OUString &_par0) +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + OString aName(OUStringToOString(_par0,getOwnConnection()->getTextEncoding())); + N3SQLSetCursorName(m_aStatementHandle, reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(aName.getStr())), static_cast<SQLSMALLINT>(aName.getLength())); +} + +bool OStatement_Base::isUsingBookmarks() const +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + return SQL_UB_OFF != getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS); +} + +bool OStatement_Base::getEscapeProcessing() const +{ + OSL_ENSURE( m_aStatementHandle, "StatementHandle is null!" ); + return SQL_NOSCAN_OFF == getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS); +} + +void OStatement_Base::setUsingBookmarks(bool _bUseBookmark) +{ + OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!"); + SQLULEN nValue = _bUseBookmark ? SQL_UB_VARIABLE : SQL_UB_OFF; + setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS, nValue); +} + +::cppu::IPropertyArrayHelper* OStatement_Base::createArrayHelper( ) const +{ + Sequence< Property > aProps(10); + Property* pProperties = aProps.getArray(); + sal_Int32 nPos = 0; + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME), + PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING), + PROPERTY_ID_ESCAPEPROCESSING, cppu::UnoType<bool>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION), + PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE), + PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE), + PROPERTY_ID_MAXFIELDSIZE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS), + PROPERTY_ID_MAXROWS, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT), + PROPERTY_ID_QUERYTIMEOUT, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY), + PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE), + PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0); + pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_USEBOOKMARKS), + PROPERTY_ID_USEBOOKMARKS, cppu::UnoType<bool>::get(), 0); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +::cppu::IPropertyArrayHelper & OStatement_Base::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OStatement_Base::convertFastPropertyValue( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + bool bConverted = false; + try + { + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getQueryTimeOut()); + break; + + case PROPERTY_ID_MAXFIELDSIZE: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getMaxFieldSize()); + break; + + case PROPERTY_ID_MAXROWS: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getMaxRows()); + break; + + case PROPERTY_ID_CURSORNAME: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getCursorName()); + break; + + case PROPERTY_ID_RESULTSETCONCURRENCY: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getResultSetConcurrency()); + break; + + case PROPERTY_ID_RESULTSETTYPE: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getResultSetType()); + break; + + case PROPERTY_ID_FETCHDIRECTION: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection()); + break; + + case PROPERTY_ID_FETCHSIZE: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize()); + break; + + case PROPERTY_ID_USEBOOKMARKS: + bConverted = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, isUsingBookmarks()); + break; + + case PROPERTY_ID_ESCAPEPROCESSING: + bConverted = ::comphelper::tryPropertyValue( rConvertedValue, rOldValue, rValue, getEscapeProcessing() ); + break; + + } + } + catch(const SQLException&) + { + // throw Exception(e.Message,*this); + } + return bConverted; +} + +void OStatement_Base::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + try + { + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + setQueryTimeOut(comphelper::getINT64(rValue)); + break; + case PROPERTY_ID_MAXFIELDSIZE: + setMaxFieldSize(comphelper::getINT64(rValue)); + break; + case PROPERTY_ID_MAXROWS: + setMaxRows(comphelper::getINT64(rValue)); + break; + case PROPERTY_ID_CURSORNAME: + setCursorName(comphelper::getString(rValue)); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + setResultSetConcurrency(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_RESULTSETTYPE: + setResultSetType(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_FETCHDIRECTION: + setFetchDirection(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_FETCHSIZE: + setFetchSize(comphelper::getINT32(rValue)); + break; + case PROPERTY_ID_USEBOOKMARKS: + setUsingBookmarks(comphelper::getBOOL(rValue)); + break; + case PROPERTY_ID_ESCAPEPROCESSING: + setEscapeProcessing( ::comphelper::getBOOL( rValue ) ); + break; + default: + OSL_FAIL( "OStatement_Base::setFastPropertyValue_NoBroadcast: what property?" ); + break; + } + } + catch(const SQLException& ) + { + // throw Exception(e.Message,*this); + } +} + +void OStatement_Base::getFastPropertyValue(Any& rValue,sal_Int32 nHandle) const +{ + switch(nHandle) + { + case PROPERTY_ID_QUERYTIMEOUT: + rValue <<= getQueryTimeOut(); + break; + case PROPERTY_ID_MAXFIELDSIZE: + rValue <<= getMaxFieldSize(); + break; + case PROPERTY_ID_MAXROWS: + rValue <<= getMaxRows(); + break; + case PROPERTY_ID_CURSORNAME: + rValue <<= getCursorName(); + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + rValue <<= getResultSetConcurrency(); + break; + case PROPERTY_ID_RESULTSETTYPE: + rValue <<= getResultSetType(); + break; + case PROPERTY_ID_FETCHDIRECTION: + rValue <<= getFetchDirection(); + break; + case PROPERTY_ID_FETCHSIZE: + rValue <<= getFetchSize(); + break; + case PROPERTY_ID_USEBOOKMARKS: + rValue <<= isUsingBookmarks(); + break; + case PROPERTY_ID_ESCAPEPROCESSING: + rValue <<= getEscapeProcessing(); + break; + default: + OSL_FAIL( "OStatement_Base::getFastPropertyValue: what property?" ); + break; + } +} + +IMPLEMENT_SERVICE_INFO(OStatement,"com.sun.star.sdbcx.OStatement","com.sun.star.sdbc.Statement"); + +void SAL_CALL OStatement_Base::acquire() throw() +{ + OStatement_BASE::acquire(); +} + +void SAL_CALL OStatement_Base::release() throw() +{ + OStatement_BASE::release(); +} + +void SAL_CALL OStatement::acquire() throw() +{ + OStatement_BASE2::acquire(); +} + +void SAL_CALL OStatement::release() throw() +{ + OStatement_BASE2::release(); +} + +OResultSet* OStatement_Base::createResulSet() +{ + return new OResultSet(m_aStatementHandle,this); +} + +Reference< css::beans::XPropertySetInfo > SAL_CALL OStatement_Base::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +SQLUINTEGER OStatement_Base::getCursorProperties(SQLINTEGER _nCursorType, bool bFirst) +{ + SQLUINTEGER nValueLen = 0; + try + { + SQLUSMALLINT nAskFor = SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2; + if(SQL_CURSOR_KEYSET_DRIVEN == _nCursorType) + nAskFor = bFirst ? SQL_KEYSET_CURSOR_ATTRIBUTES1 : SQL_KEYSET_CURSOR_ATTRIBUTES2; + else if(SQL_CURSOR_STATIC == _nCursorType) + nAskFor = bFirst ? SQL_STATIC_CURSOR_ATTRIBUTES1 : SQL_STATIC_CURSOR_ATTRIBUTES2; + else if(SQL_CURSOR_FORWARD_ONLY == _nCursorType) + nAskFor = bFirst ? SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 : SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2; + else if(SQL_CURSOR_DYNAMIC == _nCursorType) + nAskFor = bFirst ? SQL_DYNAMIC_CURSOR_ATTRIBUTES1 : SQL_DYNAMIC_CURSOR_ATTRIBUTES2; + + + OTools::GetInfo(getOwnConnection(),getConnectionHandle(),nAskFor,nValueLen,nullptr); + } + catch(const Exception&) + { // we don't want our result destroy here + nValueLen = 0; + } + return nValueLen; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/OTools.cxx b/connectivity/source/drivers/odbc/OTools.cxx new file mode 100644 index 000000000..4781415de --- /dev/null +++ b/connectivity/source/drivers/odbc/OTools.cxx @@ -0,0 +1,797 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <odbc/OTools.hxx> +#include <odbc/OFunctions.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <osl/endian.h> +#include <odbc/OConnection.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <string.h> + +using namespace connectivity::odbc; +using namespace com::sun::star::uno; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::util; + +namespace { +size_t sqlTypeLen ( SQLSMALLINT _nType ) +{ + switch (_nType) + { + case SQL_C_SSHORT: + case SQL_C_SHORT: + return sizeof(SQLSMALLINT); + case SQL_C_USHORT: + return sizeof(SQLUSMALLINT); + case SQL_C_SLONG: + case SQL_C_LONG: + return sizeof(SQLINTEGER); + case SQL_C_ULONG: + return sizeof(SQLUINTEGER); + case SQL_C_FLOAT: + return sizeof(SQLREAL); + case SQL_C_DOUBLE: + static_assert(sizeof(SQLDOUBLE) == sizeof(SQLFLOAT), "SQLDOUBLE/SQLFLOAT confusion"); + return sizeof(SQLDOUBLE); + case SQL_C_BIT: + return sizeof(SQLCHAR); + case SQL_C_STINYINT: + case SQL_C_TINYINT: + return sizeof(SQLSCHAR); + case SQL_C_UTINYINT: + return sizeof(SQLCHAR); + case SQL_C_SBIGINT: + return sizeof(SQLBIGINT); + case SQL_C_UBIGINT: + return sizeof(SQLUBIGINT); + /* UnixODBC gives this the same value as SQL_C_UBIGINT + case SQL_C_BOOKMARK: + return sizeof(BOOKMARK); */ + case SQL_C_TYPE_DATE: + case SQL_C_DATE: + return sizeof(SQL_DATE_STRUCT); + case SQL_C_TYPE_TIME: + case SQL_C_TIME: + return sizeof(SQL_TIME_STRUCT); + case SQL_C_TYPE_TIMESTAMP: + case SQL_C_TIMESTAMP: + return sizeof(SQL_TIMESTAMP_STRUCT); + case SQL_C_NUMERIC: + return sizeof(SQL_NUMERIC_STRUCT); + case SQL_C_GUID: + return sizeof(SQLGUID); + case SQL_C_INTERVAL_YEAR: + case SQL_C_INTERVAL_MONTH: + case SQL_C_INTERVAL_DAY: + case SQL_C_INTERVAL_HOUR: + case SQL_C_INTERVAL_MINUTE: + case SQL_C_INTERVAL_SECOND: + case SQL_C_INTERVAL_YEAR_TO_MONTH: + case SQL_C_INTERVAL_DAY_TO_HOUR: + case SQL_C_INTERVAL_DAY_TO_MINUTE: + case SQL_C_INTERVAL_DAY_TO_SECOND: + case SQL_C_INTERVAL_HOUR_TO_MINUTE: + case SQL_C_INTERVAL_HOUR_TO_SECOND: + case SQL_C_INTERVAL_MINUTE_TO_SECOND: + return sizeof(SQL_INTERVAL_STRUCT); + // ** Variable-sized datatypes -> cannot predict length + case SQL_C_CHAR: + case SQL_C_WCHAR: + case SQL_C_BINARY: + // UnixODBC gives this the same value as SQL_C_BINARY + //case SQL_C_VARBOOKMARK: + // Unknown datatype -> cannot predict length + default: + return static_cast<size_t>(-1); + } +} + +void appendSQLWCHARs(OUStringBuffer & s, SQLWCHAR const * d, sal_Int32 n) +{ + static_assert( + sizeof (SQLWCHAR) == sizeof (sal_Unicode) || sizeof (SQLWCHAR) == 4, + "bad SQLWCHAR"); + if (sizeof (SQLWCHAR) == sizeof (sal_Unicode)) { + s.append(reinterpret_cast<sal_Unicode const *>(d), n); + } else { + for (sal_Int32 i = 0; i != n; ++i) { + s.appendUtf32(d[i]); + } + } +} +} + + +void OTools::getValue( OConnection const * _pConnection, + SQLHANDLE _aStatementHandle, + sal_Int32 columnIndex, + SQLSMALLINT _nType, + bool &_bWasNull, + const css::uno::Reference< css::uno::XInterface >& _xInterface, + void* _pValue, + SQLLEN _nSize) +{ + const size_t properSize = sqlTypeLen(_nType); + if ( properSize == static_cast<size_t>(-1) ) + SAL_WARN( "connectivity.drivers", "connectivity::odbc::OTools::getValue: unknown SQL type - cannot check buffer size"); + else + { + OSL_ENSURE(static_cast<size_t>(_nSize) == properSize, "connectivity::odbc::OTools::getValue got wrongly sized memory region to write result to"); + if ( o3tl::make_unsigned(_nSize) > properSize ) + { + SAL_WARN( "connectivity.drivers", "memory region is too big - trying to fudge it"); + memset(_pValue, 0, _nSize); +#ifdef OSL_BIGENDIAN + // This is skewed in favour of integer types + _pValue = static_cast<char*>(_pValue) + _nSize - properSize; +#endif + } + } + OSL_ENSURE(o3tl::make_unsigned(_nSize) >= properSize, "memory region is too small"); + SQLLEN pcbValue = SQL_NULL_DATA; + OTools::ThrowException(_pConnection, + (*reinterpret_cast<T3SQLGetData>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetData)))(_aStatementHandle, + static_cast<SQLUSMALLINT>(columnIndex), + _nType, + _pValue, + _nSize, + &pcbValue), + _aStatementHandle,SQL_HANDLE_STMT,_xInterface,false); + _bWasNull = pcbValue == SQL_NULL_DATA; +} + +void OTools::bindValue( OConnection const * _pConnection, + SQLHANDLE _aStatementHandle, + sal_Int32 columnIndex, + SQLSMALLINT _nType, + SQLSMALLINT _nMaxLen, + const void* _pValue, + void* _pData, + SQLLEN * const pLen, + const css::uno::Reference< css::uno::XInterface >& _xInterface, + rtl_TextEncoding _nTextEncoding, + bool _bUseOldTimeDate) +{ + SQLRETURN nRetcode; + SQLSMALLINT fSqlType; + SQLSMALLINT fCType; + + OTools::getBindTypes( false, + _bUseOldTimeDate, + _nType, + fCType, + fSqlType); + + if (columnIndex != 0 && !_pValue) + { + *pLen = SQL_NULL_DATA; + nRetcode = (*reinterpret_cast<T3SQLBindCol>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::BindCol)))(_aStatementHandle, + static_cast<SQLUSMALLINT>(columnIndex), + fCType, + _pData, + _nMaxLen, + pLen + ); + } + else + { + try + { + switch (_nType) + { + case SQL_CHAR: + case SQL_VARCHAR: + { + OString aString(OUStringToOString(*static_cast<OUString const *>(_pValue),_nTextEncoding)); + *pLen = SQL_NTS; + *static_cast<OString*>(_pData) = aString; + + // Pointer on Char* + _pData = const_cast<char *>(aString.getStr()); + } break; + case SQL_BIGINT: + *static_cast<sal_Int64*>(_pData) = *static_cast<sal_Int64 const *>(_pValue); + *pLen = sizeof(sal_Int64); + break; + case SQL_DECIMAL: + case SQL_NUMERIC: + { + OString aString = OString::number(*static_cast<double const *>(_pValue)); + *pLen = static_cast<SQLSMALLINT>(aString.getLength()); + *static_cast<OString*>(_pData) = aString; + // Pointer on Char* + _pData = const_cast<char *>(static_cast<OString*>(_pData)->getStr()); + } break; + case SQL_BIT: + case SQL_TINYINT: + *static_cast<sal_Int8*>(_pData) = *static_cast<sal_Int8 const *>(_pValue); + *pLen = sizeof(sal_Int8); + break; + + case SQL_SMALLINT: + *static_cast<sal_Int16*>(_pData) = *static_cast<sal_Int16 const *>(_pValue); + *pLen = sizeof(sal_Int16); + break; + case SQL_INTEGER: + *static_cast<sal_Int32*>(_pData) = *static_cast<sal_Int32 const *>(_pValue); + *pLen = sizeof(sal_Int32); + break; + case SQL_FLOAT: + *static_cast<float*>(_pData) = *static_cast<float const *>(_pValue); + *pLen = sizeof(float); + break; + case SQL_REAL: + case SQL_DOUBLE: + *static_cast<double*>(_pData) = *static_cast<double const *>(_pValue); + *pLen = sizeof(double); + break; + case SQL_BINARY: + case SQL_VARBINARY: + { + _pData = const_cast<sal_Int8 *>(static_cast<const css::uno::Sequence< sal_Int8 > *>(_pValue)->getConstArray()); + *pLen = static_cast<const css::uno::Sequence< sal_Int8 > *>(_pValue)->getLength(); + } break; + case SQL_LONGVARBINARY: + { + /* see https://msdn.microsoft.com/en-us/library/ms716238%28v=vs.85%29.aspx + * for an explanation of that apparently weird cast */ + _pData = reinterpret_cast<void*>(static_cast<uintptr_t>(columnIndex)); + sal_Int32 nLen = static_cast<const css::uno::Sequence< sal_Int8 > *>(_pValue)->getLength(); + *pLen = static_cast<SQLLEN>(SQL_LEN_DATA_AT_EXEC(nLen)); + } + break; + case SQL_LONGVARCHAR: + { + /* see https://msdn.microsoft.com/en-us/library/ms716238%28v=vs.85%29.aspx + * for an explanation of that apparently weird cast */ + _pData = reinterpret_cast<void*>(static_cast<uintptr_t>(columnIndex)); + sal_Int32 nLen = static_cast<OUString const *>(_pValue)->getLength(); + *pLen = static_cast<SQLLEN>(SQL_LEN_DATA_AT_EXEC(nLen)); + } break; + case SQL_DATE: + *pLen = sizeof(DATE_STRUCT); + *static_cast<DATE_STRUCT*>(_pData) = *static_cast<DATE_STRUCT const *>(_pValue); + break; + case SQL_TIME: + *pLen = sizeof(TIME_STRUCT); + *static_cast<TIME_STRUCT*>(_pData) = *static_cast<TIME_STRUCT const *>(_pValue); + break; + case SQL_TIMESTAMP: + *pLen = sizeof(TIMESTAMP_STRUCT); + *static_cast<TIMESTAMP_STRUCT*>(_pData) = *static_cast<TIMESTAMP_STRUCT const *>(_pValue); + break; + } + } + catch ( ... ) + { + } + + nRetcode = (*reinterpret_cast<T3SQLBindCol>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::BindCol)))(_aStatementHandle, + static_cast<SQLUSMALLINT>(columnIndex), + fCType, + _pData, + _nMaxLen, + pLen + ); + } + + OTools::ThrowException(_pConnection,nRetcode,_aStatementHandle,SQL_HANDLE_STMT,_xInterface); +} + +void OTools::ThrowException(const OConnection* _pConnection, + const SQLRETURN _rRetCode, + const SQLHANDLE _pContext, + const SQLSMALLINT _nHandleType, + const Reference< XInterface >& _xInterface, + const bool _bNoFound) +{ + switch(_rRetCode) + { + case SQL_NEED_DATA: + case SQL_STILL_EXECUTING: + case SQL_SUCCESS: + + case SQL_SUCCESS_WITH_INFO: + return; + case SQL_NO_DATA_FOUND: + if(_bNoFound) + return; // no need to throw an exception + break; + case SQL_ERROR: break; + + + case SQL_INVALID_HANDLE: SAL_WARN( "connectivity.drivers", "SdbODBC3_SetStatus: SQL_INVALID_HANDLE"); + throw SQLException(); + } + + // Additional Information on the latest ODBC-functioncall available + // SQLError provides this Information. + + SDB_ODBC_CHAR szSqlState[5]; + SQLINTEGER pfNativeError; + SDB_ODBC_CHAR szErrorMessage[SQL_MAX_MESSAGE_LENGTH]; + szErrorMessage[0] = '\0'; + SQLSMALLINT pcbErrorMsg = 0; + + // Information for latest operation: + // when hstmt != SQL_NULL_HSTMT is (Used from SetStatus in SdbCursor, SdbTable, ...), + // then the status of the latest statements will be fetched, without the Status of the last + // statements of this connection [what in this case will probably be the same, but the Reference + // Manual isn't totally clear in this...]. + // corresponding for hdbc. + SQLRETURN n = (*reinterpret_cast<T3SQLGetDiagRec>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetDiagRec)))(_nHandleType,_pContext,1, + szSqlState, + &pfNativeError, + szErrorMessage,sizeof szErrorMessage - 1,&pcbErrorMsg); + OSL_ENSURE(n != SQL_INVALID_HANDLE,"SdbODBC3_SetStatus: SQLError returned SQL_INVALID_HANDLE"); + OSL_ENSURE(n == SQL_SUCCESS || n == SQL_SUCCESS_WITH_INFO || n == SQL_NO_DATA_FOUND || n == SQL_ERROR,"SdbODBC3_SetStatus: SQLError failed"); + + rtl_TextEncoding _nTextEncoding = osl_getThreadTextEncoding(); + // For the Return Code of SQLError see ODBC 2.0 Programmer's Reference Page 287ff + throw SQLException( OUString(reinterpret_cast<char *>(szErrorMessage), pcbErrorMsg, _nTextEncoding), + _xInterface, + OUString(reinterpret_cast<char *>(szSqlState), 5, _nTextEncoding), + pfNativeError, + Any() + ); + +} + +Sequence<sal_Int8> OTools::getBytesValue(const OConnection* _pConnection, + const SQLHANDLE _aStatementHandle, + const sal_Int32 columnIndex, + const SQLSMALLINT _fSqlType, + bool &_bWasNull, + const Reference< XInterface >& _xInterface) +{ + sal_Int8 aCharArray[2048]; + // First try to fetch the data with the little Buffer: + const SQLLEN nMaxLen = sizeof aCharArray; + SQLLEN pcbValue = SQL_NO_TOTAL; + Sequence<sal_Int8> aData; + + OSL_ENSURE( _fSqlType != SQL_CHAR && _fSqlType != SQL_VARCHAR && _fSqlType != SQL_LONGVARCHAR && + _fSqlType != SQL_WCHAR && _fSqlType != SQL_WVARCHAR && _fSqlType != SQL_WLONGVARCHAR, + "connectivity::odbc::OTools::getBytesValue called with character _fSqlType"); + + while (pcbValue == SQL_NO_TOTAL || pcbValue > nMaxLen) + { + OTools::ThrowException(_pConnection, + (*reinterpret_cast<T3SQLGetData>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetData)))( + _aStatementHandle, + static_cast<SQLUSMALLINT>(columnIndex), + _fSqlType, + static_cast<SQLPOINTER>(aCharArray), + nMaxLen, + &pcbValue), + _aStatementHandle,SQL_HANDLE_STMT,_xInterface); + + _bWasNull = pcbValue == SQL_NULL_DATA; + if(_bWasNull) + return Sequence<sal_Int8>(); + + SQLLEN nReadBytes; + // After the SQLGetData that wrote out to aCharArray the last byte of the data, + // pcbValue will not be SQL_NO_TOTAL -> we have a reliable count + if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxLen) ) + { + // we filled the buffer + nReadBytes = nMaxLen; + } + else + { + nReadBytes = pcbValue; + } + const sal_Int32 nLen = aData.getLength(); + aData.realloc(nLen + nReadBytes); + memcpy(aData.getArray() + nLen, aCharArray, nReadBytes); + } + return aData; +} + +OUString OTools::getStringValue(OConnection const * _pConnection, + SQLHANDLE _aStatementHandle, + sal_Int32 columnIndex, + SQLSMALLINT _fSqlType, + bool &_bWasNull, + const Reference< XInterface >& _xInterface, + const rtl_TextEncoding _nTextEncoding) +{ + OUStringBuffer aData; + switch(_fSqlType) + { + case SQL_WVARCHAR: + case SQL_WCHAR: + case SQL_WLONGVARCHAR: + { + SQLWCHAR waCharArray[2048]; + static_assert(sizeof(waCharArray) % sizeof(SQLWCHAR) == 0, "must fit in evenly"); + static_assert(sizeof(SQLWCHAR) == 2 || sizeof(SQLWCHAR) == 4, "must be 2 or 4"); + // Size == number of bytes, Len == number of UTF-16 or UCS4 code units + const SQLLEN nMaxSize = sizeof(waCharArray); + const SQLLEN nMaxLen = sizeof(waCharArray) / sizeof(SQLWCHAR); + static_assert(nMaxLen * sizeof(SQLWCHAR) == nMaxSize, "sizes must match"); + + // read the unicode data + SQLLEN pcbValue = SQL_NO_TOTAL; + while ((pcbValue == SQL_NO_TOTAL ) || (pcbValue >= nMaxSize) ) + { + OTools::ThrowException(_pConnection, + (*reinterpret_cast<T3SQLGetData>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetData)))( + _aStatementHandle, + static_cast<SQLUSMALLINT>(columnIndex), + SQL_C_WCHAR, + &waCharArray, + SQLLEN(nMaxLen)*sizeof(sal_Unicode), + &pcbValue), + _aStatementHandle,SQL_HANDLE_STMT,_xInterface); + _bWasNull = pcbValue == SQL_NULL_DATA; + if(_bWasNull) + return OUString(); + + SQLLEN nReadChars; + OSL_ENSURE( (pcbValue < 0) || (pcbValue % 2 == 0), + "ODBC: SQLGetData of SQL_C_WCHAR returned odd number of bytes"); + if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxSize) ) + { + // we filled the buffer; remove the terminating null character + nReadChars = nMaxLen-1; + if ( waCharArray[nReadChars] != 0) + { + SAL_WARN( "connectivity.drivers", "Buggy ODBC driver? Did not null-terminate (variable length) data!"); + ++nReadChars; + } + } + else + { + nReadChars = pcbValue/sizeof(SQLWCHAR); + } + + appendSQLWCHARs(aData, waCharArray, nReadChars); + } + break; + } + default: + { + char aCharArray[2048]; + // read the unicode data + const SQLLEN nMaxLen = sizeof(aCharArray); + SQLLEN pcbValue = SQL_NO_TOTAL; + + while ((pcbValue == SQL_NO_TOTAL ) || (pcbValue >= nMaxLen) ) + { + OTools::ThrowException(_pConnection, + (*reinterpret_cast<T3SQLGetData>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetData)))( + _aStatementHandle, + static_cast<SQLUSMALLINT>(columnIndex), + SQL_C_CHAR, + &aCharArray, + nMaxLen, + &pcbValue), + _aStatementHandle,SQL_HANDLE_STMT,_xInterface); + _bWasNull = pcbValue == SQL_NULL_DATA; + if(_bWasNull) + return OUString(); + + SQLLEN nReadChars; + if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxLen) ) + { + // we filled the buffer; remove the terminating null character + nReadChars = nMaxLen-1; + if ( aCharArray[nReadChars] != 0) + { + SAL_WARN( "connectivity.drivers", "Buggy ODBC driver? Did not null-terminate (variable length) data!"); + ++nReadChars; + } + } + else + { + nReadChars = pcbValue; + } + + aData.append(OUString(aCharArray, nReadChars, _nTextEncoding)); + + } + break; + } + } + + return aData.makeStringAndClear(); +} + +void OTools::GetInfo(OConnection const * _pConnection, + SQLHANDLE _aConnectionHandle, + SQLUSMALLINT _nInfo, + OUString &_rValue, + const Reference< XInterface >& _xInterface, + rtl_TextEncoding _nTextEncoding) +{ + char aValue[512]; + SQLSMALLINT nValueLen=0; + OTools::ThrowException(_pConnection, + (*reinterpret_cast<T3SQLGetInfo>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetInfo)))(_aConnectionHandle,_nInfo,aValue,(sizeof aValue)-1,&nValueLen), + _aConnectionHandle,SQL_HANDLE_DBC,_xInterface); + + _rValue = OUString(aValue,nValueLen,_nTextEncoding); +} + +void OTools::GetInfo(OConnection const * _pConnection, + SQLHANDLE _aConnectionHandle, + SQLUSMALLINT _nInfo, + sal_Int32 &_rValue, + const Reference< XInterface >& _xInterface) +{ + SQLSMALLINT nValueLen; + _rValue = 0; // in case the driver uses only 16 of the 32 bits (as it does, for example, for SQL_CATALOG_LOCATION) + OTools::ThrowException(_pConnection, + (*reinterpret_cast<T3SQLGetInfo>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetInfo)))(_aConnectionHandle,_nInfo,&_rValue,sizeof _rValue,&nValueLen), + _aConnectionHandle,SQL_HANDLE_DBC,_xInterface); +} + +void OTools::GetInfo(OConnection const * _pConnection, + SQLHANDLE _aConnectionHandle, + SQLUSMALLINT _nInfo, + SQLUINTEGER &_rValue, + const Reference< XInterface >& _xInterface) +{ + SQLSMALLINT nValueLen; + _rValue = 0; // in case the driver uses only 16 of the 32 bits (as it does, for example, for SQL_CATALOG_LOCATION) + OTools::ThrowException(_pConnection, + (*reinterpret_cast<T3SQLGetInfo>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetInfo)))(_aConnectionHandle,_nInfo,&_rValue,sizeof _rValue,&nValueLen), + _aConnectionHandle,SQL_HANDLE_DBC,_xInterface); +} + +void OTools::GetInfo(OConnection const * _pConnection, + SQLHANDLE _aConnectionHandle, + SQLUSMALLINT _nInfo, + SQLUSMALLINT &_rValue, + const Reference< XInterface >& _xInterface) +{ + SQLSMALLINT nValueLen; + _rValue = 0; // in case the driver uses only 16 of the 32 bits (as it does, for example, for SQL_CATALOG_LOCATION) + OTools::ThrowException(_pConnection, + (*reinterpret_cast<T3SQLGetInfo>(_pConnection->getOdbcFunction(ODBC3SQLFunctionId::GetInfo)))(_aConnectionHandle,_nInfo,&_rValue,sizeof _rValue,&nValueLen), + _aConnectionHandle,SQL_HANDLE_DBC,_xInterface); +} + +sal_Int32 OTools::MapOdbcType2Jdbc(SQLSMALLINT _nType) +{ + sal_Int32 nValue = DataType::VARCHAR; + switch(_nType) + { + case SQL_BIT: + nValue = DataType::BIT; + break; + case SQL_TINYINT: + nValue = DataType::TINYINT; + break; + case SQL_SMALLINT: + nValue = DataType::SMALLINT; + break; + case SQL_INTEGER: + nValue = DataType::INTEGER; + break; + case SQL_BIGINT: + nValue = DataType::BIGINT; + break; + case SQL_FLOAT: + nValue = DataType::FLOAT; + break; + case SQL_REAL: + nValue = DataType::REAL; + break; + case SQL_DOUBLE: + nValue = DataType::DOUBLE; + break; + case SQL_NUMERIC: + nValue = DataType::NUMERIC; + break; + case SQL_DECIMAL: + nValue = DataType::DECIMAL; + break; + case SQL_WCHAR: + case SQL_CHAR: + nValue = DataType::CHAR; + break; + case SQL_WVARCHAR: + case SQL_VARCHAR: + nValue = DataType::VARCHAR; + break; + case SQL_WLONGVARCHAR: + case SQL_LONGVARCHAR: + nValue = DataType::LONGVARCHAR; + break; + case SQL_TYPE_DATE: + case SQL_DATE: + nValue = DataType::DATE; + break; + case SQL_TYPE_TIME: + case SQL_TIME: + nValue = DataType::TIME; + break; + case SQL_TYPE_TIMESTAMP: + case SQL_TIMESTAMP: + nValue = DataType::TIMESTAMP; + break; + case SQL_BINARY: + nValue = DataType::BINARY; + break; + case SQL_VARBINARY: + case SQL_GUID: + nValue = DataType::VARBINARY; + break; + case SQL_LONGVARBINARY: + nValue = DataType::LONGVARBINARY; + break; + default: + OSL_FAIL("Invalid type"); + } + return nValue; +} + +// jdbcTypeToOdbc +// Convert the JDBC SQL type to the correct ODBC type + +SQLSMALLINT OTools::jdbcTypeToOdbc(sal_Int32 jdbcType) +{ + // For the most part, JDBC types match ODBC types. We'll + // just convert the ones that we know are different + + sal_Int32 odbcType = jdbcType; + + switch (jdbcType) + { + case DataType::DATE: + odbcType = SQL_DATE; + break; + case DataType::TIME: + odbcType = SQL_TIME; + break; + case DataType::TIMESTAMP: + odbcType = SQL_TIMESTAMP; + break; + // ODBC doesn't have any notion of CLOB or BLOB + case DataType::CLOB: + odbcType = SQL_LONGVARCHAR; + break; + case DataType::BLOB: + odbcType = SQL_LONGVARBINARY; + break; + } + + return odbcType; +} + +void OTools::getBindTypes(bool _bUseWChar, + bool _bUseOldTimeDate, + SQLSMALLINT _nOdbcType, + SQLSMALLINT& fCType, + SQLSMALLINT& fSqlType + ) +{ + switch(_nOdbcType) + { + case SQL_CHAR: if(_bUseWChar) + { + fCType = SQL_C_WCHAR; + fSqlType = SQL_WCHAR; + } + else + { + fCType = SQL_C_CHAR; + fSqlType = SQL_CHAR; + } + break; + case SQL_VARCHAR: if(_bUseWChar) + { + fCType = SQL_C_WCHAR; + fSqlType = SQL_WVARCHAR; + } + else + { + fCType = SQL_C_CHAR; + fSqlType = SQL_VARCHAR; + } + break; + case SQL_LONGVARCHAR: if(_bUseWChar) + { + fCType = SQL_C_WCHAR; + fSqlType = SQL_WLONGVARCHAR; + } + else + { + fCType = SQL_C_CHAR; + fSqlType = SQL_LONGVARCHAR; + } + break; + case SQL_DECIMAL: fCType = _bUseWChar ? SQL_C_WCHAR : SQL_C_CHAR; + fSqlType = SQL_DECIMAL; break; + case SQL_NUMERIC: fCType = _bUseWChar ? SQL_C_WCHAR : SQL_C_CHAR; + fSqlType = SQL_NUMERIC; break; + case SQL_BIT: fCType = SQL_C_TINYINT; + fSqlType = SQL_INTEGER; break; + case SQL_TINYINT: fCType = SQL_C_TINYINT; + fSqlType = SQL_TINYINT; break; + case SQL_SMALLINT: fCType = SQL_C_SHORT; + fSqlType = SQL_SMALLINT; break; + case SQL_INTEGER: fCType = SQL_C_LONG; + fSqlType = SQL_INTEGER; break; + case SQL_BIGINT: fCType = SQL_C_SBIGINT; + fSqlType = SQL_BIGINT; break; + case SQL_FLOAT: fCType = SQL_C_FLOAT; + fSqlType = SQL_FLOAT; break; + case SQL_REAL: fCType = SQL_C_DOUBLE; + fSqlType = SQL_REAL; break; + case SQL_DOUBLE: fCType = SQL_C_DOUBLE; + fSqlType = SQL_DOUBLE; break; + case SQL_BINARY: fCType = SQL_C_BINARY; + fSqlType = SQL_BINARY; break; + case SQL_VARBINARY: + fCType = SQL_C_BINARY; + fSqlType = SQL_VARBINARY; break; + case SQL_LONGVARBINARY: fCType = SQL_C_BINARY; + fSqlType = SQL_LONGVARBINARY; break; + case SQL_DATE: + if(_bUseOldTimeDate) + { + fCType = SQL_C_DATE; + fSqlType = SQL_DATE; + } + else + { + fCType = SQL_C_TYPE_DATE; + fSqlType = SQL_TYPE_DATE; + } + break; + case SQL_TIME: + if(_bUseOldTimeDate) + { + fCType = SQL_C_TIME; + fSqlType = SQL_TIME; + } + else + { + fCType = SQL_C_TYPE_TIME; + fSqlType = SQL_TYPE_TIME; + } + break; + case SQL_TIMESTAMP: + if(_bUseOldTimeDate) + { + fCType = SQL_C_TIMESTAMP; + fSqlType = SQL_TIMESTAMP; + } + else + { + fCType = SQL_C_TYPE_TIMESTAMP; + fSqlType = SQL_TYPE_TIMESTAMP; + } + break; + default: fCType = SQL_C_BINARY; + fSqlType = SQL_LONGVARBINARY; break; + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/odbc/odbc.component b/connectivity/source/drivers/odbc/odbc.component new file mode 100644 index 000000000..4fa186ea2 --- /dev/null +++ b/connectivity/source/drivers/odbc/odbc.component @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="odbc" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.ODBCDriver"> + <service name="com.sun.star.sdbc.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/odbc/oservices.cxx b/connectivity/source/drivers/odbc/oservices.cxx new file mode 100644 index 000000000..6461f8dde --- /dev/null +++ b/connectivity/source/drivers/odbc/oservices.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "ORealDriver.hxx" +#include <odbc/ODriver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace connectivity::odbc; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XSingleServiceFactory; +using ::com::sun::star::lang::XMultiServiceFactory; + +typedef Reference< XSingleServiceFactory > (*createFactoryFunc) + ( + const Reference< XMultiServiceFactory > & rServiceManager, + const OUString & rComponentName, + ::cppu::ComponentInstantiation pCreateFunction, + const Sequence< OUString > & rServiceNames, + rtl_ModuleCount* + ); + +namespace { + +struct ProviderRequest +{ + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > const xServiceManager; + OUString const sImplementationName; + + ProviderRequest( + void* pServiceManager, + char const* pImplementationName + ) + : xServiceManager(static_cast<XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER( + const OUString& Implname, + const Sequence< OUString > & Services, + ::cppu::ComponentInstantiation Factory, + createFactoryFunc creator + ) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator( xServiceManager, sImplementationName,Factory, Services,nullptr); + } + catch(...) + { + } + } + return xRet.is(); + } + + void* getProvider() const { return xRet.get(); } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* odbc_component_getFactory( + const char* pImplementationName, + void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager,pImplementationName); + + aReq.CREATE_PROVIDER( + ODBCDriver::getImplementationName_Static(), + ODBCDriver::getSupportedServiceNames_Static(), + ODBCDriver_CreateInstance, ::cppu::createSingleFactory) + ; + + if(aReq.xRet.is()) + aReq.xRet->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/postgresql-sdbc-impl.component b/connectivity/source/drivers/postgresql/postgresql-sdbc-impl.component new file mode 100644 index 000000000..89164b6e7 --- /dev/null +++ b/connectivity/source/drivers/postgresql/postgresql-sdbc-impl.component @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * +--> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="postgresql_sdbc_impl" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="org.openoffice.comp.connectivity.pq.Connection.noext"> + <service name="com.sun.star.sdbc.Connection"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/postgresql/postgresql-sdbc.component b/connectivity/source/drivers/postgresql/postgresql-sdbc.component new file mode 100644 index 000000000..ebb200954 --- /dev/null +++ b/connectivity/source/drivers/postgresql/postgresql-sdbc.component @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * +--> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="postgresql_sdbc" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="org.openoffice.comp.connectivity.pq.Driver.noext"> + <service name="com.sun.star.sdbc.Driver"/> + </implementation> +</component> diff --git a/connectivity/source/drivers/postgresql/pq_array.cxx b/connectivity/source/drivers/postgresql/pq_array.cxx new file mode 100644 index 000000000..5ae646f23 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_array.cxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <comphelper/sequence.hxx> + + +#include "pq_array.hxx" +#include "pq_statics.hxx" +#include "pq_sequenceresultset.hxx" + + +using com::sun::star::sdbc::SQLException; +using com::sun::star::uno::Any; + +using com::sun::star::uno::Sequence; +namespace pq_sdbc_driver +{ + + +OUString Array::getBaseTypeName( ) +{ + return "varchar"; +} + +sal_Int32 Array::getBaseType( ) +{ + return css::sdbc::DataType::VARCHAR; +} + +css::uno::Sequence< css::uno::Any > Array::getArray( + const css::uno::Reference< css::container::XNameAccess >& /* typeMap */ ) +{ + return comphelper::containerToSequence(m_data); +} + +css::uno::Sequence< css::uno::Any > Array::getArrayAtIndex( + sal_Int32 index, + sal_Int32 count, + const css::uno::Reference< css::container::XNameAccess >& /* typeMap */ ) +{ + checkRange( index, count ); + return Sequence< Any > ( &m_data[index-1], count ); +} + +css::uno::Reference< css::sdbc::XResultSet > Array::getResultSet( + const css::uno::Reference< css::container::XNameAccess >& typeMap ) +{ + return getResultSetAtIndex( 0 , m_data.size() , typeMap ); +} + +css::uno::Reference< css::sdbc::XResultSet > Array::getResultSetAtIndex( + sal_Int32 index, + sal_Int32 count, + const css::uno::Reference< css::container::XNameAccess >& /* typeMap */ ) +{ + checkRange( index, count ); + std::vector< std::vector< Any > > ret( count ); + + for( int i = 0 ; i < count ; i ++ ) + { + std::vector< Any > row( 2 ); + row[0] <<= static_cast<sal_Int32>( i + index ); + row[1] = m_data[i+index-1]; + ret[i] = row; + } + + return new SequenceResultSet( + m_xMutex, m_owner, getStatics().resultSetArrayColumnNames, ret, m_tc ); +} + + +void Array::checkRange( sal_Int32 index, sal_Int32 count ) +{ + if( index >= 1 && index -1 + count <= static_cast<sal_Int32>(m_data.size()) ) + return; + throw SQLException( + "Array::getArrayAtIndex(): allowed range for index + count " + + OUString::number( m_data.size() ) + + ", got " + OUString::number( index ) + + " + " + OUString::number( count ), + *this, OUString(), 1, Any()); + +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_array.hxx b/connectivity/source/drivers/postgresql/pq_array.hxx new file mode 100644 index 000000000..c0ed6aa97 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_array.hxx @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_ARRAY_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_ARRAY_HXX +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/sdbc/XArray.hpp> + +#include "pq_connection.hxx" +#include <vector> + +namespace pq_sdbc_driver +{ + +class Array : public cppu::WeakImplHelper< css::sdbc::XArray > +{ + std::vector< css::uno::Any > m_data; + css::uno::Reference< css::uno::XInterface > m_owner; + css::uno::Reference< css::script::XTypeConverter > m_tc; + rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + +public: + Array( + const rtl::Reference< comphelper::RefCountedMutex > & mutex, + const std::vector< css::uno::Any > & data, + const css::uno::Reference< css::uno::XInterface > & owner, + const css::uno::Reference< css::script::XTypeConverter > &tc) : + m_data( data ), + m_owner( owner ), + m_tc( tc ), + m_xMutex( mutex ) + {} + +public: // XArray + + // Methods + virtual OUString SAL_CALL getBaseTypeName( ) override; + + virtual sal_Int32 SAL_CALL getBaseType( ) override; + + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getArray( + const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getArrayAtIndex( + sal_Int32 index, + sal_Int32 count, + const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL + getResultSet( + const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getResultSetAtIndex( + sal_Int32 index, + sal_Int32 count, + const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + +private: + void checkRange( sal_Int32 index, sal_Int32 count ); +}; + + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_baseresultset.cxx b/connectivity/source/drivers/postgresql/pq_baseresultset.cxx new file mode 100644 index 000000000..8fc7140e4 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_baseresultset.cxx @@ -0,0 +1,613 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <comphelper/sequence.hxx> + +#include "pq_tools.hxx" +#include "pq_array.hxx" +#include "pq_baseresultset.hxx" + +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <connectivity/dbconversion.hxx> + +using osl::MutexGuard; + + +using com::sun::star::beans::XPropertySetInfo; + +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Reference; +using com::sun::star::uno::XInterface; + +using com::sun::star::lang::IllegalArgumentException; + +using com::sun::star::sdbc::SQLException; + + +using com::sun::star::beans::Property; + +using namespace dbtools; + +namespace pq_sdbc_driver +{ +static ::cppu::IPropertyArrayHelper & getResultSetPropertyArrayHelper() +{ + // LEM TODO: this needs to be kept in sync with other, e.g. pq_statics.css:508 + // Should really share! + // At least use for the handles the #define'd values in .hxx file... + static ::cppu::OPropertyArrayHelper arrayHelper( + Sequence<Property>{ + Property( + "CursorName", 0, + ::cppu::UnoType<OUString>::get() , 0 ), + Property( + "EscapeProcessing", 1, + cppu::UnoType<bool>::get() , 0 ), + Property( + "FetchDirection", 2, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "FetchSize", 3, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "IsBookmarkable", 4, + cppu::UnoType<bool>::get() , 0 ), + Property( + "ResultSetConcurrency", 5, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "ResultSetType", 6, + ::cppu::UnoType<sal_Int32>::get() , 0 )}, + true ); + return arrayHelper; +} + +BaseResultSet::BaseResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< XInterface > & owner, + sal_Int32 rowCount, + sal_Int32 colCount, + const Reference< css::script::XTypeConverter > & tc ) + : BaseResultSet_BASE( refMutex->GetMutex() ) + , OPropertySetHelper( BaseResultSet_BASE::rBHelper ) + , m_owner( owner ) + , m_tc( tc ) + , m_xMutex( refMutex ) + , m_row( -1 ) + , m_rowCount( rowCount ) + , m_fieldCount( colCount ) + , m_wasNull(false) +{ +} + +// LEM TODO: refMutex->GetMutex() should live longer than OComponentHelper, +// but calling OComponentHelper::dispose explicitly here calls +// BaseResultSet::~BaseResultSet in an infinite loop :( +BaseResultSet::~BaseResultSet() +{ +} + +Any BaseResultSet::queryInterface( const Type & rType ) +{ + Any aRet = BaseResultSet_BASE::queryInterface(rType); + return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType); +} + +// void BaseResultSet::close( ) throw (SQLException, RuntimeException) +// { +// Reference< XInterface > owner; +// { +// ResultSetGuard guard(*this); +// if( m_result ) +// { +// PQclear(m_result ); +// m_result = 0; +// m_row = -1; +// } +// owner = m_owner; +// m_owner.clear(); +// } +// } + +Sequence<Type > BaseResultSet::getTypes() +{ + static Sequence< Type > collection( + ::comphelper::concatSequences( + OPropertySetHelper::getTypes(), + BaseResultSet_BASE::getTypes())); + return collection; +} + +Sequence< sal_Int8> BaseResultSet::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// Reference< XResultSetMetaData > BaseResultSet::getMetaData( ) throw (SQLException, RuntimeException) +// { +// ResultSetGuard guard(*this); +// checkClosed(); +// return new ResultSetMetaData( m_xMutex, this, &m_result ); +// } + +sal_Bool BaseResultSet::next( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + m_row ++; + return m_row < m_rowCount; +} + +sal_Bool BaseResultSet::isBeforeFirst( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + return m_row == -1; +} + +sal_Bool BaseResultSet::isAfterLast( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + return m_row >= m_rowCount; +} + +sal_Bool BaseResultSet::isFirst( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + return m_row == 0 && m_rowCount; +} + +sal_Bool BaseResultSet::isLast( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + return m_row >= 0 && m_row + 1 == m_rowCount; +} + +void BaseResultSet::beforeFirst( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + m_row = -1; +} + +void BaseResultSet::afterLast( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + m_row = m_rowCount; +} + +sal_Bool BaseResultSet::first( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + bool bRet = ( m_rowCount > 0 ); + if( bRet ) + m_row = 0; + return bRet; +} + +sal_Bool BaseResultSet::last( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + bool bRet = ( m_rowCount > 0 ); + if( bRet ) + m_row = m_rowCount -1; + return bRet; +} + +sal_Int32 BaseResultSet::getRow( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + return m_row +1; +} + +sal_Bool BaseResultSet::absolute( sal_Int32 row ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + if( row > 0 ) + { + m_row = row -1; + if( m_row > m_rowCount ) + m_row = m_rowCount; + } + else + { + m_row = m_rowCount + row; + if( m_row < -1 ) + m_row = -1; + } + return true; +} + +sal_Bool BaseResultSet::relative( sal_Int32 rows ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + m_row += rows; + + if( m_row > m_rowCount ) + m_row = m_rowCount; + else if ( m_row < -1 ) + m_row = -1; + return true; +} + +sal_Bool BaseResultSet::previous( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + bool bRet = ( m_row != -1 ); + if( bRet ) + m_row --; + return bRet; +} + +void BaseResultSet::refreshRow( ) +{ + // TODO: not supported for now +} + +sal_Bool BaseResultSet::rowUpdated( ) +{ + return false; +} + +sal_Bool BaseResultSet::rowInserted( ) +{ + return false; +} + +sal_Bool BaseResultSet::rowDeleted( ) +{ + return false; +} + +Reference< XInterface > BaseResultSet::getStatement() +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + return m_owner; +} + + +//----------------- XRow interface ---------------------------------------------------- + +sal_Bool BaseResultSet::wasNull( ) +{ + return m_wasNull; +} + +Any BaseResultSet::convertTo( const Any & val , const Type & type ) +{ + Any aRet; + try + { + aRet = m_tc->convertTo( val , type ); + } + catch( css::lang::IllegalArgumentException & ) + {} + catch( css::script::CannotConvertException & ) + {} + return aRet; +} + +sal_Bool BaseResultSet::getBoolean( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( columnIndex ); + checkRowIndex(); + + OUString str = getString( columnIndex ); + + if( str.getLength() > 0 ) + { + switch(str[0]) + { + case '1': + case 't': + case 'T': + case 'y': + case 'Y': + + return true; + } + } + return false; +} + +sal_Int8 BaseResultSet::getByte( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( columnIndex ); + checkRowIndex(); + sal_Int8 b = 0; + convertTo( getValue( columnIndex ), cppu::UnoType<decltype(b)>::get()) >>= b; + return b; +} + +sal_Int16 BaseResultSet::getShort( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( columnIndex ); + checkRowIndex(); + sal_Int16 i = 0; + convertTo( getValue( columnIndex ), cppu::UnoType<decltype(i)>::get()) >>= i; + return i; +} + +OUString BaseResultSet::getString( sal_Int32 columnIndex ) +{ + MutexGuard guard(m_xMutex->GetMutex()); + checkClosed(); + checkColumnIndex( columnIndex ); + checkRowIndex(); + OUString ret; + convertTo( getValue( columnIndex ), cppu::UnoType<decltype(ret)>::get() ) >>= ret; +// printf( "BaseResultSet::getString() %s\n" , OUStringToOString( ret, RTL_TEXTENCODING_ASCII_US ).getStr() ); + return ret; +} + +sal_Int32 BaseResultSet::getInt( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( columnIndex ); + checkRowIndex(); + sal_Int32 i = 0; + convertTo( getValue( columnIndex ), cppu::UnoType<decltype(i)>::get()) >>= i; + return i; +} + +sal_Int64 BaseResultSet::getLong( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( columnIndex ); + checkRowIndex(); + sal_Int64 i = 0; + convertTo( getValue( columnIndex ), cppu::UnoType<decltype(i)>::get()) >>= i; + return i; +} + +float BaseResultSet::getFloat( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( columnIndex ); + checkRowIndex(); + float f = 0.; + convertTo( getValue( columnIndex ), cppu::UnoType<decltype(f)>::get()) >>= f; + return f; +} + +double BaseResultSet::getDouble( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( columnIndex ); + double d = 0.; + convertTo( getValue( columnIndex ), cppu::UnoType<decltype(d)>::get()) >>= d; + return d; +} + +Sequence< sal_Int8 > BaseResultSet::getBytes( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( columnIndex ); + checkRowIndex(); + + Sequence< sal_Int8 > ret; + OUString ustr; + if( ! (getValue( columnIndex ) >>= ustr) ) + m_wasNull = true; + else + { + // if this is a binary, it must contain escaped data ! + OString val = OUStringToOString( ustr, RTL_TEXTENCODING_ASCII_US ); + + size_t length; + char * res = reinterpret_cast<char*>(PQunescapeBytea( reinterpret_cast<unsigned char const *>(val.getStr()), &length)); + ret = Sequence< sal_Int8 > ( reinterpret_cast<sal_Int8*>(res), length ); + if( res ) + free( res ); + } + return ret; +} + + +css::util::Date BaseResultSet::getDate( sal_Int32 columnIndex ) +{ + return DBTypeConversion::toDate( getString( columnIndex ) ); +} + +css::util::Time BaseResultSet::getTime( sal_Int32 columnIndex ) +{ + return DBTypeConversion::toTime( getString( columnIndex ) ); +} + +css::util::DateTime BaseResultSet::getTimestamp( sal_Int32 columnIndex ) +{ + return DBTypeConversion::toDateTime( getString( columnIndex ) ); +} + + // LEM TODO: these look like they are missing an actual implementation +Reference< css::io::XInputStream > BaseResultSet::getBinaryStream( sal_Int32 /* columnIndex */ ) +{ + return nullptr; +} + +Reference< css::io::XInputStream > BaseResultSet::getCharacterStream( sal_Int32 /* columnIndex */ ) +{ + return nullptr; +} + +Any BaseResultSet::getObject( + sal_Int32 /* columnIndex */, + const Reference< css::container::XNameAccess >& /* typeMap */ ) +{ + return Any(); +} + +Reference< css::sdbc::XRef > BaseResultSet::getRef( sal_Int32 /* columnIndex */ ) +{ + return Reference< css::sdbc::XRef > (); +} + +Reference< css::sdbc::XBlob > BaseResultSet::getBlob( sal_Int32 /* columnIndex */ ) +{ + return Reference< css::sdbc::XBlob > (); +} + +Reference< css::sdbc::XClob > BaseResultSet::getClob( sal_Int32 /* columnIndex */ ) +{ + return Reference< css::sdbc::XClob > (); +} + +Reference< css::sdbc::XArray > BaseResultSet::getArray( sal_Int32 columnIndex ) +{ + return new Array( m_xMutex, parseArray( getString( columnIndex ) ), *this, m_tc ); +} + +::cppu::IPropertyArrayHelper & BaseResultSet::getInfoHelper() +{ + return getResultSetPropertyArrayHelper(); +} + +sal_Bool BaseResultSet::convertFastPropertyValue( + Any & /* rConvertedValue */, Any & /* rOldValue */, sal_Int32 nHandle, const Any& rValue ) +{ + bool bRet; + switch( nHandle ) + { + case BASERESULTSET_CURSOR_NAME: + { + OUString val; + bRet = ( rValue >>= val ); + m_props[nHandle] <<= val; + break; + } + case BASERESULTSET_ESCAPE_PROCESSING: + case BASERESULTSET_IS_BOOKMARKABLE: + { + bool val(false); + bRet = ( rValue >>= val ); + m_props[nHandle] <<= val; + break; + } + case BASERESULTSET_FETCH_DIRECTION: + case BASERESULTSET_FETCH_SIZE: + case BASERESULTSET_RESULT_SET_CONCURRENCY: + case BASERESULTSET_RESULT_SET_TYPE: + { + sal_Int32 val; + bRet = ( rValue >>= val ); + m_props[nHandle] <<= val; + break; + } + default: + { + throw IllegalArgumentException( + "pq_resultset: Invalid property handle (" + OUString::number( nHandle ) + ")", + *this, 2 ); + } + } + return bRet; +} + + +void BaseResultSet::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle,const Any& rValue ) +{ + m_props[nHandle] = rValue; +} + +void BaseResultSet::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + rValue = m_props[nHandle]; +} + +Reference < XPropertySetInfo > BaseResultSet::getPropertySetInfo() +{ + return OPropertySetHelper::createPropertySetInfo( getResultSetPropertyArrayHelper() ); +} + +void BaseResultSet::disposing() +{ + close(); +} + +void BaseResultSet::checkColumnIndex(sal_Int32 index ) +{ + if( index < 1 || index > m_fieldCount ) + { + throw SQLException( + "pq_resultset: index out of range (" + + OUString::number( index ) + + ", allowed range is 1 to " + OUString::number( m_fieldCount ) + + ")", + *this, OUString(), 1, Any() ); + } + +} + +void BaseResultSet::checkRowIndex() +{ + if( m_row < 0 || m_row >= m_rowCount ) + { + throw SQLException( + "pq_baseresultset: row index out of range, allowed is 0 to " + OUString::number( m_rowCount -1 ) + + ", got " + OUString::number( m_row ), + *this, OUString(),1, Any() ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_baseresultset.hxx b/connectivity/source/drivers/postgresql/pq_baseresultset.hxx new file mode 100644 index 000000000..27ec2e62a --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_baseresultset.hxx @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_BASERESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_BASERESULTSET_HXX + +#include <cppuhelper/propshlp.hxx> +#include <cppuhelper/component.hxx> + +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include "pq_connection.hxx" + +namespace pq_sdbc_driver +{ + +static const sal_Int32 BASERESULTSET_CURSOR_NAME = 0; +static const sal_Int32 BASERESULTSET_ESCAPE_PROCESSING = 1; +static const sal_Int32 BASERESULTSET_FETCH_DIRECTION = 2; +static const sal_Int32 BASERESULTSET_FETCH_SIZE = 3; +static const sal_Int32 BASERESULTSET_IS_BOOKMARKABLE = 4; +static const sal_Int32 BASERESULTSET_RESULT_SET_CONCURRENCY = 5; +static const sal_Int32 BASERESULTSET_RESULT_SET_TYPE = 6; + +#define BASERESULTSET_SIZE 7 + +typedef ::cppu::WeakComponentImplHelper< css::sdbc::XCloseable, + css::sdbc::XResultSetMetaDataSupplier, + css::sdbc::XResultSet, + css::sdbc::XRow, + css::sdbc::XColumnLocate + > BaseResultSet_BASE; +class BaseResultSet : public BaseResultSet_BASE, + public cppu::OPropertySetHelper +{ +protected: + css::uno::Any m_props[BASERESULTSET_SIZE]; + css::uno::Reference< css::uno::XInterface > m_owner; + css::uno::Reference< css::script::XTypeConverter > m_tc; + ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + sal_Int32 m_row; + sal_Int32 m_rowCount; + sal_Int32 m_fieldCount; + bool m_wasNull; + +protected: + /** mutex should be locked before called + + @throws css::sdbc::SQLException + @throws css::uno::RuntimeException + */ + virtual void checkClosed() = 0; + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkColumnIndex( sal_Int32 index ); + void checkRowIndex(); + + virtual css::uno::Any getValue( sal_Int32 columnIndex ) = 0; + css::uno::Any convertTo( + const css::uno::Any &str, const css::uno::Type &type ); + +protected: + BaseResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + sal_Int32 rowCount, + sal_Int32 columnCount, + const css::uno::Reference< css::script::XTypeConverter > &tc ); + virtual ~BaseResultSet() override; + +public: // XInterface + virtual void SAL_CALL acquire() throw() override { BaseResultSet_BASE::acquire(); } + virtual void SAL_CALL release() throw() override { BaseResultSet_BASE::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // XCloseable +// virtual void SAL_CALL close( ) +// throw (css::sdbc::SQLException, css::uno::RuntimeException) = 0; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XResultSetMetaDataSupplier +// virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) +// throw (css::sdbc::SQLException, css::uno::RuntimeException) = 0; + +public: // XResultSet + // Methods + virtual sal_Bool SAL_CALL next( ) override; + virtual sal_Bool SAL_CALL isBeforeFirst( ) override; + virtual sal_Bool SAL_CALL isAfterLast( ) override; + virtual sal_Bool SAL_CALL isFirst( ) override; + virtual sal_Bool SAL_CALL isLast( ) override; + virtual void SAL_CALL beforeFirst( ) override; + virtual void SAL_CALL afterLast( ) override; + virtual sal_Bool SAL_CALL first( ) override; + virtual sal_Bool SAL_CALL last( ) override; + virtual sal_Int32 SAL_CALL getRow( ) override; + virtual sal_Bool SAL_CALL absolute( sal_Int32 row ) override; + virtual sal_Bool SAL_CALL relative( sal_Int32 rows ) override; + virtual sal_Bool SAL_CALL previous( ) override; + virtual void SAL_CALL refreshRow( ) override; + virtual sal_Bool SAL_CALL rowUpdated( ) override; + virtual sal_Bool SAL_CALL rowInserted( ) override; + virtual sal_Bool SAL_CALL rowDeleted( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getStatement() override; + + +public: // XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( + sal_Int32 columnIndex, + const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + +public: // XColumnLocate +// virtual sal_Int32 SAL_CALL findColumn( const OUString& columnName ) +// throw (css::sdbc::SQLException, css::uno::RuntimeException) = 0; + +public: // OPropertySetHelper + virtual cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + using ::cppu::OPropertySetHelper::getFastPropertyValue; + + void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle ) const override; + + // XPropertySet + css::uno::Reference < css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + +public: // OComponentHelper + virtual void SAL_CALL disposing() override; + + +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_connection.cxx b/connectivity/source/drivers/postgresql/pq_connection.cxx new file mode 100644 index 000000000..66c30c893 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_connection.cxx @@ -0,0 +1,609 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <vector> +#include <time.h> +#include <string.h> + +#include <memory> + +#include "pq_connection.hxx" +#include "pq_statement.hxx" +#include "pq_preparedstatement.hxx" +#include "pq_databasemetadata.hxx" +#include "pq_xtables.hxx" +#include "pq_xviews.hxx" +#include "pq_xusers.hxx" + +#include <rtl/uuid.h> +#include <rtl/bootstrap.hxx> +#include <sal/log.hxx> +#include <o3tl/enumarray.hxx> +#include <osl/module.h> + +#include <cppuhelper/implementationentry.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/script/Converter.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +using osl::MutexGuard; + +using com::sun::star::container::XNameAccess; + +using com::sun::star::lang::XComponent; +using com::sun::star::lang::IllegalArgumentException; + +using com::sun::star::script::Converter; +using com::sun::star::script::XTypeConverter; + +using com::sun::star::uno::RuntimeException; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Reference; +using com::sun::star::uno::XInterface; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::UNO_QUERY_THROW; +using com::sun::star::uno::XComponentContext; +using com::sun::star::uno::Any; + +using com::sun::star::beans::PropertyValue; + +using com::sun::star::sdbc::XCloseable; +using com::sun::star::sdbc::SQLException; +using com::sun::star::sdbc::XPreparedStatement; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XDatabaseMetaData; + +namespace pq_sdbc_driver +{ + +namespace { + +// Helper class for statement lifetime management +class ClosableReference : public cppu::WeakImplHelper< css::uno::XReference > +{ + rtl::Reference<Connection> m_conn; + ::rtl::ByteSequence m_id; +public: + ClosableReference( const ::rtl::ByteSequence & id , Connection *that ) + : m_conn( that ), m_id( id ) + { + } + + virtual void SAL_CALL dispose() override + { + if( m_conn.is() ) + { + m_conn->removeFromWeakMap(m_id); + m_conn.clear(); + } + } +}; + +} + +static OUString ConnectionGetImplementationName() +{ + return "org.openoffice.comp.connectivity.pq.Connection.noext"; +} +static css::uno::Sequence<OUString> ConnectionGetSupportedServiceNames() +{ + return Sequence< OUString > { "com.sun.star.sdbc.Connection" }; +} + +Connection::Connection( + const rtl::Reference< comphelper::RefCountedMutex > &refMutex, + const css::uno::Reference< css::uno::XComponentContext > & ctx ) + : ConnectionBase( refMutex->GetMutex() ), + m_ctx( ctx ) , + m_xMutex( refMutex ) +{ +} + +Connection::~Connection() +{ + if( m_settings.pConnection ) + { + PQfinish( m_settings.pConnection ); + m_settings.pConnection = nullptr; + } +} +typedef std::vector< css::uno::Reference< css::sdbc::XCloseable > > CloseableVector; + +typedef std::vector< css::uno::Reference< css::lang::XComponent > > DisposeableVector; + +void Connection::close() +{ + CloseableVector vectorCloseable; + DisposeableVector vectorDispose; + { + MutexGuard guard( m_xMutex->GetMutex() ); + // silently ignore, if the connection has been closed already + if( m_settings.pConnection ) + { + SAL_INFO("connectivity.postgresql", "closing connection"); + PQfinish( m_settings.pConnection ); + m_settings.pConnection = nullptr; + } + + vectorDispose.push_back( Reference< XComponent > ( m_settings.users, UNO_QUERY ) ); + vectorDispose.push_back( Reference< XComponent > ( m_settings.tables , UNO_QUERY ) ); + vectorDispose.push_back( Reference< XComponent > ( m_meta, UNO_QUERY ) ); + m_meta.clear(); + m_settings.tables.clear(); + m_settings.users.clear(); + + for (auto const& statement : m_myStatements) + { + Reference< XCloseable > r = statement.second; + if( r.is() ) + vectorCloseable.push_back( r ); + } + } + + // close all created statements + for (auto const& elem : vectorCloseable) + elem->close(); + + // close all created statements + for (auto const& elem : vectorDispose) + { + if( elem.is() ) + elem->dispose(); + } +} + + +void Connection::removeFromWeakMap( const ::rtl::ByteSequence & id ) +{ + // shrink the list ! + MutexGuard guard( m_xMutex->GetMutex() ); + WeakHashMap::iterator ii = m_myStatements.find( id ); + if( ii != m_myStatements.end() ) + m_myStatements.erase( ii ); +} + +Reference< XStatement > Connection::createStatement() +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + + Statement *stmt = new Statement( m_xMutex, this , &m_settings ); + Reference< XStatement > ret( stmt ); + ::rtl::ByteSequence id( 16 ); + rtl_createUuid( reinterpret_cast<sal_uInt8*>(id.getArray()), nullptr, false ); + m_myStatements[ id ] = Reference< XCloseable > ( stmt ); + stmt->queryAdapter()->addReference( new ClosableReference( id, this ) ); + return ret; +} + +Reference< XPreparedStatement > Connection::prepareStatement( const OUString& sql ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + + OString byteSql = OUStringToOString( sql, ConnectionSettings::encoding ); + PreparedStatement *stmt = new PreparedStatement( m_xMutex, this, &m_settings, byteSql ); + Reference< XPreparedStatement > ret = stmt; + + ::rtl::ByteSequence id( 16 ); + rtl_createUuid( reinterpret_cast<sal_uInt8*>(id.getArray()), nullptr, false ); + m_myStatements[ id ] = Reference< XCloseable > ( stmt ); + stmt->queryAdapter()->addReference( new ClosableReference( id, this ) ); + return ret; +} + +Reference< XPreparedStatement > Connection::prepareCall( const OUString& ) +{ + throw SQLException( + "pq_driver: Callable statements not supported", + Reference< XInterface > (), OUString() , 1, Any() ); +} + + +OUString Connection::nativeSQL( const OUString& sql ) +{ + return sql; +} + +void Connection::setAutoCommit( sal_Bool ) +{ + // UNSUPPORTED +} + +sal_Bool Connection::getAutoCommit() +{ + // UNSUPPORTED + return true; +} + +void Connection::commit() +{ + // UNSUPPORTED +} + +void Connection::rollback() +{ + // UNSUPPORTED +} + +sal_Bool Connection::isClosed() +{ + return m_settings.pConnection == nullptr; +} + +Reference< XDatabaseMetaData > Connection::getMetaData() +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + if( ! m_meta.is() ) + m_meta = new DatabaseMetaData( m_xMutex, this, &m_settings ); + return m_meta; +} + +void Connection::setReadOnly( sal_Bool ) +{ + // UNSUPPORTED + +} + +sal_Bool Connection::isReadOnly() +{ + // UNSUPPORTED + return false; +} + +void Connection::setCatalog( const OUString& ) +{ + // UNSUPPORTED +} + +OUString Connection::getCatalog() +{ + MutexGuard guard( m_xMutex->GetMutex() ); + if( m_settings.pConnection == nullptr ) + { + throw SQLException( "pq_connection: connection is closed", *this, + OUString(), 1, Any() ); + } + char * p = PQdb(m_settings.pConnection ); + return OUString( p, strlen(p) , ConnectionSettings::encoding ); +} + +void Connection::setTransactionIsolation( sal_Int32 ) +{ + // UNSUPPORTED +} + +sal_Int32 Connection::getTransactionIsolation() +{ + // UNSUPPORTED + return 0; +} + +Reference< XNameAccess > Connection::getTypeMap() +{ + Reference< XNameAccess > t; + { + MutexGuard guard( m_xMutex->GetMutex() ); + t = m_typeMap; + } + return t; +} + +void Connection::setTypeMap( const Reference< XNameAccess >& typeMap ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + m_typeMap = typeMap; +} +Any Connection::getWarnings() +{ + return Any(); +} + +void Connection::clearWarnings() +{ +} + +namespace { + +class cstr_vector +{ + std::vector<char*> values; + std::vector<bool> acquired; +public: + cstr_vector () : values(), acquired() { values.reserve(8); acquired.reserve(8); } + ~cstr_vector () + { + OSL_ENSURE(values.size() == acquired.size(), "pq_connection: cstr_vector values and acquired size mismatch"); + std::vector<bool>::const_iterator pa = acquired.begin(); + for( const auto& v : values ) + { + if (*pa) + free(v); + ++pa; + } + } + void push_back(const char* s, __sal_NoAcquire) + { + values.push_back(const_cast<char*>(s)); + acquired.push_back(false); + } + void push_back(char* s) + { + values.push_back(s); + acquired.push_back(true); + } + // This const_cast is there for compatibility with PostgreSQL <= 9.1; + // PostgreSQL >= 9.2 has the right const qualifiers in the headers + // for a return type of "char const*const*". + char const** c_array() const { return const_cast <const char**>(values.data()); } +}; + +} + +static void properties2arrays( const Sequence< PropertyValue > & args, + const Reference< XTypeConverter> &tc, + rtl_TextEncoding enc, + cstr_vector &keywords, + cstr_vector &values) +{ + // LEM TODO: can we just blindly take all properties? + // I.e. they are prefiltered to have only relevant ones? + // Else, at least support all keywords from + // http://www.postgresql.org/docs/9.0/interactive/libpq-connect.html + + static const char* keyword_list[] = { + "password", + "user", + "port", + "dbname", + "connect_timeout", + "options", + "requiressl" + }; + + for( PropertyValue const & prop : args ) + { + bool append = false; + for(const char* j : keyword_list) + { + if( prop.Name.equalsIgnoreAsciiCaseAscii( j )) + { + keywords.push_back( j, SAL_NO_ACQUIRE ); + append = true; + break; + } + } + + if( append ) + { + OUString value; + tc->convertTo( prop.Value, cppu::UnoType<decltype(value)>::get() ) >>= value; + char *v = strdup(OUStringToOString(value, enc).getStr()); + values.push_back ( v ); + } + else + { + // ignore for now + SAL_WARN("connectivity.postgresql", "sdbc-postgresql: unknown argument '" << prop.Name << "' having value: " << prop.Value ); + } + } +} + +void Connection::initialize( const Sequence< Any >& aArguments ) +{ + OUString url; + Sequence< PropertyValue > args; + + Reference< XTypeConverter > tc( Converter::create(m_ctx) ); + if( ! tc.is() ) + { + throw RuntimeException( + "pq_driver: Couldn't instantiate converter service" ); + } + if( aArguments.getLength() != 2 ) + { + throw IllegalArgumentException( + "pq_driver: expected 2 arguments, got " + OUString::number( aArguments.getLength( ) ), + Reference< XInterface > () , 0 ); + } + + if( ! (aArguments[0] >>= url) ) + { + throw IllegalArgumentException( + "pq_driver: expected string as first argument, got " + + aArguments[0].getValueType().getTypeName(), + *this, 0 ); + } + + tc->convertTo( aArguments[1], cppu::UnoType<decltype(args)>::get() ) >>= args; + + OString o; + int nColon = url.indexOf( ':' ); + if( nColon != -1 ) + { + nColon = url.indexOf( ':' , 1+ nColon ); + if( nColon != -1 ) + { + o = OUStringToOString( url.getStr()+nColon+1, ConnectionSettings::encoding ); + } + } + { + cstr_vector keywords; + cstr_vector values; + + if ( o.getLength() > 0 ) + { + char *err; + std::shared_ptr<PQconninfoOption> oOpts(PQconninfoParse(o.getStr(), &err), PQconninfoFree); + if (oOpts == nullptr) + { + OUString errorMessage; + if ( err != nullptr) + { + errorMessage = OUString( err, strlen(err), ConnectionSettings::encoding ); + free(err); + } + else + errorMessage = "#no error message#"; + // HY092 is "Invalid attribute/option identifier." + // Just the most likely error; the error might be HY024 "Invalid attribute value". + throw SQLException( + "Error in database URL '" + url + "':\n" + errorMessage, + *this, "HY092", 5, Any() ); + } + + for ( PQconninfoOption * opt = oOpts.get(); opt->keyword != nullptr; ++opt) + { + if ( opt->val != nullptr ) + { + keywords.push_back(strdup(opt->keyword)); + values.push_back(strdup(opt->val)); + } + } + } + properties2arrays( args , tc, ConnectionSettings::encoding, keywords, values ); + keywords.push_back(nullptr, SAL_NO_ACQUIRE); + values.push_back(nullptr, SAL_NO_ACQUIRE); + + m_settings.pConnection = PQconnectdbParams( keywords.c_array(), values.c_array(), 0 ); + } + if( ! m_settings.pConnection ) + throw RuntimeException("pq_driver: out of memory" ); + if( PQstatus( m_settings.pConnection ) == CONNECTION_BAD ) + { + const char * error = PQerrorMessage( m_settings.pConnection ); + OUString errorMessage( error, strlen( error) , RTL_TEXTENCODING_ASCII_US ); + PQfinish( m_settings.pConnection ); + m_settings.pConnection = nullptr; + throw SQLException( + "Couldn't establish database connection to '" + url + "'\n" + + errorMessage, + *this, errorMessage, CONNECTION_BAD, Any() ); + } + PQsetClientEncoding( m_settings.pConnection, "UNICODE" ); + char *p = PQuser( m_settings.pConnection ); + m_settings.user = OUString( p, strlen(p), RTL_TEXTENCODING_UTF8); + p = PQdb( m_settings.pConnection ); + m_settings.catalog = OUString( p, strlen(p), RTL_TEXTENCODING_UTF8); + m_settings.tc = tc; + + SAL_INFO("connectivity.postgresql", "connection to '" << url << "' successfully opened"); +} + +void Connection::disposing() +{ + close(); +} + +void Connection::checkClosed() +{ + if( !m_settings.pConnection ) + throw SQLException( "pq_connection: Connection already closed", + *this, OUString(), 1, Any() ); +} + +Reference< XNameAccess > Connection::getTables() +{ + SAL_INFO("connectivity.postgresql", "Connection::getTables() got called"); + MutexGuard guard( m_xMutex->GetMutex() ); + if( !m_settings.tables.is() ) + m_settings.tables = Tables::create( m_xMutex, this, &m_settings , &m_settings.pTablesImpl); + else + // TODO: how to overcome the performance problem ? + Reference< css::util::XRefreshable > ( m_settings.tables, UNO_QUERY_THROW )->refresh(); + return m_settings.tables; +} + +Reference< XNameAccess > Connection::getViews() +{ + SAL_INFO("connectivity.postgresql", "Connection::getViews() got called"); + MutexGuard guard( m_xMutex->GetMutex() ); + if( !m_settings.views.is() ) + m_settings.views = Views::create( m_xMutex, this, &m_settings, &(m_settings.pViewsImpl) ); + else + // TODO: how to overcome the performance problem ? + Reference< css::util::XRefreshable > ( m_settings.views, UNO_QUERY_THROW )->refresh(); + return m_settings.views; +} + + +Reference< XNameAccess > Connection::getUsers() +{ + SAL_INFO("connectivity.postgresql", "Connection::getUsers() got called"); + + MutexGuard guard( m_xMutex->GetMutex() ); + if( !m_settings.users.is() ) + m_settings.users = Users::create( m_xMutex, this, &m_settings ); + return m_settings.users; +} + +/// @throws Exception +static Reference< XInterface > ConnectionCreateInstance( + const Reference< XComponentContext > & ctx ) +{ + ::rtl::Reference< comphelper::RefCountedMutex > ref = new comphelper::RefCountedMutex; + return * new Connection( ref, ctx ); +} + +} // end namespace + + +static const struct cppu::ImplementationEntry g_entries[] = +{ + { + pq_sdbc_driver::ConnectionCreateInstance, pq_sdbc_driver::ConnectionGetImplementationName, + pq_sdbc_driver::ConnectionGetSupportedServiceNames, cppu::createSingleComponentFactory, + nullptr , 0 + }, + { nullptr, nullptr, nullptr, nullptr, nullptr, 0 } +}; + + +extern "C" +{ + +SAL_DLLPUBLIC_EXPORT void * postgresql_sdbc_impl_component_getFactory( + const char * pImplName, void * pServiceManager, void * pRegistryKey ) +{ + return cppu::component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_connection.hxx b/connectivity/source/drivers/postgresql/pq_connection.hxx new file mode 100644 index 000000000..f8d19c406 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_connection.hxx @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_CONNECTION_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_CONNECTION_HXX + +#include <config_lgpl.h> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XUsersSupplier.hpp> +#include <com/sun/star/sdbcx/XViewsSupplier.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> + +#include <com/sun/star/container/XNameAccess.hpp> + +#include <rtl/ref.hxx> +#include <rtl/byteseq.hxx> + +#include <comphelper/refcountedmutex.hxx> + +#include <cppuhelper/weakref.hxx> +#include <cppuhelper/compbase.hxx> +#include <functional> + +#include <libpq-fe.h> +#include <unordered_map> + +namespace pq_sdbc_driver +{ +struct ConnectionSettings; +class Tables; +class Views; +struct ConnectionSettings +{ + ConnectionSettings() : + pConnection(nullptr), + maxNameLen(0), + maxIndexKeys(0), + pTablesImpl(nullptr), + pViewsImpl(nullptr) + {} + static const rtl_TextEncoding encoding = RTL_TEXTENCODING_UTF8; + PGconn *pConnection; + sal_Int32 maxNameLen; + sal_Int32 maxIndexKeys; + css::uno::Reference< css::script::XTypeConverter > tc; + css::uno::Reference< css::container::XNameAccess > tables; + css::uno::Reference< css::container::XNameAccess > users; + css::uno::Reference< css::container::XNameAccess > views; + Tables *pTablesImpl; // needed to implement renaming of tables / views + Views *pViewsImpl; // needed to implement renaming of tables / views + OUString user; + OUString catalog; +}; + + +typedef cppu::WeakComponentImplHelper< + css::sdbc::XConnection, + css::sdbc::XWarningsSupplier, + css::lang::XInitialization, + css::sdbcx::XTablesSupplier, + css::sdbcx::XViewsSupplier, + css::sdbcx::XUsersSupplier > ConnectionBase; + +// some types +struct HashByteSequence +{ + sal_Int32 operator () ( const ::rtl::ByteSequence & seq ) const + { + return *reinterpret_cast<sal_Int32 const *>(seq.getConstArray()); + } +}; + +typedef std::unordered_map< + ::rtl::ByteSequence, + css::uno::WeakReference< css::sdbc::XCloseable >, + HashByteSequence > WeakHashMap; + + +typedef std::unordered_map +< + sal_Int32, + OUString +> Int2StringMap; + +class Connection : public ConnectionBase +{ + css::uno::Reference< css::uno::XComponentContext > m_ctx; + css::uno::Reference< css::container::XNameAccess > m_typeMap; + ConnectionSettings m_settings; + ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + css::uno::Reference< css::sdbc::XDatabaseMetaData > m_meta; + WeakHashMap m_myStatements; + +private: + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkClosed(); + +public: + Connection( + const rtl::Reference< comphelper::RefCountedMutex > &refMutex, + const css::uno::Reference< css::uno::XComponentContext > & ctx ); + + virtual ~Connection( ) override; + +public: // XCloseable + virtual void SAL_CALL close() override; + +public: // XConnection + + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override ; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( + const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( + const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual void SAL_CALL setAutoCommit( sal_Bool autoCommit ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual void SAL_CALL setReadOnly( sal_Bool readOnly ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL setCatalog( const OUString& catalog ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual void SAL_CALL setTransactionIsolation( sal_Int32 level ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + virtual void SAL_CALL setTypeMap( + const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + +public: // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + +public: // XInitialization + virtual void SAL_CALL initialize( + const css::uno::Sequence< css::uno::Any >& aArguments ) override; + +public: // XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( ) override; + +public: // XUsersSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getUsers( ) override; + +public: // XViewsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getViews( ) override; + +public: + virtual void SAL_CALL disposing() override; + +public: // helper function + void removeFromWeakMap( const ::rtl::ByteSequence & seq ); +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_databasemetadata.cxx b/connectivity/source/drivers/postgresql/pq_databasemetadata.cxx new file mode 100644 index 000000000..4794d21e8 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_databasemetadata.cxx @@ -0,0 +1,2510 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Some portions were adapted from JDBC PostgreSQL driver: + * + * Copyright (c) 2004-2008, PostgreSQL Global Development Group + * + * Licence of original JDBC driver code: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the PostgreSQL Global Development Group nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************/ + +#include <algorithm> +#include <sal/log.hxx> +#include "pq_databasemetadata.hxx" +#include "pq_driver.hxx" +#include "pq_sequenceresultset.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +#include <rtl/ustrbuf.hxx> +#include <sal/macros.h> +#include <com/sun/star/sdbc/TransactionIsolation.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/IndexType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/ColumnSearch.hpp> + +using ::osl::MutexGuard; + + +using namespace com::sun::star::sdbc; + +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Any; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::UNO_QUERY_THROW; + +namespace pq_sdbc_driver +{ +// These are pre-processor versions of KeyRule.idl declarations +// These are inherited from JDBC, and thus won't change anytime soon. +// Having them as pre-processor definitions allows to include them +// into compile-time strings (through SAL_STRINGIFY), which can be passed to ASCII_STR. +// That is without resorting to horrendous hacks in template meta-programming. +#define KEYRULE_CASCADE 0 +#define KEYRULE_RESTRICT 1 +#define KEYRULE_SET_NULL 2 +#define KEYRULE_NO_ACTION 4 +#define KEYRULE_SET_DEFAULT 4 +// Ditto for Deferrability.idl +#define DEFERRABILITY_INITIALLY_DEFERRED 5 +#define DEFERRABILITY_INITIALLY_IMMEDIATE 6 +#define DEFERRABILITY_NONE 7 + +DatabaseMetaData::DatabaseMetaData( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ) + : m_xMutex( refMutex ), + m_pSettings( pSettings ), + m_origin( origin ), + m_getIntSetting_stmt ( m_origin->prepareStatement("SELECT setting FROM pg_catalog.pg_settings WHERE name=?") ) +{ + init_getReferences_stmt(); + init_getPrivs_stmt(); +} + +sal_Bool DatabaseMetaData::allProceduresAreCallable( ) +{ + // TODO + return false; +} + +sal_Bool DatabaseMetaData::allTablesAreSelectable( ) +{ + return true; +} + +OUString DatabaseMetaData::getURL( ) +{ + // TODO + // LEM TODO: implement + return OUString(); +} + +OUString DatabaseMetaData::getUserName( ) +{ + return m_pSettings->user; +} + +sal_Bool DatabaseMetaData::isReadOnly( ) +{ + return false; +} + + +sal_Bool DatabaseMetaData::nullsAreSortedHigh( ) +{ + // Whether NULL values are considered, for sorting purposes, LARGER than any other value. + // Specification: http://download.oracle.com/javase/6/docs/api/java/sql/DatabaseMetaData.html#nullsAreSortedHigh() + // PostgreSQL behaviour: http://www.postgresql.org/docs/9.1/static/queries-order.html + return true; +} + +sal_Bool DatabaseMetaData::nullsAreSortedLow( ) +{ + return ! nullsAreSortedHigh(); +} + +sal_Bool DatabaseMetaData::nullsAreSortedAtStart( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::nullsAreSortedAtEnd( ) +{ + return false; +} + +OUString DatabaseMetaData::getDatabaseProductName( ) +{ + return "PostgreSQL"; +} + +OUString DatabaseMetaData::getDatabaseProductVersion( ) +{ + return OUString::createFromAscii( PQparameterStatus( m_pSettings->pConnection, "server_version" ) ); +} +OUString DatabaseMetaData::getDriverName( ) +{ + return "postgresql-sdbc"; +} + +OUString DatabaseMetaData::getDriverVersion( ) +{ + return PQ_SDBC_DRIVER_VERSION; +} + +sal_Int32 DatabaseMetaData::getDriverMajorVersion( ) +{ + return PQ_SDBC_MAJOR; +} + +sal_Int32 DatabaseMetaData::getDriverMinorVersion( ) +{ + return PQ_SDBC_MINOR; +} + +sal_Bool DatabaseMetaData::usesLocalFiles( ) +{ + // LEM TODO: + // http://wiki.openoffice.org/wiki/Documentation/DevGuide/Database/XDatabaseMetaData_Interface + // says "Returns true when the catalog name of the + // database should not appear in the DatasourceBrowser + // of OpenOffice.org API, otherwise false is returned." + // So, hmmm, think about it. + return false; +} + +sal_Bool DatabaseMetaData::usesLocalFilePerTable( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::supportsMixedCaseIdentifiers( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::storesUpperCaseIdentifiers( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::storesLowerCaseIdentifiers( ) +{ + return true; +} + + +sal_Bool DatabaseMetaData::storesMixedCaseIdentifiers( ) +{ + return false; +} + + +sal_Bool DatabaseMetaData::supportsMixedCaseQuotedIdentifiers( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::storesUpperCaseQuotedIdentifiers( ) +{ + return false; +} + + +sal_Bool DatabaseMetaData::storesLowerCaseQuotedIdentifiers( ) +{ + return false; +} + + +sal_Bool DatabaseMetaData::storesMixedCaseQuotedIdentifiers( ) +{ + return false; +} + + +OUString DatabaseMetaData::getIdentifierQuoteString( ) +{ + return "\""; +} + +OUString DatabaseMetaData::getSQLKeywords( ) +{ + // In Java 6, this is all keywords that are not SQL:2003 + // In Java 2 v1.4 and as per LibreOffice SDK doc, this is all keywords that are not SQL92 + // I understand this to mean "reserved keywords" only. + // See http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html + // LEM TODO: consider using pg_get_keywords(), filter on catcode + return + "ANALYSE," + "ANALYZE," + "ARRAY," //SQL:1999 + "ASYMMETRIC," //SQL:2003 + "BINARY," //SQL:1999 + "CONCURRENTLY," + "CURRENT_CATALOG," //SQL:2008 + "CURRENT_ROLE," //SQL:1999 + "CURRENT_SCHEMA," //SQL:2008 + "DO," + "FREEZE," + "ILIKE," + "ISNULL," + "LIMIT," //SQL:1999; non-reserved in SQL:2003 + "LOCALTIME," //SQL:1999 + "LOCALTIMESTAMP," //SQL:1999 + "NOTNULL," + "OFFSET," //SQL:2008 + "OVER," //SQL:2003 + "PLACING," //non-reserved in SQL:2003 + "RETURNING," //non-reserved in SQL:2008 + "SIMILAR," //SQL:2003 + "VARIADIC," + "VERBOSE," + "WINDOW" //SQL:2003 + ; +} +OUString DatabaseMetaData::getNumericFunctions( ) +{ + // See http://www.postgresql.org/docs/9.1/static/functions-math.html + // LEM TODO: Err... http://wiki.openoffice.org/wiki/Documentation/DevGuide/Database/Support_Scalar_Functions + // says this should be "Open Group CLI" names, not PostgreSQL names. + // Currently this is just a list of supported functions in PostgreSQL, with PostgreSQL names. + // And it is my job to map from Open Group CLI names/syntax to PostgreSQL names/syntax. Where? By parsing the SQL??? + // Should look at what the JDBC driver is doing. + return + "abs," + "cbrt," + "ceil," + "ceiling," + "degrees," + "div," + "exp," + "floor," + "ln," + "log," + "mod," + "pi," + "power," + "radians," + "random," + "round," + "setseed," + "sign," + "sqrt," + "trunc," + "width_bucket," + "acos," + "asin," + "atan," + "atan2," + "cos," + "cot," + "sin," + "tan" + ; +} + +OUString DatabaseMetaData::getStringFunctions( ) +{ + // See http://www.postgresql.org/docs/9.1/static/functions-string.html + return + "bit_length," + "char_length," + "character_length," + "lower," + "octet_length," + "overlay," + "position," + "substring," + "trim," + "upper," + "ascii," + "btrim," + "chr," + "concat," + "concat_ws," + "convert," + "convert_from," + "convert_to," + "decode," + "encode," + "format," + "initcap," + "left," + "length," + "lpad," + "ltrim," + "md5," + "pg_client_encoding," + "quote_ident," + "quote_literal," + "quote_nullable," + "regexp_matches," + "regexp_replace," + "regexp_split_to_array," + "regexp_split_to_table," + "repeat," + "replace," + "reverse," + "right," + "rpad," + "rtrim," + "split_part," + "strpos," + "substr," + "to_ascii," + "to_hex," + "translate" + ; +} + +OUString DatabaseMetaData::getSystemFunctions( ) +{ + // See http://www.postgresql.org/docs/9.1/static/functions-info.html + // and http://www.postgresql.org/docs/9.1/static/functions-admin.html + return + "current_catalog," + "current_database," + "current_query," + "current_schema," + "current_schemas," + "current_user," + "inet_client_addr," + "inet_client_port," + "inet_server_addr," + "inet_server_port," + "pg_backend_pid," + "pg_conf_load_time," + "pg_is_other_temp_schema," + "pg_listening_channels," + "pg_my_temp_schema," + "pg_postmaster_start_time," + "session_user," + "user," + "version," + "has_any_column_privilege," + "has_any_column_privilege," + "has_any_column_privilege," + "has_column_privilege," + "has_database_privilege," + "has_foreign_data_wrapper_privilege," + "has_function_privilege," + "has_language_privilege," + "has_schema_privilege," + "has_sequence_privilege," + "has_server_privilege," + "has_table_privilege," + "has_tablespace_privilege," + "pg_has_role," + "pg_collation_is_visible," + "pg_conversion_is_visible," + "pg_function_is_visible," + "pg_opclass_is_visible," + "pg_operator_is_visible," + "pg_table_is_visible," + "pg_ts_config_is_visible," + "pg_ts_dict_is_visible," + "pg_ts_parser_is_visible," + "pg_ts_template_is_visible," + "pg_type_is_visible," + "format_type," + "pg_describe_object," + "pg_get_constraintdef," + "pg_get_expr," + "pg_get_functiondef," + "pg_get_function_arguments," + "pg_get_function_identity_arguments," + "pg_get_function_result," + "pg_get_indexdef," + "pg_get_keywords," + "pg_get_ruledef," + "pg_get_serial_sequence," + "pg_get_triggerdef," + "pg_get_userbyid," + "pg_get_viewdef," + "pg_options_to_table," + "pg_tablespace_databases," + "pg_typeof," + "col_description," + "obj_description," + "shobj_description," + "txid_current," + "txid_current_snapshot," + "txid_snapshot_xip," + "txid_snapshot_xmax," + "txid_snapshot_xmin," + "txid_visible_in_snapshot," + "xmin," + "xmax," + "xip_list," + "current_setting," + "set_config," + "pg_cancel_backend," + "pg_reload_conf," + "pg_rotate_logfile," + "pg_terminate_backend," + "pg_create_restore_point," + "pg_current_xlog_insert_location," + "pg_current_xlog_location," + "pg_start_backup," + "pg_stop_backup," + "pg_switch_xlog," + "pg_xlogfile_name," + "pg_xlogfile_name_offset," + "pg_is_in_recovery," + "pg_last_xlog_receive_location," + "pg_last_xlog_replay_location," + "pg_last_xact_replay_timestamp," + "pg_is_xlog_replay_paused," + "pg_xlog_replay_pause," + "pg_xlog_replay_resume," + "pg_column_size," + "pg_database_size," + "pg_indexes_size," + "pg_relation_size," + "pg_size_pretty," + "pg_table_size," + "pg_tablespace_size," + "pg_tablespace_size," + "pg_total_relation_size," + "pg_relation_filenode," + "pg_relation_filepath," + "pg_ls_dir," + "pg_read_file," + "pg_read_binary_file," + "pg_stat_file," + "pg_advisory_lock," + "pg_advisory_lock_shared," + "pg_advisory_unlock," + "pg_advisory_unlock_all," + "pg_advisory_unlock_shared," + "pg_advisory_xact_lock," + "pg_advisory_xact_lock_shared," + "pg_try_advisory_lock," + "pg_try_advisory_lock_shared," + "pg_try_advisory_xact_lock," + "pg_try_advisory_xact_lock_shared," + "pg_sleep" + ; +} +OUString DatabaseMetaData::getTimeDateFunctions( ) +{ + // TODO + return + "age," + "age," + "clock_timestamp," + "current_date," + "current_time," + "current_timestamp," + "date_part," + "date_part," + "date_trunc," + "extract," + "extract," + "isfinite," + "isfinite," + "isfinite," + "justify_days," + "justify_hours," + "justify_interval," + "localtime," + "localtimestamp," + "now," + "statement_timestamp," + "timeofday," + "transaction_timestamp," + ; +} +OUString DatabaseMetaData::getSearchStringEscape( ) +{ + return "\\"; +} +OUString DatabaseMetaData::getExtraNameCharacters( ) +{ + return "$"; +} + +sal_Bool DatabaseMetaData::supportsAlterTableWithAddColumn( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsAlterTableWithDropColumn( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsColumnAliasing( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::nullPlusNonNullIsNull( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsTypeConversion( ) +{ + // LEM: this is specifically whether the "CONVERT" function is supported + // It seems that in PostgreSQL, that function is only for string encoding, so no. + return false; +} + +sal_Bool DatabaseMetaData::supportsConvert( sal_Int32, sal_Int32 ) +{ + return false; +} + +sal_Bool DatabaseMetaData::supportsTableCorrelationNames( ) +{ + // LEM: A correlation name is "bar" in "SELECT foo FROM qux [AS] bar WHERE ..." + return true; +} + + +sal_Bool DatabaseMetaData::supportsDifferentTableCorrelationNames( ) +{ + return false; +} +sal_Bool DatabaseMetaData::supportsExpressionsInOrderBy( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsOrderByUnrelated( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsGroupBy( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsGroupByUnrelated( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsGroupByBeyondSelect( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsLikeEscapeClause( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsMultipleResultSets( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsMultipleTransactions( ) +{ + // Allows multiple transactions open at once (on different connections!) + return true; +} + +sal_Bool DatabaseMetaData::supportsNonNullableColumns( ) +{ + return true; +} + + +sal_Bool DatabaseMetaData::supportsMinimumSQLGrammar( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsCoreSQLGrammar( ) +{ + // LEM: jdbc driver says not, although the comments in it seem old + // fdo#45249 Base query design won't use any aggregate function + // (except COUNT(*) unless we say yes, so say yes. + // Actually, Base assumes *also* support for aggregate functions "collect, fusion, intersection" + // as soon as supportsCoreSQLGrammar() returns true. + // Those are *not* Core SQL, though. They are in optional feature S271 "Basic multiset support" + return true; +} + +sal_Bool DatabaseMetaData::supportsExtendedSQLGrammar( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::supportsANSI92EntryLevelSQL( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsANSI92IntermediateSQL( ) +{ + // LEM: jdbc driver says not, although the comments in it seem old + return false; +} + +sal_Bool DatabaseMetaData::supportsANSI92FullSQL( ) +{ + // LEM: jdbc driver says not, although the comments in it seem old + return false; +} + +sal_Bool DatabaseMetaData::supportsIntegrityEnhancementFacility( ) +{ + // LEM: jdbc driver says yes, although comment says they are not sure what this means... + return true; +} + +sal_Bool DatabaseMetaData::supportsOuterJoins( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsFullOuterJoins( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsLimitedOuterJoins( ) +{ + return true; +} + + +OUString DatabaseMetaData::getSchemaTerm( ) +{ + return "SCHEMA"; +} + +OUString DatabaseMetaData::getProcedureTerm( ) +{ + return "function"; +} + +OUString DatabaseMetaData::getCatalogTerm( ) +{ + return "DATABASE"; +} + +sal_Bool DatabaseMetaData::isCatalogAtStart( ) +{ + return true; +} + +OUString DatabaseMetaData::getCatalogSeparator( ) +{ + return "."; +} + +sal_Bool DatabaseMetaData::supportsSchemasInDataManipulation( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsSchemasInProcedureCalls( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsSchemasInTableDefinitions( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsSchemasInIndexDefinitions( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsSchemasInPrivilegeDefinitions( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsCatalogsInDataManipulation( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::supportsCatalogsInProcedureCalls( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::supportsCatalogsInTableDefinitions( ) +{ + return false; +} + + +sal_Bool DatabaseMetaData::supportsCatalogsInIndexDefinitions( ) +{ + return false; +} + + +sal_Bool DatabaseMetaData::supportsCatalogsInPrivilegeDefinitions( ) +{ + return false; +} + + +// LEM TODO: positioned (through cursor) updates and deletes seem +// to be supported; see {UPDATE,DELETE} /table/ (...) WHERE CURRENT OF /cursor_name/" syntax +// and http://www.postgresql.org/docs/9.1/static/view-pg-cursors.html +// http://www.postgresql.org/docs/9.1/static/libpq-example.html actually uses a cursor :) +sal_Bool DatabaseMetaData::supportsPositionedDelete( ) +{ + // LEM: jdbc driver says not, although the comments in it seem old + return false; +} + +sal_Bool DatabaseMetaData::supportsPositionedUpdate( ) +{ + // LEM: jdbc driver says not, although the comments in it seem old + return false; +} + + +sal_Bool DatabaseMetaData::supportsSelectForUpdate( ) +{ + return true; +} + + +sal_Bool DatabaseMetaData::supportsStoredProcedures( ) +{ + return true; +} + + +sal_Bool DatabaseMetaData::supportsSubqueriesInComparisons( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsSubqueriesInExists( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsSubqueriesInIns( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsSubqueriesInQuantifieds( ) +{ + // LEM: jdbc driver says yes, although comment says they don't know what this means... + return true; +} + +sal_Bool DatabaseMetaData::supportsCorrelatedSubqueries( ) +{ + return true; +} +sal_Bool DatabaseMetaData::supportsUnion( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsUnionAll( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsOpenCursorsAcrossCommit( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::supportsOpenCursorsAcrossRollback( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::supportsOpenStatementsAcrossCommit( ) +{ + return true; +} +sal_Bool DatabaseMetaData::supportsOpenStatementsAcrossRollback( ) +{ + return true; +} + +sal_Int32 DatabaseMetaData::getMaxBinaryLiteralLength( ) +{ + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxCharLiteralLength( ) +{ + return 0; +} + +// Copied / adapted / simplified from JDBC driver +sal_Int32 DatabaseMetaData::getIntSetting(const OUString& settingName) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + + Reference< XParameters > params(m_getIntSetting_stmt, UNO_QUERY_THROW ); + params->setString(1, settingName ); + Reference< XResultSet > rs = m_getIntSetting_stmt->executeQuery(); + Reference< XRow > xRow( rs , UNO_QUERY_THROW ); + OSL_VERIFY(rs->next()); + OSL_ENSURE(rs->isFirst(), "postgresql-sdbc DatabaseMetaData getIntSetting not on first row"); + OSL_ENSURE(rs->isLast(), "postgresql-sdbc DatabaseMetaData getIntSetting not on last row"); + return xRow->getInt(1); +} + +sal_Int32 DatabaseMetaData::getMaxNameLength() +{ + if ( m_pSettings->maxNameLen == 0) + m_pSettings->maxNameLen = getIntSetting( "max_identifier_length" ); + OSL_ENSURE(m_pSettings->maxNameLen, "postgresql-sdbc: maxNameLen is zero"); + return m_pSettings->maxNameLen; +} + +sal_Int32 DatabaseMetaData::getMaxIndexKeys() +{ + if ( m_pSettings->maxIndexKeys == 0) + m_pSettings->maxIndexKeys = getIntSetting("max_index_keys"); + OSL_ENSURE(m_pSettings->maxIndexKeys, "postgresql-sdbc: maxIndexKeys is zero"); + return m_pSettings->maxIndexKeys; +} + +sal_Int32 DatabaseMetaData::getMaxColumnNameLength( ) +{ + return getMaxNameLength(); +} + +sal_Int32 DatabaseMetaData::getMaxColumnsInGroupBy( ) +{ + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxColumnsInIndex( ) +{ + return getMaxIndexKeys(); +} + +sal_Int32 DatabaseMetaData::getMaxColumnsInOrderBy( ) +{ + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxColumnsInSelect( ) +{ + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxColumnsInTable( ) +{ + return 1600; +} + +sal_Int32 DatabaseMetaData::getMaxConnections( ) +{ + // LEM: The JDBC driver returns an arbitrary 8192; truth is as much as OS / hardware supports + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxCursorNameLength( ) //TODO, don't know +{ + return getMaxNameLength(); +} + +sal_Int32 DatabaseMetaData::getMaxIndexLength( ) //TODO, don't know +{ + // LEM: that's the index itself, not its name + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxSchemaNameLength( ) +{ + return getMaxNameLength(); +} + +sal_Int32 DatabaseMetaData::getMaxProcedureNameLength( ) +{ + return getMaxNameLength(); +} + +sal_Int32 DatabaseMetaData::getMaxCatalogNameLength( ) +{ + return getMaxNameLength(); +} + +sal_Int32 DatabaseMetaData::getMaxRowSize( ) +{ + // jdbc driver says 1GB, but http://www.postgresql.org/about/ says 1.6TB + // and that 1GB is the maximum _field_ size + // The row limit does not fit into a sal_Int32 + return 0; +} + +sal_Bool DatabaseMetaData::doesMaxRowSizeIncludeBlobs( ) +{ + // LEM: Err... PostgreSQL basically does not do BLOBs well + // In any case, BLOBs do not change the maximal row length AFAIK + return true; +} + +sal_Int32 DatabaseMetaData::getMaxStatementLength( ) +{ + // LEM: actually, that would be 2^sizeof(size_t)-1 + // on the server? on the client (because of libpq)? minimum of the two? not sure + // Anyway, big, so say unlimited. + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxStatements( ) //TODO, don't know +{ + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxTableNameLength( ) +{ + return getMaxNameLength(); +} + +sal_Int32 DatabaseMetaData::getMaxTablesInSelect( ) +{ + return 0; +} + +sal_Int32 DatabaseMetaData::getMaxUserNameLength( ) +{ + return getMaxNameLength(); +} + +sal_Int32 DatabaseMetaData::getDefaultTransactionIsolation( ) +{ + return css::sdbc::TransactionIsolation::READ_COMMITTED; +} + +sal_Bool DatabaseMetaData::supportsTransactions( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsTransactionIsolationLevel( sal_Int32 level ) +{ + if ( level == css::sdbc::TransactionIsolation::READ_COMMITTED + || level == css::sdbc::TransactionIsolation::SERIALIZABLE + || level == css::sdbc::TransactionIsolation::READ_UNCOMMITTED + || level == css::sdbc::TransactionIsolation::REPEATABLE_READ) + return true; + else + return false; +} + +sal_Bool DatabaseMetaData::supportsDataDefinitionAndDataManipulationTransactions( ) +{ + return true; +} + +sal_Bool DatabaseMetaData::supportsDataManipulationTransactionsOnly( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::dataDefinitionCausesTransactionCommit( ) +{ + return false; +} + +sal_Bool DatabaseMetaData::dataDefinitionIgnoredInTransactions( ) +{ + return false; +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getProcedures( + const css::uno::Any&, + const OUString&, + const OUString& ) +{ +// 1. PROCEDURE_CAT string => procedure catalog (may be NULL ) +// 2. PROCEDURE_SCHEM string => procedure schema (may be NULL ) +// 3. PROCEDURE_NAME string => procedure name +// 4. reserved for future use +// 5. reserved for future use +// 6. reserved for future use +// 7. REMARKS string => explanatory comment on the procedure +// 8. PROCEDURE_TYPE short => kind of procedure: +// * UNKNOWN - May return a result +// * NO - Does not return a result +// * RETURN - Returns a result + +// LEM TODO: implement +// LEM TODO: at least fake the columns, even if no row. + MutexGuard guard( m_xMutex->GetMutex() ); + return new SequenceResultSet( + m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > > (), m_pSettings->tc ); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getProcedureColumns( + const css::uno::Any&, + const OUString&, + const OUString&, + const OUString& ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); +// LEM TODO: implement +// LEM TODO: at least fake the columns, even if no row. + return new SequenceResultSet( + m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc ); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getTables( + const css::uno::Any&, + const OUString& schemaPattern, + const OUString& tableNamePattern, + const css::uno::Sequence< OUString >& ) +{ + Statics &statics = getStatics(); + + MutexGuard guard( m_xMutex->GetMutex() ); + + SAL_INFO("connectivity.postgresql", "DatabaseMetaData::getTables() got called with " << schemaPattern << "." << tableNamePattern); + + // ignore catalog, as a single pq connection does not support multiple catalogs + + // LEM TODO: this does not give the right column names, not the right number of columns, etc. + // Take "inspiration" from JDBC driver + // Ah, this is used to create a XResultSet manually... Try to do it directly in SQL + Reference< XPreparedStatement > statement = m_origin->prepareStatement( + "SELECT " + "DISTINCT ON (pg_namespace.nspname, relname ) " // avoid duplicates (pg_settings !) + "pg_namespace.nspname, relname, relkind, pg_description.description " + "FROM pg_namespace, pg_class LEFT JOIN pg_description ON pg_class.oid = pg_description.objoid " + "WHERE relnamespace = pg_namespace.oid " + "AND ( relkind = 'r' OR relkind = 'v') " + "AND pg_namespace.nspname LIKE ? " + "AND relname LIKE ? " +// "ORDER BY pg_namespace.nspname || relname" + ); + + Reference< XParameters > parameters( statement, UNO_QUERY_THROW ); + parameters->setString( 1 , schemaPattern ); + parameters->setString( 2 , tableNamePattern ); + + Reference< XResultSet > rs = statement->executeQuery(); + Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + std::vector< std::vector<Any> > vec; + + while( rs->next() ) + { + std::vector< Any > row( 5 ); + + row[0] <<= m_pSettings->catalog; + row[1] <<= xRow->getString( 1 ); + row[2] <<= xRow->getString( 2 ); + OUString type = xRow->getString(3); + if( type == "r" ) + { + if( xRow->getString(1) == "pg_catalog" ) + { + row[3] <<= statics.SYSTEM_TABLE; + } + else + { + row[3] <<= statics.TABLE; + } + } + else if( type == "v" ) + { + row[3] <<= statics.VIEW; + } + else + { + row[3] <<= statics.UNKNOWN; + } + row[4] <<= xRow->getString(4); + + // no description in postgresql AFAIK + vec.push_back( row ); + } + Reference< XCloseable > closeable( statement, UNO_QUERY ); + if( closeable.is() ) + closeable->close(); + + return new SequenceResultSet( + m_xMutex, *this, statics.tablesRowNames, vec, m_pSettings->tc ); +} + +namespace +{ + // sort no schema first, then "public", then normal schemas, then internal schemas + int compare_schema(const OUString &nsA, const OUString &nsB) + { + if (nsA.isEmpty()) + { + return nsB.isEmpty() ? 0 : -1; + } + else if (nsB.isEmpty()) + { + assert(!nsA.isEmpty()); + return 1; + } + else if(nsA == "public") + { + return (nsB == "public") ? 0 : -1; + } + else if(nsB == "public") + { + assert(nsA != "public"); + return 1; + } + else if(nsA.startsWith("pg_")) + { + if(nsB.startsWith("pg_")) + return nsA.compareTo(nsB); + else + return 1; + } + else if(nsB.startsWith("pg_")) + { + return -1; + } + else + { + return nsA.compareTo(nsB); + } + } + + struct SortInternalSchemasLastAndPublicFirst + { + bool operator () ( const std::vector< Any > & a, const std::vector< Any > & b ) + { + OUString valueA; + OUString valueB; + a[0] >>= valueA; + b[0] >>= valueB; + return compare_schema(valueA, valueB); + } + }; +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getSchemas( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + + SAL_INFO("connectivity.postgresql", "DatabaseMetaData::getSchemas() got called"); + + // <b>TABLE_SCHEM</b> string =&gt; schema name + Reference< XStatement > statement = m_origin->createStatement(); + Reference< XResultSet > rs = statement->executeQuery( + "SELECT nspname from pg_namespace" ); + // LEM TODO: look at JDBC driver and consider doing the same + // in particular, excluding temporary schemas, but maybe better through pg_is_other_temp_schema(oid) OR == pg_my_temp_schema() + + Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + std::vector< std::vector<Any> > vec; + while( rs->next() ) + { + std::vector<Any> row(1); + row[0] <<= xRow->getString(1); + vec.push_back( row ); + } + + // sort public first, sort internal schemas last, sort rest in alphabetic order + std::sort( vec.begin(), vec.end(), SortInternalSchemasLastAndPublicFirst() ); + + Reference< XCloseable > closeable( statement, UNO_QUERY ); + if( closeable.is() ) + closeable->close(); + return new SequenceResultSet( + m_xMutex, *this, getStatics().schemaNames, vec, m_pSettings->tc ); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getCatalogs( ) +{ + // LEM TODO: return the current catalog like JDBC driver? + // at least fake the columns, even if no content + MutexGuard guard( m_xMutex->GetMutex() ); + return new SequenceResultSet( + m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc ); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getTableTypes( ) +{ + // LEM TODO: this can be made dynamic, see JDBC driver + MutexGuard guard( m_xMutex->GetMutex() ); + return new SequenceResultSet( + m_xMutex, *this, getStatics().tableTypeNames, getStatics().tableTypeData, + m_pSettings->tc ); +} + + +/** returns the constant from sdbc.DataType + */ +sal_Int32 typeNameToDataType( const OUString &typeName, const OUString &typtype ) +{ +// sal_Int32 ret = css::sdbc::DataType::DISTINCT; + // map all unknown types to memo (longvarchar). This allows to show them in + // string representation. Additionally, the edit-table-type-selection-box + // is not so unusable anymore. + sal_Int32 ret = css::sdbc::DataType::LONGVARCHAR; + if( typtype == "b" ) + { + // as long as the OOo framework does not support arrays, + // the user is better of with interpreting arrays as strings ! +// if( typeName.getLength() && '_' == typeName[0] ) +// { +// it's just a naming convention, but as long as we don't have anything better, +// we take it as granted +// ret = css::sdbc::DataType::ARRAY; +// } + // base type + Statics &statics = getStatics(); + BaseTypeMap::const_iterator ii = statics.baseTypeMap.find( typeName ); + if( ii != statics.baseTypeMap.end() ) + { + ret = ii->second; + } + } + else if( typtype == "c" ) + { + ret = css::sdbc::DataType::STRUCT; + } + else if( typtype == "d" ) + { + ret = css::sdbc::DataType::LONGVARCHAR; + } + return ret; +} + +namespace { + bool isSystemColumn( sal_Int16 attnum ) + { + return attnum <= 0; + } + + // is not exported by the postgres header + const int PQ_VARHDRSZ = sizeof( sal_Int32 ); + + // Oh, quelle horreur + // LEM TODO: Need to severely rewrite that! + // should probably just "do the same" as ODBC or JDBC drivers... + void extractPrecisionAndScale( + sal_Int32 dataType, sal_Int32 atttypmod, sal_Int32 *precision, sal_Int32 *scale ) + { + if( atttypmod < PQ_VARHDRSZ ) + { + *precision = 0; + *scale = 0; + } + else + { + switch( dataType ) + { + case css::sdbc::DataType::NUMERIC: + case css::sdbc::DataType::DECIMAL: + { + *precision = ( ( atttypmod - PQ_VARHDRSZ ) >> 16 ) & 0xffff; + *scale = (atttypmod - PQ_VARHDRSZ ) & 0xffff; + break; + } + default: + *precision = atttypmod - PQ_VARHDRSZ; + *scale = 0; + } + } + } + + struct DatabaseTypeDescription + { + DatabaseTypeDescription() + {} + DatabaseTypeDescription( const OUString &name, const OUString & type ) : + typeName( name ), + typeType( type ) + {} + DatabaseTypeDescription( const DatabaseTypeDescription &source ) : + typeName( source.typeName ), + typeType( source.typeType ) + {} + DatabaseTypeDescription & operator = ( const DatabaseTypeDescription & source ) + { + typeName = source.typeName; + typeType = source.typeType; + return *this; + } + OUString typeName; + OUString typeType; + }; +} + +typedef std::unordered_map +< + sal_Int32, + DatabaseTypeDescription +> Oid2DatabaseTypeDescriptionMap; + +static void columnMetaData2DatabaseTypeDescription( + Oid2DatabaseTypeDescriptionMap &oidMap, + const Reference< XResultSet > &rs, + const Reference< XStatement > &stmt ) +{ + Reference< XRow > row( rs, UNO_QUERY_THROW ); + int domains = 0; + OUStringBuffer queryBuf(128); + queryBuf.append( "SELECT oid,typtype,typname FROM pg_TYPE WHERE " ); + while( rs->next() ) + { + if( row->getString( 9 ) == "d" && oidMap.find( row->getInt( 12 ) ) == oidMap.end() ) + { + oidMap[row->getInt(12)] = DatabaseTypeDescription(); + if( domains ) + queryBuf.append( " OR " ); + queryBuf.append( "oid = " ); + queryBuf.append( row->getInt(12 ) ); + domains ++; + } + } + rs->beforeFirst(); + + if( domains ) + { + Reference< XResultSet > rsDomain = stmt->executeQuery( queryBuf.makeStringAndClear() ); + row.set( rsDomain, UNO_QUERY_THROW ); + while( rsDomain->next() ) + { + oidMap[row->getInt(1)] = DatabaseTypeDescription(row->getString(3), row->getString(2) ); + } + disposeNoThrow( stmt ); + } + +} + + +css::uno::Reference< XResultSet > DatabaseMetaData::getColumns( + const css::uno::Any&, + const OUString& schemaPattern, + const OUString& tableNamePattern, + const OUString& columnNamePattern ) +{ + // LEM TODO: review in comparison with JDBC driver + Statics &statics = getStatics(); + + // continue ! + MutexGuard guard( m_xMutex->GetMutex() ); + + SAL_INFO("connectivity.postgresql", "DatabaseMetaData::getColumns() got called with " + << schemaPattern << "." << tableNamePattern << "." << columnNamePattern); + + // ignore catalog, as a single pq connection + // does not support multiple catalogs anyway + // We don't use information_schema.columns because it contains + // only the columns the current user has any privilege over. + + // 1. TABLE_CAT string => table catalog (may be NULL) + // => not supported + // 2. TABLE_SCHEM string => table schema (may be NULL) + // => pg_namespace.nspname + // 3. TABLE_NAME string => table name + // => pg_class.relname + // 4. COLUMN_NAME string => column name + // => pg_attribute.attname + // 5. DATA_TYPE short => SQL type from java.sql.Types + // => pg_type.typname => sdbc.DataType + // 6. TYPE_NAME string => Data source dependent type name, for a UDT the + // type name is fully qualified + // => pg_type.typname + // 7. COLUMN_SIZE long => column size. For char or date types this is + // the maximum number of characters, for numeric + // or decimal types this is precision. + // => pg_attribute.atttypmod + // 8. BUFFER_LENGTH is not used. + // => not used + // 9. DECIMAL_DIGITS long => the number of fractional digits + // => don't know ! TODO ! + // 10. NUM_PREC_RADIX long => Radix (typically either 10 or 2) + // => TODO ?? + // 11. NULLABLE long => is NULL allowed? + // NO_NULLS - might not allow NULL values + // NULABLE - definitely allows NULL values + // NULLABLE_UNKNOWN - nullability unknown + // => pg_attribute.attnotnull + // 12. REMARKS string => comment describing column (may be NULL ) + // => pg_description.description + // 13. COLUMN_DEF string => default value (may be NULL) + // => pg_type.typdefault + // 14. SQL_DATA_TYPE long => unused + // => empty + // 15. SQL_DATETIME_SUB long => unused + // => empty + // 16. CHAR_OCTET_LENGTH long => for char types the maximum number of + // bytes in the column + // => pg_type.typlen + // 17. ORDINAL_POSITION int => index of column in table (starting at 1) + // pg_attribute.attnum + // 18. IS_NULLABLE string => "NO" means column definitely does not allow + // NULL values; "YES" means the column might + // allow NULL values. An empty string means + // nobody knows. + // => pg_attribute.attnotnull + OUString strDefaultValue = getColExprForDefaultSettingVal(m_pSettings); + Reference< XPreparedStatement > statement = m_origin->prepareStatement( + "SELECT pg_namespace.nspname, " // 1 + "pg_class.relname, " // 2 + "pg_attribute.attname, " // 3 + "pg_type.typname, " // 4 + "pg_attribute.atttypmod, " // 5 + "pg_attribute.attnotnull, " // 6 + "pg_type.typdefault, " // 7 + "pg_type.typtype, " // 8 + + strDefaultValue + // 9 + ",pg_description.description, " // 10 + "pg_type.typbasetype, " // 11 + "pg_attribute.attnum " // 12 + "FROM pg_class, " + "pg_attribute LEFT JOIN pg_attrdef ON pg_attribute.attrelid = pg_attrdef.adrelid AND pg_attribute.attnum = pg_attrdef.adnum " + "LEFT JOIN pg_description ON pg_attribute.attrelid = pg_description.objoid AND pg_attribute.attnum=pg_description.objsubid," + " pg_type, pg_namespace " + "WHERE pg_attribute.attrelid = pg_class.oid " + "AND pg_attribute.atttypid = pg_type.oid " + "AND pg_class.relnamespace = pg_namespace.oid " + "AND NOT pg_attribute.attisdropped " + "AND pg_namespace.nspname LIKE ? " + "AND pg_class.relname LIKE ? " + "AND pg_attribute.attname LIKE ? " + "ORDER BY pg_namespace.nspname, pg_class.relname, pg_attribute.attnum" + ); + + Reference< XParameters > parameters( statement, UNO_QUERY_THROW ); + parameters->setString( 1 , schemaPattern ); + parameters->setString( 2 , tableNamePattern ); + parameters->setString( 3 , columnNamePattern ); + + Reference< XResultSet > rs = statement->executeQuery(); + Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + std::vector< std::vector<Any> > vec; + + Oid2DatabaseTypeDescriptionMap domainMap; + Reference< XStatement > domainTypeStmt = m_origin->createStatement(); + columnMetaData2DatabaseTypeDescription( domainMap, rs, domainTypeStmt ); + + sal_uInt32 colNum(0); + OUString sSchema( "#invalid#" ); + OUString sTable( "#invalid#" ); + + while( rs->next() ) + { + if( ! isSystemColumn( xRow->getShort( 12 ) ) ) + { + OUString sNewSchema( xRow->getString(1) ); + OUString sNewTable( xRow->getString(2) ); + if ( sNewSchema != sSchema || sNewTable != sTable ) + { + colNum = 1; + sSchema = sNewSchema; + sTable = sNewTable; + } + else + ++colNum; + sal_Int32 precision, scale, type; + std::vector< Any > row( 18 ); + row[0] <<= m_pSettings->catalog; + row[1] <<= sNewSchema; + row[2] <<= sNewTable; + row[3] <<= xRow->getString(3); + if( xRow->getString(8) == "d" ) + { + DatabaseTypeDescription desc( domainMap[xRow->getInt(11)] ); + type = typeNameToDataType( desc.typeName, desc.typeType ); + } + else + { + type = typeNameToDataType( xRow->getString(4), xRow->getString(8) ); + } + extractPrecisionAndScale( type, xRow->getInt(5) , &precision, &scale ); + row[4] <<= type; + row[5] <<= xRow->getString(4); + row[6] <<= precision; + // row[7] BUFFER_LENGTH not used + row[8] <<= scale; + // row[9] RADIX TODO + if( xRow->getBoolean( 6 ) && ! isSystemColumn(xRow->getInt( 12 )) ) + { + row[10] <<= OUString::number(css::sdbc::ColumnValue::NO_NULLS); + row[17] <<= statics.NO; + } + else + { + row[10] <<= OUString::number(css::sdbc::ColumnValue::NULLABLE); + row[17] <<= statics.YES; + } + + row[11] <<= xRow->getString( 10 ); // comment + row[12] <<= xRow->getString( 9 ); // COLUMN_DEF = pg_type.typdefault + // row[13] SQL_DATA_TYPE not used + // row[14] SQL_DATETIME_SUB not used + row[15] <<= precision; + row[16] <<= colNum ; + + vec.push_back( row ); + } + } + Reference< XCloseable > closeable( statement, UNO_QUERY ); + if( closeable.is() ) + closeable->close(); + + return new SequenceResultSet( + m_xMutex, *this, statics.columnRowNames, vec, m_pSettings->tc ); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getColumnPrivileges( + const css::uno::Any&, + const OUString& schema, + const OUString& table, + const OUString& columnNamePattern ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + + SAL_INFO("connectivity.postgresql", "DatabaseMetaData::getColumnPrivileges() got called with " + << schema << "." << table << "." << columnNamePattern); + + Reference< XParameters > parameters( m_getColumnPrivs_stmt, UNO_QUERY_THROW ); + parameters->setString( 1 , schema ); + parameters->setString( 2 , table ); + parameters->setString( 3 , columnNamePattern ); + + Reference< XResultSet > rs = m_getColumnPrivs_stmt->executeQuery(); + + return rs; +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getTablePrivileges( + const css::uno::Any&, + const OUString& schemaPattern, + const OUString& tableNamePattern ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + + SAL_INFO("connectivity.postgresql", "DatabaseMetaData::getTablePrivileges() got called with " + << schemaPattern << "." << tableNamePattern); + + Reference< XParameters > parameters( m_getTablePrivs_stmt, UNO_QUERY_THROW ); + parameters->setString( 1 , schemaPattern ); + parameters->setString( 2 , tableNamePattern ); + + Reference< XResultSet > rs = m_getTablePrivs_stmt->executeQuery(); + + return rs; +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getBestRowIdentifier( + const css::uno::Any&, + const OUString&, + const OUString&, + sal_Int32, + sal_Bool ) +{ + //LEM TODO: implement! See JDBC driver + MutexGuard guard( m_xMutex->GetMutex() ); + return new SequenceResultSet( + m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc ); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getVersionColumns( + const css::uno::Any&, + const OUString&, + const OUString& ) +{ + //LEM TODO: implement! See JDBC driver + MutexGuard guard( m_xMutex->GetMutex() ); + return new SequenceResultSet( + m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc ); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getPrimaryKeys( + const css::uno::Any&, + const OUString& schema, + const OUString& table ) +{ + //LEM TODO: review + MutexGuard guard( m_xMutex->GetMutex() ); + +// 1. TABLE_CAT string => table catalog (may be NULL ) +// 2. TABLE_SCHEM string => table schema (may be NULL ) +// 3. TABLE_NAME string => table name +// 4. COLUMN_NAME string => column name +// 5. KEY_SEQ short => sequence number within primary key +// 6. PK_NAME string => primary key name (may be NULL ) + + SAL_INFO("connectivity.postgresql", "DatabaseMetaData::getPrimaryKeys() got called with " + << schema << "." << table); + + Reference< XPreparedStatement > statement = m_origin->prepareStatement( + "SELECT nmsp.nspname, " + "cl.relname, " + "con.conkey, " + "con.conname, " + "con.conrelid " + "FROM pg_constraint as con,pg_class as cl, pg_namespace as nmsp " + "WHERE con.connamespace = nmsp.oid AND con.conrelid = cl.oid AND con.contype = 'p' " + "AND nmsp.nspname LIKE ? AND cl.relname LIKE ?" ); + + Reference< XParameters > parameters( statement, UNO_QUERY_THROW ); + parameters->setString( 1 , schema ); + parameters->setString( 2 , table ); + + Reference< XResultSet > rs = statement->executeQuery(); + Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + std::vector< std::vector<Any> > vec; + + while( rs->next() ) + { + std::vector< Any > row( 6 ); + row[0] <<= m_pSettings->catalog; + row[1] <<= xRow->getString(1); + row[2] <<= xRow->getString(2); + OUString array = xRow->getString(3); + row[4] <<= xRow->getString(5); // the relid + row[5] <<= xRow->getString(4); + + int i = 0; + // now retrieve the columns information + // unfortunately, postgresql does not allow array of variable size in + // WHERE clauses (in the default installation), so we have to choose + // this expensive and somewhat ugly way + // annotation: postgresql shouldn't have chosen an array here, instead they + // should have multiple rows per table + // LEM: to transform an array into several rows, see unnest; + // it is as simple as "SELECT foo, bar, unnest(qux) FROM ..." + // where qux is the column that contains an array. + while( array[i] && '}' != array[i] ) + { + i++; + int start = i; + while( array[i] && array[i] != '}' && array[i] != ',' ) i++; + row[3] <<= array.copy(start, i - start ); + vec.push_back( row ); + } + } + + { + Reference< XCloseable > closeable( statement, UNO_QUERY ); + if( closeable.is() ) + closeable->close(); + } + + + OUString lastTableOid; + sal_Int32 index = 0; + std::vector< std::vector< Any > > ret( vec.size() ); + int elements = 0; + for (auto const& elem : vec) + { + + std::vector< Any > row = elem; + OUString tableOid; + OUString attnum; + + row[4] >>= tableOid; + row[3] >>= attnum; + statement = m_origin->prepareStatement( + "SELECT att.attname FROM " + "pg_attribute AS att, pg_class AS cl WHERE " + "att.attrelid = ? AND att.attnum = ?" ); + + parameters.set( statement, UNO_QUERY_THROW ); + parameters->setString( 1 , tableOid ); + parameters->setString( 2 , attnum ); + + rs = statement->executeQuery(); + xRow.set( rs, UNO_QUERY_THROW ); + if( rs->next() ) + { + // column name + row[3] <<= xRow->getString( 1 ); + if( tableOid != lastTableOid ) + index = 1; + lastTableOid = tableOid; + row[4] <<= OUString::number( index ); + index ++; + } + { + Reference< XCloseable > closeable( statement, UNO_QUERY ); + if( closeable.is() ) + closeable->close(); + } + ret[elements] = row; + elements ++; + } + return new SequenceResultSet( + m_xMutex, *this, getStatics().primaryKeyNames, ret, m_pSettings->tc ); +} + +// Copied / adapted / simplified from JDBC driver +#define SQL_CASE_KEYRULE " WHEN 'c' THEN " SAL_STRINGIFY(KEYRULE_CASCADE) \ + " WHEN 'n' THEN " SAL_STRINGIFY(KEYRULE_SET_NULL) \ + " WHEN 'd' THEN " SAL_STRINGIFY(KEYRULE_SET_DEFAULT) \ + " WHEN 'r' THEN " SAL_STRINGIFY(KEYRULE_RESTRICT) \ + " WHEN 'a' THEN " SAL_STRINGIFY(KEYRULE_NO_ACTION) \ + " ELSE NULL " + +#define SQL_GET_REFERENCES \ + "WITH con AS (SELECT oid, conname, contype, condeferrable, condeferred, conrelid, confrelid, confupdtype, confdeltype, generate_subscripts(conkey,1) AS conkeyseq, unnest(conkey) AS conkey , unnest(confkey) AS confkey FROM pg_catalog.pg_constraint) " \ + "SELECT NULL::text AS PKTABLE_CAT, pkn.nspname AS PKTABLE_SCHEM, pkc.relname AS PKTABLE_NAME, pka.attname AS PKCOLUMN_NAME, " \ + " NULL::text AS FKTABLE_CAT, fkn.nspname AS FKTABLE_SCHEM, fkc.relname AS FKTABLE_NAME, fka.attname AS FKCOLUMN_NAME, " \ + " con.conkeyseq AS KEY_SEQ, " \ + " CASE con.confupdtype " \ + SQL_CASE_KEYRULE \ + " END AS UPDATE_RULE, " \ + " CASE con.confdeltype " \ + SQL_CASE_KEYRULE \ + " END AS DELETE_RULE, " \ + " con.conname AS FK_NAME, pkic.relname AS PK_NAME, " \ + " CASE " \ + " WHEN con.condeferrable AND con.condeferred THEN " SAL_STRINGIFY(DEFERRABILITY_INITIALLY_DEFERRED) \ + " WHEN con.condeferrable THEN " SAL_STRINGIFY(DEFERRABILITY_INITIALLY_IMMEDIATE) \ + " ELSE " SAL_STRINGIFY(DEFERRABILITY_NONE) \ + " END AS DEFERRABILITY " \ + "FROM " \ + " pg_catalog.pg_namespace pkn, pg_catalog.pg_class pkc, pg_catalog.pg_attribute pka, " \ + " pg_catalog.pg_namespace fkn, pg_catalog.pg_class fkc, pg_catalog.pg_attribute fka, " \ + " con, pg_catalog.pg_depend dep, pg_catalog.pg_class pkic " \ + "WHERE pkn.oid = pkc.relnamespace AND pkc.oid = pka.attrelid AND pka.attnum = con.confkey AND con.confrelid = pkc.oid " \ + " AND fkn.oid = fkc.relnamespace AND fkc.oid = fka.attrelid AND fka.attnum = con.conkey AND con.conrelid = fkc.oid " \ + " AND con.contype = 'f' AND con.oid = dep.objid AND pkic.oid = dep.refobjid AND pkic.relkind = 'i' AND dep.classid = 'pg_constraint'::regclass::oid AND dep.refclassid = 'pg_class'::regclass::oid " + +#define SQL_GET_REFERENCES_PSCHEMA " AND pkn.nspname = ? " +#define SQL_GET_REFERENCES_PTABLE " AND pkc.relname = ? " +#define SQL_GET_REFERENCES_FSCHEMA " AND fkn.nspname = ? " +#define SQL_GET_REFERENCES_FTABLE " AND fkc.relname = ? " +#define SQL_GET_REFERENCES_ORDER_SOME_PTABLE "ORDER BY fkn.nspname, fkc.relname, conkeyseq" +#define SQL_GET_REFERENCES_ORDER_NO_PTABLE "ORDER BY pkn.nspname, pkc.relname, conkeyseq" + +#define SQL_GET_REFERENCES_NONE_NONE_NONE_NONE \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_ORDER_NO_PTABLE + +#define SQL_GET_REFERENCES_SOME_NONE_NONE_NONE \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PSCHEMA \ + SQL_GET_REFERENCES_ORDER_NO_PTABLE + +#define SQL_GET_REFERENCES_NONE_SOME_NONE_NONE \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PTABLE \ + SQL_GET_REFERENCES_ORDER_SOME_PTABLE + +#define SQL_GET_REFERENCES_SOME_SOME_NONE_NONE \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PSCHEMA \ + SQL_GET_REFERENCES_PTABLE \ + SQL_GET_REFERENCES_ORDER_SOME_PTABLE + +#define SQL_GET_REFERENCES_NONE_NONE_SOME_NONE \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_FSCHEMA \ + SQL_GET_REFERENCES_ORDER_NO_PTABLE + +#define SQL_GET_REFERENCES_NONE_NONE_NONE_SOME \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_FTABLE \ + SQL_GET_REFERENCES_ORDER_NO_PTABLE + +#define SQL_GET_REFERENCES_NONE_NONE_SOME_SOME \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_FSCHEMA \ + SQL_GET_REFERENCES_FTABLE \ + SQL_GET_REFERENCES_ORDER_NO_PTABLE + +#define SQL_GET_REFERENCES_SOME_NONE_SOME_NONE \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PSCHEMA \ + SQL_GET_REFERENCES_FSCHEMA \ + SQL_GET_REFERENCES_ORDER_NO_PTABLE + +#define SQL_GET_REFERENCES_SOME_NONE_NONE_SOME \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PSCHEMA \ + SQL_GET_REFERENCES_FTABLE \ + SQL_GET_REFERENCES_ORDER_NO_PTABLE + +#define SQL_GET_REFERENCES_SOME_NONE_SOME_SOME \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PSCHEMA \ + SQL_GET_REFERENCES_FSCHEMA \ + SQL_GET_REFERENCES_FTABLE \ + SQL_GET_REFERENCES_ORDER_NO_PTABLE + +#define SQL_GET_REFERENCES_NONE_SOME_SOME_NONE \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PTABLE \ + SQL_GET_REFERENCES_FSCHEMA \ + SQL_GET_REFERENCES_ORDER_SOME_PTABLE + +#define SQL_GET_REFERENCES_NONE_SOME_NONE_SOME \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PTABLE \ + SQL_GET_REFERENCES_FTABLE \ + SQL_GET_REFERENCES_ORDER_SOME_PTABLE + +#define SQL_GET_REFERENCES_NONE_SOME_SOME_SOME \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PTABLE \ + SQL_GET_REFERENCES_FSCHEMA \ + SQL_GET_REFERENCES_FTABLE \ + SQL_GET_REFERENCES_ORDER_SOME_PTABLE + +#define SQL_GET_REFERENCES_SOME_SOME_SOME_NONE \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PSCHEMA \ + SQL_GET_REFERENCES_PTABLE \ + SQL_GET_REFERENCES_FSCHEMA \ + SQL_GET_REFERENCES_ORDER_SOME_PTABLE + +#define SQL_GET_REFERENCES_SOME_SOME_NONE_SOME \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PSCHEMA \ + SQL_GET_REFERENCES_PTABLE \ + SQL_GET_REFERENCES_FTABLE \ + SQL_GET_REFERENCES_ORDER_SOME_PTABLE + +#define SQL_GET_REFERENCES_SOME_SOME_SOME_SOME \ + SQL_GET_REFERENCES \ + SQL_GET_REFERENCES_PSCHEMA \ + SQL_GET_REFERENCES_PTABLE \ + SQL_GET_REFERENCES_FSCHEMA \ + SQL_GET_REFERENCES_FTABLE \ + SQL_GET_REFERENCES_ORDER_SOME_PTABLE + +void DatabaseMetaData::init_getReferences_stmt () +{ + m_getReferences_stmt[0] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_NONE_NONE_NONE); + m_getReferences_stmt[1] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_NONE_NONE_NONE); + m_getReferences_stmt[2] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_SOME_NONE_NONE); + m_getReferences_stmt[3] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_SOME_NONE_NONE); + m_getReferences_stmt[4] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_NONE_SOME_NONE); + m_getReferences_stmt[5] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_NONE_SOME_NONE); + m_getReferences_stmt[6] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_SOME_SOME_NONE); + m_getReferences_stmt[7] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_SOME_SOME_NONE); + m_getReferences_stmt[8] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_NONE_NONE_SOME); + m_getReferences_stmt[9] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_NONE_NONE_SOME); + m_getReferences_stmt[10] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_SOME_NONE_SOME); + m_getReferences_stmt[11] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_SOME_NONE_SOME); + m_getReferences_stmt[12] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_NONE_SOME_SOME); + m_getReferences_stmt[13] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_NONE_SOME_SOME); + m_getReferences_stmt[14] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_SOME_SOME_SOME); + m_getReferences_stmt[15] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_SOME_SOME_SOME); +} + +void DatabaseMetaData::init_getPrivs_stmt () +{ + OUStringBuffer sSQL(300); + sSQL.append( + " SELECT dp.TABLE_CAT, dp.TABLE_SCHEM, dp.TABLE_NAME, dp.GRANTOR, pr.rolname AS GRANTEE, dp.privilege, dp.is_grantable " + " FROM (" + " SELECT table_catalog AS TABLE_CAT, table_schema AS TABLE_SCHEM, table_name," + " grantor, grantee, privilege_type AS PRIVILEGE, is_grantable" + " FROM information_schema.table_privileges"); + if ( PQserverVersion( m_pSettings->pConnection ) < 90200 ) + // information_schema.table_privileges does not fill in default ACLs when no ACL + // assume default ACL is "owner has all privileges" and add it + sSQL.append( + " UNION " + " SELECT current_database() AS TABLE_CAT, pn.nspname AS TABLE_SCHEM, c.relname AS TABLE_NAME," + " ro.rolname AS GRANTOR, rg.rolname AS GRANTEE, p.privilege, 'YES' AS is_grantable" + " FROM pg_catalog.pg_class c," + " (VALUES ('SELECT'), ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) p (privilege)," + " pg_catalog.pg_roles ro," + " ( SELECT oid, rolname FROM pg_catalog.pg_roles" + " UNION ALL" + " VALUES (0::oid, 'PUBLIC')" + " ) AS rg (oid, rolname)," + " pg_catalog.pg_namespace pn" + " WHERE c.relkind IN ('r', 'v') AND c.relacl IS NULL AND pg_has_role(rg.oid, c.relowner, 'USAGE')" + " AND c.relowner=ro.oid AND c.relnamespace = pn.oid"); + sSQL.append( + " ) dp," + " (SELECT oid, rolname FROM pg_catalog.pg_roles UNION ALL VALUES (0, 'PUBLIC')) pr" + " WHERE table_schem LIKE ? AND table_name LIKE ? AND (dp.grantee = 'PUBLIC' OR pg_has_role(pr.oid, dp.grantee, 'USAGE'))" + " ORDER BY table_schem, table_name, privilege" ); + + m_getTablePrivs_stmt = m_origin->prepareStatement( sSQL.makeStringAndClear() ); + + sSQL.append( + " SELECT dp.TABLE_CAT, dp.TABLE_SCHEM, dp.TABLE_NAME, dp.COLUMN_NAME, dp.GRANTOR, pr.rolname AS GRANTEE, dp.PRIVILEGE, dp.IS_GRANTABLE FROM (" + " SELECT table_catalog AS TABLE_CAT, table_schema AS TABLE_SCHEM, table_name, column_name," + " grantor, grantee, privilege_type AS PRIVILEGE, is_grantable" + " FROM information_schema.column_privileges"); + if ( PQserverVersion( m_pSettings->pConnection ) < 90200 ) + // information_schema.table_privileges does not fill in default ACLs when no ACL + // assume default ACL is "owner has all privileges" and add it + sSQL.append( + " UNION " + " SELECT current_database() AS TABLE_CAT, pn.nspname AS TABLE_SCHEM, c.relname AS TABLE_NAME, a.attname AS column_name," + " ro.rolname AS GRANTOR, rg.rolname AS GRANTEE, p.privilege, 'YES' AS is_grantable" + " FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a," + " (VALUES ('SELECT'), ('INSERT'), ('UPDATE'), ('REFERENCES')) p (privilege)," + " pg_catalog.pg_roles ro," + " ( SELECT oid, rolname FROM pg_catalog.pg_roles" + " UNION ALL" + " VALUES (0::oid, 'PUBLIC')" + " ) AS rg (oid, rolname)," + " pg_catalog.pg_namespace pn" + " WHERE c.relkind IN ('r', 'v') AND c.relacl IS NULL AND pg_has_role(rg.oid, c.relowner, 'USAGE')" + " AND c.relowner=ro.oid AND c.relnamespace = pn.oid AND a.attrelid = c.oid AND a.attnum > 0"); + sSQL.append( + " ) dp," + " (SELECT oid, rolname FROM pg_catalog.pg_roles UNION ALL VALUES (0, 'PUBLIC')) pr" + " WHERE table_schem = ? AND table_name = ? AND column_name LIKE ? AND (dp.grantee = 'PUBLIC' OR pg_has_role(pr.oid, dp.grantee, 'USAGE'))" + " ORDER BY column_name, privilege" ); + + m_getColumnPrivs_stmt = m_origin->prepareStatement( sSQL.makeStringAndClear() ); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getImportedExportedKeys( + const Any& /* primaryCatalog */, + const OUString& primarySchema, + const OUString& primaryTable, + const Any& /* foreignCatalog */, + const OUString& foreignSchema, + const OUString& foreignTable ) +{ + unsigned int i = 0; + if ( ! primarySchema.isEmpty() ) + i |= 0x01; + if ( ! primaryTable.isEmpty() ) + i |= 0x02; + if ( ! foreignSchema.isEmpty() ) + i |= 0x04; + if ( ! foreignTable.isEmpty() ) + i |= 0x08; + + Reference< XPreparedStatement > stmt = m_getReferences_stmt[i]; + Reference< XParameters > param ( stmt, UNO_QUERY_THROW ); + + unsigned int j = 1; + if ( i & 0x01 ) + param->setString( j++, primarySchema ); + if ( i & 0x02 ) + param->setString( j++, primaryTable ); + if ( i & 0x04 ) + param->setString( j++, foreignSchema ); + if ( i & 0x08 ) + param->setString( j++, foreignTable ); + + Reference< XResultSet > rs = stmt->executeQuery(); + + return rs; +} + + +css::uno::Reference< XResultSet > DatabaseMetaData::getImportedKeys( + const css::uno::Any& catalog, + const OUString& schema, + const OUString& table ) +{ + return getImportedExportedKeys(Any(), OUString(), OUString(), catalog, schema, table); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getExportedKeys( + const css::uno::Any& catalog, + const OUString& schema, + const OUString& table ) +{ + return getImportedExportedKeys(catalog, schema, table, Any(), OUString(), OUString()); +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getCrossReference( + const css::uno::Any& primaryCatalog, + const OUString& primarySchema, + const OUString& primaryTable, + const css::uno::Any& foreignCatalog, + const OUString& foreignSchema, + const OUString& foreignTable ) +{ + return getImportedExportedKeys( primaryCatalog, primarySchema, primaryTable, foreignCatalog, foreignSchema, foreignTable ); +} + +namespace +{ + struct TypeInfoByDataTypeSorter + { + bool operator () ( const std::vector< Any > & a, const std::vector< Any > & b ) + { + OUString valueA; + OUString valueB; + a[1 /*DATA_TYPE*/] >>= valueA; + b[1 /*DATA_TYPE*/] >>= valueB; + if( valueB.toInt32() == valueA.toInt32() ) + { + OUString nameA; + OUString nameB; + a[0 /*TYPE_NAME*/] >>= nameA; + b[0 /*TYPE_NAME*/] >>= nameB; + OUString nsA, tnA, nsB, tnB; + + // parse typename into schema and typename + sal_Int32 nIndex=0; + nsA = nameA.getToken(0, '.', nIndex); + if (nIndex<0) + { + tnA = nsA; + nsA.clear(); + } + else + { + tnA = nameA.getToken(0, '.', nIndex); + assert(nIndex < 0); + } + + nIndex=0; + nsB = nameB.getToken(0, '.', nIndex); + if (nIndex<0) + { + tnB = nsB; + nsB.clear(); + } + else + { + tnB = nameB.getToken(0, '.', nIndex); + assert(nIndex < 0); + } + + const int ns_comp = compare_schema(nsA, nsB); + if(ns_comp == 0) + { + if(nsA.isEmpty()) + { + assert(nsB.isEmpty()); + // within each type category, sort privileged choice first + if( tnA == "int4" || tnA == "varchar" || tnA == "char" || tnA == "text") + return true; + if( tnB == "int4" || tnB == "varchar" || tnB == "char" || tnB == "text") + return false; + } + return nameA.compareTo( nameB ) < 0; + } + else + { + return ns_comp < 0; + } + } + + return valueA.toInt32() < valueB.toInt32(); + } + }; + + sal_Int32 calcSearchable( sal_Int32 dataType ) + { + sal_Int32 ret = css::sdbc::ColumnSearch::FULL; + if( css::sdbc::DataType::BINARY == dataType || + css::sdbc::DataType::VARBINARY == dataType || + css::sdbc::DataType::LONGVARBINARY == dataType ) + ret = css::sdbc::ColumnSearch::NONE; + + return ret; + } + + sal_Int32 getMaxScale( sal_Int32 dataType ) + { + // LEM TODO: review, see where used, see JDBC, ... + sal_Int32 ret = 0; + if( dataType == css::sdbc::DataType::NUMERIC ) + ret = 1000; // see pg-docs DataType/numeric +// else if( dataType == DataType::DOUBLE ) +// ret = 308; +// else if( dataType == DataType::FLOAT ) +// ret = + return ret; + } + + OUString construct_full_typename(const OUString &ns, const OUString &tn) + { + if(ns.isEmpty() || ns == "pg_catalog") + return tn; + else + return ns + "." + tn; + } + + void pgTypeInfo2ResultSet( + std::vector< std::vector<Any> > &vec, + const Reference< XResultSet > &rs ) + { + static const sal_Int32 TYPE_NAME = 0; // string Type name + static const sal_Int32 DATA_TYPE = 1; // short SQL data type from java.sql.Types + static const sal_Int32 PRECISION = 2; // long maximum precision + static const sal_Int32 CREATE_PARAMS = 5; // string => parameters used in creating the type (may be NULL ) + static const sal_Int32 NULLABLE = 6; // short ==> can you use NULL for this type? + // - NO_NULLS - does not allow NULL values + // - NULLABLE - allows NULL values + // - NULLABLE_UNKNOWN - nullability unknown + + static const sal_Int32 CASE_SENSITIVE = 7; // boolean==> is it case sensitive + static const sal_Int32 SEARCHABLE = 8; // short ==>; can you use + // "WHERE" based on this type: + // - NONE - No support + // - CHAR - Only supported with WHERE .. LIKE + // - BASIC - Supported except for WHERE .. LIKE + // - FULL - Supported for all WHERE .. + static const sal_Int32 UNSIGNED_ATTRIBUTE = 9; // boolean ==> is it unsigned? + // FIXED_PREC_SCALE = 10; boolean ==> can it be a money value? + static const sal_Int32 AUTO_INCREMENT = 11; // boolean ==> can it be used for + // an auto-increment value? + static const sal_Int32 MINIMUM_SCALE = 13; // short ==> minimum scale supported + static const sal_Int32 MAXIMUM_SCALE = 14; // short ==> maximum scale supported + static const sal_Int32 NUM_PREC_RADIX = 17; // long ==> usually 2 or 10 + + /* not filled so far + 3. LITERAL_PREFIX string ==> prefix used to quote a literal + (may be <NULL/>) + 4. LITERAL_SUFFIX string ==> suffix used to quote a literal + (may be <NULL/>) + 5. CREATE_PARAMS string ==> parameters used in creating the type (may be <NULL/>) + 12. LOCAL_TYPE_NAME string ==> localized version of type name (may be <NULL/>) + 15, SQL_DATA_TYPE long ==> unused + 16. SQL_DATETIME_SUB long ==> unused + */ + Reference< XRow > xRow( rs, UNO_QUERY_THROW ); + while( rs->next() ) + { + std::vector< Any > row(18); + + sal_Int32 dataType =typeNameToDataType(xRow->getString(5),xRow->getString(2)); + sal_Int32 precision = xRow->getString(3).toInt32(); + + if( dataType == css::sdbc::DataType::CHAR || + ( dataType == css::sdbc::DataType::VARCHAR && + xRow->getString(TYPE_NAME+1).equalsIgnoreAsciiCase("varchar") ) ) + { + // reflect varchar as varchar with upper limit ! + //NOTE: the sql spec requires varchar to have an upper limit, however + // in postgresql the upper limit is optional, no limit means unlimited + // length (=1GB). + precision = 0x40000000; // about 1 GB, see character type docs in postgresql + row[CREATE_PARAMS] <<= OUString("length"); + } + else if( dataType == css::sdbc::DataType::NUMERIC ) + { + precision = 1000; + row[CREATE_PARAMS] <<= OUString("length, scale"); + } + + row[TYPE_NAME] <<= construct_full_typename(xRow->getString(6), xRow->getString(1)); + row[DATA_TYPE] <<= OUString::number(dataType); + row[PRECISION] <<= OUString::number( precision ); + sal_Int32 nullable = xRow->getBoolean(4) ? + css::sdbc::ColumnValue::NO_NULLS : + css::sdbc::ColumnValue::NULLABLE; + row[NULLABLE] <<= OUString::number(nullable); + row[CASE_SENSITIVE] <<= OUString::number(1); + row[SEARCHABLE] <<= OUString::number( calcSearchable( dataType ) ); + row[UNSIGNED_ATTRIBUTE] <<= OUString("0"); + if( css::sdbc::DataType::INTEGER == dataType || + css::sdbc::DataType::BIGINT == dataType ) + row[AUTO_INCREMENT] <<= OUString("1"); // TODO + else + row[AUTO_INCREMENT] <<= OUString("0"); // TODO + row[MINIMUM_SCALE] <<= OUString("0"); // TODO: what is this ? + row[MAXIMUM_SCALE] <<= OUString::number( getMaxScale( dataType ) ); + row[NUM_PREC_RADIX] <<= OUString("10"); // TODO: what is this ? + vec.push_back( row ); + } + } +} + + +css::uno::Reference< XResultSet > DatabaseMetaData::getTypeInfo( ) +{ + // Note: Indexes start at 0 (in the API doc, they start at 1) + MutexGuard guard( m_xMutex->GetMutex() ); + + SAL_INFO("connectivity.postgresql", "DatabaseMetaData::getTypeInfo() got called"); + + Reference< XStatement > statement = m_origin->createStatement(); + Reference< XResultSet > rs = statement->executeQuery( + "SELECT pg_type.typname AS typname," //1 + "pg_type.typtype AS typtype," //2 + "pg_type.typlen AS typlen," //3 + "pg_type.typnotnull AS typnotnull," //4 + "pg_type.typname AS typname, " //5 + "pg_namespace.nspname as typns " //6 + "FROM pg_type LEFT JOIN pg_namespace ON pg_type.typnamespace=pg_namespace.oid " + "WHERE pg_type.typtype = 'b' " + "OR pg_type.typtype = 'p'" + ); + + std::vector< std::vector<Any> > vec; + pgTypeInfo2ResultSet( vec, rs ); + + // check for domain types + rs = statement->executeQuery( + "SELECT t1.typname as typname," + "t2.typtype AS typtype," + "t2.typlen AS typlen," + "t2.typnotnull AS typnotnull," + "t2.typname as realtypname, " + "pg_namespace.nspname as typns " + "FROM pg_type as t1 LEFT JOIN pg_type AS t2 ON t1.typbasetype=t2.oid LEFT JOIN pg_namespace ON t1.typnamespace=pg_namespace.oid " + "WHERE t1.typtype = 'd'" ); + pgTypeInfo2ResultSet( vec, rs ); + + std::sort( vec.begin(), vec.end(), TypeInfoByDataTypeSorter() ); + + return new SequenceResultSet( + m_xMutex, + *this, + getStatics().typeinfoColumnNames, + vec, + m_pSettings->tc, + &( getStatics().typeInfoMetaData )); +} + + +css::uno::Reference< XResultSet > DatabaseMetaData::getIndexInfo( + const css::uno::Any& , + const OUString& schema, + const OUString& table, + sal_Bool unique, + sal_Bool ) +{ + //LEM TODO: review + MutexGuard guard( m_xMutex->GetMutex() ); + + /* + 1. TABLE_CAT string -> table catalog (may be NULL ) + 2. TABLE_SCHEM string -> table schema (may be NULL ) + 3. TABLE_NAME string -> table name + 4. NON_UNIQUE boolean -> Can index values be non-unique? + false when TYPE is tableIndexStatistic + 5. INDEX_QUALIFIER string -> index catalog (may be NULL ); + NULL when TYPE is tableIndexStatistic + 6. INDEX_NAME string -> index name; NULL when TYPE is tableIndexStatistic + 7. TYPE short -> index type: + * 0 - this identifies table statistics that are returned + in conjunction with a table's index descriptions + * CLUSTERED - this is a clustered index + * HASHED - this is a hashed index + * OTHER - this is some other style of index + 8. ORDINAL_POSITION short -> column sequence number within index; + zero when TYPE is tableIndexStatistic + 9. COLUMN_NAME string -> column name; NULL when TYPE is tableIndexStatistic + 10. ASC_OR_DESC string -> column sort sequence, "A"= ascending, + "D" = descending, may be NULL if sort sequence + is not supported; NULL when TYPE is tableIndexStatistic + 11. CARDINALITY long -> When TYPE is tableIndexStatistic, then this is + the number of rows in the table; otherwise, it + is the number of unique values in the index. + 12. PAGES long -> When TYPE is tableIndexStatisic then this is + the number of pages used for the table, otherwise + it is the number of pages used for the current index. + 13. FILTER_CONDITION string -> Filter condition, if any. (may be NULL ) + + */ + static const sal_Int32 C_SCHEMA = 1; + static const sal_Int32 C_TABLENAME = 2; + static const sal_Int32 C_INDEXNAME = 3; + static const sal_Int32 C_IS_CLUSTERED = 4; + static const sal_Int32 C_IS_UNIQUE = 5; + // C_IS_PRIMARY = 6 + static const sal_Int32 C_COLUMNS = 7; + + static const sal_Int32 R_TABLE_SCHEM = 1; + static const sal_Int32 R_TABLE_NAME = 2; + static const sal_Int32 R_NON_UNIQUE = 3; + static const sal_Int32 R_INDEX_NAME = 5; + static const sal_Int32 R_TYPE = 6; + static const sal_Int32 R_ORDINAL_POSITION = 7; + static const sal_Int32 R_COLUMN_NAME = 8; + + Reference< XPreparedStatement > stmt = m_origin->prepareStatement( + "SELECT nspname, " // 1 + "pg_class.relname, " // 2 + "class2.relname, " // 3 + "indisclustered, " // 4 + "indisunique, " // 5 + "indisprimary, " // 6 + "indkey " // 7 + "FROM pg_index INNER JOIN pg_class ON indrelid = pg_class.oid " + "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid " + "INNER JOIN pg_class as class2 ON pg_index.indexrelid = class2.oid " + "WHERE nspname = ? AND pg_class.relname = ?" ); + + Reference< XParameters > param ( stmt, UNO_QUERY_THROW ); + param->setString( 1, schema ); + param->setString( 2, table ); + Reference< XResultSet > rs = stmt->executeQuery(); + Reference< XRow > xRow ( rs, UNO_QUERY_THROW ); + + std::vector< std::vector<Any> > vec; + while( rs->next() ) + { + std::vector< sal_Int32 > columns = parseIntArray( xRow->getString(C_COLUMNS) ); + Reference< XPreparedStatement > columnsStmt = m_origin->prepareStatement( + "SELECT attnum, attname " + "FROM pg_attribute " + " INNER JOIN pg_class ON attrelid = pg_class.oid " + " INNER JOIN pg_namespace ON pg_class.relnamespace=pg_namespace.oid " + " WHERE pg_namespace.nspname=? AND pg_class.relname=?" ); + Reference< XParameters > paramColumn ( columnsStmt, UNO_QUERY_THROW ); + OUString currentSchema = xRow->getString( C_SCHEMA ); + OUString currentTable = xRow->getString( C_TABLENAME ); + OUString currentIndexName = xRow->getString( C_INDEXNAME ); + bool isNonUnique = ! xRow->getBoolean( C_IS_UNIQUE ); + sal_Int32 indexType = xRow->getBoolean( C_IS_CLUSTERED ) ? + css::sdbc::IndexType::CLUSTERED : + css::sdbc::IndexType::HASHED; + + paramColumn->setString( C_SCHEMA, currentSchema ); + paramColumn->setString( C_TABLENAME, currentTable ); + + Reference< XResultSet > rsColumn = columnsStmt->executeQuery(); + Reference< XRow > rowColumn( rsColumn, UNO_QUERY_THROW ); + while( rsColumn->next() ) + { + auto findIt = std::find( columns.begin(), columns.end(), rowColumn->getInt( 1 ) ); + if( findIt != columns.end() && ( ! isNonUnique || ! unique ) ) + { + std::vector< Any > result( 13 ); + result[R_TABLE_SCHEM] <<= currentSchema; + result[R_TABLE_NAME] <<= currentTable; + result[R_INDEX_NAME] <<= currentIndexName; + result[R_NON_UNIQUE] <<= isNonUnique; + result[R_TYPE] <<= indexType; + result[R_COLUMN_NAME] <<= rowColumn->getString(2); + sal_Int32 nPos = static_cast<sal_Int32>(findIt - columns.begin() +1); // MSVC++ nonsense + result[R_ORDINAL_POSITION] <<= nPos; + vec.push_back( result ); + } + } + } + return new SequenceResultSet( + m_xMutex, *this, getStatics().indexinfoColumnNames, + vec, + m_pSettings->tc ); +} + +sal_Bool DatabaseMetaData::supportsResultSetType( sal_Int32 setType ) +{ + if ( setType == css::sdbc::ResultSetType::SCROLL_SENSITIVE ) + return false; + else + return true; +} + +sal_Bool DatabaseMetaData::supportsResultSetConcurrency( + sal_Int32 setType, sal_Int32 ) +{ + if ( ! supportsResultSetType( setType ) ) + return false; + else + return true; +} + +sal_Bool DatabaseMetaData::ownUpdatesAreVisible( sal_Int32 /* setType */ ) +{ + return true; +} + +sal_Bool DatabaseMetaData::ownDeletesAreVisible( sal_Int32 /* setType */ ) +{ + return true; +} + +sal_Bool DatabaseMetaData::ownInsertsAreVisible( sal_Int32 /* setType */ ) +{ + return true; +} + +sal_Bool DatabaseMetaData::othersUpdatesAreVisible( sal_Int32 /* setType */ ) +{ + return false; +} + +sal_Bool DatabaseMetaData::othersDeletesAreVisible( sal_Int32 /* setType */ ) +{ + return false; +} + +sal_Bool DatabaseMetaData::othersInsertsAreVisible( sal_Int32 /* setType */ ) +{ + return false; +} + +sal_Bool DatabaseMetaData::updatesAreDetected( sal_Int32 /* setType */ ) +{ + return false; +} + +sal_Bool DatabaseMetaData::deletesAreDetected( sal_Int32 /* setType */ ) +{ + return false; +} +sal_Bool DatabaseMetaData::insertsAreDetected( sal_Int32 /* setType */ ) +{ + return false; +} + +sal_Bool DatabaseMetaData::supportsBatchUpdates( ) +{ + return true; +} + +css::uno::Reference< XResultSet > DatabaseMetaData::getUDTs( const css::uno::Any&, const OUString&, const OUString&, const css::uno::Sequence< sal_Int32 >& ) +{ + //LEM TODO: implement! See JDBC driver + MutexGuard guard( m_xMutex->GetMutex() ); + return new SequenceResultSet( + m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc ); +} + +css::uno::Reference< css::sdbc::XConnection > DatabaseMetaData::getConnection() +{ + return m_origin; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_databasemetadata.hxx b/connectivity/source/drivers/postgresql/pq_databasemetadata.hxx new file mode 100644 index 000000000..c016886f2 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_databasemetadata.hxx @@ -0,0 +1,240 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_DATABASEMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_DATABASEMETADATA_HXX + +#include "pq_connection.hxx" +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace pq_sdbc_driver +{ + +class DatabaseMetaData : + public ::cppu::WeakImplHelper< css::sdbc::XDatabaseMetaData > +{ + ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + ConnectionSettings *m_pSettings; + css::uno::Reference< css::sdbc::XConnection > m_origin; + css::uno::Reference< css::sdbc::XPreparedStatement > m_getIntSetting_stmt; + css::uno::Reference< css::sdbc::XPreparedStatement > m_getReferences_stmt[16]; + css::uno::Reference< css::sdbc::XPreparedStatement > m_getTablePrivs_stmt; + css::uno::Reference< css::sdbc::XPreparedStatement > m_getColumnPrivs_stmt; + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + sal_Int32 getIntSetting(const OUString& settingName); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + sal_Int32 getMaxIndexKeys(); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + sal_Int32 getMaxNameLength(); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::sdbc::XResultSet > getImportedExportedKeys( + const css::uno::Any& primaryCatalog, const OUString& primarySchema, const OUString& primaryTable, + const css::uno::Any& foreignCatalog, const OUString& foreignSchema, const OUString& foreignTable ); + void init_getReferences_stmt (); + void init_getPrivs_stmt (); + +public: + DatabaseMetaData( + const ::rtl::Reference< comphelper::RefCountedMutex > & reMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings + ); + +public: + // Methods + virtual sal_Bool SAL_CALL allProceduresAreCallable( ) override; + virtual sal_Bool SAL_CALL allTablesAreSelectable( ) override; + virtual OUString SAL_CALL getURL( ) override; + virtual OUString SAL_CALL getUserName( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedHigh( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedLow( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtStart( ) override; + virtual sal_Bool SAL_CALL nullsAreSortedAtEnd( ) override; + virtual OUString SAL_CALL getDatabaseProductName( ) override; + virtual OUString SAL_CALL getDatabaseProductVersion( ) override; + virtual OUString SAL_CALL getDriverName( ) override; + virtual OUString SAL_CALL getDriverVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMajorVersion( ) override; + virtual sal_Int32 SAL_CALL getDriverMinorVersion( ) override; + virtual sal_Bool SAL_CALL usesLocalFiles( ) override; + virtual sal_Bool SAL_CALL usesLocalFilePerTable( ) override; + virtual sal_Bool SAL_CALL supportsMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesMixedCaseIdentifiers( ) override; + virtual sal_Bool SAL_CALL supportsMixedCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesUpperCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesLowerCaseQuotedIdentifiers( ) override; + virtual sal_Bool SAL_CALL storesMixedCaseQuotedIdentifiers( ) override; + virtual OUString SAL_CALL getIdentifierQuoteString( ) override; + virtual OUString SAL_CALL getSQLKeywords( ) override; + virtual OUString SAL_CALL getNumericFunctions( ) override; + virtual OUString SAL_CALL getStringFunctions( ) override; + virtual OUString SAL_CALL getSystemFunctions( ) override; + virtual OUString SAL_CALL getTimeDateFunctions( ) override; + virtual OUString SAL_CALL getSearchStringEscape( ) override; + virtual OUString SAL_CALL getExtraNameCharacters( ) override; + virtual sal_Bool SAL_CALL supportsAlterTableWithAddColumn( ) override; + virtual sal_Bool SAL_CALL supportsAlterTableWithDropColumn( ) override; + virtual sal_Bool SAL_CALL supportsColumnAliasing( ) override; + virtual sal_Bool SAL_CALL nullPlusNonNullIsNull( ) override; + virtual sal_Bool SAL_CALL supportsTypeConversion( ) override; + virtual sal_Bool SAL_CALL supportsConvert( sal_Int32 fromType, sal_Int32 toType ) override; + virtual sal_Bool SAL_CALL supportsTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsDifferentTableCorrelationNames( ) override; + virtual sal_Bool SAL_CALL supportsExpressionsInOrderBy( ) override; + virtual sal_Bool SAL_CALL supportsOrderByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupBy( ) override; + virtual sal_Bool SAL_CALL supportsGroupByUnrelated( ) override; + virtual sal_Bool SAL_CALL supportsGroupByBeyondSelect( ) override; + virtual sal_Bool SAL_CALL supportsLikeEscapeClause( ) override; + virtual sal_Bool SAL_CALL supportsMultipleResultSets( ) override; + virtual sal_Bool SAL_CALL supportsMultipleTransactions( ) override; + virtual sal_Bool SAL_CALL supportsNonNullableColumns( ) override; + virtual sal_Bool SAL_CALL supportsMinimumSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsCoreSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsExtendedSQLGrammar( ) override; + virtual sal_Bool SAL_CALL supportsANSI92EntryLevelSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92IntermediateSQL( ) override; + virtual sal_Bool SAL_CALL supportsANSI92FullSQL( ) override; + virtual sal_Bool SAL_CALL supportsIntegrityEnhancementFacility( ) override; + virtual sal_Bool SAL_CALL supportsOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsFullOuterJoins( ) override; + virtual sal_Bool SAL_CALL supportsLimitedOuterJoins( ) override; + virtual OUString SAL_CALL getSchemaTerm( ) override; + virtual OUString SAL_CALL getProcedureTerm( ) override; + virtual OUString SAL_CALL getCatalogTerm( ) override; + virtual sal_Bool SAL_CALL isCatalogAtStart( ) override; + virtual OUString SAL_CALL getCatalogSeparator( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInDataManipulation( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInTableDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsSchemasInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInDataManipulation( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInProcedureCalls( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInTableDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInIndexDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsCatalogsInPrivilegeDefinitions( ) override; + virtual sal_Bool SAL_CALL supportsPositionedDelete( ) override; + virtual sal_Bool SAL_CALL supportsPositionedUpdate( ) override; + virtual sal_Bool SAL_CALL supportsSelectForUpdate( ) override; + virtual sal_Bool SAL_CALL supportsStoredProcedures( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInComparisons( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInExists( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInIns( ) override; + virtual sal_Bool SAL_CALL supportsSubqueriesInQuantifieds( ) override; + virtual sal_Bool SAL_CALL supportsCorrelatedSubqueries( ) override; + virtual sal_Bool SAL_CALL supportsUnion( ) override; + virtual sal_Bool SAL_CALL supportsUnionAll( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossRollback( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossCommit( ) override; + virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossRollback( ) override; + virtual sal_Int32 SAL_CALL getMaxBinaryLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCharLiteralLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInGroupBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInIndex( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInOrderBy( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInSelect( ) override; + virtual sal_Int32 SAL_CALL getMaxColumnsInTable( ) override; + virtual sal_Int32 SAL_CALL getMaxConnections( ) override; + virtual sal_Int32 SAL_CALL getMaxCursorNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxIndexLength( ) override; + virtual sal_Int32 SAL_CALL getMaxSchemaNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxProcedureNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxCatalogNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxRowSize( ) override; + virtual sal_Bool SAL_CALL doesMaxRowSizeIncludeBlobs( ) override; + virtual sal_Int32 SAL_CALL getMaxStatementLength( ) override; + virtual sal_Int32 SAL_CALL getMaxStatements( ) override; + virtual sal_Int32 SAL_CALL getMaxTableNameLength( ) override; + virtual sal_Int32 SAL_CALL getMaxTablesInSelect( ) override; + virtual sal_Int32 SAL_CALL getMaxUserNameLength( ) override; + virtual sal_Int32 SAL_CALL getDefaultTransactionIsolation( ) override; + virtual sal_Bool SAL_CALL supportsTransactions( ) override; + virtual sal_Bool SAL_CALL supportsTransactionIsolationLevel( sal_Int32 level ) override; + virtual sal_Bool SAL_CALL supportsDataDefinitionAndDataManipulationTransactions( ) override; + virtual sal_Bool SAL_CALL supportsDataManipulationTransactionsOnly( ) override; + virtual sal_Bool SAL_CALL dataDefinitionCausesTransactionCommit( ) override; + virtual sal_Bool SAL_CALL dataDefinitionIgnoredInTransactions( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getProcedures( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getProcedureColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTables( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const css::uno::Sequence< OUString >& types ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getSchemas( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getCatalogs( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTableTypes( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumnPrivileges( const css::uno::Any& catalog, const OUString& schema, const OUString& table, const OUString& columnNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTablePrivileges( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getBestRowIdentifier( const css::uno::Any& catalog, const OUString& schema, const OUString& table, sal_Int32 scope, sal_Bool nullable ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getVersionColumns( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getPrimaryKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getImportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getExportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getCrossReference( const css::uno::Any& primaryCatalog, const OUString& primarySchema, const OUString& primaryTable, const css::uno::Any& foreignCatalog, const OUString& foreignSchema, const OUString& foreignTable ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTypeInfo( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getIndexInfo( const css::uno::Any& catalog, const OUString& schema, const OUString& table, sal_Bool unique, sal_Bool approximate ) override; + virtual sal_Bool SAL_CALL supportsResultSetType( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 concurrency ) override; + virtual sal_Bool SAL_CALL ownUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL ownInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersUpdatesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersDeletesAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL othersInsertsAreVisible( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL updatesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL deletesAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL insertsAreDetected( sal_Int32 setType ) override; + virtual sal_Bool SAL_CALL supportsBatchUpdates( ) override; + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getUDTs( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& typeNamePattern, const css::uno::Sequence< sal_Int32 >& types ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_driver.cxx b/connectivity/source/drivers/postgresql/pq_driver.cxx new file mode 100644 index 000000000..99571112c --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_driver.cxx @@ -0,0 +1,314 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <comphelper/processfactory.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implementationentry.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include "pq_driver.hxx" + +using osl::MutexGuard; + +using com::sun::star::lang::XSingleComponentFactory; +using com::sun::star::lang::XServiceInfo; +using com::sun::star::lang::XComponent; + +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Reference; +using com::sun::star::uno::XInterface; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::XComponentContext; +using com::sun::star::uno::Any; + +using com::sun::star::beans::PropertyValue; + +using com::sun::star::sdbc::XConnection; +using com::sun::star::sdbc::DriverPropertyInfo; + +using com::sun::star::sdbcx::XTablesSupplier; + + +namespace pq_sdbc_driver +{ + +static OUString DriverGetImplementationName() +{ + return "org.openoffice.comp.connectivity.pq.Driver.noext"; +} + +static Sequence< OUString > DriverGetSupportedServiceNames() +{ + return Sequence< OUString > { "com.sun.star.sdbc.Driver" }; +} + +Reference< XConnection > Driver::connect( + const OUString& url,const Sequence< PropertyValue >& info ) +{ + if( ! acceptsURL( url ) ) // XDriver spec tells me to do so ... + return Reference< XConnection > (); + + Sequence< Any > seq ( 2 ); + seq[0] <<= url; + seq[1] <<= info; + return Reference< XConnection> ( + m_smgr->createInstanceWithArgumentsAndContext( + "org.openoffice.comp.connectivity.pq.Connection.noext", + seq, m_ctx ), + UNO_QUERY ); +} + +sal_Bool Driver::acceptsURL( const OUString& url ) +{ + return url.startsWith( "sdbc:postgresql:" ); +} + +Sequence< DriverPropertyInfo > Driver::getPropertyInfo( + const OUString&,const Sequence< PropertyValue >& ) +{ + return Sequence< DriverPropertyInfo > (); +} + +sal_Int32 Driver::getMajorVersion( ) +{ + return PQ_SDBC_MAJOR; +} + + +sal_Int32 Driver::getMinorVersion( ) +{ + return PQ_SDBC_MINOR; +} + + // XServiceInfo +OUString SAL_CALL Driver::getImplementationName() +{ + return DriverGetImplementationName(); +} + +sal_Bool Driver::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > Driver::getSupportedServiceNames() +{ + return DriverGetSupportedServiceNames(); +} + +// XComponent +void Driver::disposing() +{ + +} + + +Reference< XTablesSupplier > Driver::getDataDefinitionByConnection( + const Reference< XConnection >& connection ) +{ + return Reference< XTablesSupplier >( connection , UNO_QUERY ); +} + +Reference< XTablesSupplier > Driver::getDataDefinitionByURL( + const OUString& url, const Sequence< PropertyValue >& info ) +{ + return Reference< XTablesSupplier > ( connect( url, info ), UNO_QUERY ); +} + + +static Reference< XInterface > DriverCreateInstance( const Reference < XComponentContext > & ctx ) +{ + Reference< XInterface > ret = * new Driver( ctx ); + return ret; +} + +namespace { + +class OOneInstanceComponentFactory : + public MutexHolder, + public cppu::WeakComponentImplHelper< XSingleComponentFactory, XServiceInfo > +{ +public: + OOneInstanceComponentFactory( + const OUString & rImplementationName_, + cppu::ComponentFactoryFunc fptr, + const Sequence< OUString > & serviceNames, + const Reference< XComponentContext > & defaultContext) : + cppu::WeakComponentImplHelper< XSingleComponentFactory, XServiceInfo >( m_mutex ), + m_create( fptr ), + m_serviceNames( serviceNames ), + m_implName( rImplementationName_ ), + m_defaultContext( defaultContext ) + { + } + + // XSingleComponentFactory + virtual Reference< XInterface > SAL_CALL createInstanceWithContext( + Reference< XComponentContext > const & xContext ) override; + virtual Reference< XInterface > SAL_CALL createInstanceWithArgumentsAndContext( + Sequence< Any > const & rArguments, + Reference< XComponentContext > const & xContext ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { + return m_implName; + } + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + Sequence< OUString > SAL_CALL getSupportedServiceNames() override + { + return m_serviceNames; + } + + // XComponent + virtual void SAL_CALL disposing() override; + +private: + cppu::ComponentFactoryFunc m_create; + Sequence< OUString > m_serviceNames; + OUString m_implName; + Reference< XInterface > m_theInstance; + Reference< XComponentContext > m_defaultContext; +}; + +} + +Reference< XInterface > OOneInstanceComponentFactory::createInstanceWithArgumentsAndContext( + Sequence< Any > const &, const Reference< XComponentContext > & ctx ) +{ + return createInstanceWithContext( ctx ); +} + +Reference< XInterface > OOneInstanceComponentFactory::createInstanceWithContext( + const Reference< XComponentContext > & ctx ) +{ + if( ! m_theInstance.is() ) + { + // work around the problem in sdbc + Reference< XComponentContext > useCtx = ctx; + if( ! useCtx.is() ) + useCtx = m_defaultContext; + Reference< XInterface > theInstance = m_create( useCtx ); + MutexGuard guard( osl::Mutex::getGlobalMutex() ); + if( ! m_theInstance.is () ) + { + m_theInstance = theInstance; + } + } + return m_theInstance; +} + +void OOneInstanceComponentFactory::disposing() +{ + Reference< XComponent > rComp; + { + MutexGuard guard( osl::Mutex::getGlobalMutex() ); + rComp.set( m_theInstance, UNO_QUERY ); + m_theInstance.clear(); + } + if( rComp.is() ) + rComp->dispose(); +} + +// Reference< XSingleComponentFactory > createOneInstanceComponentFactory( +// cppu::ComponentFactoryFunc fptr, +// OUString const & rImplementationName, +// css::uno::Sequence< OUString > const & rServiceNames, +// rtl_ModuleCount * pModCount = 0 ) +// +// { +// return new OOneInstanceComponentFactory( rImplementationName, fptr , rServiceNames); +// } + +} + +static const struct cppu::ImplementationEntry g_entries[] = +{ + { + pq_sdbc_driver::DriverCreateInstance, pq_sdbc_driver::DriverGetImplementationName, + pq_sdbc_driver::DriverGetSupportedServiceNames, nullptr, + nullptr , 0 + }, + { nullptr, nullptr, nullptr, nullptr, nullptr, 0 } +}; + +extern "C" +{ + +SAL_DLLPUBLIC_EXPORT void * postgresql_sdbc_component_getFactory( + const char * pImplName, void * pServiceManager, void * ) +{ + // need to extract the defaultcontext, because the way, sdbc + // bypasses the servicemanager, does not allow to use the + // XSingleComponentFactory interface ... + void * pRet = nullptr; + Reference< XSingleComponentFactory > xFactory; + Reference< css::lang::XMultiServiceFactory > xSmgr( + static_cast< XInterface * >(pServiceManager), + css::uno::UNO_QUERY_THROW ); + + for( sal_Int32 i = 0 ; g_entries[i].create ; i ++ ) + { + OUString implName = g_entries[i].getImplementationName(); + if( implName.equalsAscii( pImplName ) ) + { + Reference< XComponentContext > defaultContext( + comphelper::getComponentContext( xSmgr ) ); + xFactory = new pq_sdbc_driver::OOneInstanceComponentFactory( + implName, + g_entries[i].create, + g_entries[i].getSupportedServiceNames(), + defaultContext ); + } + } + + if( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + return pRet; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_driver.hxx b/connectivity/source/drivers/postgresql/pq_driver.hxx new file mode 100644 index 000000000..4cd0ee736 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_driver.hxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_DRIVER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_DRIVER_HXX + +#include <osl/mutex.hxx> +#include <sal/macros.h> +#include <cppuhelper/compbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <com/sun/star/sdbc/XDriver.hpp> +#include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp> + +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace pq_sdbc_driver +{ + +#define PQ_SDBC_DRIVER_VERSION SAL_STRINGIFY(PQ_SDBC_MAJOR) "." \ + SAL_STRINGIFY(PQ_SDBC_MINOR) "." \ + SAL_STRINGIFY(PQ_SDBC_MICRO) + +struct MutexHolder { osl::Mutex m_mutex; }; +// use this to switch off sdbc support ! +// typedef cppu::WeakComponentImplHelper< +// css::sdbc::XDriver, +// css::lang::XServiceInfo +// > DriverBase ; +typedef cppu::WeakComponentImplHelper< + css::sdbc::XDriver, + css::lang::XServiceInfo, + css::sdbcx::XDataDefinitionSupplier > DriverBase ; +class Driver : public MutexHolder, public DriverBase +{ + css::uno::Reference< css::uno::XComponentContext > m_ctx; + css::uno::Reference< css::lang::XMultiComponentFactory > m_smgr; + +public: + explicit Driver ( const css::uno::Reference < css::uno::XComponentContext > & ctx ) + : DriverBase( m_mutex ), + m_ctx( ctx ), + m_smgr( ctx->getServiceManager() ) + {} + +public: // XDriver + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL connect( + const OUString& url, + const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + + virtual sal_Bool SAL_CALL acceptsURL( const OUString& url ) override; + + virtual css::uno::Sequence< css::sdbc::DriverPropertyInfo > SAL_CALL getPropertyInfo( + const OUString& url, + const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + + virtual sal_Int32 SAL_CALL getMajorVersion( ) override; + virtual sal_Int32 SAL_CALL getMinorVersion( ) override; + +public: // XServiceInfo + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +public: // XDataDefinitionSupplier + virtual css::uno::Reference< css::sdbcx::XTablesSupplier > SAL_CALL + getDataDefinitionByConnection( + const css::uno::Reference< css::sdbc::XConnection >& connection ) override; + virtual css::uno::Reference< css::sdbcx::XTablesSupplier > SAL_CALL + getDataDefinitionByURL( + const OUString& url, + const css::uno::Sequence< css::beans::PropertyValue >& info ) override; + + // XComponent + virtual void SAL_CALL disposing() override; + +}; + + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_fakedupdateableresultset.cxx b/connectivity/source/drivers/postgresql/pq_fakedupdateableresultset.cxx new file mode 100644 index 000000000..a75897ccb --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_fakedupdateableresultset.cxx @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include "pq_fakedupdateableresultset.hxx" +#include <com/sun/star/sdbc/SQLException.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> + + +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; + +using com::sun::star::sdbc::SQLException; +using com::sun::star::sdbc::XResultSetUpdate; +using com::sun::star::sdbc::XRowUpdate; + +namespace pq_sdbc_driver +{ + +FakedUpdateableResultSet::FakedUpdateableResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + ConnectionSettings **pSettings, + PGresult *result, + const OUString &schema, + const OUString &table, + const OUString &aReason ) + : ResultSet( mutex, owner, pSettings, result, schema, table ), + m_aReason( aReason ) +{} + + +css::uno::Any FakedUpdateableResultSet::queryInterface( + const css::uno::Type & reqType ) +{ + Any ret = ResultSet::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< XResultSetUpdate * > ( this ), + static_cast< XRowUpdate * > ( this ) ); + return ret; +} + + +css::uno::Sequence< css::uno::Type > FakedUpdateableResultSet::getTypes() +{ + static cppu::OTypeCollection s_collection( + cppu::UnoType<XResultSetUpdate>::get(), + cppu::UnoType<XRowUpdate>::get(), + ResultSet::getTypes()); + + return s_collection.getTypes(); + +} + +css::uno::Sequence< sal_Int8> FakedUpdateableResultSet::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void FakedUpdateableResultSet::insertRow( ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateRow( ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::deleteRow( ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); + } + +void FakedUpdateableResultSet::cancelRowUpdates( ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::moveToInsertRow( ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::moveToCurrentRow( ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + + +void FakedUpdateableResultSet::updateNull( sal_Int32 /* columnIndex */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateBoolean( sal_Int32 /* columnIndex */, sal_Bool /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateByte( sal_Int32 /* columnIndex */, sal_Int8 /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateShort( sal_Int32 /* columnIndex */, sal_Int16 /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateInt( sal_Int32 /* columnIndex */, sal_Int32 /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateLong( sal_Int32 /* columnIndex */, sal_Int64 /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateFloat( sal_Int32 /* columnIndex */, float /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateDouble( sal_Int32 /* columnIndex */, double /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateString( sal_Int32 /* columnIndex */, const OUString& /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateBytes( sal_Int32 /* columnIndex */, const css::uno::Sequence< sal_Int8 >& /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateDate( sal_Int32 /* columnIndex */, const css::util::Date& /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateTime( sal_Int32 /* columnIndex */, const css::util::Time& /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateTimestamp( sal_Int32 /* columnIndex */, const css::util::DateTime& /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateBinaryStream( sal_Int32 /* columnIndex */, const css::uno::Reference< css::io::XInputStream >& /* x */, sal_Int32 /* length */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateCharacterStream( sal_Int32 /* columnIndex */, const css::uno::Reference< css::io::XInputStream >& /* x */, sal_Int32 /* length */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateObject( sal_Int32 /* columnIndex */, const css::uno::Any& /* x */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +void FakedUpdateableResultSet::updateNumericObject( sal_Int32 /* columnIndex */, const css::uno::Any& /* x */, sal_Int32 /* scale */ ) +{ + throw SQLException( m_aReason, *this, OUString(),1,Any() ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_fakedupdateableresultset.hxx b/connectivity/source/drivers/postgresql/pq_fakedupdateableresultset.hxx new file mode 100644 index 000000000..65113344c --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_fakedupdateableresultset.hxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_FAKEDUPDATEABLERESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_FAKEDUPDATEABLERESULTSET_HXX + +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> + +#include "pq_resultset.hxx" + +namespace pq_sdbc_driver +{ +/** necessary to avoid crashes in OOo, when an updateable result set is requested, + but cannot be delivered. + */ +class FakedUpdateableResultSet : + public ResultSet, + public css::sdbc::XResultSetUpdate, + public css::sdbc::XRowUpdate +{ + OUString m_aReason; + +public: + FakedUpdateableResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + ConnectionSettings **pSettings, + PGresult *result, + const OUString &schema, + const OUString &table, + const OUString &aReason ); + +public: // XInterface + virtual void SAL_CALL acquire() throw() override { ResultSet::acquire(); } + virtual void SAL_CALL release() throw() override { ResultSet::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XResultSetUpdate + virtual void SAL_CALL insertRow( ) override; + virtual void SAL_CALL updateRow( ) override; + virtual void SAL_CALL deleteRow( ) override; + virtual void SAL_CALL cancelRowUpdates( ) override; + virtual void SAL_CALL moveToInsertRow( ) override; + virtual void SAL_CALL moveToCurrentRow( ) override; + +public: // XRowUpdate + virtual void SAL_CALL updateNull( sal_Int32 columnIndex ) override; + virtual void SAL_CALL updateBoolean( sal_Int32 columnIndex, sal_Bool x ) override; + virtual void SAL_CALL updateByte( sal_Int32 columnIndex, sal_Int8 x ) override; + virtual void SAL_CALL updateShort( sal_Int32 columnIndex, sal_Int16 x ) override; + virtual void SAL_CALL updateInt( sal_Int32 columnIndex, sal_Int32 x ) override; + virtual void SAL_CALL updateLong( sal_Int32 columnIndex, sal_Int64 x ) override; + virtual void SAL_CALL updateFloat( sal_Int32 columnIndex, float x ) override; + virtual void SAL_CALL updateDouble( sal_Int32 columnIndex, double x ) override; + virtual void SAL_CALL updateString( sal_Int32 columnIndex, const OUString& x ) override; + virtual void SAL_CALL updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL updateDate( sal_Int32 columnIndex, const css::util::Date& x ) override; + virtual void SAL_CALL updateTime( sal_Int32 columnIndex, const css::util::Time& x ) override; + virtual void SAL_CALL updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL updateBinaryStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateCharacterStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateObject( sal_Int32 columnIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL updateNumericObject( sal_Int32 columnIndex, const css::uno::Any& x, sal_Int32 scale ) override; + +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_preparedstatement.cxx b/connectivity/source/drivers/postgresql/pq_preparedstatement.cxx new file mode 100644 index 000000000..2e3523203 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_preparedstatement.cxx @@ -0,0 +1,737 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/log.hxx> +#include "pq_preparedstatement.hxx" +#include "pq_tools.hxx" +#include "pq_statics.hxx" +#include "pq_statement.hxx" + +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> + + +#include <comphelper/sequence.hxx> + +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +#include <memory> +#include <string.h> + +#include <connectivity/dbconversion.hxx> + +using osl::MutexGuard; + + +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Reference; +using com::sun::star::uno::UNO_QUERY; + +using com::sun::star::lang::IllegalArgumentException; + +using com::sun::star::sdbc::XCloseable; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XRef; +using com::sun::star::sdbc::XBlob; +using com::sun::star::sdbc::XClob; +using com::sun::star::sdbc::XArray; +using com::sun::star::sdbc::XConnection; +using com::sun::star::sdbc::SQLException; + +using com::sun::star::beans::Property; +using com::sun::star::beans::XPropertySetInfo; + +using namespace dbtools; + +namespace pq_sdbc_driver +{ +static ::cppu::IPropertyArrayHelper & getPreparedStatementPropertyArrayHelper() +{ + static ::cppu::OPropertyArrayHelper arrayHelper( + Sequence<Property>{ + Property( + "CursorName", 0, + ::cppu::UnoType<OUString>::get() , 0 ), + Property( + "EscapeProcessing", 1, + cppu::UnoType<bool>::get() , 0 ), + Property( + "FetchDirection", 2, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "FetchSize", 3, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "MaxFieldSize", 4, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "MaxRows", 5, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "QueryTimeOut", 6, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "ResultSetConcurrency", 7, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "ResultSetType", 8, + ::cppu::UnoType<sal_Int32>::get() , 0 )}, + true ); + static ::cppu::IPropertyArrayHelper *pArrayHelper = &arrayHelper; + + return *pArrayHelper; +} + +static bool isOperator( char c ) +{ + static const char * const operators = "<>=()!/&%.,;"; + + const char * w = operators; + while (*w && *w != c) + { + ++w; + } + return *w != 0; +} + +static bool isNamedParameterStart( const OString & o , int index ) +{ + return o[index] == ':' && ( + isWhitespace( o[index-1] ) || isOperator(o[index-1]) ); +} + +static bool isQuoted( const OString & str ) +{ + return str[0] == '"' || str[0] == '\''; +} + +PreparedStatement::PreparedStatement( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< XConnection > & conn, + struct ConnectionSettings *pSettings, + const OString & stmt ) + : PreparedStatement_BASE(refMutex->GetMutex()) + , OPropertySetHelper(PreparedStatement_BASE::rBHelper) + , m_connection(conn) + , m_pSettings(pSettings) + , m_stmt(stmt) + , m_xMutex(refMutex) + , m_multipleResultAvailable(false) + , m_multipleResultUpdateCount(0) + , m_lastOidInserted( InvalidOid ) +{ + m_props[PREPARED_STATEMENT_QUERY_TIME_OUT] <<= sal_Int32(0); + m_props[PREPARED_STATEMENT_MAX_ROWS] <<= sal_Int32(0); + m_props[PREPARED_STATEMENT_RESULT_SET_CONCURRENCY] <<= + css::sdbc::ResultSetConcurrency::READ_ONLY; + m_props[PREPARED_STATEMENT_RESULT_SET_TYPE] <<= + css::sdbc::ResultSetType::SCROLL_INSENSITIVE; + + splitSQL( m_stmt, m_splittedStatement ); + int elements = 0; + for(const OString & str : m_splittedStatement) + { + // ignore quoted strings... + if( ! isQuoted( str ) ) + { + // the ':' cannot be the first or the last part of the + // token, + // the ? cannot be the first part of the token , so we start + // at one + for( int index = 1 ; index < str.getLength() ; index ++ ) + { + if( str[index] == '?' || + isNamedParameterStart( str , index ) + ) + { + elements ++; + } + } + } + } + m_vars = std::vector< OString >( elements ); +} + +PreparedStatement::~PreparedStatement() +{ +} + +void PreparedStatement::checkColumnIndex( sal_Int32 parameterIndex ) +{ + if( parameterIndex < 1 || parameterIndex > static_cast<sal_Int32>(m_vars.size()) ) + { + throw SQLException( + "pq_preparedstatement: parameter index out of range (expected 1 to " + + OUString::number( m_vars.size() ) + + ", got " + OUString::number( parameterIndex ) + + ", statement '" + OStringToOUString( m_stmt, ConnectionSettings::encoding ) + + "')", + *this, OUString(), 1, Any () ); + } +} +void PreparedStatement::checkClosed() +{ + if( ! m_pSettings || ! m_pSettings->pConnection ) + throw SQLException( + "pq_driver: PreparedStatement or connection has already been closed !", + *this, OUString(),1,Any()); +} + +Any PreparedStatement::queryInterface( const Type & rType ) +{ + Any aRet = PreparedStatement_BASE::queryInterface(rType); + return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType); +} + + +Sequence< Type > PreparedStatement::getTypes() +{ + static Sequence< Type > collection( + ::comphelper::concatSequences( + OPropertySetHelper::getTypes(), + PreparedStatement_BASE::getTypes())); + + return collection; +} + +Sequence< sal_Int8> PreparedStatement::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void PreparedStatement::close( ) +{ + // let the connection die without acquired mutex ! + Reference< XConnection > r; + Reference< XCloseable > resultSet; + { + MutexGuard guard( m_xMutex->GetMutex() ); + m_pSettings = nullptr; + r = m_connection; + m_connection.clear(); + + resultSet = m_lastResultset; + m_lastResultset.clear(); + } + if( resultSet.is() ) + { + resultSet->close(); + } +} + +void PreparedStatement::raiseSQLException( const char * errorMsg ) +{ + OUStringBuffer buf(128); + buf.append( "pq_driver: "); + buf.append( + OUString( errorMsg, strlen(errorMsg) , ConnectionSettings::encoding ) ); + buf.append( " (caused by statement '" ); + buf.appendAscii( m_executedStatement.getStr() ); + buf.append( "')" ); + OUString error = buf.makeStringAndClear(); + SAL_WARN("connectivity.postgresql", error); + throw SQLException( error, *this, OUString(), 1, Any() ); +} + +Reference< XResultSet > PreparedStatement::executeQuery( ) +{ + if( ! execute( ) ) + { + raiseSQLException( "not a query" ); + } + return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY ); +} + +sal_Int32 PreparedStatement::executeUpdate( ) +{ + if( execute( ) ) + { + raiseSQLException( "not a command" ); + } + return m_multipleResultUpdateCount; +} + +sal_Bool PreparedStatement::execute( ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + + OStringBuffer buf( m_stmt.getLength() *2 ); + + std::vector< OString >::size_type vars = 0; + for(const OString & str : m_splittedStatement) + { + // LEM TODO: instead of this manual mucking with SQL + // could we use PQexecParams / PQExecPrepared / ...? + // Only snafu is giving the types of the parameters and + // that it needs $1, $2, etc instead of "?" + +// printf( "Split %d %s\n" , i , str.getStr() ); + if( isQuoted( str ) ) + { + buf.append( str ); + } + else + { + int start = 0,index; + for( index = 1 ; index < str.getLength() ; index ++ ) + { + if( str[index] == '?' ) + { + buf.append( str.getStr()+start, index - start ); + buf.append( m_vars[vars] ); + vars ++; + start =index+1; + } + else + { + if ( isNamedParameterStart( str, index ) ) + { + buf.append( str.getStr()+start, index -start ); + buf.append( m_vars[vars] ); + + // skip to the end of the named parameter + while ( index < str.getLength() + && !( isWhitespace(str[index]) + || isOperator (str[index]))) + { + ++index; + } + start = index; + vars ++; + } + } + } +// if( index +1 >= str.getLength() ) +// { + buf.append( str.getStr() + start, index -start ); +// } + } + } + + m_executedStatement = buf.makeStringAndClear(); + + Reference< XCloseable > lastResultSet = m_lastResultset; + if( lastResultSet.is() ) + lastResultSet->close(); + + m_lastResultset.clear(); + m_lastTableInserted.clear(); + + struct CommandData data; + data.refMutex = m_xMutex; + data.ppSettings = &m_pSettings; + data.pLastOidInserted = &m_lastOidInserted; + data.pLastQuery = &m_lastQuery; + data.pMultipleResultUpdateCount = &m_multipleResultUpdateCount; + data.pMultipleResultAvailable = &m_multipleResultAvailable; + data.pLastTableInserted = &m_lastTableInserted; + data.pLastResultset = &m_lastResultset; + data.owner = *this; + data.tableSupplier.set( m_connection, UNO_QUERY ); + data.concurrency = extractIntProperty( this, getStatics().RESULT_SET_CONCURRENCY ); + + return executePostgresCommand( m_executedStatement , &data ); // see pq_statement.cxx +} + +Reference< XConnection > PreparedStatement::getConnection( ) +{ + Reference< XConnection > ret; + { + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + ret = m_connection; + } + return ret; +} + + +void PreparedStatement::setNull( sal_Int32 parameterIndex, sal_Int32 ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + m_vars[parameterIndex-1] = OString( "NULL" ); +} + +void PreparedStatement::setObjectNull( + sal_Int32 parameterIndex, sal_Int32, const OUString& ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + m_vars[parameterIndex-1] = OString( "NULL" ); +} + + +void PreparedStatement::setBoolean( sal_Int32 parameterIndex, sal_Bool x ) +{ + MutexGuard guard(m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + if( x ) + m_vars[parameterIndex-1] = OString( "'t'" ); + else + m_vars[parameterIndex-1] = OString( "'f'" ); +} + +void PreparedStatement::setByte( sal_Int32 parameterIndex, sal_Int8 x ) +{ + setInt(parameterIndex,x); +} + +void PreparedStatement::setShort( sal_Int32 parameterIndex, sal_Int16 x ) +{ + setInt(parameterIndex, x ); +} + +void PreparedStatement::setInt( sal_Int32 parameterIndex, sal_Int32 x ) +{ +// printf( "setString %d %d\n ", parameterIndex, x); + MutexGuard guard(m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + m_vars[parameterIndex-1] = "'" + OString::number(x) + "'"; +} + +void PreparedStatement::setLong( sal_Int32 parameterIndex, sal_Int64 x ) +{ + MutexGuard guard(m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + m_vars[parameterIndex-1] = "'" + OString::number(x) + "'"; +} + +void PreparedStatement::setFloat( sal_Int32 parameterIndex, float x ) +{ + MutexGuard guard(m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + m_vars[parameterIndex-1] = "'" + OString::number(x) + "'"; +} + +void PreparedStatement::setDouble( sal_Int32 parameterIndex, double x ) +{ + MutexGuard guard(m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + m_vars[parameterIndex-1] = "'" + OString::number(x) + "'"; +} + +void PreparedStatement::setString( sal_Int32 parameterIndex, const OUString& x ) +{ +// printf( "setString %d %s\n ", parameterIndex, +// OUStringToOString( x , RTL_TEXTENCODING_ASCII_US ).getStr()); + MutexGuard guard(m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + OStringBuffer buf( 20 ); + buf.append( "'" ); + OString y = OUStringToOString( x, ConnectionSettings::encoding ); + buf.ensureCapacity( y.getLength() * 2 + 2 ); + int len = PQescapeString( const_cast<char*>(buf.getStr())+1, y.getStr() , y.getLength() ); + buf.setLength( 1 + len ); + buf.append( "'" ); + m_vars[parameterIndex-1] = buf.makeStringAndClear(); +} + +void PreparedStatement::setBytes( + sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x ) +{ + MutexGuard guard(m_xMutex->GetMutex() ); + checkClosed(); + checkColumnIndex( parameterIndex ); + size_t len; + struct Free { void operator ()(void * p) const { free(p); } }; + std::unique_ptr<unsigned char, Free> escapedString( + PQescapeBytea( reinterpret_cast<unsigned char const *>(x.getConstArray()), x.getLength(), &len)); + if( ! escapedString ) + { + throw SQLException( + "pq_preparedstatement.setBytes: Error during converting bytesequence to an SQL conform string", + *this, OUString(), 1, Any() ); + } + m_vars[parameterIndex-1] + = "'" + rtl::OStringView(reinterpret_cast<char *>(escapedString.get()), len -1) + "'"; +} + + +void PreparedStatement::setDate( sal_Int32 parameterIndex, const css::util::Date& x ) +{ + setString( parameterIndex, DBTypeConversion::toDateString( x ) ); +} + +void PreparedStatement::setTime( sal_Int32 parameterIndex, const css::util::Time& x ) +{ + setString( parameterIndex, DBTypeConversion::toTimeString( x ) ); +} + +void PreparedStatement::setTimestamp( + sal_Int32 parameterIndex, const css::util::DateTime& x ) +{ + setString( parameterIndex, DBTypeConversion::toDateTimeString( x ) ); +} + +void PreparedStatement::setBinaryStream( + sal_Int32, + const Reference< css::io::XInputStream >&, + sal_Int32 ) +{ + throw SQLException( + "pq_preparedstatement: setBinaryStream not implemented", + *this, OUString(), 1, Any () ); +} + +void PreparedStatement::setCharacterStream( + sal_Int32, + const Reference< css::io::XInputStream >&, + sal_Int32 ) +{ + throw SQLException( + "pq_preparedstatement: setCharacterStream not implemented", + *this, OUString(), 1, Any () ); +} + +void PreparedStatement::setObject( sal_Int32 parameterIndex, const Any& x ) +{ + if( ! implSetObject( this, parameterIndex, x )) + { + throw SQLException( + "pq_preparedstatement::setObject: can't convert value of type " + x.getValueTypeName(), + *this, OUString(), 1, Any () ); + } +} + +void PreparedStatement::setObjectWithInfo( + sal_Int32 parameterIndex, + const Any& x, + sal_Int32 targetSqlType, + sal_Int32 ) +{ + if( css::sdbc::DataType::DECIMAL == targetSqlType || + css::sdbc::DataType::NUMERIC == targetSqlType ) + { + double myDouble = 0.0; + OUString myString; + if( x >>= myDouble ) + { + myString = OUString::number( myDouble ); + } + else + { + x >>= myString; + } + if( myString.isEmpty() ) + { + throw SQLException( + "pq_preparedstatement::setObjectWithInfo: can't convert value of type " + + x.getValueTypeName() + " to type DECIMAL or NUMERIC", + *this, OUString(), 1, Any () ); + } + + setString( parameterIndex, myString ); + } + else + { + setObject( parameterIndex, x ); + } + +} + +void PreparedStatement::setRef( + sal_Int32, + const Reference< XRef >& ) +{ + throw SQLException( + "pq_preparedstatement: setRef not implemented", + *this, OUString(), 1, Any () ); +} + +void PreparedStatement::setBlob( + sal_Int32, + const Reference< XBlob >& ) +{ + throw SQLException( + "pq_preparedstatement: setBlob not implemented", + *this, OUString(), 1, Any () ); +} + +void PreparedStatement::setClob( + sal_Int32, + const Reference< XClob >& ) +{ + throw SQLException( + "pq_preparedstatement: setClob not implemented", + *this, OUString(), 1, Any () ); +} + +void PreparedStatement::setArray( + sal_Int32 parameterIndex, + const Reference< XArray >& x ) +{ + setString( parameterIndex, array2String( x->getArray( nullptr ) ) ); +} + +void PreparedStatement::clearParameters( ) +{ + MutexGuard guard(m_xMutex->GetMutex() ); + m_vars = std::vector< OString >( m_vars.size() ); +} + +Any PreparedStatement::getWarnings( ) +{ + return Any(); +} + +void PreparedStatement::clearWarnings( ) +{ +} + +Reference< css::sdbc::XResultSetMetaData > PreparedStatement::getMetaData() +{ + Reference< css::sdbc::XResultSetMetaData > ret; + Reference< css::sdbc::XResultSetMetaDataSupplier > supplier( m_lastResultset, UNO_QUERY ); + if( supplier.is() ) + ret = supplier->getMetaData(); + return ret; +} + +::cppu::IPropertyArrayHelper & PreparedStatement::getInfoHelper() +{ + return getPreparedStatementPropertyArrayHelper(); +} + + +sal_Bool PreparedStatement::convertFastPropertyValue( + Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) +{ + bool bRet; + rOldValue = m_props[nHandle]; + switch( nHandle ) + { + case PREPARED_STATEMENT_CURSOR_NAME: + { + OUString val; + bRet = ( rValue >>= val ); + rConvertedValue <<= val; + break; + } + case PREPARED_STATEMENT_ESCAPE_PROCESSING: + { + bool val(false); + bRet = ( rValue >>= val ); + rConvertedValue <<= val; + break; + } + case PREPARED_STATEMENT_FETCH_DIRECTION: + case PREPARED_STATEMENT_FETCH_SIZE: + case PREPARED_STATEMENT_MAX_FIELD_SIZE: + case PREPARED_STATEMENT_MAX_ROWS: + case PREPARED_STATEMENT_QUERY_TIME_OUT: + case PREPARED_STATEMENT_RESULT_SET_CONCURRENCY: + case PREPARED_STATEMENT_RESULT_SET_TYPE: + { + sal_Int32 val; + bRet = ( rValue >>= val ); + rConvertedValue <<= val; + break; + } + default: + { + throw IllegalArgumentException( + "pq_statement: Invalid property handle (" + + OUString::number( nHandle ) + ")", + *this, 2 ); + } + } + return bRet; +} + + +void PreparedStatement::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle,const Any& rValue ) +{ + m_props[nHandle] = rValue; +} + +void PreparedStatement::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + rValue = m_props[nHandle]; +} + +Reference < XPropertySetInfo > PreparedStatement::getPropertySetInfo() +{ + return OPropertySetHelper::createPropertySetInfo( getPreparedStatementPropertyArrayHelper() ); +} + +void PreparedStatement::disposing() +{ + close(); +} + + +Reference< XResultSet > PreparedStatement::getResultSet( ) +{ + return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY ); +} +sal_Int32 PreparedStatement::getUpdateCount( ) +{ + return m_multipleResultUpdateCount; +} +sal_Bool PreparedStatement::getMoreResults( ) +{ + Reference< XCloseable > lastResultSet = m_lastResultset; + if( lastResultSet.is() ) + lastResultSet->close(); + m_multipleResultUpdateCount = -1; + return false; +} + +Reference< XResultSet > PreparedStatement::getGeneratedValues( ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + return getGeneratedValuesFromLastInsert( + m_pSettings, m_connection, m_lastOidInserted, m_lastTableInserted, m_lastQuery ); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_preparedstatement.hxx b/connectivity/source/drivers/postgresql/pq_preparedstatement.hxx new file mode 100644 index 000000000..01eb2441f --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_preparedstatement.hxx @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_PREPAREDSTATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_PREPAREDSTATEMENT_HXX +#include <vector> + +#include <libpq-fe.h> + +#include <cppuhelper/propshlp.hxx> +#include <cppuhelper/component.hxx> + +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XMultipleResults.hpp> +#include <com/sun/star/sdbc/XGeneratedResultSet.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> + +#include "pq_connection.hxx" +namespace pq_sdbc_driver +{ + +static const sal_Int32 PREPARED_STATEMENT_CURSOR_NAME = 0; +static const sal_Int32 PREPARED_STATEMENT_ESCAPE_PROCESSING = 1; +static const sal_Int32 PREPARED_STATEMENT_FETCH_DIRECTION = 2; +static const sal_Int32 PREPARED_STATEMENT_FETCH_SIZE = 3; +static const sal_Int32 PREPARED_STATEMENT_MAX_FIELD_SIZE = 4; +static const sal_Int32 PREPARED_STATEMENT_MAX_ROWS = 5; +static const sal_Int32 PREPARED_STATEMENT_QUERY_TIME_OUT = 6; +static const sal_Int32 PREPARED_STATEMENT_RESULT_SET_CONCURRENCY = 7; +static const sal_Int32 PREPARED_STATEMENT_RESULT_SET_TYPE = 8; + +#define PREPARED_STATEMENT_SIZE 9 + +typedef ::cppu::WeakComponentImplHelper< css::sdbc::XPreparedStatement, + css::sdbc::XParameters, + css::sdbc::XCloseable, + css::sdbc::XWarningsSupplier, + css::sdbc::XMultipleResults, + css::sdbc::XGeneratedResultSet, + css::sdbc::XResultSetMetaDataSupplier + > PreparedStatement_BASE; +class PreparedStatement : public PreparedStatement_BASE, + public cppu::OPropertySetHelper +{ +private: + css::uno::Any m_props[PREPARED_STATEMENT_SIZE]; + css::uno::Reference< css::sdbc::XConnection > m_connection; + ConnectionSettings *m_pSettings; + css::uno::Reference< css::sdbc::XCloseable > m_lastResultset; + OString m_stmt; + OString m_executedStatement; + ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + std::vector< OString > m_vars; + std::vector< OString > m_splittedStatement; + bool m_multipleResultAvailable; + sal_Int32 m_multipleResultUpdateCount; + sal_Int32 m_lastOidInserted; + OUString m_lastTableInserted; + OString m_lastQuery; + +public: + /** + * @param ppConnection The piece of memory, pConnection points to, is accessible + * as long as a reference to parameter con is held. + */ + PreparedStatement( const rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection> & con, + struct ConnectionSettings *pSettings, + const OString &stmt ); + + virtual ~PreparedStatement() override; +public: // XInterface + virtual void SAL_CALL acquire() throw() override { PreparedStatement_BASE::acquire(); } + virtual void SAL_CALL release() throw() override { PreparedStatement_BASE::release(); } + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & reqType ) override; + +public: // XCloseable + virtual void SAL_CALL close( ) override; + +public: // XPreparedStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery() override; + virtual sal_Int32 SAL_CALL executeUpdate( ) override; + virtual sal_Bool SAL_CALL execute( ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; +public: // XParameters + virtual void SAL_CALL setNull( sal_Int32 parameterIndex, sal_Int32 sqlType ) override; + virtual void SAL_CALL setObjectNull( + sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) override; + virtual void SAL_CALL setBoolean( sal_Int32 parameterIndex, sal_Bool x ) override; + virtual void SAL_CALL setByte( sal_Int32 parameterIndex, sal_Int8 x ) override; + virtual void SAL_CALL setShort( sal_Int32 parameterIndex, sal_Int16 x ) override; + virtual void SAL_CALL setInt( sal_Int32 parameterIndex, sal_Int32 x ) override; + virtual void SAL_CALL setLong( sal_Int32 parameterIndex, sal_Int64 x ) override; + virtual void SAL_CALL setFloat( sal_Int32 parameterIndex, float x ) override; + virtual void SAL_CALL setDouble( sal_Int32 parameterIndex, double x ) override; + virtual void SAL_CALL setString( sal_Int32 parameterIndex, const OUString& x ) override; + virtual void SAL_CALL setBytes( + sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL setDate( sal_Int32 parameterIndex, const css::util::Date& x ) override; + virtual void SAL_CALL setTime( sal_Int32 parameterIndex, const css::util::Time& x ) override; + virtual void SAL_CALL setTimestamp( + sal_Int32 parameterIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL setBinaryStream( + sal_Int32 parameterIndex, + const css::uno::Reference< css::io::XInputStream >& x, + sal_Int32 length ) override; + virtual void SAL_CALL setCharacterStream( + sal_Int32 parameterIndex, + const css::uno::Reference< css::io::XInputStream >& x, + sal_Int32 length ) override; + virtual void SAL_CALL setObject( sal_Int32 parameterIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL setObjectWithInfo( + sal_Int32 parameterIndex, + const css::uno::Any& x, + sal_Int32 targetSqlType, + sal_Int32 scale ) override; + virtual void SAL_CALL setRef( + sal_Int32 parameterIndex, + const css::uno::Reference< css::sdbc::XRef >& x ) override; + virtual void SAL_CALL setBlob( + sal_Int32 parameterIndex, + const css::uno::Reference< css::sdbc::XBlob >& x ) override; + virtual void SAL_CALL setClob( + sal_Int32 parameterIndex, + const css::uno::Reference< css::sdbc::XClob >& x ) override; + virtual void SAL_CALL setArray( + sal_Int32 parameterIndex, + const css::uno::Reference< css::sdbc::XArray >& x ) override; + virtual void SAL_CALL clearParameters( ) override; + +public: // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // OPropertySetHelper + virtual cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + using ::cppu::OPropertySetHelper::getFastPropertyValue; + + void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle ) const override; + + // XPropertySet + css::uno::Reference < css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + +public: // XGeneratedResultSet + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL + getGeneratedValues( ) override; + +public: // XMultipleResults + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getResultSet( ) override; + virtual sal_Int32 SAL_CALL getUpdateCount( ) override; + virtual sal_Bool SAL_CALL getMoreResults( ) override; + +public: // XResultSetMetaDataSupplier (is required by framework (see + // dbaccess/source/core/api/preparedstatement.cxx::getMetaData() ) + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + +public: // OComponentHelper + virtual void SAL_CALL disposing() override; + +private: + void checkColumnIndex( sal_Int32 parameterIndex ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkClosed(); + /// @throws css::sdbc::SQLException + void raiseSQLException( const char * errorMsg ); +// PGresult *pgExecute( OString *pQuery ); +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_resultset.cxx b/connectivity/source/drivers/postgresql/pq_resultset.cxx new file mode 100644 index 000000000..556bae92d --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_resultset.cxx @@ -0,0 +1,308 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include "pq_resultset.hxx" +#include "pq_resultsetmetadata.hxx" + +#include <connectivity/dbexception.hxx> + +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + + +using osl::MutexGuard; + +using com::sun::star::uno::Any; +using com::sun::star::uno::Reference; +using com::sun::star::uno::XInterface; + +using com::sun::star::sdbc::SQLException; +using com::sun::star::sdbc::XResultSetMetaData; + + +namespace pq_sdbc_driver +{ + +void ResultSet::checkClosed() +{ + if( ! m_result ) + { + throw SQLException( "pq_resultset: already closed", + *this, OUString(), 1, Any() ); + } + + if( ! m_ppSettings || ! *m_ppSettings || ! (*m_ppSettings)->pConnection ) + { + throw SQLException( "pq_resultset: statement has been closed already", + *this, OUString(), 1, Any() ); + } + +} + +ResultSet::ResultSet( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< XInterface > & owner, + ConnectionSettings **ppSettings, + PGresult * result, + const OUString &schema, + const OUString &table) + : BaseResultSet( + refMutex, owner, PQntuples( result ), + PQnfields( result ),(*ppSettings)->tc ), + m_result( result ), + m_schema( schema ), + m_table( table ), + m_ppSettings( ppSettings ) +{ + // LEM TODO: shouldn't these things be inherited from the statement or something like that? + // Positioned update/delete not supported, so no cursor name + // Fetch direction and size are cursor-specific things, so not used now. + // Fetch size not set + m_props[ BASERESULTSET_FETCH_DIRECTION ] <<= css::sdbc::FetchDirection::UNKNOWN; + // No escape processing for now + m_props[ BASERESULTSET_ESCAPE_PROCESSING ] <<= false; + // Bookmarks not implemented for now + m_props[ BASERESULTSET_IS_BOOKMARKABLE ] <<= false; + m_props[ BASERESULTSET_RESULT_SET_CONCURRENCY ] <<= css::sdbc::ResultSetConcurrency::READ_ONLY; + m_props[ BASERESULTSET_RESULT_SET_TYPE ] <<= css::sdbc::ResultSetType::SCROLL_INSENSITIVE; +} + + +Any ResultSet::getValue( sal_Int32 columnIndex ) +{ + Any ret; + if( PQgetisnull( m_result, m_row, columnIndex -1 ) ) + { + m_wasNull = true; + } + else + { + m_wasNull = false; + ret <<= OUString( + PQgetvalue( m_result, m_row , columnIndex -1 ) , + PQgetlength( m_result, m_row , columnIndex -1 ) , + ConnectionSettings::encoding ); + + } + return ret; +} + +ResultSet::~ResultSet() +{} + +void ResultSet::close( ) +{ + Reference< XInterface > owner; + { + MutexGuard guard( m_xMutex->GetMutex() ); + if( m_result ) + { + PQclear(m_result ); + m_result = nullptr; + m_row = -1; + } + owner = m_owner; + m_owner.clear(); + } +} + +Reference< XResultSetMetaData > ResultSet::getMetaData( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + return new ResultSetMetaData( + m_xMutex, this, this, m_ppSettings, m_result, m_schema, m_table ); +} + +sal_Int32 ResultSet::findColumn( const OUString& columnName ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + sal_Int32 res = PQfnumber( m_result, + OUStringToOString( columnName, ConnectionSettings::encoding ).getStr()); + /* PQfnumber return -1 for not found, which is what we want + * other than that we use col number as 1-based not 0-based */ + if(res >= 0) + { + res += 1; + } + else + { + ::dbtools::throwInvalidColumnException( columnName, *this ); + assert(false); + } + return res; +} + +static bool isNumber( const char * data, sal_Int32 len ) +{ + bool ret = false; + if( len ) + { + ret = true; + for( int i = 0 ; i < len ; i ++ ) + { + if( ( data[i] >= '0' && data[i] <= '9' ) || + data[i] == '-' || data[i] == '+' || data[i] == '.' || data[i] == ',' ) + { + if( data[i] == '-' && i != 0 && i != len-1 ) + { + // no number, maybe a date + ret = false; + break; + } + } + else + { + ret = false; + break; + } + } + } + return ret; +} + +static bool isInteger( const char * data, sal_Int32 len ) +{ + bool ret = false; + if( len ) + { + ret = true; + for( int i = 0 ; i < len ; i ++ ) + { + if( ( data[i] >= '0' && data[i] <= '9' ) || + data[i] == '-' || data[i] == '+' ) + { + if( data[i] == '-' && i != 0 && i != len-1 ) + { + // no number, maybe a date + ret = false; + break; + } + } + else + { + ret = false; + break; + } + } + } + return ret; +} + +static bool isDate( const char * data, sal_Int32 len ) +{ + return 10 == len && + '-' == data[4] && + '-' == data[7] && + isInteger( &(data[0]),4 ) && + isInteger( &(data[5]),2) && + isInteger( &(data[8]),2 ); +} + +static bool isTime( const char * data, sal_Int32 len ) +{ + return 8 == len && + ':' == data[2] && + ':' == data[5] && + isInteger( &(data[0]),2 ) && + isInteger( &(data[3]),2) && + isInteger( &(data[6]),2 ); + +} + +static bool isTimestamp( const char * data, sal_Int32 len ) +{ + return len == 19 && isDate( data, 10) && isTime( &(data[11]),8 ); +} + +sal_Int32 ResultSet::guessDataType( sal_Int32 column ) +{ + // we don't look into more than 100 rows ... + sal_Int32 ret = css::sdbc::DataType::INTEGER; + + int maxRows = std::min<sal_Int32>( m_rowCount, 100 ); + for( int i = 0 ; i < maxRows ; i ++ ) + { + if( ! PQgetisnull( m_result, i , column-1 ) ) + { + const char * p = PQgetvalue( m_result, i , column -1 ); + int len = PQgetlength( m_result, i , column -1 ); + + if( css::sdbc::DataType::INTEGER == ret ) + { + if( ! isInteger( p,len ) ) + ret = css::sdbc::DataType::NUMERIC; + } + if( css::sdbc::DataType::NUMERIC == ret ) + { + if( ! isNumber( p,len ) ) + { + ret = css::sdbc::DataType::DATE; + } + } + if( css::sdbc::DataType::DATE == ret ) + { + if( ! isDate( p,len ) ) + { + ret = css::sdbc::DataType::TIME; + } + } + if( css::sdbc::DataType::TIME == ret ) + { + if( ! isTime( p,len ) ) + { + ret = css::sdbc::DataType::TIMESTAMP; + } + } + if( css::sdbc::DataType::TIMESTAMP == ret ) + { + if( ! isTimestamp( p,len ) ) + { + ret = css::sdbc::DataType::LONGVARCHAR; + break; + } + } + } + } + return ret; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_resultset.hxx b/connectivity/source/drivers/postgresql/pq_resultset.hxx new file mode 100644 index 000000000..4b2bb6f41 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_resultset.hxx @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_RESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_RESULTSET_HXX + +#include <cppuhelper/propshlp.hxx> +#include <cppuhelper/component.hxx> + +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include "pq_connection.hxx" +#include "pq_baseresultset.hxx" + +namespace pq_sdbc_driver +{ + +class ResultSet : public BaseResultSet +{ +protected: + PGresult *m_result; + OUString m_schema; + OUString m_table; + ConnectionSettings **m_ppSettings; + +protected: + /** mutex should be locked before called + */ + virtual void checkClosed() override; + + /** unchecked, acquire mutex before calling + */ + virtual css::uno::Any getValue( sal_Int32 columnIndex ) override; + +public: + ResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + ConnectionSettings **pSettings, + PGresult *result, + const OUString &schema, + const OUString &table ); + virtual ~ResultSet() override; + +public: // XCloseable + virtual void SAL_CALL close( ) override; + +public: // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + +public: // XColumnLocate + virtual sal_Int32 SAL_CALL findColumn( const OUString& columnName ) override; + +public: + sal_Int32 guessDataType( sal_Int32 column ); +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_resultsetmetadata.cxx b/connectivity/source/drivers/postgresql/pq_resultsetmetadata.cxx new file mode 100644 index 000000000..0452324a1 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_resultsetmetadata.cxx @@ -0,0 +1,441 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <rtl/ustrbuf.hxx> + +#include "pq_resultsetmetadata.hxx" +#include "pq_resultset.hxx" +#include "pq_tools.hxx" +#include "pq_statics.hxx" + +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> + +#include <string.h> + +using osl::MutexGuard; + + +using com::sun::star::uno::Any; +using com::sun::star::uno::Exception; +using com::sun::star::uno::Reference; +using com::sun::star::uno::UNO_QUERY; + + +using com::sun::star::sdbc::SQLException; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbcx::XColumnsSupplier; +using com::sun::star::sdbcx::XTablesSupplier; + +using com::sun::star::beans::XPropertySet; +using com::sun::star::container::XNameAccess; + + +namespace pq_sdbc_driver +{ + +// struct ColumnMetaData +// { +// OUString tableName; +// OUString schemaTableName; +// OUString typeName; +// css::sdbc::DataType type; +// sal_Int32 precision; +// sal_Int32 scale; +// sal_Bool isCurrency; +// sal_Bool isNullable; +// sal_Bool isAutoIncrement; +// sal_Bool isReadOnly; +// sal_Bool isSigned; +// }; + +// is not exported by the postgres header +const static int PQ_VARHDRSZ = sizeof( sal_Int32 ); + +static void extractPrecisionAndScale( sal_Int32 atttypmod, sal_Int32 *precision, sal_Int32 *scale ) +{ + if( atttypmod < PQ_VARHDRSZ ) + { + *precision = 0; + *scale = 0; + } + else + { + if( atttypmod & 0xffff0000 ) + { + *precision = ( ( atttypmod - PQ_VARHDRSZ ) >> 16 ) & 0xffff; + *scale = (atttypmod - PQ_VARHDRSZ ) & 0xffff; + } + else + { + *precision = atttypmod - PQ_VARHDRSZ; + *scale = 0; + } + } +} + +ResultSetMetaData::ResultSetMetaData( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XResultSet > & origin, + ResultSet * pResultSet, + ConnectionSettings **ppSettings, + PGresult const *pResult, + const OUString &schemaName, + const OUString &tableName ) : + m_xMutex( refMutex ), + m_ppSettings( ppSettings ), + m_origin( origin ), + m_tableName( tableName ), + m_schemaName( schemaName ), + m_colDesc( PQnfields( pResult ) ), + m_pResultSet( pResultSet ), + m_checkedForTable( false ), + m_checkedForTypes( false ), + m_colCount( PQnfields( pResult ) ) +{ + + // extract all needed information from the result object, so that we don't + // need it anymore after this call ! + for( int col = 0; col < m_colCount ; col ++ ) + { + sal_Int32 size = PQfsize( pResult, col ); + size = -1 == size ? 25 : size; + m_colDesc[col].displaySize = size; + + extractPrecisionAndScale( + PQfmod( pResult, col ), + & ( m_colDesc[col].precision ), + & ( m_colDesc[col].scale ) ); + char *name = PQfname( pResult, col ); + m_colDesc[col].name = OUString( name, strlen(name) , ConnectionSettings::encoding ); + m_colDesc[col].typeOid = PQftype( pResult, col ); + m_colDesc[col].type = css::sdbc::DataType::LONGVARCHAR; + } +} + +void ResultSetMetaData::checkForTypes() +{ + if( m_checkedForTypes ) + return; + + Reference< XStatement > stmt = + extractConnectionFromStatement( m_origin->getStatement() )->createStatement(); + DisposeGuard guard( stmt ); + OUStringBuffer buf(128); + buf.append( "SELECT oid, typname, typtype FROM pg_type WHERE "); + for( int i = 0 ; i < m_colCount ; i ++ ) + { + if( i > 0 ) + buf.append( " OR " ); + int oid = m_colDesc[i].typeOid; + buf.append( "oid=" ); + buf.append( static_cast<sal_Int32>(oid) ); + } + Reference< XResultSet > rs = stmt->executeQuery( buf.makeStringAndClear() ); + Reference< XRow > xRow( rs, UNO_QUERY ); + while( rs->next() ) + { + Oid oid = xRow->getInt( 1 ); + OUString typeName = xRow->getString( 2 ); + OUString typType = xRow->getString( 3 ); + + sal_Int32 type = typeNameToDataType( typeName, typType ); + + for( sal_Int32 j = 0; j < m_colCount ; j ++ ) + { + if( m_colDesc[j].typeOid == oid ) + { + m_colDesc[j].typeName = typeName; + m_colDesc[j].type = type; + } + } + } + m_checkedForTypes = true; +} + +void ResultSetMetaData::checkTable() +{ + if( m_checkedForTable ) + return; + + m_checkedForTable = true; + if( !m_tableName.getLength() ) + return; + + Reference< css::container::XNameAccess > tables = (*m_ppSettings)->tables; + if( ! tables.is() ) + { + + Reference< XTablesSupplier > supplier( + extractConnectionFromStatement( m_origin->getStatement() ), UNO_QUERY); + if( supplier.is() ) + tables = supplier->getTables(); + } + if( tables.is() ) + { + const OUString name (getTableName ( 1 )); + const OUString schema (getSchemaName( 1 )); + const OUString composedName( schema.isEmpty() ? name : (schema + "." + name) ); + tables->getByName( composedName ) >>= m_table; + } +} + +sal_Int32 ResultSetMetaData::getIntColumnProperty( const OUString & name, int index, int def ) +{ + sal_Int32 ret = def; // give defensive answers, when data is not available + try + { + MutexGuard guard( m_xMutex->GetMutex() ); + checkColumnIndex( index ); + Reference< XPropertySet > set = getColumnByIndex( index ); + + if( set.is() ) + { + set->getPropertyValue( name ) >>= ret; + } + } + catch( css::uno::Exception & ) + { + } + return ret; +} + +bool ResultSetMetaData::getBoolColumnProperty( const OUString & name, int index, bool def ) +{ + bool ret = def; + try + { + MutexGuard guard( m_xMutex->GetMutex() ); + checkColumnIndex( index ); + Reference< XPropertySet > set = getColumnByIndex( index ); + if( set.is() ) + { + set->getPropertyValue( name ) >>= ret; + } + } + catch( css::uno::Exception & ) + { + } + + return ret; +} + +Reference< css::beans::XPropertySet > ResultSetMetaData::getColumnByIndex( int index ) +{ + Reference< XPropertySet > ret; + checkTable(); + if( m_table.is() ) + { + OUString columnName = getColumnName( index ); + Reference< XColumnsSupplier > supplier( m_table, UNO_QUERY ); + if( supplier.is() ) + { + Reference< XNameAccess > columns = supplier->getColumns(); + if( columns.is() && columns->hasByName( columnName ) ) + { + columns->getByName( columnName ) >>= ret; + } + } + } + return ret; +} + +// Methods +sal_Int32 ResultSetMetaData::getColumnCount( ) +{ + return m_colCount; +} + +sal_Bool ResultSetMetaData::isAutoIncrement( sal_Int32 column ) +{ + + bool ret = getBoolColumnProperty( getStatics().IS_AUTO_INCREMENT, column, false ); + return ret; +} + +sal_Bool ResultSetMetaData::isCaseSensitive( sal_Int32 ) +{ + return true; // ??? hmm, numeric types or +} + +sal_Bool ResultSetMetaData::isSearchable( sal_Int32 ) +{ + return true; // mmm, what types are not searchable ? +} + +sal_Bool ResultSetMetaData::isCurrency( sal_Int32 column ) +{ + return getBoolColumnProperty( getStatics().IS_CURRENCY, column, false ); +} + +sal_Int32 ResultSetMetaData::isNullable( sal_Int32 column ) +{ + return getIntColumnProperty( + getStatics().IS_NULLABLE, column, css::sdbc::ColumnValue::NULLABLE_UNKNOWN ); +} + +sal_Bool ResultSetMetaData::isSigned( sal_Int32 ) +{ + return true; +} + +sal_Int32 ResultSetMetaData::getColumnDisplaySize( sal_Int32 column ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkColumnIndex( column ); + return m_colDesc[column-1].displaySize; +} + +OUString ResultSetMetaData::getColumnLabel( sal_Int32 column ) +{ + return getColumnName( column); +} + +OUString ResultSetMetaData::getColumnName( sal_Int32 column ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkColumnIndex( column ); + + return m_colDesc[column-1].name; +} + +OUString ResultSetMetaData::getSchemaName( sal_Int32 ) +{ + return m_schemaName; +} + +sal_Int32 ResultSetMetaData::getPrecision( sal_Int32 column ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkColumnIndex( column ); + return m_colDesc[column-1].precision; +} + +sal_Int32 ResultSetMetaData::getScale( sal_Int32 column ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkColumnIndex( column ); + return m_colDesc[column-1].scale; +} + +OUString ResultSetMetaData::getTableName( sal_Int32 ) +{ +// LEM TODO This is very fishy... Should probably return the table to which that column belongs! + return m_tableName; +} + +OUString ResultSetMetaData::getCatalogName( sal_Int32 ) +{ + // can do this through XConnection.getCatalog() ! + return OUString(); +} +sal_Int32 ResultSetMetaData::getColumnType( sal_Int32 column ) +{ + int ret = getIntColumnProperty( getStatics().TYPE, column, -100 ); + if( -100 == ret ) + { + checkForTypes(); + if( css::sdbc::DataType::LONGVARCHAR == m_colDesc[column-1].type && m_pResultSet ) + m_colDesc[column-1].type = m_pResultSet->guessDataType( column ); + ret = m_colDesc[column-1].type; + } + return ret; +} + +OUString ResultSetMetaData::getColumnTypeName( sal_Int32 column ) +{ + OUString ret; // give defensive answers, when data is not available + try + { + MutexGuard guard( m_xMutex->GetMutex() ); + checkColumnIndex( column ); + Reference< XPropertySet > set = getColumnByIndex( column ); + + if( set.is() ) + { + set->getPropertyValue( getStatics().TYPE_NAME ) >>= ret; + } + else + { + checkForTypes(); + ret = m_colDesc[column-1].typeName; + } + } + catch( css::uno::Exception & ) + { + } + return ret; +} + + +sal_Bool ResultSetMetaData::isReadOnly( sal_Int32 ) +{ + return false; +} + +sal_Bool ResultSetMetaData::isWritable( sal_Int32 column ) +{ + return ! isReadOnly( column ); // what's the sense if this method ? +} + +sal_Bool ResultSetMetaData::isDefinitelyWritable( sal_Int32 column ) +{ + return isWritable(column); // uhh, now it becomes really esoteric... +} +OUString ResultSetMetaData::getColumnServiceName( sal_Int32 ) +{ + return OUString(); +} + +void ResultSetMetaData::checkColumnIndex(sal_Int32 columnIndex) +{ + if( columnIndex < 1 || columnIndex > m_colCount ) + { + throw SQLException( + "pq_resultsetmetadata: index out of range (expected 1 to " + + OUString::number( m_colCount ) + ", got " + OUString::number( columnIndex ), + *this, OUString(), 1, Any() ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_resultsetmetadata.hxx b/connectivity/source/drivers/postgresql/pq_resultsetmetadata.hxx new file mode 100644 index 000000000..2c1b5e5e5 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_resultsetmetadata.hxx @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_RESULTSETMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_RESULTSETMETADATA_HXX +#include <vector> + +#include "pq_connection.hxx" + +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace pq_sdbc_driver +{ + +struct ColDesc +{ + OUString name; + sal_Int32 precision; + sal_Int32 scale; + sal_Int32 displaySize; + Oid typeOid; + OUString typeName; + sal_Int32 type; +}; + +class ResultSet; + +class ResultSetMetaData : + public ::cppu::WeakImplHelper< css::sdbc::XResultSetMetaData > +{ + ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + ConnectionSettings **m_ppSettings; + css::uno::Reference< css::sdbc::XResultSet > m_origin; + css::uno::Reference< css::beans::XPropertySet > m_table; + OUString m_tableName; + OUString m_schemaName; + std::vector< ColDesc > m_colDesc; + ResultSet *m_pResultSet; + + bool m_checkedForTable; + bool m_checkedForTypes; + + sal_Int32 m_colCount; + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkColumnIndex( sal_Int32 columnIndex ); + void checkTable(); + void checkForTypes(); + css::uno::Reference< css::beans::XPropertySet > getColumnByIndex( int index ); + + sal_Int32 getIntColumnProperty( const OUString & name, int index, int def ); + bool getBoolColumnProperty( const OUString & name, int index, bool def ); + +public: + ResultSetMetaData( + const ::rtl::Reference< comphelper::RefCountedMutex > & reMutex, + const css::uno::Reference< css::sdbc::XResultSet > & origin, + ResultSet *pResultSet, + ConnectionSettings **pSettings, + PGresult const *pResult, + const OUString &schemaName, + const OUString &tableName ); + +public: + // Methods + virtual sal_Int32 SAL_CALL getColumnCount( ) override; + virtual sal_Bool SAL_CALL isAutoIncrement( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCaseSensitive( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSearchable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCurrency( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL isNullable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSigned( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnDisplaySize( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnLabel( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnName( sal_Int32 column ) override; + virtual OUString SAL_CALL getSchemaName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getPrecision( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getScale( sal_Int32 column ) override; + virtual OUString SAL_CALL getTableName( sal_Int32 column ) override; + virtual OUString SAL_CALL getCatalogName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnType( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnTypeName( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isReadOnly( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isWritable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isDefinitelyWritable( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnServiceName( sal_Int32 column ) override; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_sequenceresultset.cxx b/connectivity/source/drivers/postgresql/pq_sequenceresultset.cxx new file mode 100644 index 000000000..02030729a --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_sequenceresultset.cxx @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + + +#include "pq_sequenceresultset.hxx" +#include "pq_sequenceresultsetmetadata.hxx" + +#include <connectivity/dbexception.hxx> + +#include <com/sun/star/sdbc/SQLException.hpp> + +using com::sun::star::sdbc::XResultSetMetaData; + +using com::sun::star::uno::Reference; +using com::sun::star::uno::Any; + +namespace pq_sdbc_driver +{ + +void SequenceResultSet::checkClosed() +{ + // we never close :o) +} + + +Any SequenceResultSet::getValue( sal_Int32 columnIndex ) +{ + m_wasNull = ! m_data[m_row][columnIndex -1 ].hasValue(); + return m_data[m_row][columnIndex -1 ]; +} + +SequenceResultSet::SequenceResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + const std::vector< OUString > &colNames, + const std::vector< std::vector< Any > > &data, + const Reference< css::script::XTypeConverter > & tc, + const ColumnMetaDataVector *pVec) : + BaseResultSet( mutex, owner, data.size(), colNames.size(), tc ), + m_data(data ), + m_columnNames( colNames ) +{ + if( pVec ) + { + m_meta = new SequenceResultSetMetaData( *pVec, m_columnNames.size() ); + } +} + +SequenceResultSet::~SequenceResultSet() +{ + +} + +void SequenceResultSet::close( ) +{ + // a noop +} + +Reference< XResultSetMetaData > SAL_CALL SequenceResultSet::getMetaData( ) +{ + if( ! m_meta.is() ) + { + // Oh no, not again + throw css::sdbc::SQLException( + "pq_sequenceresultset: no meta supported ", *this, + // I did not find "IM001" in a specific standard, + // but it seems to be used by other systems (such as ODBC) + // and some parts of LibreOffice special-case it. + "IM001", 1, Any() ); + } + return m_meta; +} + + +sal_Int32 SAL_CALL SequenceResultSet::findColumn( + const OUString& columnName ) +{ + // no need to guard, as all members are readonly ! + for( int i = 0 ;i < m_fieldCount ; i ++ ) + { + if( columnName == m_columnNames[i] ) + { + return i+1; + } + } + ::dbtools::throwInvalidColumnException( columnName, *this ); + assert(false); + return 0; // Never reached +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_sequenceresultset.hxx b/connectivity/source/drivers/postgresql/pq_sequenceresultset.hxx new file mode 100644 index 000000000..3b97e24a3 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_sequenceresultset.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_SEQUENCERESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_SEQUENCERESULTSET_HXX + +#include <cppuhelper/propshlp.hxx> +#include <cppuhelper/component.hxx> + +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include "pq_connection.hxx" +#include "pq_baseresultset.hxx" +#include "pq_statics.hxx" + +namespace pq_sdbc_driver +{ + +class SequenceResultSet : public BaseResultSet +{ +protected: + std::vector< std::vector< css::uno::Any > > m_data; + + std::vector< OUString > m_columnNames; + css::uno::Reference< css::sdbc::XResultSetMetaData > m_meta; + +protected: + /** mutex should be locked before called + */ + virtual void checkClosed() override; + + /** unchecked, acquire mutex before calling + */ + virtual css::uno::Any getValue( sal_Int32 columnIndex ) override; + +public: + SequenceResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + const std::vector< OUString > &colNames, + const std::vector< std::vector< css::uno::Any > > &data, + const css::uno::Reference< css::script::XTypeConverter > &tc, + const ColumnMetaDataVector *pVec = nullptr); + virtual ~SequenceResultSet() override; + +public: // XCloseable + virtual void SAL_CALL close( ) override; + +public: // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + +public: // XColumnLocate + virtual sal_Int32 SAL_CALL findColumn( const OUString& columnName ) override; +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_sequenceresultsetmetadata.cxx b/connectivity/source/drivers/postgresql/pq_sequenceresultsetmetadata.cxx new file mode 100644 index 000000000..d45ffc0a6 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_sequenceresultsetmetadata.cxx @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 200? by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include "pq_sequenceresultsetmetadata.hxx" + +#include <com/sun/star/sdbc/SQLException.hpp> + +using com::sun::star::uno::Any; + +using com::sun::star::sdbc::SQLException; + +namespace pq_sdbc_driver +{ + +SequenceResultSetMetaData::SequenceResultSetMetaData( + const ColumnMetaDataVector &metaDataVector, + int colCount ) : + m_columnData( metaDataVector ), + m_colCount( colCount ) +{ +} + + +// Methods +sal_Int32 SequenceResultSetMetaData::getColumnCount( ) +{ + return m_colCount; +} + +sal_Bool SequenceResultSetMetaData::isAutoIncrement( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].isAutoIncrement; +} + +sal_Bool SequenceResultSetMetaData::isCaseSensitive( sal_Int32 /* column */ ) +{ + + return true; // ??? hmm, numeric types or +} + +sal_Bool SequenceResultSetMetaData::isSearchable( sal_Int32 /* column */ ) +{ + return true; // mmm, what types are not searchable ? +} + +sal_Bool SequenceResultSetMetaData::isCurrency( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].isCurrency; +} + +sal_Int32 SequenceResultSetMetaData::isNullable( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].isNullable ? 1 : 0; +} + +sal_Bool SequenceResultSetMetaData::isSigned( sal_Int32 /* column */ ) +{ + return true; +} + +sal_Int32 SequenceResultSetMetaData::getColumnDisplaySize( sal_Int32 /* column */ ) +{ + return 50; +} + +OUString SequenceResultSetMetaData::getColumnLabel( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].columnName; +} + +OUString SequenceResultSetMetaData::getColumnName( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].columnName; +} + +OUString SequenceResultSetMetaData::getSchemaName( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].schemaTableName; +} + + +sal_Int32 SequenceResultSetMetaData::getPrecision( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].precision; +} + +sal_Int32 SequenceResultSetMetaData::getScale( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].scale; +} + +OUString SequenceResultSetMetaData::getTableName( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].tableName; +} + +OUString SequenceResultSetMetaData::getCatalogName( sal_Int32 /* column */ ) +{ + // can do this through XConnection.getCatalog() ! + return OUString(); +} +sal_Int32 SequenceResultSetMetaData::getColumnType( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].type; +} + +OUString SequenceResultSetMetaData::getColumnTypeName( sal_Int32 column ) +{ + checkColumnIndex( column ); + return m_columnData[column-1].typeName; +} + + +sal_Bool SequenceResultSetMetaData::isReadOnly( sal_Int32 /* column */ ) +{ + return false; +} + +sal_Bool SequenceResultSetMetaData::isWritable( sal_Int32 column ) +{ + return ! isReadOnly( column ); // what's the sense if this method ? +} + +sal_Bool SequenceResultSetMetaData::isDefinitelyWritable( sal_Int32 column ) +{ + return isWritable(column); // uhh, now it becomes really esoteric... +} +OUString SequenceResultSetMetaData::getColumnServiceName( sal_Int32 /* column */ ) +{ + return OUString(); +} + +void SequenceResultSetMetaData::checkColumnIndex(sal_Int32 columnIndex) +{ + if( columnIndex < 1 || columnIndex > m_colCount ) + { + throw SQLException( + "pq_sequenceresultsetmetadata: index out of range (expected 1 to " + + OUString::number( m_colCount ) + + ", got " + OUString::number( columnIndex ), + *this, OUString(), 1, Any() ); + } +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_sequenceresultsetmetadata.hxx b/connectivity/source/drivers/postgresql/pq_sequenceresultsetmetadata.hxx new file mode 100644 index 000000000..2bba06aa7 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_sequenceresultsetmetadata.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 200? by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_SEQUENCERESULTSETMETADATA_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_SEQUENCERESULTSETMETADATA_HXX + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> + +#include "pq_connection.hxx" +#include "pq_statics.hxx" + +namespace pq_sdbc_driver +{ + class SequenceResultSetMetaData : + public ::cppu::WeakImplHelper< css::sdbc::XResultSetMetaData > + { + ColumnMetaDataVector m_columnData; + sal_Int32 m_colCount; + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkColumnIndex( sal_Int32 columnIndex ); + + public: + SequenceResultSetMetaData( + const ColumnMetaDataVector &vec, + int colCount ); + + public: + // Methods + virtual sal_Int32 SAL_CALL getColumnCount( ) override; + virtual sal_Bool SAL_CALL isAutoIncrement( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCaseSensitive( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSearchable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isCurrency( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL isNullable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isSigned( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnDisplaySize( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnLabel( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnName( sal_Int32 column ) override; + virtual OUString SAL_CALL getSchemaName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getPrecision( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getScale( sal_Int32 column ) override; + virtual OUString SAL_CALL getTableName( sal_Int32 column ) override; + virtual OUString SAL_CALL getCatalogName( sal_Int32 column ) override; + virtual sal_Int32 SAL_CALL getColumnType( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnTypeName( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isReadOnly( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isWritable( sal_Int32 column ) override; + virtual sal_Bool SAL_CALL isDefinitelyWritable( sal_Int32 column ) override; + virtual OUString SAL_CALL getColumnServiceName( sal_Int32 column ) override; + }; + +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_SEQUENCERESULTSETMETADATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_statement.cxx b/connectivity/source/drivers/postgresql/pq_statement.cxx new file mode 100644 index 000000000..dd7ab576b --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_statement.cxx @@ -0,0 +1,891 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/log.hxx> +#include "pq_statement.hxx" +#include "pq_fakedupdateableresultset.hxx" +#include "pq_updateableresultset.hxx" +#include "pq_tools.hxx" +#include "pq_statics.hxx" + +#include <osl/time.h> + +#include <rtl/ustrbuf.hxx> +#include <rtl/strbuf.hxx> + +#include <comphelper/sequence.hxx> + +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> + +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbcx/XKeysSupplier.hpp> + +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> + +#include <string.h> + +using osl::MutexGuard; + + +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Reference; +using com::sun::star::uno::XInterface; +using com::sun::star::uno::UNO_QUERY; + +using com::sun::star::lang::IllegalArgumentException; + +using com::sun::star::sdbc::XCloseable; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XPreparedStatement; +using com::sun::star::sdbc::XParameters; +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XConnection; +using com::sun::star::sdbc::SQLException; + +using com::sun::star::sdbcx::XColumnsSupplier; +using com::sun::star::sdbcx::XKeysSupplier; + +using com::sun::star::beans::Property; +using com::sun::star::beans::XPropertySetInfo; +using com::sun::star::beans::XPropertySet; + +using com::sun::star::container::XNameAccess; +using com::sun::star::container::XEnumerationAccess; +using com::sun::star::container::XEnumeration; +using com::sun::star::container::XIndexAccess; + +namespace pq_sdbc_driver +{ +static ::cppu::IPropertyArrayHelper & getStatementPropertyArrayHelper() +{ + static ::cppu::OPropertyArrayHelper arrayHelper( + Sequence<Property>{ + Property( + "CursorName", 0, + ::cppu::UnoType<OUString>::get() , 0 ), + Property( + "EscapeProcessing", 1, + cppu::UnoType<bool>::get() , 0 ), + Property( + "FetchDirection", 2, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "FetchSize", 3, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "MaxFieldSize", 4, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "MaxRows", 5, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "QueryTimeOut", 6, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "ResultSetConcurrency", 7, + ::cppu::UnoType<sal_Int32>::get() , 0 ), + Property( + "ResultSetType", 8, + ::cppu::UnoType<sal_Int32>::get() , 0 )}, + true ); + + return arrayHelper; +} + +Statement::Statement( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< XConnection > & conn, + struct ConnectionSettings *pSettings ) + : Statement_BASE( refMutex->GetMutex() ) + , OPropertySetHelper( Statement_BASE::rBHelper ) + , m_connection( conn ) + , m_pSettings( pSettings ) + , m_xMutex( refMutex ) + , m_multipleResultAvailable(false) + , m_multipleResultUpdateCount(0) + , m_lastOidInserted(InvalidOid) +{ + m_props[STATEMENT_QUERY_TIME_OUT] <<= sal_Int32(0); + m_props[STATEMENT_MAX_ROWS] <<= sal_Int32(0); + m_props[STATEMENT_RESULT_SET_CONCURRENCY] <<= + css::sdbc::ResultSetConcurrency::READ_ONLY; + m_props[STATEMENT_RESULT_SET_TYPE] <<= + css::sdbc::ResultSetType::SCROLL_INSENSITIVE; +} + +Statement::~Statement() +{ +} + +void Statement::checkClosed() +{ + if( ! m_pSettings || ! m_pSettings->pConnection ) + throw SQLException( + "pq_driver: Statement or connection has already been closed !", + *this, OUString(),1,Any()); +} + +Any Statement::queryInterface( const Type & rType ) +{ + Any aRet = Statement_BASE::queryInterface(rType); + return aRet.hasValue() ? aRet : OPropertySetHelper::queryInterface(rType); +} + + +Sequence< Type > Statement::getTypes() +{ + static Sequence< Type > collection( + ::comphelper::concatSequences( + OPropertySetHelper::getTypes(), + Statement_BASE::getTypes())); + + return collection; +} + +Sequence< sal_Int8> Statement::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void Statement::close( ) +{ + // let the connection die without acquired mutex ! + Reference< XConnection > r; + Reference< XCloseable > resultSet; + { + MutexGuard guard( m_xMutex->GetMutex() ); + m_pSettings = nullptr; + r = m_connection; + m_connection.clear(); + + resultSet = m_lastResultset; + m_lastResultset.clear(); + } + if( resultSet.is() ) + { + resultSet->close(); + } + +} + +void Statement::raiseSQLException( + const OUString & sql, const char * errorMsg ) +{ + OUString error = "pq_driver: " + + OUString( errorMsg, strlen(errorMsg), ConnectionSettings::encoding ) + + " (caused by statement '" + sql + "')"; + SAL_WARN("connectivity.postgresql", error); + throw SQLException( error, *this, OUString(), 1, Any() ); +} + +Reference< XResultSet > Statement::executeQuery(const OUString& sql ) +{ + if( ! execute( sql ) ) + { + raiseSQLException( sql, "not a query" ); + } + return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY ); +} + +sal_Int32 Statement::executeUpdate( const OUString& sql ) +{ + if( execute( sql ) ) + { + raiseSQLException( sql, "not a command" ); + } + return m_multipleResultUpdateCount; +} + +/// @throws SQLException +static void raiseSQLException( + const Reference< XInterface> & owner, + const OString & sql, + const char * errorMsg, + const char *errorType = nullptr ) +{ + OUStringBuffer buf(128); + buf.append( "pq_driver: "); + if( errorType ) + { + buf.append( "[" ); + buf.appendAscii( errorType ); + buf.append( "]" ); + } + buf.append( + OUString( errorMsg, strlen(errorMsg) , ConnectionSettings::encoding ) ); + buf.append( " (caused by statement '" ); + buf.append( OStringToOUString( sql, ConnectionSettings::encoding ) ); + buf.append( "')" ); + OUString error = buf.makeStringAndClear(); + SAL_WARN("connectivity.postgresql", error); + throw SQLException( error, owner, OUString(), 1, Any() ); +} + + +// returns the elements of the primary key of the given table +// static Sequence< Reference< css::beans::XPropertySet > > lookupKeys( +static std::vector< OUString > lookupKeys( + const Reference< css::container::XNameAccess > &tables, + const OUString & table, + OUString *pSchema, + OUString *pTable) +{ + std::vector< OUString > ret; + Reference< XKeysSupplier > keySupplier; + Statics & st = getStatics(); + + if( tables->hasByName( table ) ) + tables->getByName( table ) >>= keySupplier; + else if( -1 == table.indexOf( '.' ) ) + { + // it wasn't a fully qualified name. Now need to skip through all tables. + Reference< XEnumerationAccess > enumerationAccess( tables, UNO_QUERY ); + + Reference< css::container::XEnumeration > enumeration = + enumerationAccess->createEnumeration(); + while( enumeration->hasMoreElements() ) + { + Reference< XPropertySet > set; + enumeration->nextElement() >>= set; + OUString name; +// OUString schema; + + if( set->getPropertyValue( st.NAME ) >>= name ) + { +// printf( "searching %s %s\n", +// OUStringToOString( schema, RTL_TEXTENCODING_ASCII_US ).getStr(), +// OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() ); + if( name == table ) + { + + if( keySupplier.is() ) + { + // is ambiguous, as I don't know postgresql searchpath, + // I can't continue here, as I may write to a different table + keySupplier.clear(); + SAL_INFO("connectivity.postgresql", "Can't offer updateable result set because table " << name << " is duplicated, add schema to resolve ambiguity"); + break; + } + keySupplier.set( set, UNO_QUERY ); + } + } + } + } + else + { + SAL_INFO("connectivity.postgresql", "Can't offer updateable result set ( table " << table << " is unknown)"); + } + + if( keySupplier.is() ) + { + Reference< XPropertySet > set( keySupplier, UNO_QUERY ); + set->getPropertyValue( getStatics().NAME ) >>= *pTable; + set->getPropertyValue( getStatics().SCHEMA_NAME ) >>= *pSchema; + set.clear(); + + Reference< XEnumerationAccess > keys ( keySupplier->getKeys(), UNO_QUERY ); + Reference< XEnumeration > enumeration = keys->createEnumeration(); + while( enumeration->hasMoreElements() ) + { + enumeration->nextElement() >>= set; + sal_Int32 keyType = 0; + if( (set->getPropertyValue( st.TYPE ) >>= keyType ) && + keyType == css::sdbcx::KeyType::PRIMARY ) + { + Reference< XColumnsSupplier > columns( set, UNO_QUERY ); + Reference< XIndexAccess > indexAccess( columns->getColumns(), UNO_QUERY ); + + int length = indexAccess->getCount(); + ret.resize( length ); +// printf( "primary key for Table %s is ", +// OUStringToOString( table, RTL_TEXTENCODING_ASCII_US ).getStr() ); + for( int i = 0 ; i < length ; i ++ ) + { + indexAccess->getByIndex( i ) >>= set; + OUString name; + set->getPropertyValue( st.NAME ) >>= name; + ret[i] = name; +// printf( "%s," , OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ).getStr() ); + } +// printf( "\n" ); + } + } + if( ret.empty() ) + { + SAL_INFO("connectivity.postgresql", "Can't offer updateable result set ( table " << table << " does not have a primary key)"); + } + } + return ret; +} + +bool executePostgresCommand( const OString & cmd, struct CommandData *data ) +{ + ConnectionSettings *pSettings = *(data->ppSettings); + + sal_Int32 duration = osl_getGlobalTimer(); + PGresult *result = PQexec( pSettings->pConnection, cmd.getStr() ); + duration = osl_getGlobalTimer() - duration; + if( ! result ) + raiseSQLException( + data->owner, cmd, PQerrorMessage( pSettings->pConnection ) ); + + ExecStatusType state = PQresultStatus( result ); + *(data->pLastOidInserted) = 0; + data->pLastTableInserted->clear(); + *(data->pLastQuery) = cmd; + + bool ret = false; + switch( state ) + { + case PGRES_COMMAND_OK: + { + *(data->pMultipleResultUpdateCount) = atoi( PQcmdTuples( result ) ); + *(data->pMultipleResultAvailable) = false; + + // in case an oid value is available, we retrieve it + *(data->pLastOidInserted) = PQoidValue( result ); + + // in case it was a single insert, extract the name of the table, + // otherwise the table name is empty + *(data->pLastTableInserted) = + extractTableFromInsert( OStringToOUString( cmd, ConnectionSettings::encoding ) ); + + OString strMain = "executed command '" + cmd + "' successfully ('" + OString::number(*( data->pMultipleResultUpdateCount )) + + "), duration=" + OString::number(duration) + "ms"; + + OString strOption; + if( *(data->pLastOidInserted) ) + { + strOption += ", usedOid=" + OString::number( *(data->pLastOidInserted) ) + ", diagnosedTable=" + + OUStringToOString(*data->pLastTableInserted, ConnectionSettings::encoding); + } + SAL_INFO("connectivity.postgresql", strMain + strOption); + PQclear( result ); + break; + } + case PGRES_TUPLES_OK: // success + { + // In case it is a single table, it has a primary key and all columns + // belonging to the primary key are in the result set, allow updateable result sets + // otherwise, don't + OUString table, schema; + std::vector< OString > vec; + tokenizeSQL( cmd, vec ); + OUString sourceTable = + OStringToOUString( + extractSingleTableFromSelect( vec ), ConnectionSettings::encoding ); + + if( data->concurrency == + css::sdbc::ResultSetConcurrency::UPDATABLE ) + { + OString aReason; + if( sourceTable.getLength() ) + { + std::vector< OUString > sourceTableKeys = lookupKeys( + pSettings->tables.is() ? + pSettings->tables : data->tableSupplier->getTables() , + sourceTable, + &schema, + &table); + + // check, whether the columns are in the result set (required !) + int i; + for( i = 0 ; i < static_cast<int>(sourceTableKeys.size()) ; i ++ ) + { + if( -1 == PQfnumber( + result, + OUStringToOString( sourceTableKeys[i] , + ConnectionSettings::encoding ).getStr()) ) + { + break; + } + } + + if( !sourceTableKeys.empty() && i == static_cast<int>(sourceTableKeys.size()) ) + { + *(data->pLastResultset) = + UpdateableResultSet::createFromPGResultSet( + data->refMutex, data->owner, data->ppSettings, result, + schema, table,sourceTableKeys ); + } + else if( ! table.getLength() ) + { + aReason = "can't support updateable resultset, because a single table in the " + "WHERE part of the statement could not be identified (" + cmd + "."; + } + else if( !sourceTableKeys.empty() ) + { + OStringBuffer buf( 128 ); + buf.append( "can't support updateable resultset for table " ); + buf.append( OUStringToOString( schema, ConnectionSettings::encoding ) ); + buf.append( "." ); + buf.append( OUStringToOString( table, ConnectionSettings::encoding ) ); + buf.append( ", because resultset does not contain a part of the primary key ( column " ); + buf.append( OUStringToOString( sourceTableKeys[i], ConnectionSettings::encoding ) ); + buf.append( " is missing )" ); + aReason = buf.makeStringAndClear(); + } + else + { + + aReason = "can't support updateable resultset for table " + + OUStringToOString( schema, ConnectionSettings::encoding ) + "." + + OUStringToOString( table, ConnectionSettings::encoding ) + + ", because resultset table does not have a primary key "; + } + } + else + { + SAL_WARN("connectivity.postgresql", "can't support updateable result for selects with multiple tables (" << cmd << ")"); + } + if( ! (*(data->pLastResultset)).is() ) + { + SAL_WARN("connectivity.postgresql", aReason); + + // TODO: How to react here correctly ? + // remove this piece of code + *(data->pLastResultset) = + new FakedUpdateableResultSet( + data->refMutex, data->owner, + data->ppSettings,result, schema, table, + OStringToOUString( aReason, ConnectionSettings::encoding) ); + } + + } + else if( sourceTable.getLength() > 0) + { + splitConcatenatedIdentifier( sourceTable, &schema, &table ); + } + + sal_Int32 returnedRows = PQntuples( result ); + if( ! data->pLastResultset->is() ) + *(data->pLastResultset) = + Reference< XCloseable > ( + new ResultSet( + data->refMutex, data->owner, + data->ppSettings,result, schema, table ) ); + *(data->pMultipleResultAvailable) = true; + ret = true; + SAL_INFO("connectivity.postgresql", "executed query '" << cmd << "' successfully, duration=" << duration << "ms, returnedRows=" << returnedRows << "."); + break; + } + case PGRES_EMPTY_QUERY: + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + case PGRES_BAD_RESPONSE: + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + default: + raiseSQLException( + data->owner, cmd, PQresultErrorMessage( result ) , PQresStatus( state ) ); + } + return ret; + +} + +static Sequence< OUString > getPrimaryKeyColumnNames( + const Reference< XConnection > & connection, const OUString &schemaName, const OUString &tableName ) +{ + Sequence< OUString > ret; + + Int2StringMap mapIndex2Name; + fillAttnum2attnameMap( mapIndex2Name, connection, schemaName, tableName ); + + // retrieve the primary key ... + Reference< XPreparedStatement > stmt = connection->prepareStatement( + "SELECT conkey " // 7 + "FROM pg_constraint INNER JOIN pg_class ON conrelid = pg_class.oid " + "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid " + "LEFT JOIN pg_class AS class2 ON confrelid = class2.oid " + "LEFT JOIN pg_namespace AS nmsp2 ON class2.relnamespace=nmsp2.oid " + "WHERE pg_class.relname = ? AND pg_namespace.nspname = ? AND pg_constraint.contype='p'" ); + DisposeGuard guard( stmt ); + Reference< XParameters > paras( stmt, UNO_QUERY ); + paras->setString( 1 , tableName ); + paras->setString( 2 , schemaName ); + Reference< XResultSet > rs = stmt->executeQuery(); + Reference< XRow > xRow( rs , UNO_QUERY ); + + if( rs->next() ) + { + ret = convertMappedIntArray2StringArray( mapIndex2Name, string2intarray(xRow->getString( 1 ) ) ); + } + return ret; +} + +static void getAutoValues( + String2StringMap & result, + const Reference< XConnection > & connection, + const OUString &schemaName, + const OUString & tableName, + ConnectionSettings *pConnectionSettings ) +{ + OUString strDefaultValue = getColExprForDefaultSettingVal(pConnectionSettings); + Reference< XPreparedStatement > stmt = connection->prepareStatement( + "SELECT pg_attribute.attname, " + strDefaultValue + + "FROM pg_class, pg_namespace, pg_attribute " + "LEFT JOIN pg_attrdef ON pg_attribute.attrelid = pg_attrdef.adrelid AND " + "pg_attribute.attnum = pg_attrdef.adnum " + "WHERE pg_attribute.attrelid = pg_class.oid AND " + "pg_class.relnamespace = pg_namespace.oid AND " + "pg_namespace.nspname = ? AND " + // LEM TODO: this is weird; why "LIKE" and not "="? + // Most probably gives problems if tableName contains '%' + "pg_class.relname LIKE ? AND " + + strDefaultValue + " != ''" + ); + DisposeGuard guard( stmt ); + Reference< XParameters > paras( stmt, UNO_QUERY ); + paras->setString( 1 , schemaName ); + paras->setString( 2 , tableName ); + Reference< XResultSet > rs = stmt->executeQuery(); + Reference< XRow > xRow( rs , UNO_QUERY ); + + while( rs->next() ) + { + result[ OUStringToOString( xRow->getString( 1 ), RTL_TEXTENCODING_ASCII_US) ] = + OUStringToOString( xRow->getString(2), RTL_TEXTENCODING_ASCII_US ); + } +} + +Reference< XResultSet > getGeneratedValuesFromLastInsert( + ConnectionSettings *pConnectionSettings, + const Reference< XConnection > &connection, + sal_Int32 nLastOid, + const OUString & lastTableInserted, + const OString & lastQuery ) +{ + Reference< XResultSet > ret; + OUString query; + OUString schemaName, tableName; + splitConcatenatedIdentifier( + lastTableInserted, &schemaName, &tableName ); + + if( nLastOid && lastTableInserted.getLength() ) + { + OUStringBuffer buf( 128 ); + buf.append( "SELECT * FROM " ); + if( schemaName.getLength() ) + bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings ); + else + bufferQuoteIdentifier( buf, lastTableInserted, pConnectionSettings ); + buf.append( " WHERE oid = " ); + buf.append( nLastOid ); + query = buf.makeStringAndClear(); + } + else if ( lastTableInserted.getLength() && lastQuery.getLength() ) + { + // extract nameValue Pairs + String2StringMap namedValues; + extractNameValuePairsFromInsert( namedValues, lastQuery ); + + // debug ... +// OStringBuffer buf( 128); +// buf.append( "extracting name/value from '" ); +// buf.append( lastQuery.getStr() ); +// buf.append( "' to [" ); +// for( String2StringMap::iterator ii = namedValues.begin() ; ii != namedValues.end() ; ++ii ) +// { +// buf.append( ii->first.getStr() ); +// buf.append( "=" ); +// buf.append( ii->second.getStr() ); +// buf.append( "," ); +// } +// buf.append( "]\n" ); +// printf( "%s", buf.makeStringAndClear() ); + + // TODO: make also unqualified tables names work here. Have a look at 2.8.3. The Schema Search Path + // in postgresql doc + + const Sequence< OUString > keyColumnNames = getPrimaryKeyColumnNames( connection, schemaName, tableName ); + if( keyColumnNames.hasElements() ) + { + OUStringBuffer buf( 128 ); + buf.append( "SELECT * FROM " ); + bufferQuoteQualifiedIdentifier(buf, schemaName, tableName, pConnectionSettings ); + buf.append( " WHERE " ); + bool bAdditionalCondition = false; + String2StringMap autoValues; + for( OUString const & columnNameUnicode : keyColumnNames ) + { + OUString value; + OString columnName = OUStringToOString( columnNameUnicode, ConnectionSettings::encoding ); + bool bColumnMatchNamedValue = false; + for (auto const& namedValue : namedValues) + { + if( columnName.equalsIgnoreAsciiCase( namedValue.first ) ) + { + value = OStringToOUString( namedValue.second , ConnectionSettings::encoding ); + bColumnMatchNamedValue = true; + break; + } + } + + // check, if a column of the primary key was not inserted explicitly, + if( !bColumnMatchNamedValue ) + { + if( autoValues.empty() ) + { + getAutoValues( autoValues, connection, schemaName, tableName, pConnectionSettings ); + } + // this could mean, that the column is a default or auto value, check this ... + bool bColumnMatchAutoValue = false; + for (auto const& autoValue : autoValues) + { + if( columnName.equalsIgnoreAsciiCase( autoValue.first ) ) + { + // it is indeed an auto value. + value = OStringToOUString(autoValue.second, RTL_TEXTENCODING_ASCII_US ); + // check, whether it is a sequence + + if( autoValue.second.startsWith("nextval(") ) + { + // retrieve current sequence value: + OUStringBuffer myBuf(128 ); + myBuf.append( "SELECT currval(" ); + myBuf.appendAscii( &(autoValue.second.getStr()[8])); + value = querySingleValue( connection, myBuf.makeStringAndClear() ); + } + bColumnMatchAutoValue = true; + break; + } + } + if( !bColumnMatchAutoValue ) + { + // it even was no autovalue, no sense to continue as we can't query the + // inserted row + buf.truncate(); + break; + } + } + + if( bAdditionalCondition ) + buf.append( " AND " ); + bufferQuoteIdentifier( buf, columnNameUnicode, pConnectionSettings ); + buf.append( " = " ); + buf.append( value ); + bAdditionalCondition = true; + } + query = buf.makeStringAndClear(); + } + } + + if( query.getLength() ) + { + Reference< css::sdbc::XStatement > stmt = connection->createStatement(); + ret = stmt->executeQuery( query ); + } + + return ret; + +} + +sal_Bool Statement::execute( const OUString& sql ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + OString cmd = OUStringToOString( sql, m_pSettings ); + + Reference< XCloseable > lastResultSetHolder = m_lastResultset; + if( lastResultSetHolder.is() ) + lastResultSetHolder->close(); + + m_lastResultset.clear(); + m_lastTableInserted.clear(); + + struct CommandData data; + data.refMutex = m_xMutex; + data.ppSettings = &m_pSettings; + data.pLastOidInserted = &m_lastOidInserted; + data.pLastQuery = &m_lastQuery; + data.pMultipleResultUpdateCount = &m_multipleResultUpdateCount; + data.pMultipleResultAvailable = &m_multipleResultAvailable; + data.pLastTableInserted = &m_lastTableInserted; + data.pLastResultset = &m_lastResultset; + data.owner = *this; + data.tableSupplier.set( m_connection, UNO_QUERY ); + data.concurrency = + extractIntProperty( this, getStatics().RESULT_SET_CONCURRENCY ); + return executePostgresCommand( cmd , &data ); +} + +Reference< XConnection > Statement::getConnection( ) +{ + Reference< XConnection > ret; + { + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + ret = m_connection; + } + return ret; +} + + +Any Statement::getWarnings( ) +{ + return Any(); +} + +void Statement::clearWarnings( ) +{ +} + +Reference< css::sdbc::XResultSetMetaData > Statement::getMetaData() +{ + Reference< css::sdbc::XResultSetMetaData > ret; + Reference< css::sdbc::XResultSetMetaDataSupplier > supplier( m_lastResultset, UNO_QUERY ); + if( supplier.is() ) + ret = supplier->getMetaData(); + return ret; +} + + +::cppu::IPropertyArrayHelper & Statement::getInfoHelper() +{ + return getStatementPropertyArrayHelper(); +} + + +sal_Bool Statement::convertFastPropertyValue( + Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) +{ + rOldValue = m_props[nHandle]; + bool bRet; + switch( nHandle ) + { + case STATEMENT_CURSOR_NAME: + { + OUString val; + bRet = ( rValue >>= val ); + rConvertedValue <<= val; + break; + } + case STATEMENT_ESCAPE_PROCESSING: + { + bool val(false); + bRet = ( rValue >>= val ); + rConvertedValue <<= val; + break; + } + case STATEMENT_FETCH_DIRECTION: + case STATEMENT_FETCH_SIZE: + case STATEMENT_MAX_FIELD_SIZE: + case STATEMENT_MAX_ROWS: + case STATEMENT_QUERY_TIME_OUT: + case STATEMENT_RESULT_SET_CONCURRENCY: + case STATEMENT_RESULT_SET_TYPE: + { + sal_Int32 val; + bRet = ( rValue >>= val ); + rConvertedValue <<= val; + break; + } + default: + { + throw IllegalArgumentException( + "pq_statement: Invalid property handle (" + + OUString::number( nHandle ) + ")", + *this, 2 ); + } + } + return bRet; +} + + +void Statement::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle,const Any& rValue ) +{ + m_props[nHandle] = rValue; +} + +void Statement::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + rValue = m_props[nHandle]; +} + +Reference < XPropertySetInfo > Statement::getPropertySetInfo() +{ + return OPropertySetHelper::createPropertySetInfo( getStatementPropertyArrayHelper() ); +} + + +Reference< XResultSet > Statement::getResultSet( ) +{ + return Reference< XResultSet > ( m_lastResultset, css::uno::UNO_QUERY ); +} + +sal_Int32 Statement::getUpdateCount( ) +{ + return m_multipleResultUpdateCount; +} + +sal_Bool Statement::getMoreResults( ) +{ + // The PostgreSQL C interface always returns a single result, + // so we will never have multiple ones. + // Implicitly close the open resultset (if any) as per spec, + // and setup to signal "no more result, neither as resultset, + // nor as update count". + Reference< XCloseable > lastResultSetHolder = m_lastResultset; + if( lastResultSetHolder.is() ) + lastResultSetHolder->close(); + m_multipleResultUpdateCount = -1; + return false; +} + + +void Statement::disposing() +{ + close(); +} + +Reference< XResultSet > Statement::getGeneratedValues( ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + return getGeneratedValuesFromLastInsert( + m_pSettings, m_connection, m_lastOidInserted, m_lastTableInserted, m_lastQuery ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_statement.hxx b/connectivity/source/drivers/postgresql/pq_statement.hxx new file mode 100644 index 000000000..e53c203af --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_statement.hxx @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_STATEMENT_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_STATEMENT_HXX +#include <cppuhelper/propshlp.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/component.hxx> + +#include <libpq-fe.h> + +#include "pq_connection.hxx" +#include <com/sun/star/sdbc/XMultipleResults.hpp> +#include <com/sun/star/sdbc/XGeneratedResultSet.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> + +namespace pq_sdbc_driver +{ + +static const sal_Int32 STATEMENT_CURSOR_NAME = 0; +static const sal_Int32 STATEMENT_ESCAPE_PROCESSING = 1; +static const sal_Int32 STATEMENT_FETCH_DIRECTION = 2; +static const sal_Int32 STATEMENT_FETCH_SIZE = 3; +static const sal_Int32 STATEMENT_MAX_FIELD_SIZE = 4; +static const sal_Int32 STATEMENT_MAX_ROWS = 5; +static const sal_Int32 STATEMENT_QUERY_TIME_OUT = 6; +static const sal_Int32 STATEMENT_RESULT_SET_CONCURRENCY = 7; +static const sal_Int32 STATEMENT_RESULT_SET_TYPE = 8; + +#define STATEMENT_SIZE 9 + +typedef ::cppu::WeakComponentImplHelper< css::sdbc::XStatement, + css::sdbc::XCloseable, + css::sdbc::XWarningsSupplier, + css::sdbc::XMultipleResults, + css::sdbc::XGeneratedResultSet, + css::sdbc::XResultSetMetaDataSupplier + > Statement_BASE; + +class Statement : public Statement_BASE, + public cppu::OPropertySetHelper +{ +private: + css::uno::Any m_props[STATEMENT_SIZE]; + css::uno::Reference< css::sdbc::XConnection > m_connection; + ConnectionSettings *m_pSettings; + css::uno::Reference< css::sdbc::XCloseable > m_lastResultset; + ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + bool m_multipleResultAvailable; + sal_Int32 m_multipleResultUpdateCount; + sal_Int32 m_lastOidInserted; + OUString m_lastTableInserted; + OString m_lastQuery; + +public: + /** + * @param ppConnection The piece of memory, pConnection points to, is accessible + * as long as a reference to parameter con is held. + */ + Statement( const rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection> & con, + struct ConnectionSettings *pSettings ); + + virtual ~Statement() override; +public: // XInterface + virtual void SAL_CALL acquire() throw() override { Statement_BASE::acquire(); } + virtual void SAL_CALL release() throw() override { Statement_BASE::release(); } + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & reqType ) override; + +public: // XCloseable + virtual void SAL_CALL close( ) override; + +public: // XStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( + const OUString& sql ) override; + virtual sal_Int32 SAL_CALL executeUpdate( const OUString& sql ) override; + virtual sal_Bool SAL_CALL execute( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; + +public: // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // OPropertySetHelper + virtual cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + using ::cppu::OPropertySetHelper::getFastPropertyValue; + + void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle ) const override; + + // XPropertySet + css::uno::Reference < css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + +public: // XGeneratedResultSet + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL + getGeneratedValues( ) override; + +public: // XMultipleResults + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getResultSet( ) override; + virtual sal_Int32 SAL_CALL getUpdateCount( ) override; + virtual sal_Bool SAL_CALL getMoreResults( ) override; + +public: // OComponentHelper + virtual void SAL_CALL disposing() override; + +public: // XResultSetMetaDataSupplier (is required by framework (see + // dbaccess/source/core/api/preparedstatement.cxx::getMetaData() ) + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + +private: + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkClosed(); + /// @throws css::sdbc::SQLException + void raiseSQLException( const OUString & sql, const char * errorMsg ); +}; + + +struct CommandData +{ + ConnectionSettings **ppSettings; + sal_Int32 *pLastOidInserted; + sal_Int32 *pMultipleResultUpdateCount; + bool *pMultipleResultAvailable; + OUString *pLastTableInserted; + css::uno::Reference< css::sdbc::XCloseable > *pLastResultset; + OString *pLastQuery; + ::rtl::Reference< comphelper::RefCountedMutex > refMutex; + css::uno::Reference< css::uno::XInterface > owner; + css::uno::Reference< css::sdbcx::XTablesSupplier > tableSupplier; + sal_Int32 concurrency; +}; + +bool executePostgresCommand( const OString & cmd, struct CommandData *data ); +css::uno::Reference< css::sdbc::XResultSet > getGeneratedValuesFromLastInsert( + ConnectionSettings *pConnectionSettings, + const css::uno::Reference< css::sdbc::XConnection > &connection, + sal_Int32 nLastOid, + const OUString & lastTableInserted, + const OString & lastQuery ); + + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_statics.cxx b/connectivity/source/drivers/postgresql/pq_statics.cxx new file mode 100644 index 000000000..a68242e2c --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_statics.cxx @@ -0,0 +1,672 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include "pq_statics.hxx" +#include "pq_updateableresultset.hxx" +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; + +using com::sun::star::beans::PropertyAttribute::READONLY; +using com::sun::star::beans::Property; + +namespace pq_sdbc_driver +{ + +namespace { + +struct DefColumnMetaData +{ + const char * columnName; + const char * tableName; + const char * schemaTableName; + const char * typeName; + sal_Int32 type; + sal_Int32 precision; + sal_Int32 scale; + bool isCurrency; + bool isNullable; + bool isAutoIncrement; +}; + +struct BaseTypeDef { const char * typeName; sal_Int32 value; }; + +struct PropertyDef +{ + PropertyDef( const OUString &str, const Type &t ) + : name( str ) , type( t ) {} + OUString name; + css::uno::Type type; +}; + +struct PropertyDefEx : public PropertyDef +{ + PropertyDefEx( const OUString & str, const Type &t , sal_Int32 a ) + : PropertyDef( str, t ) , attribute( a ) + {} + sal_Int32 attribute; +}; + +} + +static cppu::IPropertyArrayHelper * createPropertyArrayHelper( + PropertyDef const *props, int count , sal_Int16 attr ) +{ + Sequence< Property > seq( count ); + for( int i = 0 ; i < count ; i ++ ) + { + seq[i] = Property( props[i].name, i, props[i].type, attr ); + } + return new cppu::OPropertyArrayHelper( seq, true ); +} + +static cppu::IPropertyArrayHelper * createPropertyArrayHelper( + PropertyDefEx const *props, int count ) +{ + Sequence< Property > seq( count ); + for( int i = 0 ; i < count ; i ++ ) + { + seq[i] = Property( props[i].name, i, props[i].type, props[i].attribute ); + } + return new cppu::OPropertyArrayHelper( seq, true ); +} + +Statics & getStatics() +{ + static Statics* p = []() { + static Statics statics ; + statics.SYSTEM_TABLE = "SYSTEM TABLE"; + statics.TABLE = "TABLE"; + statics.VIEW = "VIEW"; + statics.UNKNOWN = "UNKNOWN"; + statics.YES = "YES"; + statics.NO = "NO"; + statics.NO_NULLS = "NO_NULLS"; + statics.NULABLE = "NULABLE"; + statics.NULLABLE_UNKNOWN = "NULLABLE_UNKNOWN"; + + statics.TYPE = "Type"; + statics.TYPE_NAME = "TypeName"; + statics.NAME = "Name"; + statics.SCHEMA_NAME = "SchemaName"; + statics.CATALOG_NAME = "CatalogName"; + statics.DESCRIPTION = "Description"; + statics.PRIVILEGES = "Privileges"; + + statics.DEFAULT_VALUE = "DefaultValue"; + statics.IS_AUTO_INCREMENT = "IsAutoIncrement"; + statics.IS_CURRENCY = "IsCurrency"; + statics.IS_NULLABLE = "IsNullable"; + statics.IS_ROW_VERSISON = "IsRowVersion"; + statics.PRECISION = "Precision"; + statics.SCALE = "Scale"; + + statics.cPERCENT = "%"; + statics.BEGIN = "BEGIN"; + statics.COMMIT = "COMMIT"; + statics.ROLLBACK = "ROLLBACK"; + + statics.KEY = "Key"; + statics.REFERENCED_TABLE = "ReferencedTable"; + statics.UPDATE_RULE = "UpdateRule"; + statics.DELETE_RULE = "DeleteRule"; + statics.PRIVATE_COLUMNS = "PrivateColumns"; + statics.PRIVATE_FOREIGN_COLUMNS = "PrivateForeignColumns"; + + statics.KEY_COLUMN = "KeyColumn"; + statics.RELATED_COLUMN = "RelatedColumn"; + statics.PASSWORD = "Password"; + statics.USER = "User"; + + statics.CURSOR_NAME = "CursorName"; + statics.ESCAPE_PROCESSING = "EscapeProcessing"; + statics.FETCH_DIRECTION = "FetchDirection"; + statics.FETCH_SIZE = "FetchSize"; + statics.IS_BOOKMARKABLE = "IsBookmarkable"; + statics.RESULT_SET_CONCURRENCY = "ResultSetConcurrency"; + statics.RESULT_SET_TYPE = "ResultSetType"; + + statics.COMMAND = "Command"; + statics.CHECK_OPTION = "CheckOption"; + + statics.TRUE = "t"; + statics.FALSE = "f"; + statics.IS_PRIMARY_KEY_INDEX = "IsPrimaryKeyIndex"; + statics.IS_CLUSTERED = "IsClustered"; + statics.IS_UNIQUE = "IsUnique"; + statics.IS_ASCENDING = "IsAscending"; + statics.PRIVATE_COLUMN_INDEXES = "PrivateColumnIndexes"; + statics.HELP_TEXT = "HelpText"; + + statics.CATALOG = "Catalog"; + + Type tString = cppu::UnoType<OUString>::get(); + Type tInt = cppu::UnoType<sal_Int32>::get(); + Type tBool = cppu::UnoType<bool>::get(); + Type tStringSequence = cppu::UnoType<css::uno::Sequence< OUString >>::get(); + + // Table props set + ImplementationStatics &ist = statics.refl.table; + ist.implName = "org.openoffice.comp.pq.sdbcx.Table"; + ist.serviceNames = Sequence< OUString > ( 1 ); + ist.serviceNames[0] = "com.sun.star.sdbcx.Table"; + PropertyDef tableDef[] = + { + PropertyDef( statics.CATALOG_NAME , tString ), + PropertyDef( statics.DESCRIPTION , tString ), + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.PRIVILEGES , tInt ), + PropertyDef( statics.SCHEMA_NAME , tString ), + PropertyDef( statics.TYPE , tString ) + }; + ist.pProps = createPropertyArrayHelper( + tableDef, SAL_N_ELEMENTS(tableDef), READONLY ); + + statics.refl.tableDescriptor.implName = + "org.openoffice.comp.pq.sdbcx.TableDescriptor"; + statics.refl.tableDescriptor.serviceNames = Sequence< OUString > (1); + statics.refl.tableDescriptor.serviceNames[0] = + "com.sun.star.sdbcx.TableDescriptor"; + PropertyDef tableDescDef[] = + { + PropertyDef( statics.CATALOG_NAME , tString ), + PropertyDef( statics.DESCRIPTION , tString ), + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.PRIVILEGES , tInt ), + PropertyDef( statics.SCHEMA_NAME , tString ) + }; + statics.refl.tableDescriptor.pProps = createPropertyArrayHelper( + tableDescDef, SAL_N_ELEMENTS(tableDescDef), 0 ); + + // Column props set + statics.refl.column.implName = "org.openoffice.comp.pq.sdbcx.Column"; + statics.refl.column.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.column.serviceNames[0] = "com.sun.star.sdbcx.Column"; + PropertyDefEx columnDef[] = + { + PropertyDefEx( statics.CATALOG_NAME , tString, READONLY ), + PropertyDefEx( statics.DEFAULT_VALUE, tString, READONLY ), + PropertyDefEx( statics.DESCRIPTION , tString, READONLY ), +// PropertyDefEx( statics.HELP_TEXT , tString, BOUND ), + PropertyDefEx( statics.IS_AUTO_INCREMENT, tBool, READONLY ), + PropertyDefEx( statics.IS_CURRENCY, tBool, READONLY ), + PropertyDefEx( statics.IS_NULLABLE, tInt, READONLY ), + PropertyDefEx( statics.IS_ROW_VERSISON, tBool,READONLY ), + PropertyDefEx( statics.NAME , tString,READONLY ), + PropertyDefEx( statics.PRECISION , tInt, READONLY ), + PropertyDefEx( statics.SCALE , tInt ,READONLY), + PropertyDefEx( statics.TYPE , tInt ,READONLY), + PropertyDefEx( statics.TYPE_NAME , tString ,READONLY) + }; + statics.refl.column.pProps = createPropertyArrayHelper( + columnDef, SAL_N_ELEMENTS(columnDef) ); + + statics.refl.columnDescriptor.implName = + "org.openoffice.comp.pq.sdbcx.ColumnDescriptor"; + statics.refl.columnDescriptor.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.columnDescriptor.serviceNames[0] = + "com.sun.star.sdbcx.ColumnDescriptor"; + PropertyDef columnDescDef[] = + { + PropertyDef( statics.CATALOG_NAME , tString ), + PropertyDef( statics.DEFAULT_VALUE, tString ), + PropertyDef( statics.DESCRIPTION , tString ), +// PropertyDef( statics.HELP_TEXT , tString ), + PropertyDef( statics.IS_AUTO_INCREMENT, tBool ), + PropertyDef( statics.IS_CURRENCY, tBool ), + PropertyDef( statics.IS_NULLABLE, tInt ), + PropertyDef( statics.IS_ROW_VERSISON, tBool ), + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.PRECISION , tInt ), + PropertyDef( statics.SCALE , tInt ), + PropertyDef( statics.TYPE , tInt ), + PropertyDef( statics.TYPE_NAME , tString ) + }; + + statics.refl.columnDescriptor.pProps = createPropertyArrayHelper( + columnDescDef, SAL_N_ELEMENTS(columnDescDef), 0 ); + + // Key properties + statics.refl.key.implName = "org.openoffice.comp.pq.sdbcx.Key"; + statics.refl.key.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.key.serviceNames[0] = "com.sun.star.sdbcx.Key"; + PropertyDef keyDef[] = + { + PropertyDef( statics.DELETE_RULE, tInt ), + PropertyDef( statics.NAME, tString ), + PropertyDef( statics.PRIVATE_COLUMNS, tStringSequence ), + PropertyDef( statics.PRIVATE_FOREIGN_COLUMNS, tStringSequence ), + PropertyDef( statics.REFERENCED_TABLE, tString ), + PropertyDef( statics.TYPE, tInt ), + PropertyDef( statics.UPDATE_RULE, tInt ) + }; + statics.refl.key.pProps = createPropertyArrayHelper( + keyDef, SAL_N_ELEMENTS(keyDef), READONLY ); + + + // Key properties + statics.refl.keyDescriptor.implName = + "org.openoffice.comp.pq.sdbcx.KeyDescriptor"; + statics.refl.keyDescriptor.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.keyDescriptor.serviceNames[0] = + "com.sun.star.sdbcx.KeyDescriptor"; + PropertyDef keyDescDef[] = + { + PropertyDef( statics.DELETE_RULE, tInt ), + PropertyDef( statics.NAME, tString ), + PropertyDef( statics.REFERENCED_TABLE, tString ), + PropertyDef( statics.TYPE, tInt ), + PropertyDef( statics.UPDATE_RULE, tInt ) + }; + statics.refl.keyDescriptor.pProps = createPropertyArrayHelper( + keyDescDef, SAL_N_ELEMENTS(keyDescDef), 0 ); + + + // KeyColumn props set + statics.refl.keycolumn.implName = "org.openoffice.comp.pq.sdbcx.KeyColumn"; + statics.refl.keycolumn.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.keycolumn.serviceNames[0] = "com.sun.star.sdbcx.KeyColumn"; + PropertyDef keycolumnDef[] = + { + PropertyDef( statics.CATALOG_NAME , tString ), + PropertyDef( statics.DEFAULT_VALUE, tString ), + PropertyDef( statics.DESCRIPTION , tString ), + PropertyDef( statics.IS_AUTO_INCREMENT, tBool ), + PropertyDef( statics.IS_CURRENCY, tBool ), + PropertyDef( statics.IS_NULLABLE, tInt ), + PropertyDef( statics.IS_ROW_VERSISON, tBool ), + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.PRECISION , tInt ), + PropertyDef( statics.RELATED_COLUMN, tString ), + PropertyDef( statics.SCALE , tInt ), + PropertyDef( statics.TYPE , tInt ), + PropertyDef( statics.TYPE_NAME , tString ) + }; + statics.refl.keycolumn.pProps = createPropertyArrayHelper( + keycolumnDef, SAL_N_ELEMENTS(keycolumnDef), READONLY ); + + // KeyColumn props set + statics.refl.keycolumnDescriptor.implName = + "org.openoffice.comp.pq.sdbcx.KeyColumnDescriptor"; + statics.refl.keycolumnDescriptor.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.keycolumnDescriptor.serviceNames[0] = + "com.sun.star.sdbcx.KeyColumnDescriptor"; + PropertyDef keycolumnDescDef[] = + { + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.RELATED_COLUMN, tString ) + }; + statics.refl.keycolumnDescriptor.pProps = createPropertyArrayHelper( + keycolumnDescDef, SAL_N_ELEMENTS(keycolumnDescDef), 0 ); + + // view props set + statics.refl.view.implName = "org.openoffice.comp.pq.sdbcx.View"; + statics.refl.view.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.view.serviceNames[0] = "com.sun.star.sdbcx.View"; + PropertyDef viewDef[] = + { + PropertyDef( statics.CATALOG_NAME , tString ), + PropertyDef( statics.CHECK_OPTION , tInt ), + PropertyDef( statics.COMMAND , tString ), + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.SCHEMA_NAME , tString ) + }; + statics.refl.view.pProps = createPropertyArrayHelper( + viewDef, SAL_N_ELEMENTS(viewDef), READONLY ); + + // view props set + statics.refl.viewDescriptor.implName = "org.openoffice.comp.pq.sdbcx.ViewDescriptor"; + statics.refl.viewDescriptor.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.viewDescriptor.serviceNames[0] = "com.sun.star.sdbcx.ViewDescriptor"; + statics.refl.viewDescriptor.pProps = createPropertyArrayHelper( + viewDef, SAL_N_ELEMENTS(viewDef), 0 ); // reuse view, as it is identical + // user props set + statics.refl.user.implName = "org.openoffice.comp.pq.sdbcx.User"; + statics.refl.user.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.user.serviceNames[0] = "com.sun.star.sdbcx.User"; + PropertyDef userDefRO[] = + { + PropertyDef( statics.NAME , tString ) + }; + statics.refl.user.pProps = createPropertyArrayHelper( + userDefRO, SAL_N_ELEMENTS(userDefRO), READONLY ); + + // user props set + statics.refl.userDescriptor.implName = + "org.openoffice.comp.pq.sdbcx.UserDescriptor"; + statics.refl.userDescriptor.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.userDescriptor.serviceNames[0] = + "com.sun.star.sdbcx.UserDescriptor"; + PropertyDef userDefWR[] = + { + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.PASSWORD , tString ) + }; + statics.refl.userDescriptor.pProps = createPropertyArrayHelper( + userDefWR, SAL_N_ELEMENTS(userDefWR), 0 ); + + // index props set + statics.refl.index.implName = "org.openoffice.comp.pq.sdbcx.Index"; + statics.refl.index.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.index.serviceNames[0] = "com.sun.star.sdbcx.Index"; + PropertyDef indexDef[] = + { + PropertyDef( statics.CATALOG , tString ), + PropertyDef( statics.IS_CLUSTERED, tBool ), + PropertyDef( statics.IS_PRIMARY_KEY_INDEX, tBool ), + PropertyDef( statics.IS_UNIQUE, tBool ), + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.PRIVATE_COLUMN_INDEXES, tStringSequence ) + }; + statics.refl.index.pProps = createPropertyArrayHelper( + indexDef, SAL_N_ELEMENTS(indexDef), READONLY ); + + // index props set + statics.refl.indexDescriptor.implName = + "org.openoffice.comp.pq.sdbcx.IndexDescriptor"; + statics.refl.indexDescriptor.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.indexDescriptor.serviceNames[0] = + "com.sun.star.sdbcx.IndexDescriptor"; + statics.refl.indexDescriptor.pProps = createPropertyArrayHelper( + indexDef, SAL_N_ELEMENTS(indexDef), 0 ); + + // indexColumn props set + statics.refl.indexColumn.implName = "org.openoffice.comp.pq.sdbcx.IndexColumn"; + statics.refl.indexColumn.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.indexColumn.serviceNames[0] = "com.sun.star.sdbcx.IndexColumn"; + PropertyDef indexColumnDef[] = + { + PropertyDef( statics.CATALOG_NAME , tString ), + PropertyDef( statics.DEFAULT_VALUE, tString ), + PropertyDef( statics.DESCRIPTION , tString ), + PropertyDef( statics.IS_ASCENDING, tBool ), + PropertyDef( statics.IS_AUTO_INCREMENT, tBool ), + PropertyDef( statics.IS_CURRENCY, tBool ), + PropertyDef( statics.IS_NULLABLE, tInt ), + PropertyDef( statics.IS_ROW_VERSISON, tBool ), + PropertyDef( statics.NAME , tString ), + PropertyDef( statics.PRECISION , tInt ), + PropertyDef( statics.SCALE , tInt ), + PropertyDef( statics.TYPE , tInt ), + PropertyDef( statics.TYPE_NAME , tString ) + }; + statics.refl.indexColumn.pProps = createPropertyArrayHelper( + indexColumnDef, SAL_N_ELEMENTS(indexColumnDef), READONLY ); + + // indexColumn props set + statics.refl.indexColumnDescriptor.implName = + "org.openoffice.comp.pq.sdbcx.IndexColumnDescriptor"; + statics.refl.indexColumnDescriptor.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.indexColumnDescriptor.serviceNames[0] = + "com.sun.star.sdbcx.IndexColumnDescriptor"; + PropertyDef indexColumnDescDef[] = + { + PropertyDef( statics.IS_ASCENDING, tBool ), + PropertyDef( statics.NAME , tString ) + }; + statics.refl.indexColumnDescriptor.pProps = createPropertyArrayHelper( + indexColumnDescDef, SAL_N_ELEMENTS(indexColumnDescDef), 0 ); + + // resultset + statics.refl.resultSet.implName = "org.openoffice.comp.pq.ResultSet"; + statics.refl.resultSet.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.resultSet.serviceNames[0] = "com.sun.star.sdbc.ResultSet"; + statics.refl.resultSet.types = UpdateableResultSet::getStaticTypes( false /* updateable */ ); + PropertyDef resultSet[] = + { + PropertyDef( statics.CURSOR_NAME , tString ), + PropertyDef( statics.ESCAPE_PROCESSING , tBool ), + PropertyDef( statics.FETCH_DIRECTION , tInt ), + PropertyDef( statics.FETCH_SIZE , tInt ), + PropertyDef( statics.IS_BOOKMARKABLE , tBool ), + PropertyDef( statics.RESULT_SET_CONCURRENCY , tInt ), + PropertyDef( statics.RESULT_SET_TYPE , tInt ) + }; + statics.refl.resultSet.pProps = createPropertyArrayHelper( + resultSet, SAL_N_ELEMENTS(resultSet), 0 ); + + // updateableResultset + statics.refl.updateableResultSet.implName = "org.openoffice.comp.pq.UpdateableResultSet"; + statics.refl.updateableResultSet.serviceNames = Sequence< OUString > ( 1 ); + statics.refl.updateableResultSet.serviceNames[0] = "com.sun.star.sdbc.ResultSet"; + statics.refl.updateableResultSet.types = UpdateableResultSet::getStaticTypes( true /* updateable */ ); + statics.refl.updateableResultSet.pProps = createPropertyArrayHelper( + resultSet, SAL_N_ELEMENTS(resultSet), 0 ); + + // databasemetadata + statics.tablesRowNames = std::vector< OUString > ( 5 ); + statics.tablesRowNames[TABLE_INDEX_CATALOG] = "TABLE_CAT"; + statics.tablesRowNames[TABLE_INDEX_SCHEMA] = "TABLE_SCHEM"; + statics.tablesRowNames[TABLE_INDEX_NAME] = "TABLE_NAME"; + statics.tablesRowNames[TABLE_INDEX_TYPE] = "TABLE_TYPE"; + statics.tablesRowNames[TABLE_INDEX_REMARKS] = "REMARKS"; + + statics.primaryKeyNames = std::vector< OUString > ( 6 ); + statics.primaryKeyNames[0] = "TABLE_CAT"; + statics.primaryKeyNames[1] = "TABLE_SCHEM"; + statics.primaryKeyNames[2] = "TABLE_NAME"; + statics.primaryKeyNames[3] = "COLUMN_NAME"; + statics.primaryKeyNames[4] = "KEY_SEQ"; + statics.primaryKeyNames[5] = "PK_NAME"; + + statics.SELECT = "SELECT"; + statics.UPDATE = "UPDATE"; + statics.INSERT = "INSERT"; + statics.DELETE = "DELETE"; + statics.RULE = "RULE"; + statics.REFERENCES = "REFERENCES"; + statics.TRIGGER = "TRIGGER"; + statics.EXECUTE = "EXECUTE"; + statics.USAGE = "USAGE"; + statics.CREATE = "CREATE"; + statics.TEMPORARY = "TEMPORARY"; + statics.INDEX = "Index"; + statics.INDEX_COLUMN = "IndexColumn"; + + statics.schemaNames = std::vector< OUString > ( 1 ); + statics.schemaNames[0] = "TABLE_SCHEM"; + + statics.tableTypeData = std::vector< std::vector< Any > >( 2 ); + + statics.tableTypeData[0] = std::vector< Any > ( 1 ); + statics.tableTypeData[0][0] <<= statics.TABLE; + +// statics.tableTypeData[2] = Sequence< Any > ( 1 ); +// statics.tableTypeData[2][0] <<= statics.VIEW; + + statics.tableTypeData[1] = std::vector< Any > ( 1 ); + statics.tableTypeData[1][0] <<= statics.SYSTEM_TABLE; + + statics.tableTypeNames = std::vector< OUString > ( 1 ); + statics.tableTypeNames[0] = "TABLE_TYPE"; + + statics.columnRowNames = + { + "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", + "DATA_TYPE", "TYPE_NAME", "COLUMN_SIZE", "BUFFER_LENGTH", + "DECIMAL_DIGITS", "NUM_PREC_RADIX", "NULLABLE", "REMARKS", + "COLUMN_DEF", "SQL_DATA_TYPE", "SQL_DATETIME_SUB", "CHAR_OCTET_LENGTH", + "ORDINAL_POSITION", "IS_NULLABLE" + }; + + statics.typeinfoColumnNames = + { + "TYPE_NAME", "DATA_TYPE", "PRECISION", "LITERAL_PREFIX", + "LITERAL_SUFFIX", "CREATE_PARAMS", "NULLABLE", "CASE_SENSITIVE", + "SEARCHABLE", "UNSIGNED_ATTRIBUTE", "FIXED_PREC_SCALE", + "AUTO_INCREMENT", "LOCAL_TYPE_NAME", "MINIMUM_SCALE", + "MAXIMUM_SCALE", "SQL_DATA_TYPE", "SQL_DATETIME_SUB", + "NUM_PREC_RADIX" + }; + + statics.indexinfoColumnNames = + { + "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", + "NON_UNIQUE", "INDEX_QUALIFIER", "INDEX_NAME", + "TYPE", "ORDINAL_POSITION", "COLUMN_NAME", + "ASC_OR_DESC", "CARDINALITY", "PAGES", "FILTER_CONDITION" + }; + + statics.resultSetArrayColumnNames = { "INDEX" , "VALUE" }; + + // LEM TODO see if a refresh is needed; obtain automatically from pg_catalog.pg_type? + BaseTypeDef baseTypeDefs[] = + { + { "bool" , css::sdbc::DataType::BOOLEAN }, + { "bytea", css::sdbc::DataType::VARBINARY }, + { "char" , css::sdbc::DataType::CHAR }, + + { "int8" , css::sdbc::DataType::BIGINT }, + { "serial8" , css::sdbc::DataType::BIGINT }, + + + { "int2" , css::sdbc::DataType::SMALLINT }, + + { "int4" , css::sdbc::DataType::INTEGER }, +// { "regproc" , css::sdbc::DataType::INTEGER }, +// { "oid" , css::sdbc::DataType::INTEGER }, +// { "xid" , css::sdbc::DataType::INTEGER }, +// { "cid" , css::sdbc::DataType::INTEGER }, +// { "serial", css::sdbc::DataType::INTEGER }, +// { "serial4", css::sdbc::DataType::INTEGER }, + + { "text", css::sdbc::DataType::LONGVARCHAR }, + { "bpchar", css::sdbc::DataType::CHAR }, + { "varchar", css::sdbc::DataType::VARCHAR }, + + { "float4", css::sdbc::DataType::REAL }, + { "float8", css::sdbc::DataType::DOUBLE }, + + { "numeric", css::sdbc::DataType::NUMERIC }, + { "decimal", css::sdbc::DataType::DECIMAL }, + + { "date", css::sdbc::DataType::DATE }, + { "time", css::sdbc::DataType::TIME }, + { "timestamp", css::sdbc::DataType::TIMESTAMP }, + +// { "_bool" , css::sdbc::DataType::ARRAY }, +// { "_bytea", css::sdbc::DataType::ARRAY }, +// { "_char" , css::sdbc::DataType::ARRAY }, + +// { "_int8" , css::sdbc::DataType::ARRAY }, +// { "_serial8" , css::sdbc::DataType::ARRAY }, + + +// { "_int2" , css::sdbc::DataType::ARRAY }, + +// { "_int4" , css::sdbc::DataType::ARRAY }, +// { "_regproc" , css::sdbc::DataType::ARRAY }, +// { "_oid" , css::sdbc::DataType::ARRAY }, +// { "_xid" , css::sdbc::DataType::ARRAY }, +// { "_cid" , css::sdbc::DataType::ARRAY }, + +// { "_text", css::sdbc::DataType::ARRAY }, +// { "_bpchar", css::sdbc::DataType::ARRAY }, +// { "_varchar", css::sdbc::DataType::ARRAY }, + +// { "_float4", css::sdbc::DataType::ARRAY }, +// { "_float8", css::sdbc::DataType::ARRAY }, + +// { "_numeric", css::sdbc::DataType::ARRAY }, +// { "_decimal", css::sdbc::DataType::ARRAY }, + +// { "_date", css::sdbc::DataType::ARRAY }, // switch to date later +// { "_time", css::sdbc::DataType::ARRAY }, // switch to time later + + { nullptr, 0 } + }; + int i; + for( i = 0 ; baseTypeDefs[i].typeName ; i ++ ) + { + statics.baseTypeMap[ + OUString::createFromAscii( baseTypeDefs[i].typeName) ] = + baseTypeDefs[i].value; + } + + // This is the metadata for the columns of the recordset returned + // by css::sdbc::XDatabaseMetaData::getTypeInfo(), + // that is what is returned by getTypeInfo().getMetaData() + DefColumnMetaData defTypeInfoMetaData[] = + { + { "TYPE_NAME", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::VARCHAR, 0,50,false,false,false }, // 0 + { "DATA_TYPE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::SMALLINT, 0,50,false,false,false }, // 1 + { "PRECISION", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::INTEGER, 0,50,false,false,false }, // 2 + { "LITERAL_PREFIX", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::VARCHAR, 0,50,false,false,false }, // 3 + { "LITERAL_SUFFIX", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::VARCHAR, 0,50,false,false,false }, // 4 + { "CREATE_PARAMS", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::VARCHAR, 0,50,false,false,false }, // 5 + { "NULLABLE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::INTEGER, 0,50,false,false,false }, // 6 + { "CASE_SENSITIVE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::BOOLEAN, 0,50,false,false,false }, // 7 + { "SEARCHABLE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::SMALLINT, 0,50,false,false,false }, // 8 + { "UNSIGNED_ATTRIBUTE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::BOOLEAN, 0,50,false,false,false }, // 9 + { "FIXED_PREC_SCALE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::BOOLEAN, 0,50,false,false,false }, // 10 + { "AUTO_INCREMENT", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::BOOLEAN, 0,50,false,false,false }, // 11 + { "LOCAL_TYPE_NAME", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::VARCHAR, 0,50,false,false,false }, // 12 + { "MINIMUM_SCALE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::SMALLINT, 0,50,false,false,false}, // 13 + { "MAXIMUM_SCALE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::SMALLINT, 0,50,false,false,false }, // 14 + { "SQL_DATA_TYPE", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::INTEGER, 0,50,false,false,false }, // 15 + { "SQL_DATETIME_SUB", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::INTEGER, 0,50,false,false,false}, // 16 + { "NUM_PREC_RADIX", "TYPEINFO", "pg_catalog", "", css::sdbc::DataType::INTEGER, 0,50,false,false,false }, // 17 + {nullptr,nullptr,nullptr,nullptr,0,0,0,false,false,false} + }; + + for( i = 0 ; defTypeInfoMetaData[i].columnName ; i++ ) + { + statics.typeInfoMetaData.push_back( + ColumnMetaData( + OUString::createFromAscii( defTypeInfoMetaData[i].columnName ), + OUString::createFromAscii( defTypeInfoMetaData[i].tableName ), + OUString::createFromAscii( defTypeInfoMetaData[i].schemaTableName ), + OUString::createFromAscii( defTypeInfoMetaData[i].typeName ), + defTypeInfoMetaData[i].type, + defTypeInfoMetaData[i].precision, + defTypeInfoMetaData[i].scale, + defTypeInfoMetaData[i].isCurrency, + defTypeInfoMetaData[i].isNullable, + defTypeInfoMetaData[i].isAutoIncrement ) ); + } + + return &statics; + }(); + return *p; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_statics.hxx b/connectivity/source/drivers/postgresql/pq_statics.hxx new file mode 100644 index 000000000..78051cd69 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_statics.hxx @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_STATICS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_STATICS_HXX + +#include <unordered_map> +#include <vector> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include <cppuhelper/propshlp.hxx> + +namespace pq_sdbc_driver +{ + +struct ColumnMetaData +{ + ColumnMetaData( + const OUString &_columnName, + const OUString &_tableName, + const OUString &_schemaTableName, + const OUString &_typeName, + sal_Int32 _type, + sal_Int32 _precision, + sal_Int32 _scale, + bool _isCurrency, + bool _isNullable, + bool _isAutoIncrement ) : + columnName( _columnName ), + tableName( _tableName ), + schemaTableName( _schemaTableName ), + typeName( _typeName ), + type( _type ), + precision( _precision ), + scale( _scale ), + isCurrency( _isCurrency ), + isNullable( _isNullable ), + isAutoIncrement( _isAutoIncrement ) + {} + + OUString columnName; + OUString tableName; + OUString schemaTableName; + OUString typeName; + sal_Int32 type; + sal_Int32 precision; + sal_Int32 scale; + bool isCurrency; + bool isNullable; + bool isAutoIncrement; +}; + +typedef std::vector< ColumnMetaData > ColumnMetaDataVector; + +typedef std::unordered_map +< + OUString, + sal_Int32 +> BaseTypeMap; + +struct ImplementationStatics +{ + ImplementationStatics() : + pProps(nullptr) + {} + + OUString implName; + css::uno::Sequence< OUString > serviceNames; + cppu::IPropertyArrayHelper *pProps; + css::uno::Sequence< css::uno::Type > types; +}; + +struct ReflectionImplementations +{ + struct ImplementationStatics table; + struct ImplementationStatics tableDescriptor; + struct ImplementationStatics column; + struct ImplementationStatics columnDescriptor; + struct ImplementationStatics key; + struct ImplementationStatics keyDescriptor; + struct ImplementationStatics keycolumn; + struct ImplementationStatics keycolumnDescriptor; + struct ImplementationStatics user; + struct ImplementationStatics userDescriptor; + struct ImplementationStatics view; + struct ImplementationStatics viewDescriptor; + struct ImplementationStatics index; + struct ImplementationStatics indexDescriptor; + struct ImplementationStatics indexColumn; + struct ImplementationStatics indexColumnDescriptor; + + struct ImplementationStatics updateableResultSet; + struct ImplementationStatics resultSet; +}; + +static const sal_Int32 TABLE_INDEX_CATALOG = 0; +static const sal_Int32 TABLE_INDEX_SCHEMA = 1; +static const sal_Int32 TABLE_INDEX_NAME = 2; +static const sal_Int32 TABLE_INDEX_TYPE = 3; +static const sal_Int32 TABLE_INDEX_REMARKS = 4; + +struct Statics +{ + OUString SYSTEM_TABLE; + OUString TABLE; + OUString VIEW; + OUString UNKNOWN; + OUString YES; + OUString NO; + OUString NO_NULLS; + OUString NULABLE; + OUString NULLABLE_UNKNOWN; + OUString SELECT; + OUString UPDATE; + OUString INSERT; + OUString DELETE; + OUString RULE; + OUString REFERENCES; + OUString TRIGGER; + OUString EXECUTE; + OUString USAGE; + OUString CREATE; + OUString TEMPORARY; + OUString INDEX; + OUString INDEX_COLUMN; + + OUString NAME; + OUString SCHEMA_NAME; + OUString CATALOG_NAME; + OUString DESCRIPTION; + OUString TYPE; + OUString TYPE_NAME; + OUString PRIVILEGES; + + OUString DEFAULT_VALUE; + OUString IS_AUTO_INCREMENT; + OUString IS_CURRENCY; + OUString IS_NULLABLE; + OUString IS_ROW_VERSISON; + OUString PRECISION; + OUString SCALE; + + OUString cPERCENT; + + OUString BEGIN; + OUString ROLLBACK; + OUString COMMIT; + + OUString KEY; + OUString REFERENCED_TABLE; + OUString UPDATE_RULE; + OUString DELETE_RULE; + OUString PRIVATE_COLUMNS; + OUString PRIVATE_FOREIGN_COLUMNS; + + OUString KEY_COLUMN; + OUString RELATED_COLUMN; + + OUString PASSWORD; + OUString USER; + + OUString CURSOR_NAME; + OUString ESCAPE_PROCESSING; + OUString FETCH_DIRECTION; + OUString FETCH_SIZE; + OUString IS_BOOKMARKABLE; + OUString RESULT_SET_CONCURRENCY; + OUString RESULT_SET_TYPE; + + OUString COMMAND; + OUString CHECK_OPTION; + + OUString TRUE; + OUString FALSE; + + OUString IS_PRIMARY_KEY_INDEX; + OUString IS_CLUSTERED; + OUString IS_UNIQUE; + OUString PRIVATE_COLUMN_INDEXES; + OUString HELP_TEXT; + + OUString CATALOG; + OUString IS_ASCENDING; + ReflectionImplementations refl; + + std::vector< OUString > tablesRowNames; + std::vector< OUString > columnRowNames; + std::vector< OUString > primaryKeyNames; + std::vector< OUString > schemaNames; + std::vector< OUString > tableTypeNames; + std::vector< OUString > typeinfoColumnNames; + std::vector< OUString > indexinfoColumnNames; + std::vector< OUString > resultSetArrayColumnNames; + std::vector< std::vector< css::uno::Any > > tableTypeData; + + ColumnMetaDataVector typeInfoMetaData; + BaseTypeMap baseTypeMap; + Statics(){} +private: + Statics( const Statics & ) = delete; + Statics & operator = ( const Statics & ) = delete; +}; + +Statics & getStatics(); + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_tools.cxx b/connectivity/source/drivers/postgresql/pq_tools.cxx new file mode 100644 index 000000000..bece8a806 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_tools.cxx @@ -0,0 +1,1247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/config.h> + +#include <o3tl/any.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> + +#include "pq_tools.hxx" +#include "pq_statics.hxx" + +#include <libpq-fe.h> +#include <string.h> + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::lang::XComponent; + +using com::sun::star::sdbc::SQLException; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XConnection; +using com::sun::star::sdbc::XPreparedStatement; +using com::sun::star::sdbc::XParameters; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XRow; + +using com::sun::star::sdbcx::XColumnsSupplier; + +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::UNO_QUERY_THROW; +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::XInterface; +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; + +using com::sun::star::container::XEnumeration; +using com::sun::star::container::XEnumerationAccess; + +namespace pq_sdbc_driver +{ + +OUString concatQualified( const OUString & a, const OUString &b) +{ + return a + "." + b; +} + +static OString iOUStringToOString( const OUString& str, ConnectionSettings const *settings) { + OSL_ENSURE(settings, "pgsql-sdbc: OUStringToOString got NULL settings"); + return OUStringToOString( str, ConnectionSettings::encoding ); +} + +OString OUStringToOString( const OUString& str, ConnectionSettings const *settings) { + return iOUStringToOString( str, settings ); +} + +void bufferEscapeConstant( OUStringBuffer & buf, const OUString & value, ConnectionSettings *settings ) +{ + + OString y = iOUStringToOString( value, settings ); + OStringBuffer strbuf( y.getLength() * 2 + 2 ); + int error; + int len = PQescapeStringConn(settings->pConnection, const_cast<char*>(strbuf.getStr()), y.getStr() , y.getLength(), &error ); + if ( error ) + { + char *errstr = PQerrorMessage(settings->pConnection); + // As of PostgreSQL 9.1, the only possible errors "involve invalid multibyte encoding" + // According to https://www2.opengroup.org/ogsys/jsp/publications/PublicationDetails.jsp?publicationid=11216 + // (X/Open SQL CLI, March 1995, ISBN: 1-85912-081-4, X/Open Document Number: C451) + // 22018 is for "Invalid character value" and seems to be the best match. + // We have no good XInterface Reference to pass here, so just give NULL + throw SQLException(OUString(errstr, strlen(errstr), ConnectionSettings::encoding), + nullptr, + "22018", + -1, + Any()); + } + strbuf.setLength( len ); + // Previously here RTL_TEXTENCODING_ASCII_US; as we set the PostgreSQL client_encoding to UTF8, + // we get UTF8 here, too. I'm not sure why it worked well before... + buf.append( OStringToOUString( strbuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ) ); +} + +static void ibufferQuoteConstant( OUStringBuffer & buf, const OUString & value, ConnectionSettings *settings ) +{ + buf.append( "'" ); + bufferEscapeConstant( buf, value, settings ); + buf.append( "'" ); +} + +void bufferQuoteConstant( OUStringBuffer & buf, const OUString & value, ConnectionSettings *settings ) +{ + return ibufferQuoteConstant( buf, value, settings ); +} + +void bufferQuoteAnyConstant( OUStringBuffer & buf, const Any &val, ConnectionSettings *settings ) +{ + if( val.hasValue() ) + { + OUString str; + val >>= str; + bufferQuoteConstant( buf, str, settings ); + } + else + buf.append( "NULL" ); +} + +static void ibufferQuoteIdentifier( OUStringBuffer & buf, const OUString &toQuote, ConnectionSettings *settings ) +{ + OSL_ENSURE(settings, "pgsql-sdbc: bufferQuoteIdentifier got NULL settings"); + + OString y = iOUStringToOString( toQuote, settings ); + char *cstr = PQescapeIdentifier(settings->pConnection, y.getStr(), y.getLength()); + if ( cstr == nullptr ) + { + char *errstr = PQerrorMessage(settings->pConnection); + // Implementation-defined SQLACCESS error + throw SQLException(OUString(errstr, strlen(errstr), ConnectionSettings::encoding), + nullptr, + "22018", + -1, + Any()); + } + buf.append( OStringToOUString( cstr, RTL_TEXTENCODING_UTF8 ) ); + PQfreemem( cstr ); +} + +void bufferQuoteIdentifier( OUStringBuffer & buf, const OUString &toQuote, ConnectionSettings *settings ) +{ + return ibufferQuoteIdentifier(buf, toQuote, settings); +} + + +void bufferQuoteQualifiedIdentifier( + OUStringBuffer & buf, const OUString &schema, const OUString &table, ConnectionSettings *settings ) +{ + ibufferQuoteIdentifier(buf, schema, settings); + buf.append( "." ); + ibufferQuoteIdentifier(buf, table, settings); +} + +void bufferQuoteQualifiedIdentifier( + OUStringBuffer & buf, + const OUString &schema, + const OUString &table, + const OUString &col, + ConnectionSettings *settings) +{ + ibufferQuoteIdentifier(buf, schema, settings); + buf.append( "." ); + ibufferQuoteIdentifier(buf, table, settings); + buf.append( "." ); + ibufferQuoteIdentifier(buf, col, settings); +} + + +OUString extractStringProperty( + const Reference< XPropertySet > & descriptor, const OUString &name ) +{ + OUString value; + descriptor->getPropertyValue( name ) >>= value; + return value; +} + +bool extractBoolProperty( + const Reference< XPropertySet > & descriptor, const OUString &name ) +{ + bool value = false; + descriptor->getPropertyValue( name ) >>= value; + return value; +} + +sal_Int32 extractIntProperty( + const Reference< XPropertySet > & descriptor, const OUString &name ) +{ + sal_Int32 ret = 0; + descriptor->getPropertyValue( name ) >>= ret; + return ret; +} + +void disposeObject( const css::uno::Reference< css::uno::XInterface > & r ) +{ + Reference< XComponent > comp( r, UNO_QUERY ); + if( comp.is() ) + comp->dispose(); +} + +void disposeNoThrow( const css::uno::Reference< css::uno::XInterface > & r ) +{ + try + { + disposeObject( r ); + } + catch( SQLException & ) + { + // ignore this + } + +} + +Reference< XConnection > extractConnectionFromStatement( const Reference< XInterface > & stmt ) +{ + Reference< XConnection > ret; + + Reference< css::sdbc::XStatement > owner( stmt, UNO_QUERY ); + if( owner.is() ) + ret = owner->getConnection(); + else + { + Reference< css::sdbc::XPreparedStatement > myowner( stmt, UNO_QUERY ); + if( myowner.is() ) + ret = myowner->getConnection(); + if( ! ret.is() ) + throw SQLException( + "PQSDBC: Couldn't retrieve connection from statement", + Reference< XInterface > () , OUString(), 0 , css::uno::Any() ); + } + + return ret; + +} + +DisposeGuard::DisposeGuard( const Reference< XInterface > & r ) + : d( r ) +{} + +DisposeGuard::~DisposeGuard() +{ + disposeNoThrow( d ); +} + +TransactionGuard::TransactionGuard( const Reference< XStatement > &stmt ) + : m_stmt( stmt ), + m_commited( false ) +{ + m_stmt->executeUpdate( getStatics().BEGIN ); +} + +void TransactionGuard::commit() +{ + m_stmt->executeUpdate( getStatics().COMMIT ); + m_commited = true; +} + +void TransactionGuard::executeUpdate( const OUString & sql ) +{ + m_stmt->executeUpdate( sql ); +} + +TransactionGuard::~TransactionGuard() +{ + try + { + if( ! m_commited ) + m_stmt->executeUpdate( getStatics().ROLLBACK ); + } + catch( css::uno::Exception & ) + { + // ignore, we are within a dtor + } + + disposeNoThrow( m_stmt ); +} + + +bool isWhitespace( sal_Unicode c ) +{ + return ' ' == c || 9 == c || 10 == c || 13 == c; +} + +OUString extractTableFromInsert( const OUString & sql ) +{ + OUString ret; + int i = 0; + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + + if( sql.matchIgnoreAsciiCase("insert", i) ) + { + i += 6; + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + if( sql.matchIgnoreAsciiCase("into", i) ) + { + i +=4; + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + int start = i; + bool quote = (sql[i] == '"'); + for( i++ ; i < sql.getLength() ; i ++ ) + { + if( quote && sql[i] == '"' ) + { + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + if( '.' == sql[i] ) + { + while (i < sql.getLength() && isWhitespace(sql[i])) { i++; } + if( '"' == sql[i] ) + { + // the second part of the table name does not use quotes + // parse on + quote = false; + } + } + else + { + // end quoted name, ok + break; + } + } + else + { + if( isWhitespace( sql[i] ) ) + { + // found the end of an unquoted name + break; + } + } + } + ret = sql.copy(start, i - start ).trim(); +// printf( "pq_statement: parsed table name %s from insert\n" , +// OUStringToOString( ret, RTL_TEXTENCODING_ASCII_US).getStr() ); + } + } + return ret; +} + + +static bool isOperator( char c ) +{ + bool ret; + switch(c) + { + case '+': + case '-': + case '*': + case '/': + case '<': + case '>': + case '=': + case '~': + case '!': + case '@': + case '#': + case '%': + case '^': + case '&': + case '|': + case '`': + case '?': + case '$': + ret = true; + break; + default: + ret = false; + } + return ret; +} + +void splitSQL( const OString & sql, std::vector< OString > &vec ) +{ + int length = sql.getLength(); + + int i = 0; + bool singleQuote = false; + bool doubleQuote = false; + int start = 0; + for( ; i < length ; i ++ ) + { + char c = sql[i]; + if( doubleQuote ) + { + if( '"' == c ) + { + vec.push_back( OString( &sql.getStr()[start], i-start+1 ) ); + start = i + 1; + doubleQuote = false; + } + } + else if( singleQuote ) + { + if( '\'' == c && (i+1) < length && '\'' == sql[i+1] ) + { + // two subsequent single quotes within a quoted string + // mean a single quote within the string + i ++; + } + else if( '\'' == c ) + { + vec.push_back( OString( &sql.getStr()[start], i - start +1 ) ); + start = i + 1; // leave single quotes ! + singleQuote = false; + } + } + else + { + if( '"' == c ) + { + vec.push_back( OString( &sql.getStr()[start], i - start ) ); + doubleQuote = true; + start = i; + } + else if( '\'' == c ) + { + vec.push_back( OString( &sql.getStr()[start], i - start ) ); + singleQuote = true; + start = i; + } + } + } + if( start < i ) + vec.push_back( OString( &sql.getStr()[start] , i - start ) ); + +// for( i = 0 ; i < vec.size() ; i ++ ) +// printf( "%s!" , vec[i].getStr() ); +// printf( "\n" ); + +} + +void tokenizeSQL( const OString & sql, std::vector< OString > &vec ) +{ + int length = sql.getLength(); + + int i = 0; + bool singleQuote = false; + bool doubleQuote = false; + int start = 0; + for( ; i < length ; i ++ ) + { + char c = sql[i]; + if( doubleQuote ) + { + if( '"' == c ) + { + vec.push_back( OString( &sql.getStr()[start], i-start ) ); + start = i + 1; + doubleQuote = false; + } + } + else if( singleQuote ) + { + if( '\'' == c ) + { + vec.push_back( OString( &sql.getStr()[start], i - start +1 ) ); + start = i + 1; // leave single quotes ! + singleQuote = false; + } + } + else + { + if( '"' == c ) + { + doubleQuote = true; + start = i +1; // skip double quotes ! + } + else if( '\'' == c ) + { + singleQuote = true; + start = i; // leave single quotes + } + else if( isWhitespace( c ) ) + { + if( i == start ) + start ++; // skip additional whitespace + else + { + vec.push_back( OString( &sql.getStr()[start], i - start ) ); + start = i +1; + } + } + else if( ',' == c || isOperator( c ) || '(' == c || ')' == c ) + { + if( i - start ) + vec.push_back( OString( &sql.getStr()[start], i - start ) ); + vec.push_back( OString( &sql.getStr()[i], 1 ) ); + start = i + 1; + } + else if( '.' == c ) + { + if( ( i > start && sql[start] >= '0' && sql[start] <= '9' ) || + ( i == start && i > 1 && isWhitespace( sql[i-1] ) ) ) + { + // ignore, is a literal + } + else + { + if( i - start ) + vec.push_back( OString( &sql.getStr()[start], i - start ) ); + vec.push_back( OString( "." ) ); + start = i + 1; + } + } + } + } + if( start < i ) + vec.push_back( OString( &sql.getStr()[start] , i - start ) ); + +// for( i = 0 ; i < vec.size() ; i ++ ) +// printf( "%s!" , vec[i].getStr() ); +// printf( "\n" ); +} + + +void splitConcatenatedIdentifier( const OUString & source, OUString *first, OUString *second) +{ + std::vector< OString > vec; + tokenizeSQL( OUStringToOString( source, RTL_TEXTENCODING_UTF8 ), vec ); + switch (vec.size()) + { + case 1: + first->clear(); + *second = OStringToOUString( vec[0], RTL_TEXTENCODING_UTF8 ); + break; + case 3: + *first = OStringToOUString( vec[0], RTL_TEXTENCODING_UTF8 ); + *second = OStringToOUString( vec[2], RTL_TEXTENCODING_UTF8 ); + break; + default: + SAL_WARN("connectivity.postgresql", + "pq_tools::splitConcatenatedIdentifier unexpected number of tokens in identifier: " + << vec.size()); + } +} + +OUString array2String( const css::uno::Sequence< Any > &seq ) +{ + OUStringBuffer buf(128); + int len = seq.getLength(); + buf.append( "{" ); + for( int i = 0 ; i < len ; i ++ ) + { + OUString element; + seq[i] >>= element; + + if( i > 0 ) + buf.append( "," ); + int strLength = element.getLength(); + buf.append( "\"" ); + for( int j = 0 ; j < strLength ; j ++ ) + { + sal_Unicode c = element[j]; + if( c == '\\' || c == '"' || c == '{' || c == '}' ) + { + buf.append( "\\" ); + } + buf.append( c ); + } + buf.append( "\"" ); + } + buf.append( "}" ); + return buf.makeStringAndClear(); +} + + +std::vector< Any > parseArray( const OUString & str ) +{ + int len = str.getLength(); + bool doubleQuote = false; + int brackets = 0; + int i = 0; + + OUStringBuffer current; + std::vector<Any> elements; + bool doubleQuotedValue = false; + while( i < len ) + { + sal_Unicode c = str[i]; + sal_Unicode cnext = str[i+1]; + if( doubleQuote ) + { + if( '\\' == c ) + { + i ++; + current.append( cnext ); + } + else if( '"' == c ) + { + doubleQuote = false; + doubleQuotedValue = true; // signal, that there was an empty element + } + else + { + current.append( c ); + } + } + else if ( '{' == c ) + { + brackets ++; + } + else if( '}' == c ) + { + brackets --; + if( brackets < 0 ) + { + throw SQLException( + "error during array parsing, didn't expect a } at position " + + OUString::number(i) + " ('" + str + "')", + Reference< XInterface > (), OUString(), 1, Any() ); + } + if( brackets == 0 ) + { + if( !current.isEmpty() || doubleQuotedValue ) + elements.push_back( makeAny( current.makeStringAndClear() ) ); + } + else + { + current.append( c ); + } + } + else if( '"' == c ) + { +// if( current.getLength() != 0 ) +// { +// OUStringBuffer buf; +// buf.appendAscii( "error during array parsing, didn't expect a \" at position " ); +// buf.append( i ); +// buf.append( " ('" ); +// buf.append( str ); +// buf.append( "')" ); +// throw SDBCException( +// buf.makeStringAndClear(), +// Reference< XInterface > (), 1, Any() ); +// } +// else +// { + doubleQuote = true; +// } + } + else if( ',' == c && brackets == 1) + { + doubleQuotedValue = false; + elements.push_back( makeAny( current.makeStringAndClear() ) ); + } + else if( isWhitespace( c ) ) + { + // ignore whitespace without quotes + } + else + { + current.append( c ); + } + i++; + } + return elements; +} + +std::vector< sal_Int32 > parseIntArray( const OUString & str ) +{ + sal_Int32 start = 0; + std::vector<sal_Int32> vec; +// printf( ">%s<\n" , OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); + for( sal_Int32 i = str.indexOf( ' ' ) ; i != -1 ; i = str.indexOf( ' ', start) ) + { + vec.push_back( rtl_ustr_toInt32( &str.pData->buffer[start], 10 ) ); +// printf( "found %d\n" , rtl_ustr_toInt32( &str.pData->buffer[start], 10 )); + start = i + 1; + } + vec.push_back( rtl_ustr_toInt32( &str.pData->buffer[start], 10 ) ); +// printf( "found %d\n" , rtl_ustr_toInt32( &str.pData->buffer[start], 10 )); + return vec; +} + +void fillAttnum2attnameMap( + Int2StringMap &map, + const Reference< css::sdbc::XConnection > &conn, + const OUString &schema, + const OUString &table ) +{ + Reference< XPreparedStatement > prep = conn->prepareStatement( + "SELECT attname,attnum " + "FROM pg_attribute " + "INNER JOIN pg_class ON attrelid = pg_class.oid " + "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid " + "WHERE relname=? AND nspname=?" ); + + Reference< XParameters > paras( prep, UNO_QUERY_THROW ); + paras->setString( 1 , table ); + paras->setString( 2 , schema ); + Reference< XResultSet > rs = prep->executeQuery(); + + Reference< XRow > xRow( rs , UNO_QUERY_THROW ); + while( rs->next() ) + { + map[ xRow->getInt(2) ] = xRow->getString(1); + } +} + +OString extractSingleTableFromSelect( const std::vector< OString > &vec ) +{ + OString ret; + + if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( + vec[0].pData->buffer, vec[0].pData->length, "select" , 6 , 6 ) ) + { + size_t token = 0; + + for( token = 1; token < vec.size() ; token ++ ) + { + if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( + vec[token].getStr(), vec[token].getLength(), "from" , 4 , 4 ) ) + { + // found from + break; + } + } + token ++; + + if( token < vec.size() && 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( + vec[token].pData->buffer, vec[token].pData->length, "only " , 4 , 4 ) ) + { + token ++; + } + + if( token < vec.size() && vec[token] != "(" ) + { + // it is a table or a function name + OStringBuffer buf(128); + if( '"' == vec[token][0] ) + buf.append( &(vec[token].getStr()[1]) , vec[token].getLength() -2 ); + else + buf.append( vec[token] ); + token ++; + + if( token < vec.size() ) + { + if( vec[token] == "." ) + { + buf.append( vec[token] ); + token ++; + if( token < vec.size() ) + { + if( '"' == vec[token][0] ) + buf.append( &(vec[token].getStr()[1]) , vec[token].getLength() -2 ); + else + buf.append( vec[token] ); + token ++; + } + } + } + + ret = buf.makeStringAndClear(); + // now got my table candidate + + if( token < vec.size() && vec[token] == "(" ) + { + // whoops, it is a function + ret.clear(); + } + else + { + if( token < vec.size() ) + { + if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( + vec[token].pData->buffer, vec[token].pData->length, "as" , 2, 2 ) ) + { + token += 2; // skip alias + } + } + + if( token < vec.size() ) + { + if( vec[token] == "," ) + { + // whoops, multiple tables are used + ret.clear(); + } + else + { + static const char * forbiddenKeywords[] = + { "join", "natural", "outer", "inner", "left", "right", "full" , nullptr }; + for( int i = 0 ; forbiddenKeywords[i] ; i ++ ) + { + size_t nKeywordLen = strlen(forbiddenKeywords[i]); + if( 0 == rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( + vec[token].pData->buffer, vec[token].pData->length, + forbiddenKeywords[i], nKeywordLen, + nKeywordLen ) ) + { + // whoops, it is a join + ret.clear(); + } + } + } + } + } + } + } + return ret; + +} + +OUString getColExprForDefaultSettingVal(ConnectionSettings const *settings) +{ + return (PQserverVersion( settings->pConnection ) < 80000)? + OUString("pg_attrdef.adsrc"): + OUString("pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid, true)"); +} + +css::uno::Sequence< sal_Int32 > string2intarray( const OUString & str ) +{ + css::uno::Sequence< sal_Int32 > ret; + const sal_Int32 strlen = str.getLength(); + if( str.getLength() > 1 ) + { + sal_Int32 start = 0; + sal_uInt32 c; + for (;;) + { + c = str.iterateCodePoints(&start); + if (!iswspace(c)) + break; + if ( start == strlen) + return ret; + } + if ( c != L'{' ) + return ret; + for (;;) + { + c = str.iterateCodePoints(&start); + if ( !iswspace(c) ) + break; + if ( start == strlen) + return ret; + } + if ( c == L'}' ) + return ret; + + std::vector< sal_Int32 > vec; + do + { + OUStringBuffer digits; + do + { + if(!iswspace(c)) + break; + if ( start == strlen) + return ret; + c=str.iterateCodePoints(&start); + } while ( c ); + do + { + if (!iswdigit(c)) + break; + if ( start == strlen) + return ret; + digits.append(OUString(&c, 1)); + c = str.iterateCodePoints(&start); + } while ( c ); + vec.push_back( digits.makeStringAndClear().toInt32() ); + do + { + if(!iswspace(c)) + break; + if ( start == strlen) + return ret; + c = str.iterateCodePoints(&start); + } while ( c ); + if ( c == L'}' ) + break; + if ( str.iterateCodePoints(&start) != L',' ) + return ret; + if ( start == strlen) + return ret; + } while( true ); + // vec is guaranteed non-empty + assert(vec.size() > 0); + ret = css::uno::Sequence< sal_Int32 > ( vec.data() , vec.size() ); + } + return ret; +} + + +Sequence< OUString > convertMappedIntArray2StringArray( + const Int2StringMap &map, const Sequence< sal_Int32 > &intArray ) +{ + Sequence< OUString > ret( intArray.getLength() ); + for( int i = 0; i < intArray.getLength() ; i ++ ) + { + Int2StringMap::const_iterator ii = map.find( intArray[i] ); + if( ii != map.end() ) + ret[i] = ii->second; + } + return ret; +} + + +OUString sqltype2string( const Reference< XPropertySet > & desc ) +{ + OUStringBuffer typeName; + typeName.append( extractStringProperty( desc, getStatics().TYPE_NAME ) ); + sal_Int32 precision = extractIntProperty( desc, getStatics().PRECISION ); + + if( precision ) + { + switch( extractIntProperty( desc, getStatics().TYPE ) ) + { + case css::sdbc::DataType::VARBINARY: + case css::sdbc::DataType::VARCHAR: + case css::sdbc::DataType::CHAR: + { + typeName.append( "(" ); + typeName.append( precision ); + typeName.append( ")" ); + break; + } + case css::sdbc::DataType::DECIMAL: + case css::sdbc::DataType::NUMERIC: + { + typeName.append( "(" ); + typeName.append( precision ); + typeName.append( "," ); + typeName.append( extractIntProperty( desc, getStatics().SCALE ) ); + typeName.append( ")" ); + break; + } + default: + ((void)0); + } + } + return typeName.makeStringAndClear(); +} + + +static void keyType2String( OUStringBuffer & buf, sal_Int32 keyType ) +{ + if( css::sdbc::KeyRule::CASCADE == keyType ) + { + buf.append( "CASCADE " ); + } + else if( css::sdbc::KeyRule::RESTRICT == keyType ) + { + buf.append( "RESTRICT " ); + } + else if( css::sdbc::KeyRule::SET_DEFAULT == keyType ) + { + buf.append( "SET DEFAULT " ); + } + else if( css::sdbc::KeyRule::SET_NULL == keyType ) + { + buf.append( "SET NULL " ); + } + else //if( css::sdbc::KeyRule::NO_ACTION == keyType ) + { + buf.append( "NO ACTION " ); + } +} + +void bufferKey2TableConstraint( + OUStringBuffer &buf, const Reference< XPropertySet > &key, ConnectionSettings *settings ) +{ + Statics &st = getStatics(); + sal_Int32 type = extractIntProperty( key, st.TYPE ); + OUString referencedTable = extractStringProperty( key, st.REFERENCED_TABLE ); + sal_Int32 updateRule = extractIntProperty( key, st.UPDATE_RULE ); + sal_Int32 deleteRule = extractIntProperty( key, st.DELETE_RULE ); + bool foreign = false; + if( type == css::sdbcx::KeyType::UNIQUE ) + { + buf.append( "UNIQUE( " ); + } + else if( type == css::sdbcx::KeyType::PRIMARY ) + { + buf.append( "PRIMARY KEY( " ); + } + else if( type == css::sdbcx::KeyType::FOREIGN ) + { + foreign = true; + buf.append( "FOREIGN KEY( " ); + } + + Reference< XColumnsSupplier > columns( key, UNO_QUERY ); + if( columns.is() ) + { + Reference< XEnumerationAccess > colEnumAccess( columns->getColumns(), UNO_QUERY ); + if( colEnumAccess.is() ) + { + Reference< XEnumeration > colEnum = colEnumAccess->createEnumeration(); + bool first = true; + while(colEnum.is() && colEnum->hasMoreElements() ) + { + if( first ) + { + first = false; + } + else + { + buf.append( ", " ); + } + Reference< XPropertySet > keyColumn( colEnum->nextElement(), UNO_QUERY_THROW ); + bufferQuoteIdentifier(buf, extractStringProperty( keyColumn, st.NAME ), settings ); + } + } + } + buf.append( ") " ); + + if( !foreign ) + return; + + buf.append( "REFERENCES " ); + OUString schema; + OUString tableName; + splitConcatenatedIdentifier( referencedTable, &schema, &tableName ); + bufferQuoteQualifiedIdentifier(buf , schema, tableName, settings ); + if(columns.is() ) + { + Reference< XEnumerationAccess > colEnumAccess( columns->getColumns(), UNO_QUERY); + if( colEnumAccess.is() ) + { + buf.append( " (" ); + Reference< XEnumeration > colEnum(colEnumAccess->createEnumeration()); + bool first = true; + while(colEnum.is() && colEnum->hasMoreElements() ) + { + if( first ) + { + first = false; + } + else + { + buf.append( ", " ); + } + Reference< XPropertySet > keyColumn( colEnum->nextElement(), UNO_QUERY_THROW ); + bufferQuoteIdentifier( + buf, extractStringProperty( keyColumn, st.RELATED_COLUMN ), settings ); + } + buf.append( ") " ); + } + } + + buf.append( "ON DELETE " ); + keyType2String( buf, deleteRule ); + buf.append( " ON UPDATE " ); + keyType2String( buf, updateRule ); + +} + +void extractNameValuePairsFromInsert( String2StringMap & map, const OString & lastQuery ) +{ + std::vector< OString > vec; + tokenizeSQL( lastQuery, vec ); + + int nSize = vec.size(); +// printf( "1 %d\n", nSize ); + if( !(nSize > 6 && + vec[0].equalsIgnoreAsciiCase( "insert" ) && + vec[1].equalsIgnoreAsciiCase( "into" )) ) + return; + + int n = 2; + +// printf( "1a\n" ); + // skip table name + if( vec[n+1].equalsIgnoreAsciiCase( "." ) ) + { + n +=2; + } + + n ++; + if( !vec[n].equalsIgnoreAsciiCase( "(" ) ) + return; + + std::vector< OString> names; +// printf( "2\n" ); + // extract names + n++; + while( nSize > n && ! vec[n].equalsIgnoreAsciiCase( ")" ) ) + { + names.push_back( vec[n] ); + if( nSize > n+1 && vec[n+1].equalsIgnoreAsciiCase( "," ) ) + { + n ++; + } + n++; + } + n++; + + // now read the values + if( !(nSize > n +1 && vec[n].equalsIgnoreAsciiCase("VALUES") && + vec[n+1].equalsIgnoreAsciiCase( "(" )) ) + return; + + n +=2; +// printf( "3\n" ); + for (auto& name : names) + { + if (n >= nSize) + break; + + map[name] = vec[n]; + if( nSize > n+1 && vec[n+1].equalsIgnoreAsciiCase(",") ) + { + n ++; + } + n++; + } +} + +OUString querySingleValue( + const css::uno::Reference< css::sdbc::XConnection > &connection, + const OUString &query ) +{ + OUString ret; + Reference< XStatement > stmt = connection->createStatement(); + DisposeGuard guard( stmt ); + Reference< XResultSet > rs = stmt->executeQuery( query ); + Reference< XRow > xRow( rs, UNO_QUERY ); + if( rs->next() ) + ret = xRow->getString( 1 ); + return ret; +} + + +// copied from connectivity/source/dbtools, can't use the function directly +bool implSetObject( const Reference< XParameters >& _rxParameters, + const sal_Int32 _nColumnIndex, const Any& _rValue) +{ + bool bSuccessfullyReRouted = true; + switch (_rValue.getValueTypeClass()) + { + case css::uno::TypeClass_HYPER: + { + _rxParameters->setLong( _nColumnIndex, sal_Int64(0) ); + } + break; + + case css::uno::TypeClass_VOID: + _rxParameters->setNull(_nColumnIndex,css::sdbc::DataType::VARCHAR); + break; + + case css::uno::TypeClass_STRING: + _rxParameters->setString(_nColumnIndex, *o3tl::forceAccess<OUString>(_rValue)); + break; + + case css::uno::TypeClass_BOOLEAN: + _rxParameters->setBoolean(_nColumnIndex, *o3tl::forceAccess<bool>(_rValue)); + break; + + case css::uno::TypeClass_BYTE: + _rxParameters->setByte(_nColumnIndex, *o3tl::forceAccess<sal_Int8>(_rValue)); + break; + + case css::uno::TypeClass_UNSIGNED_SHORT: + case css::uno::TypeClass_SHORT: + _rxParameters->setShort(_nColumnIndex, *o3tl::forceAccess<sal_Int16>(_rValue)); + break; + + case css::uno::TypeClass_CHAR: + _rxParameters->setString(_nColumnIndex, OUString(*o3tl::forceAccess<sal_Unicode>(_rValue))); + break; + + case css::uno::TypeClass_UNSIGNED_LONG: + case css::uno::TypeClass_LONG: + _rxParameters->setInt(_nColumnIndex, *o3tl::forceAccess<sal_Int32>(_rValue)); + break; + + case css::uno::TypeClass_FLOAT: + _rxParameters->setFloat(_nColumnIndex, *o3tl::forceAccess<float>(_rValue)); + break; + + case css::uno::TypeClass_DOUBLE: + _rxParameters->setDouble(_nColumnIndex, *o3tl::forceAccess<double>(_rValue)); + break; + + case css::uno::TypeClass_SEQUENCE: + if (auto s = o3tl::tryAccess<Sequence< sal_Int8 >>(_rValue)) + { + _rxParameters->setBytes(_nColumnIndex, *s); + } + else + bSuccessfullyReRouted = false; + break; + case css::uno::TypeClass_STRUCT: + if (auto s1 = o3tl::tryAccess<css::util::DateTime>(_rValue)) + _rxParameters->setTimestamp(_nColumnIndex, *s1); + else if (auto s2 = o3tl::tryAccess<css::util::Date>(_rValue)) + _rxParameters->setDate(_nColumnIndex, *s2); + else if (auto s3 = o3tl::tryAccess<css::util::Time>(_rValue)) + _rxParameters->setTime(_nColumnIndex, *s3); + else + bSuccessfullyReRouted = false; + break; + + case css::uno::TypeClass_INTERFACE: + { + Reference< css::io::XInputStream > xStream; + if (_rValue >>= xStream) + { + _rValue >>= xStream; + _rxParameters->setBinaryStream(_nColumnIndex, xStream, xStream->available()); + break; + } + [[fallthrough]]; + } + default: + bSuccessfullyReRouted = false; + + } + + return bSuccessfullyReRouted; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_tools.hxx b/connectivity/source/drivers/postgresql/pq_tools.hxx new file mode 100644 index 000000000..af751f8e6 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_tools.hxx @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_TOOLS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_TOOLS_HXX + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> + +#include <rtl/ustrbuf.hxx> +#include <rtl/string.hxx> + +#include "pq_connection.hxx" +#include <vector> + +namespace pq_sdbc_driver +{ +bool isWhitespace( sal_Unicode c ); + +OUString concatQualified( const OUString & a, const OUString &b); + +OString OUStringToOString( const OUString& str, ConnectionSettings const *settings); + +void bufferQuoteConstant( OUStringBuffer & buf, const OUString & str, ConnectionSettings *settings ); +void bufferQuoteAnyConstant( OUStringBuffer & buf, const css::uno::Any &val, ConnectionSettings *settings ); + +void bufferEscapeConstant( OUStringBuffer & buf, const OUString & str, ConnectionSettings *settings ); + +OUString sqltype2string( + const css::uno::Reference< css::beans::XPropertySet > & column ); + + +void bufferQuoteQualifiedIdentifier( + OUStringBuffer & buf, const OUString &schema, const OUString &name, ConnectionSettings *settings ); + +void bufferQuoteQualifiedIdentifier( + OUStringBuffer & buf, + const OUString &schema, + const OUString &name, + const OUString &col, + ConnectionSettings *settings ); + +void bufferQuoteIdentifier( OUStringBuffer & buf, const OUString &toQuote, ConnectionSettings *settings ); +void bufferKey2TableConstraint( + OUStringBuffer &buf, + const css::uno::Reference< css::beans::XPropertySet > &key, + ConnectionSettings *settings ); + +OUString extractStringProperty( + const css::uno::Reference< css::beans::XPropertySet > & descriptor, + const OUString &name ); + +sal_Int32 extractIntProperty( + const css::uno::Reference< css::beans::XPropertySet > & descriptor, + const OUString &name ); + +bool extractBoolProperty( + const css::uno::Reference< css::beans::XPropertySet > & descriptor, + const OUString &name ); + +void disposeNoThrow( const css::uno::Reference< css::uno::XInterface > & r ); +void disposeObject( const css::uno::Reference< css::uno::XInterface > & r ); + +OUString extractTableFromInsert( const OUString & sql ); +OString extractSingleTableFromSelect( const std::vector< OString > &vec ); + +OUString getColExprForDefaultSettingVal(ConnectionSettings const *settings); + +void tokenizeSQL( const OString & sql, std::vector< OString > &vec ); +void splitSQL( const OString & sql, std::vector< OString > &vec ); +std::vector< sal_Int32 > parseIntArray( const OUString & str ); +/// @throws css::sdbc::SQLException +std::vector< css::uno::Any > parseArray( const OUString & str ); + +OUString array2String( const css::uno::Sequence< css::uno::Any > &seq ); + +css::uno::Reference< css::sdbc::XConnection > extractConnectionFromStatement( + const css::uno::Reference< css::uno::XInterface > & stmt ); + +void splitConcatenatedIdentifier( const OUString & source, OUString *first, OUString *second); + + +void fillAttnum2attnameMap( + Int2StringMap &map, + const css::uno::Reference< css::sdbc::XConnection > &conn, + const OUString &schema, + const OUString &table ); + +css::uno::Sequence< sal_Int32 > string2intarray( const OUString & str ); + +css::uno::Sequence< OUString > convertMappedIntArray2StringArray( + const Int2StringMap &map, const css::uno::Sequence< sal_Int32> &source ); + +typedef std::unordered_map< OString, OString > String2StringMap; + +OUString querySingleValue( + const css::uno::Reference< css::sdbc::XConnection > &connection, + const OUString &query ); + +void extractNameValuePairsFromInsert( String2StringMap & map, const OString & lastQuery ); +sal_Int32 typeNameToDataType( const OUString &typeName, const OUString &typtype ); + +// copied from connectivity/source/dbtools, can't use the function directly +bool implSetObject( const css::uno::Reference< css::sdbc::XParameters >& _rxParameters, + const sal_Int32 _nColumnIndex, const css::uno::Any& _rValue); + +class DisposeGuard +{ + css::uno::Reference< css::uno::XInterface > d; +public: + explicit DisposeGuard(const css::uno::Reference< css::uno::XInterface > & r ); + ~DisposeGuard(); + +}; + +class TransactionGuard +{ + css::uno::Reference< css::sdbc::XStatement > m_stmt; + bool m_commited; +public: + /// takes over ownership of given statement + explicit TransactionGuard( const css::uno::Reference< css::sdbc::XStatement > &stmt ); + ~TransactionGuard( ); + + void commit(); + void executeUpdate( const OUString & sql ); +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_updateableresultset.cxx b/connectivity/source/drivers/postgresql/pq_updateableresultset.cxx new file mode 100644 index 000000000..880adc647 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_updateableresultset.cxx @@ -0,0 +1,551 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 200? by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> + +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XGeneratedResultSet.hpp> + +#include "pq_updateableresultset.hxx" +#include "pq_resultsetmetadata.hxx" +#include "pq_tools.hxx" +#include "pq_statics.hxx" + +#include <string.h> + +#include <connectivity/dbconversion.hxx> + +using osl::MutexGuard; + + +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; + +using com::sun::star::sdbc::XGeneratedResultSet; +using com::sun::star::sdbc::XResultSetMetaDataSupplier; +using com::sun::star::sdbc::SQLException; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XCloseable; +using com::sun::star::sdbc::XColumnLocate; +using com::sun::star::sdbc::XResultSetUpdate; +using com::sun::star::sdbc::XRowUpdate; +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XStatement; + +using com::sun::star::beans::XFastPropertySet; +using com::sun::star::beans::XPropertySet; +using com::sun::star::beans::XMultiPropertySet; + +using namespace dbtools; + +namespace pq_sdbc_driver +{ + + +css::uno::Reference< css::sdbc::XCloseable > UpdateableResultSet::createFromPGResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + ConnectionSettings **ppSettings, + PGresult *result, + const OUString &schema, + const OUString &table, + const std::vector< OUString > &primaryKey ) +{ + sal_Int32 columnCount = PQnfields( result ); + sal_Int32 rowCount = PQntuples( result ); + std::vector< OUString > columnNames( columnCount ); + for( int i = 0 ; i < columnCount ; i ++ ) + { + char * name = PQfname( result, i ); + columnNames[i] = OUString( name, strlen(name), ConnectionSettings::encoding ); + } + std::vector< std::vector< Any > > data( rowCount ); + + // copy all the data into unicode strings (also binaries, as we yet + // don't know, what a binary is and what not!) + for( int row = 0 ; row < rowCount ; row ++ ) + { + std::vector< Any > aRow( columnCount ); + for( int col = 0 ; col < columnCount ; col ++ ) + { + if( ! PQgetisnull( result, row, col ) ) + { + char * val = PQgetvalue( result, row, col ); + + aRow[col] <<= + OUString( val, strlen( val ), ConnectionSettings::encoding ); + } + } + data[row] = aRow; + } + + UpdateableResultSet *pRS = new UpdateableResultSet( + mutex, owner, columnNames, data, ppSettings, schema, table, primaryKey ); + + Reference <XCloseable > ret = pRS; // give it a refcount + + pRS->m_meta = new ResultSetMetaData( mutex, pRS,nullptr, ppSettings, result, schema, table ); + + PQclear( result ); // we don't need it anymore + + return ret; +} + +css::uno::Any UpdateableResultSet::queryInterface( + const css::uno::Type & reqType ) +{ + Any ret = SequenceResultSet::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< XResultSetUpdate * > ( this ), + static_cast< XRowUpdate * > ( this ) ); + return ret; +} + + +css::uno::Sequence< css::uno::Type > UpdateableResultSet::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<XResultSetUpdate>::get(), + cppu::UnoType<XRowUpdate>::get(), + SequenceResultSet::getTypes()); + + return collection.getTypes(); + +} + +css::uno::Sequence< sal_Int8> UpdateableResultSet::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +OUString UpdateableResultSet::buildWhereClause() +{ + OUString ret; + if( !m_primaryKey.empty() ) + { + OUStringBuffer buf( 128 ); + buf.append( " WHERE " ); + for( size_t i = 0 ; i < m_primaryKey.size() ; i ++ ) + { + if( i > 0 ) + buf.append( " AND " ); + sal_Int32 index = findColumn( m_primaryKey[i] ); + bufferQuoteIdentifier( buf, m_primaryKey[i], *m_ppSettings ); + buf.append( " = " ); + bufferQuoteConstant( buf, getString( index ), *m_ppSettings ); + } + ret = buf.makeStringAndClear(); + } + return ret; +} + + +void UpdateableResultSet::insertRow( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + SAL_INFO("connectivity.postgresql", "UpdateableResultSet::insertRow() got called"); + + if( ! m_insertRow ) + throw SQLException( + "pq_resultset.insertRow: moveToInsertRow has not been called !", + *this, OUString(), 1, Any() ); + + OUStringBuffer buf( 128 ); + buf.append( "INSERT INTO " ); + bufferQuoteQualifiedIdentifier( buf, m_schema, m_table, *m_ppSettings ); + buf.append( " ( " ); + + int columns = 0; + for( UpdateableFieldVector::size_type i = 0 ; i < m_updateableField.size() ; i++ ) + { + if( m_updateableField[i].isTouched ) + { + if( columns > 0 ) + buf.append( ", " ); + columns ++; + bufferQuoteIdentifier( buf, m_columnNames[i], *m_ppSettings); + } + } + buf.append( " ) VALUES ( " ); + + columns = 0; + for(const UpdateableField & i : m_updateableField) + { + if( i.isTouched ) + { + if( columns > 0 ) + buf.append( " , " ); + columns ++; + bufferQuoteAnyConstant( buf, i.value, *m_ppSettings ); + +// OUString val; +// m_updateableField[i].value >>= val; +// buf.append( val ); +// OStringToOUString(val, (*m_ppSettings)->encoding ) ); + } + } + + buf.append( " )" ); + + Reference< XStatement > stmt = + extractConnectionFromStatement(m_owner)->createStatement(); + DisposeGuard dispGuard( stmt ); + stmt->executeUpdate( buf.makeStringAndClear() ); + + // reflect the changes ! + m_rowCount ++; + m_data.resize( m_rowCount ); + m_data[m_rowCount-1] = std::vector< Any > ( m_fieldCount ); + Reference< XGeneratedResultSet > result( stmt, UNO_QUERY ); + if( result.is() ) + { + Reference< XResultSet > rs = result->getGeneratedValues(); + if( rs.is() && rs->next() ) + { + Reference< XColumnLocate > columnLocate( rs, UNO_QUERY ); + Reference< XRow> xRow ( rs, UNO_QUERY ); + for( int i = 0 ; i < m_fieldCount ; i++ ) + { + int field = columnLocate->findColumn( m_columnNames[i] ); + if( field >= 1 ) + { + m_data[m_rowCount-1][i] <<= xRow->getString( field ); +// printf( "adding %s %s\n" , +// OUStringToOString( m_columnNames[i], RTL_TEXTENCODING_ASCII_US).getStr(), +// OUStringToOString( xRow->getString( field ), RTL_TEXTENCODING_ASCII_US).getStr() ); + + } + } + } + else + { + // do the best we can ( DEFAULT and AUTO increment values fail ! ) + for( int i = 0 ; i < m_fieldCount ; i ++ ) + { + if( m_updateableField[i].isTouched ) + m_data[m_rowCount-1][i] = m_updateableField[i].value; + } + } + } + + // cleanup + m_updateableField = UpdateableFieldVector(); +} + +void UpdateableResultSet::updateRow( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + SAL_INFO("connectivity.postgresql", "UpdateableResultSet::updateRow() got called"); + + if( m_insertRow ) + throw SQLException( + "pq_resultset.updateRow: moveToCurrentRow has not been called !", + *this, OUString(), 1, Any() ); + + OUStringBuffer buf( 128 ); + buf.append( "UPDATE " ); + bufferQuoteQualifiedIdentifier( buf, m_schema, m_table, *m_ppSettings ); + buf.append( "SET " ); + + int columns = 0; + for( UpdateableFieldVector::size_type i = 0; i < m_updateableField.size() ; i ++ ) + { + if( m_updateableField[i].isTouched ) + { + if( columns > 0 ) + buf.append( ", " ); + columns ++; + + buf.append( m_columnNames[i] ); + buf.append( " = " ); + bufferQuoteAnyConstant( buf, m_updateableField[i].value, *m_ppSettings ); +// OUString val; +// m_updateableField[i].value >>= val; +// bufferQuoteConstant( buf, val ): +// buf.append( val ); + } + } + buf.append( buildWhereClause() ); + + Reference< XStatement > stmt = extractConnectionFromStatement(m_owner)->createStatement(); + DisposeGuard dispGuard( stmt ); + stmt->executeUpdate( buf.makeStringAndClear() ); + + // reflect the changes ! + for( int i = 0 ; i < m_fieldCount ; i ++ ) + { + if( m_updateableField[i].isTouched ) + m_data[m_row][i] = m_updateableField[i].value; + } + m_updateableField = UpdateableFieldVector(); +} + +void UpdateableResultSet::deleteRow( ) +{ + SAL_INFO("connectivity.postgresql", "UpdateableResultSet::deleteRow() got called"); + + if( m_insertRow ) + throw SQLException( + "pq_resultset.deleteRow: deleteRow cannot be called when on insert row !", + *this, OUString(), 1, Any() ); + + if( m_row < 0 || m_row >= m_rowCount ) + { + throw SQLException( + "deleteRow cannot be called on invalid row (" + + OUString::number(m_row) + ")", + *this, OUString(), 0, Any() ); + } + + Reference< XStatement > stmt = extractConnectionFromStatement(m_owner)->createStatement(); + DisposeGuard dispGuard( stmt ); + OUStringBuffer buf( 128 ); + buf.append( "DELETE FROM " ); + bufferQuoteQualifiedIdentifier( buf, m_schema, m_table, *m_ppSettings ); + buf.append( " " ); + buf.append( buildWhereClause() ); + + stmt->executeUpdate( buf.makeStringAndClear() ); + + // reflect the changes ! + for( int i = m_row + 1; i < m_row ; i ++ ) + { + m_data[i-1] = m_data[i]; + } + m_rowCount --; + m_data.resize( m_rowCount ); + } + +void UpdateableResultSet::cancelRowUpdates( ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + m_updateableField = UpdateableFieldVector(); +} + +void UpdateableResultSet::moveToInsertRow( ) +{ + m_insertRow = true; +} + +void UpdateableResultSet::moveToCurrentRow( ) +{ + m_insertRow = false; +} + +void UpdateableResultSet::checkUpdate( sal_Int32 columnIndex) +{ + checkColumnIndex( columnIndex ); + if( m_updateableField.empty() ) + m_updateableField = UpdateableFieldVector( m_fieldCount ); + m_updateableField[columnIndex-1].isTouched = true; +} + +void UpdateableResultSet::updateNull( sal_Int32 columnIndex ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + m_updateableField[columnIndex-1].value = Any(); +} + +void UpdateableResultSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + Statics &st = getStatics(); + m_updateableField[columnIndex-1].value <<= ( x ? st.TRUE : st.FALSE ); + +} + +void UpdateableResultSet::updateByte( sal_Int32 columnIndex, sal_Int8 x ) +{ + updateInt(columnIndex,x); +} + +void UpdateableResultSet::updateShort( sal_Int32 columnIndex, sal_Int16 x ) +{ + updateInt( columnIndex, x ); +} + +void UpdateableResultSet::updateInt( sal_Int32 columnIndex, sal_Int32 x ) +{ + updateLong( columnIndex, x ); +// MutexGuard guard( m_xMutex->GetMutex() ); +// checkClosed(); +// checkUpdate( columnIndex ); + +// m_updateableField[columnIndex-1].value <<= OUString::valueOf( x ); + +} + +void UpdateableResultSet::updateLong( sal_Int32 columnIndex, sal_Int64 x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + +// OStringBuffer buf( 20 ); +// buf.append( "'" ); +// buf.append( (sal_Int64) x ); +// buf.append( "'" ); + m_updateableField[columnIndex-1].value <<= OUString::number( x ); +} + +void UpdateableResultSet::updateFloat( sal_Int32 columnIndex, float x ) +{ + + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + m_updateableField[columnIndex-1].value <<= OUString::number( x ); +} + +void UpdateableResultSet::updateDouble( sal_Int32 columnIndex, double x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + m_updateableField[columnIndex-1].value <<= OUString::number( x ); +} + +void UpdateableResultSet::updateString( sal_Int32 columnIndex, const OUString& x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + m_updateableField[columnIndex-1].value <<= x; +} + +void UpdateableResultSet::updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + checkClosed(); + checkUpdate( columnIndex ); + + size_t len; + unsigned char * escapedString = + PQescapeBytea( reinterpret_cast<unsigned char const *>(x.getConstArray()), x.getLength(), &len); + if( ! escapedString ) + { + throw SQLException( + "pq_preparedstatement.setBytes: Error during converting bytesequence to an SQL conform string", + *this, OUString(), 1, Any() ); + } +// buf.append( (const char *)escapedString, len -1 ); + + m_updateableField[columnIndex-1].value <<= + OUString( reinterpret_cast<char*>(escapedString), len, RTL_TEXTENCODING_ASCII_US ); + free( escapedString ); +} + +void UpdateableResultSet::updateDate( sal_Int32 columnIndex, const css::util::Date& x ) +{ + updateString( columnIndex, DBTypeConversion::toDateString( x ) ); +} + +void UpdateableResultSet::updateTime( sal_Int32 columnIndex, const css::util::Time& x ) +{ + updateString( columnIndex, DBTypeConversion::toTimeString( x ) ); +} + +void UpdateableResultSet::updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) +{ + updateString( columnIndex, DBTypeConversion::toDateTimeString( x ) ); +} + +void UpdateableResultSet::updateBinaryStream( sal_Int32 /* columnIndex */, const css::uno::Reference< css::io::XInputStream >& /* x */, sal_Int32 /* length */ ) +{ +} + +void UpdateableResultSet::updateCharacterStream( sal_Int32 /* columnIndex */, const css::uno::Reference< css::io::XInputStream >& /* x */, sal_Int32 /* length */ ) +{ +} + +void UpdateableResultSet::updateObject( sal_Int32 /* columnIndex */, const css::uno::Any& /* x */ ) +{ +} + +void UpdateableResultSet::updateNumericObject( sal_Int32 /* columnIndex */, const css::uno::Any& /* x */, sal_Int32 /* scale */ ) +{ +} + + +Sequence< Type > UpdateableResultSet::getStaticTypes( bool updateable ) +{ + if( updateable ) + { + cppu::OTypeCollection collection( + cppu::UnoType<XResultSetUpdate>::get(), + cppu::UnoType<XRowUpdate>::get(), +// cppu::UnoType<css::sdbcx::XRowLocate>::get(), + getStaticTypes( false /* updateable */ ) ); + return collection.getTypes(); + } + else + { + cppu::OTypeCollection collection( + cppu::UnoType<XResultSet>::get(), + cppu::UnoType<XResultSetMetaDataSupplier>::get(), + cppu::UnoType<XRow>::get(), + cppu::UnoType<XColumnLocate>::get(), + cppu::UnoType<XCloseable>::get(), + cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<css::lang::XComponent>::get(), // OComponentHelper + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<css::uno::XAggregation>::get(), + cppu::UnoType<css::uno::XWeak>::get()); + return collection.getTypes(); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_updateableresultset.hxx b/connectivity/source/drivers/postgresql/pq_updateableresultset.hxx new file mode 100644 index 000000000..feb3f5322 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_updateableresultset.hxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 200? by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_UPDATEABLERESULTSET_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_UPDATEABLERESULTSET_HXX + +#include "pq_sequenceresultset.hxx" +#include "pq_resultsetmetadata.hxx" + +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> + +namespace pq_sdbc_driver +{ + +struct UpdateableField +{ + UpdateableField( ) + : isTouched(false) + {} + css::uno::Any value; + bool isTouched; +}; + +typedef std::vector< UpdateableField > UpdateableFieldVector; + +class UpdateableResultSet final : + public SequenceResultSet, + public css::sdbc::XResultSetUpdate, + public css::sdbc::XRowUpdate +{ + ConnectionSettings **m_ppSettings; + OUString m_schema; + OUString m_table; + std::vector< OUString > m_primaryKey; + UpdateableFieldVector m_updateableField; + bool m_insertRow; + +private: + UpdateableResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + const std::vector< OUString > &colNames, + const std::vector< std::vector< css::uno::Any > > &data, + ConnectionSettings **ppSettings, + const OUString &schema, + const OUString &table, + const std::vector< OUString > &primaryKey) + : SequenceResultSet( mutex, owner, colNames, data, (*ppSettings)->tc ), + m_ppSettings( ppSettings ), + m_schema( schema ), + m_table( table ), + m_primaryKey( primaryKey ), + m_insertRow( false ) + { + // LEM TODO: this duplicates code in pq_resultset.cxx, except for different value + // of ResultSetConcurrency. Baaad. + // Why is an updatable ResultSet a sequenceresultset in the first place? + // This seems to imply that the whole data is fetched once and kept in memory. BAAAAD. + // LEM TODO: shouldn't these things be inherited from the statement or something like that? + // Positioned update/delete not supported, so no cursor name + // Fetch direction and size are cursor-specific things, so not used now. + // Fetch size not set + m_props[ BASERESULTSET_FETCH_DIRECTION ] <<= css::sdbc::FetchDirection::UNKNOWN; + // No escape processing for now + m_props[ BASERESULTSET_ESCAPE_PROCESSING ] <<= false; + // Bookmarks not implemented for now + m_props[ BASERESULTSET_IS_BOOKMARKABLE ] <<= false; + m_props[ BASERESULTSET_RESULT_SET_CONCURRENCY ] <<= + css::sdbc::ResultSetConcurrency::UPDATABLE; + m_props[ BASERESULTSET_RESULT_SET_TYPE ] <<= + css::sdbc::ResultSetType::SCROLL_INSENSITIVE; + } + + OUString buildWhereClause(); + void checkUpdate( sal_Int32 column ); + +public: + static css::uno::Reference< css::sdbc::XCloseable > createFromPGResultSet( + const ::rtl::Reference< comphelper::RefCountedMutex > & mutex, + const css::uno::Reference< css::uno::XInterface > &owner, + ConnectionSettings **ppSettings, + PGresult *result, + const OUString &schema, + const OUString &table, + const std::vector< OUString > &primaryKey ); + +public: // XInterface + virtual void SAL_CALL acquire() throw() override { SequenceResultSet::acquire(); } + virtual void SAL_CALL release() throw() override { SequenceResultSet::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XResultSetUpdate + virtual void SAL_CALL insertRow( ) override; + virtual void SAL_CALL updateRow( ) override; + virtual void SAL_CALL deleteRow( ) override; + virtual void SAL_CALL cancelRowUpdates( ) override; + virtual void SAL_CALL moveToInsertRow( ) override; + virtual void SAL_CALL moveToCurrentRow( ) override; + +public: // XRowUpdate + virtual void SAL_CALL updateNull( sal_Int32 columnIndex ) override; + virtual void SAL_CALL updateBoolean( sal_Int32 columnIndex, sal_Bool x ) override; + virtual void SAL_CALL updateByte( sal_Int32 columnIndex, sal_Int8 x ) override; + virtual void SAL_CALL updateShort( sal_Int32 columnIndex, sal_Int16 x ) override; + virtual void SAL_CALL updateInt( sal_Int32 columnIndex, sal_Int32 x ) override; + virtual void SAL_CALL updateLong( sal_Int32 columnIndex, sal_Int64 x ) override; + virtual void SAL_CALL updateFloat( sal_Int32 columnIndex, float x ) override; + virtual void SAL_CALL updateDouble( sal_Int32 columnIndex, double x ) override; + virtual void SAL_CALL updateString( sal_Int32 columnIndex, const OUString& x ) override; + virtual void SAL_CALL updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL updateDate( sal_Int32 columnIndex, const css::util::Date& x ) override; + virtual void SAL_CALL updateTime( sal_Int32 columnIndex, const css::util::Time& x ) override; + virtual void SAL_CALL updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL updateBinaryStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateCharacterStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateObject( sal_Int32 columnIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL updateNumericObject( sal_Int32 columnIndex, const css::uno::Any& x, sal_Int32 scale ) override; + +public: + /// @throws css::uno::RuntimeException + static css::uno::Sequence< css::uno::Type > getStaticTypes( bool updateable ); + +}; + + +} + +#endif // INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_UPDATEABLERESULTSET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xbase.cxx b/connectivity/source/drivers/postgresql/pq_xbase.cxx new file mode 100644 index 000000000..1fec4130a --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xbase.cxx @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> + +#include "pq_tools.hxx" +#include "pq_xbase.hxx" + +using osl::MutexGuard; + +using com::sun::star::uno::Any; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Reference; +using com::sun::star::uno::RuntimeException; + +using com::sun::star::beans::Property; +using com::sun::star::beans::XPropertySetInfo; +using com::sun::star::beans::XPropertySet; + +namespace pq_sdbc_driver +{ + +ReflectionBase::ReflectionBase( + const OUString &implName, + const css::uno::Sequence< OUString > &supportedServices, + const ::rtl::Reference< comphelper::RefCountedMutex >& refMutex, + const css::uno::Reference< css::sdbc::XConnection > &conn, + ConnectionSettings *pSettings, + cppu::IPropertyArrayHelper & props /* must survive this object !*/ ) + : ReflectionBase_BASE( refMutex->GetMutex() ), + OPropertySetHelper( ReflectionBase_BASE::rBHelper ), + m_implName( implName ), + m_supportedServices( supportedServices ), + m_xMutex( refMutex ), + m_conn( conn ), + m_pSettings( pSettings ), + m_propsDesc( props ), + m_values( props.getProperties().getLength() ) +{} + +cppu::IPropertyArrayHelper & ReflectionBase::getInfoHelper() +{ + return m_propsDesc; +} + +sal_Bool ReflectionBase::convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) +{ + + rOldValue = m_values[nHandle]; + rConvertedValue = rValue; // TODO !!! implement correct conversion ! + m_values[nHandle] = rValue; + return true; +} + +void ReflectionBase::setPropertyValue_NoBroadcast_public( + const OUString & name, const css::uno::Any & value ) +{ + sal_Int32 nHandle = m_propsDesc.getHandleByName( name ); + if( -1 == nHandle ) + { + throw css::uno::RuntimeException( + "Unknown property '" + name + "' in " + m_implName, + *this ); + } + setFastPropertyValue_NoBroadcast( nHandle , value ); +} + +void ReflectionBase::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue ) +{ +// OUString s; +// rValue >>= s; +// printf( "setting value (handle %d):%s\n" , +// nHandle, OUStringToOString(s, RTL_TEXTENCODING_ASCII_US).getStr() ); + m_values[nHandle] = rValue; +} + +void ReflectionBase::getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle ) const +{ + rValue = m_values[nHandle]; +// OUString s; +// rValue >>= s; +// printf( "getting value (handle %d):%s\n" , +// nHandle, OUStringToOString(s, RTL_TEXTENCODING_ASCII_US).getStr() ); + +} + +Reference < css::beans::XPropertySetInfo > ReflectionBase::getPropertySetInfo() +{ + return OPropertySetHelper::createPropertySetInfo( m_propsDesc ); +} + +OUString ReflectionBase::getImplementationName() +{ + return m_implName; +} + +sal_Bool ReflectionBase::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > ReflectionBase::getSupportedServiceNames() +{ + return m_supportedServices; +} + + +Sequence< css::uno::Type > ReflectionBase::getTypes() +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + static Sequence< css::uno::Type > collection( + ::comphelper::concatSequences( + ::cppu::OPropertySetHelper::getTypes(), + ReflectionBase_BASE::getTypes() ) ); + return collection; +} + + +css::uno::Any ReflectionBase::queryInterface( + const css::uno::Type & reqType ) +{ + Any ret = ReflectionBase_BASE::queryInterface( reqType ); + return ret.hasValue() ? ret : OPropertySetHelper::queryInterface( reqType ); + +} + +Sequence< sal_Int8> ReflectionBase::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void ReflectionBase::copyValuesFrom( const Reference< XPropertySet > & set ) +{ + Reference< XPropertySetInfo > info = set->getPropertySetInfo(); + if( info.is () ) + { + Reference< XPropertySetInfo > myPropInfo = getPropertySetInfo(); + + const Sequence< Property > props = info->getProperties(); + for( Property const & prop : props ) + { + if( myPropInfo->hasPropertyByName( prop.Name ) ) + setPropertyValue_NoBroadcast_public( + prop.Name, set->getPropertyValue( prop.Name ) ); + } + } +} + +OUString ReflectionBase::getName( ) +{ + Statics & st = getStatics(); + if( getInfoHelper().hasPropertyByName( st.SCHEMA_NAME ) ) + return concatQualified( + extractStringProperty( this, getStatics().SCHEMA_NAME ), + extractStringProperty( this, getStatics().NAME ) ); + else + return extractStringProperty( this, getStatics().NAME ); +} + + +void ReflectionBase::setName( const OUString& /* aName */ ) +{ + throw RuntimeException( + "pq_sdbc::ReflectionBase::setName not implemented", + *this ); + //setPropertyValue( getStatics().NAME , makeAny( aName ) ); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xbase.hxx b/connectivity/source/drivers/postgresql/pq_xbase.hxx new file mode 100644 index 000000000..6e7ac8413 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xbase.hxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XBASE_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XBASE_HXX +#include <cppuhelper/propshlp.hxx> +#include <cppuhelper/component.hxx> +#include <cppuhelper/compbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/container/XNamed.hpp> + +#include "pq_xcontainer.hxx" + +namespace pq_sdbc_driver +{ + +typedef ::cppu::WeakComponentImplHelper< css::lang::XServiceInfo, + css::sdbcx::XDataDescriptorFactory, + css::container::XNamed + > ReflectionBase_BASE; + +class ReflectionBase : + public ReflectionBase_BASE, + public cppu::OPropertySetHelper +{ +protected: + const OUString m_implName; + const css::uno::Sequence< OUString > m_supportedServices; + ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + css::uno::Reference< css::sdbc::XConnection > m_conn; + ConnectionSettings *m_pSettings; + cppu::IPropertyArrayHelper & m_propsDesc; + std::vector< css::uno::Any > m_values; +public: + ReflectionBase( + const OUString &implName, + const css::uno::Sequence< OUString > &supportedServices, + const ::rtl::Reference< comphelper::RefCountedMutex >& refMutex, + const css::uno::Reference< css::sdbc::XConnection > &conn, + ConnectionSettings *pSettings, + cppu::IPropertyArrayHelper & props /* must survive this object !*/ ); + +public: + void copyValuesFrom( const css::uno::Reference< css::beans::XPropertySet > &set ); + +public: // for initialization purposes only, not exported via an interface ! + void setPropertyValue_NoBroadcast_public( + const OUString & name, const css::uno::Any & value ); + +public: //XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase_BASE::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase_BASE::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // OPropertySetHelper + virtual cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + using ::cppu::OPropertySetHelper::getFastPropertyValue; + + void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle ) const override; + + // XPropertySet + css::uno::Reference < css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + +public: // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override = 0; + +public: // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xcolumn.cxx b/connectivity/source/drivers/postgresql/pq_xcolumn.cxx new file mode 100644 index 000000000..c6d98b0fc --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xcolumn.cxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include "pq_xcolumn.hxx" + +using com::sun::star::uno::Reference; + +using com::sun::star::beans::XPropertySet; + +namespace pq_sdbc_driver +{ +Column::Column( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings) + : ReflectionBase( + getStatics().refl.column.implName, + getStatics().refl.column.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.column.pProps ) +{} + +Reference< XPropertySet > Column::createDataDescriptor( ) +{ + ColumnDescriptor * pColumn = new ColumnDescriptor( + m_xMutex, m_conn, m_pSettings ); + pColumn->copyValuesFrom( this ); + return Reference< XPropertySet > ( pColumn ); +} + +ColumnDescriptor::ColumnDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings) + : ReflectionBase( + getStatics().refl.columnDescriptor.implName, + getStatics().refl.columnDescriptor.serviceNames, + refMutex, + connection, + pSettings, + *getStatics().refl.columnDescriptor.pProps ) +{} + +Reference< XPropertySet > ColumnDescriptor::createDataDescriptor( ) +{ + ColumnDescriptor * pColumn = new ColumnDescriptor( + m_xMutex, m_conn, m_pSettings ); + pColumn->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pColumn ); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xcolumn.hxx b/connectivity/source/drivers/postgresql/pq_xcolumn.hxx new file mode 100644 index 000000000..ea4e099fc --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xcolumn.hxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XCOLUMN_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XCOLUMN_HXX + +#include <cppuhelper/component.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> + +#include "pq_connection.hxx" +#include "pq_xbase.hxx" + +namespace pq_sdbc_driver +{ + +class Column : public ReflectionBase +{ +public: + Column( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + +class ColumnDescriptor : public ReflectionBase +{ +public: + ColumnDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings ); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xcolumns.cxx b/connectivity/source/drivers/postgresql/pq_xcolumns.cxx new file mode 100644 index 000000000..4edb1aabc --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xcolumns.cxx @@ -0,0 +1,555 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> + +#include <cppuhelper/exc_hlp.hxx> + +#include "pq_xcolumns.hxx" +#include "pq_xcolumn.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Reference; +using com::sun::star::uno::RuntimeException; + +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XDatabaseMetaData; +using com::sun::star::sdbc::SQLException; + +namespace pq_sdbc_driver +{ + +static Any isCurrency( const OUString & typeName ) +{ + return Any( typeName.equalsIgnoreAsciiCase("money") ); +} + +// static sal_Bool isAutoIncrement8( const OUString & typeName ) +// { +// return typeName.equalsIgnoreAsciiCase("serial8") || +// typeName.equalsIgnoreAsciiCase("bigserial"); +// } + +static Any isAutoIncrement( const OUString & defaultValue ) +{ + bool ret = defaultValue.startsWith( "nextval(" ); +// printf( "%s %d\n", +// OUStringToOString(defaultValue, RTL_TEXTENCODING_ASCII_US).getStr(), +// ret ); +// { +// static const char * const serials[] = +// { +// "serial", "serial4", "serial8", "bigserial", 0 +// }; +// s sal_Bool b = sal_False; +// for( int i = 0; !b && serials[i] ; i ++ ) +// { +// b = b || typeName.equalsIgnoreAsciiCaseAscii( serials[i] ); +// } + return Any ( ret ); +} + +Columns::Columns( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName) + : Container( refMutex, origin, pSettings, "COLUMN" ), + m_schemaName( schemaName ), + m_tableName( tableName ) +{} + +Columns::~Columns() +{} + +OUString columnMetaData2SDBCX( + ReflectionBase *pBase, const css::uno::Reference< css::sdbc::XRow > &xRow ) +{ + Statics & st = getStatics(); + + // 1. TABLE_CAT string => table catalog (may be NULL) + // => not supported + // 2. TABLE_SCHEM string => table schema (may be NULL) + // => pg_namespace.nspname + // 3. TABLE_NAME string => table name + // => pg_class.relname + // 4. COLUMN_NAME string => column name + // => pg_attribute.attname + // 5. DATA_TYPE short => SQL type from java.sql.Types + // => pg_type.typname => sdbc.DataType + // 6. TYPE_NAME string => Data source dependent type name, for a UDT the + // type name is fully qualified + // => pg_type.typname + // 7. COLUMN_SIZE long => column size. For char or date types this is + // the maximum number of characters, for numeric + // or decimal types this is precision. + // => pg_type.typlen ( TODO: What is about variable size ? ) + // 8. BUFFER_LENGTH is not used. + // => not used + // 9. DECIMAL_DIGITS long => the number of fractional digits + // => don't know ! TODO ! + // 10. NUM_PREC_RADIX long => Radix (typically either 10 or 2) + // => TODO ?? + // 11. NULLABLE long => is NULL allowed? + // NO_NULLS - might not allow NULL values + // NULABLE - definitely allows NULL values + // NULLABLE_UNKNOWN - nullability unknown + // => pg_attribute.attnotnull + // 12. REMARKS string => comment describing column (may be NULL ) + // => Don't know, there does not seem to exist something like + // that in postgres + // 13. COLUMN_DEF string => default value (may be NULL) + // => pg_type.typdefault + // 14. SQL_DATA_TYPE long => unused + // => empty + // 15. SQL_DATETIME_SUB long => unused + // => empty + // 16. CHAR_OCTET_LENGTH long => for char types the maximum number of + // bytes in the column + // => pg_type.typlen + // 17. ORDINAL_POSITION int => index of column in table (starting at 1) + // pg_attribute.attnum + // 18. IS_NULLABLE string => "NO" means column definitely does not allow + // NULL values; "YES" means the column might + // allow NULL values. An empty string means + // nobody knows. + // => pg_attribute.attnotnull + + static const int COLUMN_NAME = 4; + static const int DATA_TYPE = 5; + static const int TYPE_NAME = 6; + static const int COLUMN_SIZE = 7; + static const int DECIMAL_DIGITS = 9; + static const int IS_NULLABLE = 11; + static const int DESCRIPTION = 12; + static const int DEFAULT_VALUE = 13; + + OUString name = xRow->getString( COLUMN_NAME ); + OUString typeName = xRow->getString( TYPE_NAME ); + + pBase->setPropertyValue_NoBroadcast_public( + st.NAME, makeAny( name ) ); + + pBase->setPropertyValue_NoBroadcast_public( + st.TYPE, makeAny( xRow->getInt( DATA_TYPE ) ) ); + + pBase->setPropertyValue_NoBroadcast_public( + st.TYPE_NAME, makeAny( typeName ) ); + + pBase->setPropertyValue_NoBroadcast_public( + st.PRECISION, makeAny( xRow->getInt( COLUMN_SIZE ) ) ); + + pBase->setPropertyValue_NoBroadcast_public( + st.SCALE, makeAny( xRow->getInt( DECIMAL_DIGITS ) ) ); + + pBase->setPropertyValue_NoBroadcast_public( + st.IS_NULLABLE, makeAny( xRow->getInt( IS_NULLABLE ) ) ); + + pBase->setPropertyValue_NoBroadcast_public( + st.DEFAULT_VALUE, makeAny( xRow->getString( DEFAULT_VALUE ) ) ); + +// pBase->setPropertyValue_NoBroadcast_public( +// st.DESCRIPTION, makeAny( xRow->getString( DESCRIPTION ) ) ); + +// if( pBase->getPropertySetInfo()->hasPropertyByName( st.HELP_TEXT ) ) +// pBase->setPropertyValue_NoBroadcast_public( +// st.HELP_TEXT, makeAny( xRow->getString( DESCRIPTION ) ) ); +// else // for key columns, etc. ... + pBase->setPropertyValue_NoBroadcast_public( + st.DESCRIPTION, makeAny( xRow->getString( DESCRIPTION ) ) ); + + + // maybe a better criterion than the type name can be found in future + pBase->setPropertyValue_NoBroadcast_public( + st.IS_AUTO_INCREMENT, isAutoIncrement(xRow->getString( DEFAULT_VALUE )) ); + + pBase->setPropertyValue_NoBroadcast_public( + st.IS_CURRENCY, isCurrency( typeName)); + return name; +} + + +// class CommentChanger : public cppu::WeakImplHelper< XPropertyChangeListener > +// { +// ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; +// css::uno::Reference< css::sdbc::XConnection > m_connection; +// ConnectionSettings *m_pSettings; +// OUString m_schema; +// OUString m_table; +// OUString m_column; + +// public: +// CommentChanger( +// const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, +// const css::uno::Reference< css::sdbc::XConnection > & connection, +// ConnectionSettings *pSettings, +// const OUString & schema, +// const OUString & table, +// const OUString & column ) : +// m_xMutex( refMutex ), +// m_connection( connection ), +// m_pSettings( pSettings ), +// m_schema ( schema ), +// m_table ( table ), +// m_column ( column ) +// {} + + +// // Methods +// virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) throw (css::uno::RuntimeException) +// { +// osl::MutexGuard guard( m_xMutex->GetMutex() ); +// m_connection.clear(); +// } +// // Methods +// virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) throw (css::uno::RuntimeException) +// { +// osl::MutexGuard guard( m_xMutex->GetMutex() ); +// OUStringBuffer buf( 128 ); +// OUString comment; +// evt.NewValue >>= comment; +// buf.append( "COMMENT ON COLUMN" ); +// bufferQuoteQualifiedIdentifier( buf, m_schema, m_table , m_column ); +// buf.append( "IS " ); +// bufferQuoteConstant( buf, comment,m_pSettings->encoding); + +// printf( "changing comment of column %s to %s\n", +// OUStringToOString( m_column, RTL_TEXTENCODING_ASCII_US ).getStr(), +// OUStringToOString( comment, RTL_TEXTENCODING_ASCII_US ).getStr() ); + +// m_connection->createStatement()->executeUpdate( buf.makeStringAndClear() ); +// } +// }; + +void Columns::refresh() +{ + try + { + SAL_INFO("connectivity.postgresql", "sdbcx.Columns get refreshed for table " << m_schemaName << "." << m_tableName); + osl::MutexGuard guard( m_xMutex->GetMutex() ); + + Statics &st = getStatics(); + Reference< XDatabaseMetaData > meta = m_origin->getMetaData(); + + Reference< XResultSet > rs = + meta->getColumns( Any(), m_schemaName, m_tableName, st.cPERCENT ); + + DisposeGuard disposeIt( rs ); + Reference< XRow > xRow( rs , UNO_QUERY ); + + String2IntMap map; + + m_values.clear(); + int columnIndex = 0; + while( rs->next() ) + { + Column * pColumn = + new Column( m_xMutex, m_origin, m_pSettings ); + Reference< css::beans::XPropertySet > prop = pColumn; + + OUString name = columnMetaData2SDBCX( pColumn, xRow ); +// pColumn->addPropertyChangeListener( +// st.HELP_TEXT, +// new CommentChanger( +// m_xMutex, +// m_origin, +// m_pSettings, +// m_schemaName, +// m_tableName, +// name ) ); + + { + m_values.push_back( makeAny( prop ) ); + map[ name ] = columnIndex; + ++columnIndex; + } + } + m_name2index.swap( map ); + } + catch ( css::sdbc::SQLException & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + nullptr, anyEx ); + } + fire( RefreshedBroadcaster( *this ) ); +} + + +void alterColumnByDescriptor( + const OUString & schemaName, + const OUString & tableName, + ConnectionSettings *settings, + const Reference< XStatement > &stmt, + const css::uno::Reference< css::beans::XPropertySet > & past, + const css::uno::Reference< css::beans::XPropertySet > & future) +{ + Statics & st = getStatics(); + +// if( past->getPropertyValue( st.TABLE_NAME ) != future->getPropertyValue( st.TABLE_NAME ) || +// past->getPropertyValue( st.SCHEMA_NAME ) != future->getPropertyValue( st.SCHEMA_NAME )) +// { +// OUStringBuffer buf(128); +// buf.append( "Can't move column " ); +// buf.append( extractStringProperty( past, st.COLUMN_NAME ) ); +// buf.append( " from table " ); +// buf.append( extractStringProperty( past, st.TABLE_NAME ) ); +// buf.append( " to table " ); +// buf.append( extractStringProperty( past, st.TABLE_NAME ) ); +// throw SQLException( buf.makeStringAndClear() ); +// } + +// OUString tableName = extractStringProperty( past, st.TABLE_NAME ); +// OUString schemaName = extractStringProperty( past, st.SCHEMA_NAME ); + OUString pastColumnName = extractStringProperty( past, st.NAME ); + OUString futureColumnName = extractStringProperty( future, st.NAME ); + OUString pastTypeName = sqltype2string( past ); + OUString futureTypeName = sqltype2string( future ); + + TransactionGuard transaction( stmt ); + + OUStringBuffer buf( 128 ); + if( ! pastColumnName.getLength()) + { + // create a new column + buf.append( "ALTER TABLE" ); + bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings ); + buf.append( "ADD COLUMN" ); + bufferQuoteIdentifier( buf, futureColumnName, settings ); + buf.append( futureTypeName ); + transaction.executeUpdate( buf.makeStringAndClear() ); + } + else + { + if( pastTypeName != futureTypeName ) + { + throw RuntimeException( + "Can't modify column types, drop the column and create a new one" ); + } + + if( pastColumnName != futureColumnName ) + { + buf.append( "ALTER TABLE" ); + bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings ); + buf.append( "RENAME COLUMN" ); + bufferQuoteIdentifier( buf, pastColumnName, settings ); + buf.append( "TO" ); + bufferQuoteIdentifier( buf, futureColumnName, settings ); + transaction.executeUpdate( buf.makeStringAndClear() ); + } + } + + OUString futureDefaultValue = extractStringProperty( future, st.DEFAULT_VALUE ); + OUString pastDefaultValue = extractStringProperty( past, st.DEFAULT_VALUE ); + if( futureDefaultValue != pastDefaultValue ) + { + buf.truncate(); + buf.append( "ALTER TABLE" ); + bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings ); + buf.append( "ALTER COLUMN" ); + bufferQuoteIdentifier( buf, futureColumnName, settings ); + buf.append( "SET DEFAULT " ); + // LEM TODO: check out + // default value is not quoted, caller needs to quote himself (otherwise + // how to pass e.g. nextval('something' ) ???? + buf.append( futureDefaultValue ); +// bufferQuoteConstant( buf, defaultValue, encoding ); + transaction.executeUpdate( buf.makeStringAndClear() ); + } + + sal_Int32 futureNullable = extractIntProperty( future, st.IS_NULLABLE ); + sal_Int32 pastNullable = extractIntProperty( past, st.IS_NULLABLE ); + if( futureNullable != pastNullable ) + { + buf.truncate(); + buf.append( "ALTER TABLE" ); + bufferQuoteQualifiedIdentifier( buf, schemaName, tableName, settings ); + buf.append( "ALTER COLUMN" ); + bufferQuoteIdentifier( buf, futureColumnName, settings ); + if( futureNullable == css::sdbc::ColumnValue::NO_NULLS ) + { + buf.append( "SET" ); + } + else + { + buf.append( "DROP" ); + } + buf.append( " NOT NULL" ); + transaction.executeUpdate( buf.makeStringAndClear() ); + } + +// OUString futureComment = extractStringProperty( future, st.HELP_TEXT ); +// OUString pastComment = extractStringProperty( past, st.HELP_TEXT ); +// printf( "past Comment %s, futureComment %s\n", +// OUStringToOString( pastComment, RTL_TEXTENCODING_ASCII_US ).getStr(), +// OUStringToOString( futureComment, RTL_TEXTENCODING_ASCII_US ).getStr() ); + OUString futureComment = extractStringProperty( future, st.DESCRIPTION ); + OUString pastComment = extractStringProperty( past, st.DESCRIPTION ); + + if( futureComment != pastComment ) + { + buf.truncate(); + buf.append( "COMMENT ON COLUMN" ); + bufferQuoteQualifiedIdentifier( buf, schemaName, tableName , futureColumnName, settings ); + buf.append( "IS " ); + bufferQuoteConstant( buf, futureComment, settings ); + transaction.executeUpdate( buf.makeStringAndClear() ); + } + transaction.commit(); +} + +void Columns::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& future ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + Statics & st = getStatics(); + Reference< XPropertySet > past = createDataDescriptor(); + past->setPropertyValue( st.IS_NULLABLE, makeAny( css::sdbc::ColumnValue::NULLABLE ) ); + alterColumnByDescriptor( + m_schemaName, m_tableName, m_pSettings, m_origin->createStatement() , past, future ); + + refresh(); +} + +// void Columns::dropByName( const OUString& elementName ) +// throw (css::sdbc::SQLException, +// css::container::NoSuchElementException, +// css::uno::RuntimeException) +// { +// String2IntMap::const_iterator ii = m_name2index.find( elementName ); +// if( ii == m_name2index.end() ) +// { +// OUStringBuffer buf( 128 ); +// buf.appendAscii( "Column " ); +// buf.append( elementName ); +// buf.appendAscii( " is unknown in table " ); +// buf.append( m_schemaName ); +// buf.appendAscii( "." ); +// buf.append( m_tableName ); +// buf.appendAscii( ", so it can't be dropped" ); +// throw css::container::NoSuchElementException( +// buf.makeStringAndClear(), *this ); +// } +// dropByIndex( ii->second ); +// } + +void Columns::dropByIndex( sal_Int32 index ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + if( index < 0 || index >= static_cast<sal_Int32>(m_values.size()) ) + { + throw css::lang::IndexOutOfBoundsException( + "COLUMNS: Index out of range (allowed 0 to " + + OUString::number(m_values.size() -1) + + ", got " + OUString::number( index ) + ")", + *this ); + } + + Reference< XPropertySet > set; + m_values[index] >>= set; + Statics &st = getStatics(); + OUString name; + set->getPropertyValue( st.NAME ) >>= name; + + OUStringBuffer update( 128 ); + update.append( "ALTER TABLE ONLY"); + bufferQuoteQualifiedIdentifier( update, m_schemaName, m_tableName, m_pSettings ); + update.append( "DROP COLUMN" ); + bufferQuoteIdentifier( update, name, m_pSettings ); + Reference< XStatement > stmt = m_origin->createStatement( ); + DisposeGuard disposeIt( stmt ); + stmt->executeUpdate( update.makeStringAndClear() ); + + Container::dropByIndex( index ); +} + + +css::uno::Reference< css::beans::XPropertySet > Columns::createDataDescriptor() +{ + return new ColumnDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +Reference< css::container::XNameAccess > Columns::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + Columns **ppColumns) +{ + *ppColumns = new Columns( + refMutex, origin, pSettings, schemaName, tableName ); + Reference< css::container::XNameAccess > ret = *ppColumns; + (*ppColumns)->refresh(); + + return ret; +} + + +ColumnDescriptors::ColumnDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ) + : Container( refMutex, origin, pSettings, "COLUMN-DESCRIPTOR" ) +{} + + +Reference< css::beans::XPropertySet > ColumnDescriptors::createDataDescriptor() +{ + return new ColumnDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xcolumns.hxx b/connectivity/source/drivers/postgresql/pq_xcolumns.hxx new file mode 100644 index 000000000..ab8c47914 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xcolumns.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XCOLUMNS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XCOLUMNS_HXX + +#include "pq_xcontainer.hxx" +#include "pq_xbase.hxx" + +namespace com::sun::star::sdbc { class XRow; } + +namespace pq_sdbc_driver +{ + +void alterColumnByDescriptor( + const OUString & schemaName, + const OUString & tableName, + ConnectionSettings *settings, + const css::uno::Reference< css::sdbc::XStatement > &stmt, + const css::uno::Reference< css::beans::XPropertySet > & past, + const css::uno::Reference< css::beans::XPropertySet > & future); + +OUString columnMetaData2SDBCX( + ReflectionBase *pBase, const css::uno::Reference< css::sdbc::XRow > &xRow ); + +class Columns final : public Container +{ + OUString m_schemaName; + OUString m_tableName; + +public: // instances Columns 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + Columns **pColumns); + +private: + Columns( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName); + + + virtual ~Columns() override; + +public: // XAppend + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + +// public: // XDrop +// virtual void SAL_CALL dropByName( const OUString& elementName ) +// throw (css::sdbc::SQLException, +// css::container::NoSuchElementException, +// css::uno::RuntimeException); + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + +public: // XRefreshable + virtual void SAL_CALL refresh( ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; + + +class ColumnDescriptors : public Container +{ +public: + ColumnDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xcontainer.cxx b/connectivity/source/drivers/postgresql/pq_xcontainer.cxx new file mode 100644 index 000000000..06323615f --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xcontainer.cxx @@ -0,0 +1,408 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <cppuhelper/implbase.hxx> + +#include "pq_xcontainer.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; +using com::sun::star::uno::Type; +using com::sun::star::uno::XInterface; +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::RuntimeException; + +using com::sun::star::container::NoSuchElementException; +using com::sun::star::container::XEnumeration; +using com::sun::star::container::XContainerListener; +using com::sun::star::container::ContainerEvent; +using com::sun::star::lang::IndexOutOfBoundsException; +using com::sun::star::lang::XEventListener; + + +namespace pq_sdbc_driver +{ + +namespace { + +class ReplacedBroadcaster : public EventBroadcastHelper +{ + ContainerEvent m_event; +public: + ReplacedBroadcaster( + const Reference< XInterface > & source, + const OUString & name, + const Any & newElement, + const OUString & oldElement ) : + m_event( source, makeAny( name ), newElement, makeAny(oldElement) ) + {} + + virtual void fire( XEventListener * listener ) const override + { + static_cast<XContainerListener*>(listener)->elementReplaced( m_event ); + } + virtual Type getType() const override + { + return cppu::UnoType<XContainerListener>::get(); + } +}; + +class InsertedBroadcaster : public EventBroadcastHelper +{ +public: + ContainerEvent m_event; + InsertedBroadcaster( + const Reference< XInterface > & source, + const OUString & name, + const Any & newElement ) : + m_event( source, makeAny( name ), newElement, Any() ) + {} + + virtual void fire( XEventListener * listener ) const override + { + static_cast<XContainerListener*>(listener)->elementInserted( m_event ); + } + + virtual Type getType() const override + { + return cppu::UnoType<XContainerListener>::get(); + } +}; + +class RemovedBroadcaster : public EventBroadcastHelper +{ +public: + ContainerEvent m_event; + RemovedBroadcaster( + const Reference< XInterface > & source, + const OUString & name) : + m_event( source, makeAny( name ), Any(), Any() ) + {} + + virtual void fire( XEventListener * listener ) const override + { + static_cast<XContainerListener*>(listener)->elementRemoved( m_event ); + } + + virtual Type getType() const override + { + return cppu::UnoType<XContainerListener>::get(); + } +}; + +} + +Container::Container( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &type) + : ContainerBase( refMutex->GetMutex() ), + m_xMutex( refMutex ), + m_pSettings( pSettings ), + m_origin( origin ), + m_type( type ) +{ +} + +Any Container::getByName( const OUString& aName ) +{ + String2IntMap::const_iterator ii = m_name2index.find( aName ); + if( ii == m_name2index.end() ) + { + throw NoSuchElementException( + "Element " + aName + " unknown in " + m_type + "-Container", + *this ); + } + OSL_ASSERT( ii->second >= 0 && ii->second < static_cast<int>(m_values.size()) ); + return m_values[ ii->second ]; +} + +Sequence< OUString > Container::getElementNames( ) +{ + Sequence< OUString > ret( m_values.size() ); + for( const auto& [rName, rIndex] : m_name2index ) + { + // give element names in index order ! + ret[rIndex] = rName; + } + return ret; +} + +sal_Bool Container::hasByName( const OUString& aName ) +{ + return m_name2index.find( aName ) != m_name2index.end(); +} + // Methods +Type Container::getElementType( ) +{ + return Type(); +} + +sal_Bool Container::hasElements( ) +{ + return ! m_name2index.empty(); +} + +Any Container::getByIndex( sal_Int32 Index ) +{ + if( Index < 0 || Index >= static_cast<sal_Int32>(m_values.size()) ) + { + throw IndexOutOfBoundsException( + "Index " + OUString::number( Index ) + + " out of range for " + m_type + "-Container, expected 0 <= x <= " + + OUString::number(m_values.size() -1), + *this ); + } + return m_values[Index]; +} + +sal_Int32 Container::getCount() +{ + return m_values.size(); +} + +namespace { + +class ContainerEnumeration : public ::cppu::WeakImplHelper< XEnumeration > +{ + std::vector< css::uno::Any > m_vec; + sal_Int32 m_index; +public: + explicit ContainerEnumeration( const std::vector< css::uno::Any > &vec ) + : m_vec( vec ), + m_index( -1 ) + {} + +public: + // XEnumeration + virtual sal_Bool SAL_CALL hasMoreElements( ) override; + virtual css::uno::Any SAL_CALL nextElement( ) override; + +}; + +} + +sal_Bool ContainerEnumeration::hasMoreElements() +{ + return static_cast<int>(m_vec.size()) > m_index +1; +} + +css::uno::Any ContainerEnumeration::nextElement() +{ + if( ! hasMoreElements() ) + { + throw NoSuchElementException( + "NoSuchElementException during enumeration", *this ); + } + m_index ++; + return m_vec[m_index]; +} + +Reference< XEnumeration > Container::createEnumeration( ) +{ + return new ContainerEnumeration( m_values ); +} + +void Container::addRefreshListener( + const css::uno::Reference< css::util::XRefreshListener >& l ) +{ + rBHelper.addListener( cppu::UnoType<decltype(l)>::get() , l ); +} + +void Container::removeRefreshListener( + const css::uno::Reference< css::util::XRefreshListener >& l ) +{ + rBHelper.removeListener( cppu::UnoType<decltype(l)>::get() , l ); +} + +void Container::disposing() +{ + m_origin.clear(); +} + +void Container::rename( const OUString &oldName, const OUString &newName ) +{ + Any newValue; + { + osl::MutexGuard guard ( m_xMutex->GetMutex() ); + String2IntMap::iterator ii = m_name2index.find( oldName ); + if( ii != m_name2index.end() ) + { + sal_Int32 nIndex = ii->second; + newValue = m_values[nIndex]; + m_name2index.erase( ii ); + m_name2index[ newName ] = nIndex; + } + } + fire( ReplacedBroadcaster( *this, newName, newValue, oldName ) ); + fire( RefreshedBroadcaster( *this ) ); +} + +void Container::dropByName( const OUString& elementName ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + String2IntMap::const_iterator ii = m_name2index.find( elementName ); + if( ii == m_name2index.end() ) + { + throw css::container::NoSuchElementException( + "Column " + elementName + " is unknown in " + + m_type + " container, so it can't be dropped", + *this ); + } + dropByIndex( ii->second ); +} + +void Container::dropByIndex( sal_Int32 index ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + if( index < 0 || index >=static_cast<sal_Int32>(m_values.size()) ) + { + throw css::lang::IndexOutOfBoundsException( + "Index out of range (allowed 0 to " + + OUString::number(m_values.size() -1) + + ", got " + OUString::number( index ) + + ") in " + m_type, + *this ); + } + + OUString name; + String2IntMap::iterator ii = std::find_if(m_name2index.begin(), m_name2index.end(), + [&index](const String2IntMap::value_type& rEntry) { return rEntry.second == index; }); + if (ii != m_name2index.end()) + { + name = ii->first; + m_name2index.erase( ii ); + } + + for( int i = index +1 ; i < static_cast<int>(m_values.size()) ; i ++ ) + { + m_values[i-1] = m_values[i]; + + // I know, this is expensive, but don't want to maintain another map ... + ii = std::find_if(m_name2index.begin(), m_name2index.end(), + [&i](const String2IntMap::value_type& rEntry) { return rEntry.second == i; }); + if (ii != m_name2index.end()) + { + ii->second = i-1; + } + } + m_values.resize( m_values.size() - 1 ); + + fire( RemovedBroadcaster( *this, name ) ); +} + +void Container::append( + const OUString & name, + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) + +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + + if( hasByName( name ) ) + { + throw css::container::ElementExistException( + "a " + m_type + " with name " + name + " already exists in this container", + *this ); + } + + int index = m_values.size(); + m_values.push_back( makeAny( descriptor ) ); + m_name2index[name] = index; + + fire( InsertedBroadcaster( *this, name, makeAny( descriptor ) ) ); +} + +void Container::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor) +{ + append( extractStringProperty( descriptor, getStatics().NAME ), descriptor ); +} + + +void Container::addContainerListener( + const css::uno::Reference< css::container::XContainerListener >& l ) +{ + rBHelper.addListener( cppu::UnoType<decltype(l)>::get() , l ); +} + +void Container::removeContainerListener( + const css::uno::Reference< css::container::XContainerListener >& l ) +{ + rBHelper.removeListener( cppu::UnoType<decltype(l)>::get() , l ); +} + + +void Container::fire( const EventBroadcastHelper &helper ) +{ + cppu::OInterfaceContainerHelper *container = rBHelper.getContainer( helper.getType() ); + if( !container ) + return; + + cppu::OInterfaceIteratorHelper iterator( * container ); + while( iterator.hasMoreElements() ) + { + try + { + helper.fire( static_cast<XEventListener *>(iterator.next()) ); + } + catch ( css::uno::RuntimeException & ) + { + OSL_ENSURE( false, "exception caught" ); + // loose coupling, a runtime exception shall not break anything + // TODO: log away as warning ! + } + catch( css::uno::Exception & ) + { + OSL_ENSURE( false, "exception from listener flying through" ); + throw; + } + } + +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xcontainer.hxx b/connectivity/source/drivers/postgresql/pq_xcontainer.hxx new file mode 100644 index 000000000..f53b0303a --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xcontainer.hxx @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XCONTAINER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XCONTAINER_HXX +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XContainer.hpp> + +#include <com/sun/star/sdbcx/XAppend.hpp> +#include <com/sun/star/sdbcx/XDrop.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> + +#include <com/sun/star/util/XRefreshable.hpp> + +#include <cppuhelper/compbase.hxx> + +#include <unordered_map> + +#include "pq_connection.hxx" +#include "pq_statics.hxx" + +namespace pq_sdbc_driver +{ + +class EventBroadcastHelper +{ +public: + virtual void fire(css::lang::XEventListener * listener) const = 0; + virtual css::uno::Type getType() const = 0; + virtual ~EventBroadcastHelper(){}; +}; + +class RefreshedBroadcaster : public EventBroadcastHelper +{ + css::lang::EventObject m_event; +public: + explicit RefreshedBroadcaster(const css::uno::Reference< css::uno::XInterface > & source ) : + m_event( source ) + {} + + virtual void fire( css::lang::XEventListener * listener ) const override + { + static_cast<css::util::XRefreshListener*>(listener)->refreshed( m_event ); + } + + virtual css::uno::Type getType() const override + { + return cppu::UnoType< + css::util::XRefreshListener>::get(); + } +}; + +typedef std::unordered_map +< + OUString, + sal_Int32 +> String2IntMap; + +typedef ::cppu::WeakComponentImplHelper +< + css::container::XNameAccess, + css::container::XIndexAccess, + css::container::XEnumerationAccess, + css::sdbcx::XAppend, + css::sdbcx::XDrop, + css::util::XRefreshable, + css::sdbcx::XDataDescriptorFactory, + css::container::XContainer +> ContainerBase; + +class /* abstract */ Container : public ContainerBase +{ +protected: + ::rtl::Reference< comphelper::RefCountedMutex > m_xMutex; + ConnectionSettings *m_pSettings; + css::uno::Reference< css::sdbc::XConnection > m_origin; + String2IntMap m_name2index; // maps the element name to an index + std::vector< css::uno::Any > m_values; // contains the real values + OUString m_type; + +public: + Container( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString & type // for exception messages + ); + +public: // XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + +public: // XEnumerationAccess + virtual css::uno::Reference< css::container::XEnumeration > + SAL_CALL createEnumeration( ) override; + +public: // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + // Methods + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + +public: // XAppend + // Must be overridden in Non-Descriptors. May be overridden in descriptors, when + // PropertySet.NAME != container name + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + + // helper method ! + /// @throws css::container::ElementExistException + void append( + const OUString & str, + const css::uno::Reference< css::beans::XPropertySet >& descriptor ); + + +public: // XDrop + virtual void SAL_CALL dropByName( const OUString& elementName ) override; + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override = 0; + +public: // XRefreshable + virtual void SAL_CALL refresh( ) override {} + virtual void SAL_CALL addRefreshListener( + const css::uno::Reference< css::util::XRefreshListener >& l ) override; + virtual void SAL_CALL removeRefreshListener( + const css::uno::Reference< css::util::XRefreshListener >& l ) override; + +public: + // Methods + virtual void SAL_CALL addContainerListener( + const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + virtual void SAL_CALL removeContainerListener( + const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + +public: + virtual void SAL_CALL disposing() override; + +public: + void rename( const OUString & oldName, const OUString &newName ); + +protected: + void fire( const EventBroadcastHelper & helper ); +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xindex.cxx b/connectivity/source/drivers/postgresql/pq_xindex.cxx new file mode 100644 index 000000000..c340484a1 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xindex.cxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include "pq_xindex.hxx" +#include "pq_xindexcolumns.hxx" +#include "pq_tools.hxx" +#include "pq_statics.hxx" + +using com::sun::star::container::XNameAccess; + +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; + +using com::sun::star::beans::XPropertySet; + + +namespace pq_sdbc_driver +{ +Index::Index( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings, + const OUString & schemaName, + const OUString & tableName ) + : ReflectionBase( + getStatics().refl.index.implName, + getStatics().refl.index.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.index.pProps ), + m_schemaName( schemaName ), + m_tableName( tableName ) +{} + +Reference< XPropertySet > Index::createDataDescriptor( ) +{ + IndexDescriptor * pIndex = new IndexDescriptor( + m_xMutex, m_conn, m_pSettings ); + pIndex->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pIndex ); +} + +Reference< XNameAccess > Index::getColumns( ) +{ + if( ! m_indexColumns.is() ) + { + Sequence< OUString > columnNames; + getPropertyValue( getStatics().PRIVATE_COLUMN_INDEXES ) >>= columnNames; + OUString indexName = extractStringProperty( this, getStatics().NAME ); + m_indexColumns = IndexColumns::create( + m_xMutex, m_conn, m_pSettings, m_schemaName, + m_tableName, indexName, columnNames ); + } + return m_indexColumns; +} + +Sequence<Type > Index::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<css::sdbcx::XColumnsSupplier>::get(), + ReflectionBase::getTypes()); + + return collection.getTypes(); +} + +Sequence< sal_Int8> Index::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any Index::queryInterface( const Type & reqType ) +{ + Any ret = ReflectionBase::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< css::sdbcx::XColumnsSupplier * > ( this ) ); + return ret; +} + + +IndexDescriptor::IndexDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings ) + : ReflectionBase( + getStatics().refl.indexDescriptor.implName, + getStatics().refl.indexDescriptor.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.indexDescriptor.pProps ) +{} + +Reference< XPropertySet > IndexDescriptor::createDataDescriptor( ) +{ + IndexDescriptor * pIndex = new IndexDescriptor( + m_xMutex, m_conn, m_pSettings ); + pIndex->copyValuesFrom( this ); + return Reference< XPropertySet > ( pIndex ); +} + +Reference< XNameAccess > IndexDescriptor::getColumns( ) +{ + if( ! m_indexColumns.is() ) + { + m_indexColumns = IndexColumnDescriptors::create( + m_xMutex, m_conn, m_pSettings ); +// Sequence< OUString > columnNames; +// getPropertyValue( getStatics().PRIVATE_COLUMN_INDEXES ) >>= columnNames; +// OUString indexName = extractStringProperty( this, getStatics().NAME ); +// m_indexColumns = IndexColumns::create( +// m_xMutex, m_conn, m_pSettings, m_schemaName, +// m_tableName, indexName, columnNames ); + } + return m_indexColumns; +} + +Sequence<Type > IndexDescriptor::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<css::sdbcx::XColumnsSupplier>::get(), + ReflectionBase::getTypes()); + + return collection.getTypes(); +} + +Sequence< sal_Int8> IndexDescriptor::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any IndexDescriptor::queryInterface( const Type & reqType ) +{ + Any ret = ReflectionBase::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< css::sdbcx::XColumnsSupplier * > ( this ) ); + return ret; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xindex.hxx b/connectivity/source/drivers/postgresql/pq_xindex.hxx new file mode 100644 index 000000000..26e561363 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xindex.hxx @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XINDEX_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XINDEX_HXX + +#include <cppuhelper/component.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> + +#include "pq_connection.hxx" +#include "pq_xbase.hxx" + +namespace pq_sdbc_driver +{ + +class Index : public ReflectionBase, + public css::sdbcx::XColumnsSupplier +{ + css::uno::Reference< css::container::XNameAccess > m_indexColumns; + + OUString m_schemaName; + OUString m_tableName; + +public: + Index( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName); + +public: // XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL + getColumns( ) override; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + + +class IndexDescriptor : public ReflectionBase, + public css::sdbcx::XColumnsSupplier +{ + css::uno::Reference< css::container::XNameAccess > m_indexColumns; + +public: + IndexDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + +public: // XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL + getColumns( ) override; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xindexcolumn.cxx b/connectivity/source/drivers/postgresql/pq_xindexcolumn.cxx new file mode 100644 index 000000000..58b36234b --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xindexcolumn.cxx @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include "pq_xindexcolumn.hxx" + +using com::sun::star::uno::Reference; + +using com::sun::star::beans::XPropertySet; + +namespace pq_sdbc_driver +{ +IndexColumn::IndexColumn( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings ) + : ReflectionBase( + getStatics().refl.indexColumn.implName, + getStatics().refl.indexColumn.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.indexColumn.pProps ) +{} + +Reference< XPropertySet > IndexColumn::createDataDescriptor( ) +{ + IndexColumnDescriptor * pIndexColumn = new IndexColumnDescriptor( + m_xMutex, m_conn, m_pSettings ); + pIndexColumn->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pIndexColumn ); +} + + +IndexColumnDescriptor::IndexColumnDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings ) + : ReflectionBase( + getStatics().refl.indexColumnDescriptor.implName, + getStatics().refl.indexColumnDescriptor.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.indexColumnDescriptor.pProps ) +{} + +Reference< XPropertySet > IndexColumnDescriptor::createDataDescriptor( ) +{ + IndexColumnDescriptor * pIndexColumn = new IndexColumnDescriptor( + m_xMutex, m_conn, m_pSettings ); + pIndexColumn->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pIndexColumn ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xindexcolumn.hxx b/connectivity/source/drivers/postgresql/pq_xindexcolumn.hxx new file mode 100644 index 000000000..5e7ae8d4f --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xindexcolumn.hxx @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XINDEXCOLUMN_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XINDEXCOLUMN_HXX + +#include <cppuhelper/component.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> + +#include "pq_connection.hxx" +#include "pq_xbase.hxx" + +namespace pq_sdbc_driver +{ + +class IndexColumn : public ReflectionBase +{ +public: + IndexColumn( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + +class IndexColumnDescriptor : public ReflectionBase +{ +public: + IndexColumnDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xindexcolumns.cxx b/connectivity/source/drivers/postgresql/pq_xindexcolumns.cxx new file mode 100644 index 000000000..12acb6f32 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xindexcolumns.cxx @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/log.hxx> +#include <vector> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include "pq_xcolumns.hxx" +#include "pq_xindexcolumns.hxx" +#include "pq_xindexcolumn.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; + +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XDatabaseMetaData; +using com::sun::star::sdbc::SQLException; + +namespace pq_sdbc_driver +{ + +IndexColumns::IndexColumns( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + const OUString &indexName, + const css::uno::Sequence< OUString > &columns ) + : Container( refMutex, origin, pSettings, "INDEX_COLUMN" ), + m_schemaName( schemaName ), + m_tableName( tableName ), + m_indexName( indexName ), + m_columns( columns ) +{} + +IndexColumns::~IndexColumns() +{} + +static sal_Int32 findInSequence( const Sequence< OUString > & seq , const OUString &str) +{ + int index; + for( index = 0 ; index < seq.getLength() ; index ++ ) + { + if( str == seq[index] ) + break; + } + return index; +} + +void IndexColumns::refresh() +{ + try + { + SAL_INFO("connectivity.postgresql", "sdbcx.IndexColumns get refreshed for index " << m_indexName); + + osl::MutexGuard guard( m_xMutex->GetMutex() ); + + Statics &st = getStatics(); + Reference< XDatabaseMetaData > meta = m_origin->getMetaData(); + + Reference< XResultSet > rs = + meta->getColumns( Any(), m_schemaName, m_tableName, st.cPERCENT ); + + DisposeGuard disposeIt( rs ); + Reference< XRow > xRow( rs , UNO_QUERY ); + m_values.clear(); + m_values.resize( m_columns.getLength() ); + + while( rs->next() ) + { + OUString columnName = xRow->getString( 4 ); + + sal_Int32 index = findInSequence( m_columns, columnName ); + if( index >= m_columns.getLength() ) + continue; + + IndexColumn * pIndexColumn = + new IndexColumn( m_xMutex, m_origin, m_pSettings ); + Reference< css::beans::XPropertySet > prop = pIndexColumn; + + columnMetaData2SDBCX( pIndexColumn, xRow ); + pIndexColumn->setPropertyValue_NoBroadcast_public( + st.IS_ASCENDING , makeAny( false ) ); + + m_values[ index ] <<= prop; + m_name2index[ columnName ] = index; + } + } + catch ( css::sdbc::SQLException & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } + + fire( RefreshedBroadcaster( *this ) ); +} + + +void IndexColumns::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& /*future*/ ) +{ + throw css::sdbc::SQLException( + "SDBC-POSTGRESQL: IndexesColumns.appendByDescriptor not yet implemented", + *this, OUString(), 1, Any() ); +// osl::MutexGuard guard( m_xMutex->GetMutex() ); +// Statics & st = getStatics(); +// Reference< XPropertySet > past = createDataDescriptor(); +// past->setPropertyValue( st.IS_NULLABLE, makeAny( css::sdbc::ColumnValue::NULLABLE ) ); +// alterColumnByDescriptor( +// m_schemaName, m_tableName, m_pSettings->encoding, m_origin->createStatement() , past, future ); + +} + +void IndexColumns::dropByName( const OUString& ) +{ + throw css::sdbc::SQLException( + "SDBC-POSTGRESQL: IndexesColumns.dropByName not yet implemented", + *this, OUString(), 1, Any() ); +// String2IntMap::const_iterator ii = m_name2index.find( elementName ); +// if( ii == m_name2index.end() ) +// { +// OUStringBuffer buf( 128 ); +// buf.appendAscii( "Column " ); +// buf.append( elementName ); +// buf.appendAscii( " is unknown in table " ); +// buf.append( m_schemaName ); +// buf.appendAscii( "." ); +// buf.append( m_tableName ); +// buf.appendAscii( ", so it can't be dropped" ); +// throw css::container::NoSuchElementException( +// buf.makeStringAndClear(), *this ); +// } +// dropByIndex( ii->second ); +} + +void IndexColumns::dropByIndex( sal_Int32 ) +{ + throw css::sdbc::SQLException( + "SDBC-POSTGRESQL: IndexesColumns.dropByIndex not yet implemented", + *this, OUString(), 1, Any() ); +// osl::MutexGuard guard( m_xMutex->GetMutex() ); +// if( index < 0 || index >= m_values.getLength() ) +// { +// OUStringBuffer buf( 128 ); +// buf.appendAscii( "COLUMNS: Index out of range (allowed 0 to " ); +// buf.append((sal_Int32)(m_values.getLength() -1) ); +// buf.appendAscii( ", got " ); +// buf.append( index ); +// buf.appendAscii( ")" ); +// throw css::lang::IndexOutOfBoundsException( +// buf.makeStringAndClear(), *this ); +// } + +// Reference< XPropertySet > set; +// m_values[index] >>= set; +// Statics &st = getStatics(); +// OUString name; +// set->getPropertyValue( st.NAME ) >>= name; + +// OUStringBuffer update( 128 ); +// update.appendAscii( "ALTER TABLE ONLY"); +// bufferQuoteQualifiedIdentifier( update, m_schemaName, m_tableName ); +// update.appendAscii( "DROP COLUMN" ); +// bufferQuoteIdentifier( update, name ); +// Reference< XStatement > stmt = m_origin->createStatement( ); +// DisposeGuard disposeIt( stmt ); +// stmt->executeUpdate( update.makeStringAndClear() ); + +} + + +Reference< css::beans::XPropertySet > IndexColumns::createDataDescriptor() +{ + return new IndexColumnDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +Reference< css::container::XNameAccess > IndexColumns::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + const OUString &indexName, + const Sequence< OUString > &columns ) +{ + IndexColumns *pIndexColumns = new IndexColumns( + refMutex, origin, pSettings, schemaName, tableName, indexName, columns ); + Reference< css::container::XNameAccess > ret = pIndexColumns; + pIndexColumns->refresh(); + + return ret; +} + + +IndexColumnDescriptors::IndexColumnDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings) + : Container( refMutex, origin, pSettings, getStatics().INDEX_COLUMN ) +{} + +Reference< css::container::XNameAccess > IndexColumnDescriptors::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings) +{ + return new IndexColumnDescriptors( refMutex, origin, pSettings ); +} + +css::uno::Reference< css::beans::XPropertySet > IndexColumnDescriptors::createDataDescriptor() +{ + return new IndexColumnDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xindexcolumns.hxx b/connectivity/source/drivers/postgresql/pq_xindexcolumns.hxx new file mode 100644 index 000000000..353b75ddb --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xindexcolumns.hxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XINDEXCOLUMNS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XINDEXCOLUMNS_HXX + +#include "pq_xcontainer.hxx" + +namespace pq_sdbc_driver +{ + +class IndexColumns final : public Container +{ + OUString m_schemaName; + OUString m_tableName; + OUString m_indexName; + css::uno::Sequence< OUString > m_columns; + +public: // instances IndexColumns 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + const OUString &indexName, + const css::uno::Sequence< OUString > &columns ); + +private: + IndexColumns( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + const OUString &indexName, + const css::uno::Sequence< OUString > &columns ); + + virtual ~IndexColumns() override; + +public: // XAppend + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + +public: // XDrop + virtual void SAL_CALL dropByName( const OUString& elementName ) override; + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + +public: // XRefreshable + virtual void SAL_CALL refresh( ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; + + +class IndexColumnDescriptors final : public Container +{ + +public: // instances IndexColumns 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + +private: + IndexColumnDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xindexes.cxx b/connectivity/source/drivers/postgresql/pq_xindexes.cxx new file mode 100644 index 000000000..2f6df914f --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xindexes.cxx @@ -0,0 +1,300 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include "pq_xindexes.hxx" +#include "pq_xindex.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; + +using com::sun::star::container::XEnumerationAccess; +using com::sun::star::container::XEnumeration; + + +using com::sun::star::sdbcx::XColumnsSupplier; + +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XParameters; +using com::sun::star::sdbc::XPreparedStatement; + +namespace pq_sdbc_driver +{ + +Indexes::Indexes( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName) + : Container( refMutex, origin, pSettings, getStatics().KEY ), + m_schemaName( schemaName ), + m_tableName( tableName ) +{ +} + +Indexes::~Indexes() +{} + +void Indexes::refresh() +{ + try + { + SAL_INFO("connectivity.postgresql", "sdbcx.Indexes get refreshed for table " << m_schemaName << "." << m_tableName); + + osl::MutexGuard guard( m_xMutex->GetMutex() ); + Statics & st = getStatics(); + + Int2StringMap column2NameMap; + fillAttnum2attnameMap( column2NameMap, m_origin, m_schemaName, m_tableName ); + + // see XDatabaseMetaData::getIndexInfo() + Reference< XPreparedStatement > stmt = m_origin->prepareStatement( + "SELECT nspname, " // 1 + "pg_class.relname, " // 2 + "class2.relname, " // 3 + "indisclustered, " // 4 + "indisunique, " // 5 + "indisprimary, " // 6 + "indkey " // 7 + "FROM pg_index INNER JOIN pg_class ON indrelid = pg_class.oid " + "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid " + "INNER JOIN pg_class as class2 ON pg_index.indexrelid = class2.oid " + "WHERE nspname = ? AND pg_class.relname = ?" ); + + Reference< XParameters > params( stmt, UNO_QUERY); + params->setString( 1, m_schemaName ); + params->setString( 2, m_tableName ); + Reference< XResultSet > rs = stmt->executeQuery(); + + Reference< XRow > row( rs, UNO_QUERY ); + String2IntMap map; + m_values.clear(); + sal_Int32 index = 0; + while( rs->next() ) + { + // C_SCHEMA = 1 + // C_TABLENAME = 2 + static const sal_Int32 C_INDEXNAME = 3; + static const sal_Int32 C_IS_CLUSTERED = 4; + static const sal_Int32 C_IS_UNIQUE = 5; + static const sal_Int32 C_IS_PRIMARY = 6; + static const sal_Int32 C_COLUMNS = 7; + OUString currentIndexName = row->getString( C_INDEXNAME ); + Index *pIndex = + new Index( m_xMutex, m_origin, m_pSettings, + m_schemaName, m_tableName ); + + bool isUnique = row->getBoolean( C_IS_UNIQUE ); + bool isPrimary = row->getBoolean( C_IS_PRIMARY ); + bool isClusterd = row->getBoolean( C_IS_CLUSTERED ); + Reference< css::beans::XPropertySet > prop = pIndex; + pIndex->setPropertyValue_NoBroadcast_public( + st.IS_UNIQUE, Any( isUnique ) ); + pIndex->setPropertyValue_NoBroadcast_public( + st.IS_PRIMARY_KEY_INDEX, Any( isPrimary ) ); + pIndex->setPropertyValue_NoBroadcast_public( + st.IS_CLUSTERED, Any( isClusterd ) ); + pIndex->setPropertyValue_NoBroadcast_public( + st.NAME, makeAny( currentIndexName ) ); + + std::vector< sal_Int32 > seq = parseIntArray( row->getString( C_COLUMNS ) ); + Sequence< OUString > columnNames(seq.size()); + for( size_t columns = 0 ; columns < seq.size() ; columns ++ ) + { + columnNames[columns] = column2NameMap[ seq[columns] ]; + } + + pIndex->setPropertyValue_NoBroadcast_public( + st.PRIVATE_COLUMN_INDEXES, makeAny( columnNames )); + + { + m_values.push_back( makeAny( prop ) ); + map[ currentIndexName ] = index; + ++index; + } + } + m_name2index.swap( map ); + } + catch ( css::sdbc::SQLException & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } + + fire( RefreshedBroadcaster( *this ) ); +} + + +void Indexes::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) +{ + Statics & st = getStatics(); + OUString name = extractStringProperty( descriptor, st.NAME ); + + bool isUnique = extractBoolProperty( descriptor, st.IS_UNIQUE ); + + OUStringBuffer buf( 128 ); + + buf.append( "CREATE " ); + if( isUnique ) + buf.append( "UNIQUE " ); + buf.append( "INDEX " ); + bufferQuoteIdentifier( buf, name, m_pSettings ); + buf.append( " ON " ); + bufferQuoteQualifiedIdentifier( buf, m_schemaName, m_tableName, m_pSettings ); + + buf.append( " ( " ); + + Reference< XColumnsSupplier > columns( descriptor, UNO_QUERY ); + if( columns.is() ) + { + Reference< XEnumerationAccess > access( columns->getColumns(), UNO_QUERY ); + if( access.is() ) + { + Reference< XEnumeration > xEnum( access->createEnumeration() ); + bool first = true; + while( xEnum.is() && xEnum->hasMoreElements() ) + { + Reference< XPropertySet > column( xEnum->nextElement(), UNO_QUERY ); + if( first ) + { + first = false; + } + else + { + buf.append( ", " ); + } + buf.append( extractStringProperty( column, st.NAME ) ); + } + } + } + buf.append( " ) " ); + + m_origin->createStatement()->executeUpdate( buf.makeStringAndClear() ); + refresh(); +} + +void Indexes::dropByIndex( sal_Int32 index ) +{ + + + osl::MutexGuard guard( m_xMutex->GetMutex() ); + if( index < 0 || index >= static_cast<sal_Int32>(m_values.size()) ) + { + throw css::lang::IndexOutOfBoundsException( + "Indexes: Index out of range (allowed 0 to " + + OUString::number( m_values.size() -1 ) + + ", got " + OUString::number( index ) + + ")", + *this ); + } + + Reference< XPropertySet > set; + m_values[index] >>= set; + Statics &st = getStatics(); + + OUStringBuffer buf( 128 ); + buf.append( "DROP INDEX " ); + bufferQuoteIdentifier( buf, extractStringProperty( set, st.NAME ), m_pSettings ); + m_origin->createStatement()->executeUpdate( buf.makeStringAndClear() ); + + Container::dropByIndex( index ); +} + + +css::uno::Reference< css::beans::XPropertySet > Indexes::createDataDescriptor() +{ + return new IndexDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +Reference< css::container::XNameAccess > Indexes::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString & schemaName, + const OUString & tableName) +{ + Indexes *pIndexes = new Indexes( refMutex, origin, pSettings, schemaName, tableName ); + Reference< css::container::XNameAccess > ret = pIndexes; + pIndexes->refresh(); + return ret; +} + + +IndexDescriptors::IndexDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings) + : Container( refMutex, origin, pSettings, getStatics().INDEX ) +{} + +Reference< css::container::XNameAccess > IndexDescriptors::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings) +{ + return new IndexDescriptors( refMutex, origin, pSettings ); +} + +css::uno::Reference< css::beans::XPropertySet > IndexDescriptors::createDataDescriptor() +{ + return new IndexDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xindexes.hxx b/connectivity/source/drivers/postgresql/pq_xindexes.hxx new file mode 100644 index 000000000..7991058f5 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xindexes.hxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XINDEXES_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XINDEXES_HXX + +#include "pq_xcontainer.hxx" + +namespace pq_sdbc_driver +{ +class Indexes final : public Container +{ + OUString m_schemaName; + OUString m_tableName; + +public: // instances Columns 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName); + +private: + Indexes( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName); + + virtual ~Indexes() override; + +public: // XAppend + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + +public: // XDrop + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + +public: // XRefreshable + virtual void SAL_CALL refresh( ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; + + +class IndexDescriptors final : public Container +{ +public: // instances IndexDescriptors 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + +private: + IndexDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; + +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xkey.cxx b/connectivity/source/drivers/postgresql/pq_xkey.cxx new file mode 100644 index 000000000..ed0b2669e --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xkey.cxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include "pq_xkey.hxx" +#include "pq_xkeycolumns.hxx" +#include "pq_statics.hxx" + +using com::sun::star::container::XNameAccess; + +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; + +using com::sun::star::beans::XPropertySet; + + +namespace pq_sdbc_driver +{ +Key::Key( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings, + const OUString & schemaName, + const OUString & tableName ) + : ReflectionBase( + getStatics().refl.key.implName, + getStatics().refl.key.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.key.pProps ), + m_schemaName( schemaName ), + m_tableName( tableName ) +{} + +Reference< XPropertySet > Key::createDataDescriptor( ) +{ + KeyDescriptor * pKeyDescriptor = new KeyDescriptor( + m_xMutex, m_conn, m_pSettings ); + pKeyDescriptor->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pKeyDescriptor ); +} + +Reference< XNameAccess > Key::getColumns( ) +{ + // TODO: cash columns object ! + if( !m_keyColumns.is() ) + { + Sequence< OUString > columnNames, foreignColumnNames; + getPropertyValue( getStatics().PRIVATE_COLUMNS ) >>= columnNames; + getPropertyValue( getStatics().PRIVATE_FOREIGN_COLUMNS ) >>= foreignColumnNames; + + m_keyColumns = KeyColumns::create( + m_xMutex, m_conn, m_pSettings, m_schemaName, + m_tableName, columnNames, foreignColumnNames ); + } + return m_keyColumns; +} + +Sequence<Type > Key::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<css::sdbcx::XColumnsSupplier>::get(), + ReflectionBase::getTypes()); + + return collection.getTypes(); +} + +Sequence< sal_Int8> Key::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any Key::queryInterface( const Type & reqType ) +{ + Any ret = ReflectionBase::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< css::sdbcx::XColumnsSupplier * > ( this ) ); + return ret; +} + + +KeyDescriptor::KeyDescriptor( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings ) + : ReflectionBase( + getStatics().refl.keyDescriptor.implName, + getStatics().refl.keyDescriptor.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.keyDescriptor.pProps ) +{ +} + +Reference< XPropertySet > KeyDescriptor::createDataDescriptor( ) +{ + KeyDescriptor * pKeyDescriptor = new KeyDescriptor( + m_xMutex, m_conn, m_pSettings ); + pKeyDescriptor->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pKeyDescriptor ); +} + +Reference< XNameAccess > KeyDescriptor::getColumns( ) +{ + // TODO: cash columns object ! + if( !m_keyColumns.is() ) + { + m_keyColumns = new KeyColumnDescriptors( m_xMutex, m_conn, m_pSettings ); + } + return m_keyColumns; +} + +Sequence<Type > KeyDescriptor::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<css::sdbcx::XColumnsSupplier>::get(), + ReflectionBase::getTypes()); + + return collection.getTypes(); +} + +Sequence< sal_Int8> KeyDescriptor::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any KeyDescriptor::queryInterface( const Type & reqType ) +{ + Any ret = ReflectionBase::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< css::sdbcx::XColumnsSupplier * > ( this ) ); + return ret; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xkey.hxx b/connectivity/source/drivers/postgresql/pq_xkey.hxx new file mode 100644 index 000000000..3174d1892 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xkey.hxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XKEY_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XKEY_HXX + +#include <cppuhelper/component.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> + +#include "pq_connection.hxx" +#include "pq_xbase.hxx" + +namespace pq_sdbc_driver +{ + +class Key : public ReflectionBase, + public css::sdbcx::XColumnsSupplier +{ + css::uno::Reference< css::container::XNameAccess > m_keyColumns; + + OUString m_schemaName; + OUString m_tableName; + +public: + Key( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName); + +public: // XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL + getColumns( ) override; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + + +class KeyDescriptor : public ReflectionBase, public css::sdbcx::XColumnsSupplier +{ + css::uno::Reference< css::container::XNameAccess > m_keyColumns; + +public: + KeyDescriptor( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings ); + +public: // XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL + getColumns( ) override; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; +}; + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xkeycolumn.cxx b/connectivity/source/drivers/postgresql/pq_xkeycolumn.cxx new file mode 100644 index 000000000..9eea45204 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xkeycolumn.cxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include "pq_xkeycolumn.hxx" + +using com::sun::star::uno::Reference; + +using com::sun::star::beans::XPropertySet; + +namespace pq_sdbc_driver +{ +KeyColumn::KeyColumn( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings) + : ReflectionBase( + getStatics().refl.keycolumn.implName, + getStatics().refl.keycolumn.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.keycolumn.pProps ) +{} + +Reference< XPropertySet > KeyColumn::createDataDescriptor( ) +{ + KeyColumnDescriptor * pKeyColumn = new KeyColumnDescriptor( + m_xMutex, m_conn, m_pSettings ); + pKeyColumn->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pKeyColumn ); +} + +KeyColumnDescriptor::KeyColumnDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings) + : ReflectionBase( + getStatics().refl.keycolumnDescriptor.implName, + getStatics().refl.keycolumnDescriptor.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.keycolumnDescriptor.pProps ) +{} + +Reference< XPropertySet > KeyColumnDescriptor::createDataDescriptor( ) +{ + KeyColumnDescriptor * pKeyColumn = new KeyColumnDescriptor( + m_xMutex, m_conn, m_pSettings ); + pKeyColumn->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pKeyColumn ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xkeycolumn.hxx b/connectivity/source/drivers/postgresql/pq_xkeycolumn.hxx new file mode 100644 index 000000000..14baf7f66 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xkeycolumn.hxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XKEYCOLUMN_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XKEYCOLUMN_HXX + +#include <cppuhelper/component.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> + +#include "pq_connection.hxx" +#include "pq_xbase.hxx" + +namespace pq_sdbc_driver +{ + +class KeyColumn : public ReflectionBase +{ +public: + KeyColumn( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + +class KeyColumnDescriptor : public ReflectionBase +{ +public: + KeyColumnDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + +}; + + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xkeycolumns.cxx b/connectivity/source/drivers/postgresql/pq_xkeycolumns.cxx new file mode 100644 index 000000000..890b57fa7 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xkeycolumns.cxx @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/log.hxx> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include "pq_xcolumns.hxx" +#include "pq_xkeycolumns.hxx" +#include "pq_xkeycolumn.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; + +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XDatabaseMetaData; +using com::sun::star::sdbc::SQLException; + +namespace pq_sdbc_driver +{ + +KeyColumns::KeyColumns( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + const Sequence< OUString > &columnNames, + const Sequence< OUString > &foreignColumnNames ) + : Container( refMutex, origin, pSettings, "KEY_COLUMN" ), + m_schemaName( schemaName ), + m_tableName( tableName ), + m_columnNames( columnNames ), + m_foreignColumnNames( foreignColumnNames ) +{} + +KeyColumns::~KeyColumns() +{} + + +void KeyColumns::refresh() +{ + try + { + SAL_INFO("connectivity.postgresql", "sdbcx.KeyColumns get refreshed for table " << m_schemaName << "." << m_tableName); + + osl::MutexGuard guard( m_xMutex->GetMutex() ); + + Statics &st = getStatics(); + Reference< XDatabaseMetaData > meta = m_origin->getMetaData(); + + Reference< XResultSet > rs = + meta->getColumns( Any(), m_schemaName, m_tableName, st.cPERCENT ); + + DisposeGuard disposeIt( rs ); + Reference< XRow > xRow( rs , UNO_QUERY ); + + String2IntMap map; + + m_values.clear(); + sal_Int32 columnIndex = 0; + while( rs->next() ) + { + OUString columnName = xRow->getString( 4 ); + + int keyindex; + for( keyindex = 0 ; keyindex < m_columnNames.getLength() ; keyindex ++ ) + { + if( columnName == m_columnNames[keyindex] ) + break; + } + if( m_columnNames.getLength() == keyindex ) + continue; + + KeyColumn * pKeyColumn = + new KeyColumn( m_xMutex, m_origin, m_pSettings ); + Reference< css::beans::XPropertySet > prop = pKeyColumn; + + OUString name = columnMetaData2SDBCX( pKeyColumn, xRow ); + if( keyindex < m_foreignColumnNames.getLength() ) + { + pKeyColumn->setPropertyValue_NoBroadcast_public( + st.RELATED_COLUMN, makeAny( m_foreignColumnNames[keyindex]) ); + } + + { + m_values.push_back( makeAny( prop ) ); + map[ name ] = columnIndex; + ++columnIndex; + } + } + m_name2index.swap( map ); + } + catch ( css::sdbc::SQLException & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } + + fire( RefreshedBroadcaster( *this ) ); +} + + +void KeyColumns::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& ) +{ + throw css::sdbc::SQLException( + "KeyColumns::appendByDescriptor not implemented yet", + *this, OUString(), 1, Any() ); + +// osl::MutexGuard guard( m_xMutex->GetMutex() ); +// Statics & st = getStatics(); +// Reference< XPropertySet > past = createDataDescriptor(); +// past->setPropertyValue( st.IS_NULLABLE, makeAny( css::sdbc::ColumnValue::NULLABLE ) ); +// alterColumnByDescriptor( +// m_schemaName, m_tableName, m_pSettings->encoding, m_origin->createStatement() , past, future ); + +} + + +void KeyColumns::dropByIndex( sal_Int32 ) +{ + throw css::sdbc::SQLException( + "KeyColumns::dropByIndex not implemented yet", + *this, OUString(), 1, Any() ); +// osl::MutexGuard guard( m_xMutex->GetMutex() ); +// if( index < 0 || index >= m_values.getLength() ) +// { +// OUStringBuffer buf( 128 ); +// buf.appendAscii( "COLUMNS: Index out of range (allowed 0 to " ); +// buf.append((sal_Int32)(m_values.getLength() -1) ); +// buf.appendAscii( ", got " ); +// buf.append( index ); +// buf.appendAscii( ")" ); +// throw css::lang::IndexOutOfBoundsException( +// buf.makeStringAndClear(), *this ); +// } + +// Reference< XPropertySet > set; +// m_values[index] >>= set; +// Statics &st = getStatics(); +// OUString name; +// set->getPropertyValue( st.NAME ) >>= name; + +// OUStringBuffer update( 128 ); +// update.appendAscii( "ALTER TABLE ONLY"); +// bufferQuoteQualifiedIdentifier( update, m_schemaName, m_tableName ); +// update.appendAscii( "DROP COLUMN" ); +// bufferQuoteIdentifier( update, name ); +// Reference< XStatement > stmt = m_origin->createStatement( ); +// DisposeGuard disposeIt( stmt ); +// stmt->executeUpdate( update.makeStringAndClear() ); + +} + + +Reference< css::beans::XPropertySet > KeyColumns::createDataDescriptor() +{ + return new KeyColumnDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +Reference< css::container::XNameAccess > KeyColumns::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + const Sequence< OUString > &columnNames , + const Sequence< OUString > &foreignColumnNames ) +{ + KeyColumns *pKeyColumns = new KeyColumns( + refMutex, origin, pSettings, schemaName, tableName, columnNames, foreignColumnNames ); + Reference< css::container::XNameAccess > ret = pKeyColumns; + pKeyColumns->refresh(); + + return ret; +} + + +KeyColumnDescriptors::KeyColumnDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ) + : Container( refMutex, origin, pSettings, "KEY_COLUMN" ) +{} + +Reference< css::beans::XPropertySet > KeyColumnDescriptors::createDataDescriptor() +{ + return new KeyColumnDescriptor( m_xMutex, m_origin, m_pSettings ); +} +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xkeycolumns.hxx b/connectivity/source/drivers/postgresql/pq_xkeycolumns.hxx new file mode 100644 index 000000000..0ada98aa4 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xkeycolumns.hxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XKEYCOLUMNS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XKEYCOLUMNS_HXX + +#include "pq_xcontainer.hxx" + +namespace pq_sdbc_driver +{ + +class KeyColumns final : public Container +{ + OUString m_schemaName; + OUString m_tableName; + css::uno::Sequence< OUString > m_columnNames; + css::uno::Sequence< OUString > m_foreignColumnNames; + +public: // instances KeyColumns 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + const css::uno::Sequence< OUString > &keyColumns, + const css::uno::Sequence< OUString > &foreignColumnNames ); + +private: + KeyColumns( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName, + const css::uno::Sequence< OUString > &keyColumns, + const css::uno::Sequence< OUString > &foreignColumnNames); + + virtual ~KeyColumns() override; + +public: // XAppend + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + +public: // XDrop + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + +public: // XRefreshable + virtual void SAL_CALL refresh( ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; + + +class KeyColumnDescriptors : public Container +{ +public: + KeyColumnDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xkeys.cxx b/connectivity/source/drivers/postgresql/pq_xkeys.cxx new file mode 100644 index 000000000..2297b557e --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xkeys.cxx @@ -0,0 +1,290 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include "pq_xkeys.hxx" +#include "pq_xkey.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + + +using css::beans::XPropertySet; + +using com::sun::star::uno::makeAny; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Reference; + + +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XParameters; +using com::sun::star::sdbc::XPreparedStatement; + +namespace pq_sdbc_driver +{ + +Keys::Keys( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName) + : Container( refMutex, origin, pSettings, getStatics().KEY ), + m_schemaName( schemaName ), + m_tableName( tableName ) +{} + +Keys::~Keys() +{} + +static sal_Int32 string2keytype( const OUString &type ) +{ + sal_Int32 ret = css::sdbcx::KeyType::UNIQUE; + if ( type == "p" ) + ret = css::sdbcx::KeyType::PRIMARY; + else if ( type == "f" ) + ret = css::sdbcx::KeyType::FOREIGN; + return ret; +} + +static sal_Int32 string2keyrule( const OUString & rule ) +{ + sal_Int32 ret = css::sdbc::KeyRule::NO_ACTION; + if( rule == "r" ) + ret = css::sdbc::KeyRule::RESTRICT; + else if( rule == "c" ) + ret = css::sdbc::KeyRule::CASCADE; + else if( rule == "n" ) + ret = css::sdbc::KeyRule::SET_NULL; + else if( rule == "d" ) + ret = css::sdbc::KeyRule::SET_DEFAULT; + return ret; +} + +void Keys::refresh() +{ + try + { + SAL_INFO("connectivity.postgresql", "sdbcx.Keys get refreshed for table " << m_schemaName << "." << m_tableName); + + osl::MutexGuard guard( m_xMutex->GetMutex() ); + Statics & st = getStatics(); + + Int2StringMap mainMap; + fillAttnum2attnameMap( mainMap, m_origin, m_schemaName, m_tableName ); + + Reference< XPreparedStatement > stmt = m_origin->prepareStatement( + "SELECT conname, " // 1 + "contype, " // 2 + "confupdtype, " // 3 + "confdeltype, " // 4 + "class2.relname, " // 5 + "nmsp2.nspname, " // 6 + "conkey," // 7 + "confkey " // 8 + "FROM pg_constraint INNER JOIN pg_class ON conrelid = pg_class.oid " + "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid " + "LEFT JOIN pg_class AS class2 ON confrelid = class2.oid " + "LEFT JOIN pg_namespace AS nmsp2 ON class2.relnamespace=nmsp2.oid " + "WHERE pg_class.relname = ? AND pg_namespace.nspname = ?" ); + + Reference< XParameters > paras( stmt, UNO_QUERY ); + paras->setString( 1 , m_tableName ); + paras->setString( 2 , m_schemaName ); + Reference< XResultSet > rs = stmt->executeQuery(); + + Reference< XRow > xRow( rs , UNO_QUERY ); + + String2IntMap map; + m_values.clear(); + int keyIndex = 0; + while( rs->next() ) + { + Key * pKey = + new Key( m_xMutex, m_origin, m_pSettings , m_schemaName, m_tableName ); + Reference< css::beans::XPropertySet > prop = pKey; + + pKey->setPropertyValue_NoBroadcast_public( + st.NAME, makeAny( xRow->getString( 1 ) ) ); + sal_Int32 keyType = string2keytype( xRow->getString(2) ); + pKey->setPropertyValue_NoBroadcast_public( st.TYPE, makeAny( keyType ) ); + pKey->setPropertyValue_NoBroadcast_public( + st.UPDATE_RULE, makeAny( string2keyrule( xRow->getString(3) ) ) ); + pKey->setPropertyValue_NoBroadcast_public( + st.DELETE_RULE, makeAny( string2keyrule( xRow->getString(4) ) ) ); + pKey->setPropertyValue_NoBroadcast_public( + st.PRIVATE_COLUMNS, + makeAny( + convertMappedIntArray2StringArray( + mainMap, + string2intarray( xRow->getString( 7 ) ) ) ) ); + + if( css::sdbcx::KeyType::FOREIGN == keyType ) + { + OUStringBuffer buf( 128 ); + buf.append( xRow->getString( 6 ) ).append( "." ).append( xRow->getString( 5 ) ); + pKey->setPropertyValue_NoBroadcast_public( + st.REFERENCED_TABLE, makeAny( buf.makeStringAndClear() ) ); + + Int2StringMap foreignMap; + fillAttnum2attnameMap( foreignMap, m_origin, xRow->getString(6), xRow->getString(5)); + pKey->setPropertyValue_NoBroadcast_public( + st.PRIVATE_FOREIGN_COLUMNS, + makeAny( + convertMappedIntArray2StringArray( + foreignMap, + string2intarray( xRow->getString(8) ) ) ) ); + } + + + { + map[ xRow->getString( 1 ) ] = keyIndex; + m_values.push_back( makeAny( prop ) ); + ++keyIndex; + } + } + m_name2index.swap( map ); + } + catch ( css::sdbc::SQLException & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } + + fire( RefreshedBroadcaster( *this ) ); +} + + +void Keys::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + + OUStringBuffer buf( 128 ); + buf.append( "ALTER TABLE " ); + bufferQuoteQualifiedIdentifier( buf, m_schemaName, m_tableName, m_pSettings ); + buf.append( " ADD " ); + bufferKey2TableConstraint( buf, descriptor, m_pSettings ); + + Reference< XStatement > stmt = + m_origin->createStatement(); + stmt->executeUpdate( buf.makeStringAndClear() ); +} + + +void Keys::dropByIndex( sal_Int32 index ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + if( index < 0 || index >= static_cast<sal_Int32>(m_values.size()) ) + { + throw css::lang::IndexOutOfBoundsException( + "TABLES: Index out of range (allowed 0 to " + OUString::number(m_values.size() -1) + + ", got " + OUString::number( index ) + ")", + *this ); + } + + + Reference< XPropertySet > set; + m_values[index] >>= set; + + OUStringBuffer buf( 128 ); + buf.append( "ALTER TABLE " ); + bufferQuoteQualifiedIdentifier( buf, m_schemaName, m_tableName, m_pSettings ); + buf.append( " DROP CONSTRAINT " ); + bufferQuoteIdentifier( buf, extractStringProperty( set , getStatics().NAME ), m_pSettings ); + m_origin->createStatement()->executeUpdate( buf.makeStringAndClear() ); + + + Container::dropByIndex( index ); +} + + +css::uno::Reference< css::beans::XPropertySet > Keys::createDataDescriptor() +{ + return new KeyDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +Reference< css::container::XIndexAccess > Keys::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString & schemaName, + const OUString & tableName) +{ + Keys *pKeys = new Keys( refMutex, origin, pSettings, schemaName, tableName ); + Reference< css::container::XIndexAccess > ret = pKeys; + pKeys->refresh(); + + return ret; +} + +KeyDescriptors::KeyDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings) + : Container( refMutex, origin, pSettings, getStatics().KEY ) +{} + +Reference< css::container::XIndexAccess > KeyDescriptors::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings) +{ + return new KeyDescriptors( refMutex, origin, pSettings ); +} + +css::uno::Reference< css::beans::XPropertySet > KeyDescriptors::createDataDescriptor() +{ + return new KeyDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xkeys.hxx b/connectivity/source/drivers/postgresql/pq_xkeys.hxx new file mode 100644 index 000000000..aedd46b57 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xkeys.hxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XKEYS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XKEYS_HXX + +#include "pq_xcontainer.hxx" + +namespace pq_sdbc_driver +{ +class Keys final : public Container +{ + OUString m_schemaName; + OUString m_tableName; + +public: // instances Columns 'exception safe' + static css::uno::Reference< css::container::XIndexAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName); + +private: + Keys( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + const OUString &schemaName, + const OUString &tableName); + + virtual ~Keys() override; + +public: // XAppend + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + +public: // XDrop + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + +public: // XRefreshable + virtual void SAL_CALL refresh( ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; + + +class KeyDescriptors final : public Container +{ +public: // instances Columns 'exception safe' + static css::uno::Reference< css::container::XIndexAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + +private: + KeyDescriptors( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xtable.cxx b/connectivity/source/drivers/postgresql/pq_xtable.cxx new file mode 100644 index 000000000..82b66a316 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xtable.cxx @@ -0,0 +1,395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <rtl/ustrbuf.hxx> + +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <com/sun/star/sdbc/SQLException.hpp> + +#include "pq_xtable.hxx" +#include "pq_xtables.hxx" +#include "pq_xviews.hxx" +#include "pq_xindexes.hxx" +#include "pq_xkeys.hxx" +#include "pq_xcolumns.hxx" +#include "pq_tools.hxx" +#include "pq_statics.hxx" + +using osl::MutexGuard; + +using com::sun::star::container::XNameAccess; +using com::sun::star::container::XIndexAccess; + +using com::sun::star::uno::Reference; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; +using com::sun::star::uno::Type; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::SQLException; + +namespace pq_sdbc_driver +{ +Table::Table( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings) + : ReflectionBase( + getStatics().refl.table.implName, + getStatics().refl.table.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.table.pProps ), + m_pColumns( nullptr ) +{} + +Reference< XPropertySet > Table::createDataDescriptor( ) +{ + TableDescriptor * pTable = new TableDescriptor( + m_xMutex, m_conn, m_pSettings ); + pTable->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pTable ); +} + +Reference< XNameAccess > Table::getColumns( ) +{ + if( ! m_columns.is() ) + { + m_columns = Columns::create( + m_xMutex, + m_conn, + m_pSettings, + extractStringProperty( this, getStatics().SCHEMA_NAME ), + extractStringProperty( this, getStatics().NAME ), + &m_pColumns); + } + return m_columns; +} + +Reference< XNameAccess > Table::getIndexes() +{ + if( ! m_indexes.is() ) + { + m_indexes = ::pq_sdbc_driver::Indexes::create( + m_xMutex, + m_conn, + m_pSettings, + extractStringProperty( this, getStatics().SCHEMA_NAME ), + extractStringProperty( this, getStatics().NAME ) ); + } + return m_indexes; +} + +Reference< XIndexAccess > Table::getKeys( ) +{ + if( ! m_keys.is() ) + { + m_keys = ::pq_sdbc_driver::Keys::create( + m_xMutex, + m_conn, + m_pSettings, + extractStringProperty( this, getStatics().SCHEMA_NAME ), + extractStringProperty( this, getStatics().NAME ) ); + } + return m_keys; +} + +void Table::rename( const OUString& newName ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + Statics & st = getStatics(); + + OUString oldName = extractStringProperty(this,st.NAME ); + OUString schema = extractStringProperty(this,st.SCHEMA_NAME ); + OUString fullOldName = concatQualified( schema, oldName ); + + OUString newTableName; + OUString newSchemaName; + // OOo2.0 passes schema + dot + new-table-name while + // OO1.1.x passes new Name without schema + // in case name contains a dot, it is interpreted as schema.tablename + if( newName.indexOf( '.' ) >= 0 ) + { + splitConcatenatedIdentifier( newName, &newSchemaName, &newTableName ); + } + else + { + newTableName = newName; + newSchemaName = schema; + } + OUString fullNewName = concatQualified( newSchemaName, newTableName ); + + if( extractStringProperty( this, st.TYPE ) == st.VIEW && m_pSettings->views.is() ) + { + // maintain view list (really strange API !) + Any a = m_pSettings->pViewsImpl->getByName( fullOldName ); + Reference< css::sdbcx::XRename > Xrename; + a >>= Xrename; + if( Xrename.is() ) + { + Xrename->rename( newName ); + setPropertyValue_NoBroadcast_public( st.SCHEMA_NAME, makeAny(newSchemaName) ); + } + } + else + { + if( newSchemaName != schema ) + { + // try new schema name first + try + { + OUStringBuffer buf(128); + buf.append( "ALTER TABLE" ); + bufferQuoteQualifiedIdentifier(buf, schema, oldName, m_pSettings ); + buf.append( "SET SCHEMA" ); + bufferQuoteIdentifier( buf, newSchemaName, m_pSettings ); + Reference< XStatement > statement = m_conn->createStatement(); + statement->executeUpdate( buf.makeStringAndClear() ); + setPropertyValue_NoBroadcast_public( st.SCHEMA_NAME, makeAny(newSchemaName) ); + disposeNoThrow( statement ); + schema = newSchemaName; + } + catch( css::sdbc::SQLException &e ) + { + OUString buf( e.Message + "(NOTE: Only postgresql server >= V8.1 support changing a table's schema)" ); + e.Message = buf; + throw; + } + + } + if( newTableName != oldName ) // might also be just the change of a schema name + { + OUStringBuffer buf(128); + buf.append( "ALTER TABLE" ); + bufferQuoteQualifiedIdentifier(buf, schema, oldName, m_pSettings ); + buf.append( "RENAME TO" ); + bufferQuoteIdentifier( buf, newTableName, m_pSettings ); + Reference< XStatement > statement = m_conn->createStatement(); + statement->executeUpdate( buf.makeStringAndClear() ); + disposeNoThrow( statement ); + } + } + setPropertyValue_NoBroadcast_public( st.NAME, makeAny(newTableName) ); + // inform the container of the name change ! + if( m_pSettings->tables.is() ) + { + m_pSettings->pTablesImpl->rename( fullOldName, fullNewName ); + } +} + +void Table::alterColumnByName( + const OUString& colName, + const Reference< XPropertySet >& descriptor ) +{ + Reference< css::container::XNameAccess > columns = getColumns(); + + OUString newName = extractStringProperty(descriptor, getStatics().NAME ); + ::pq_sdbc_driver::alterColumnByDescriptor( + extractStringProperty( this, getStatics().SCHEMA_NAME ), + extractStringProperty( this, getStatics().NAME ), + m_pSettings, + m_conn->createStatement(), + Reference< css::beans::XPropertySet>( columns->getByName( colName ), UNO_QUERY) , + descriptor ); + + if( colName != newName ) + { +// m_pColumns->rename( colName, newName ); + m_pColumns->refresh(); + } +} + +void Table::alterColumnByIndex( + sal_Int32 index, + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) +{ + Reference< css::container::XIndexAccess > columns( getColumns(), UNO_QUERY ); + Reference< css::beans::XPropertySet> column(columns->getByIndex( index ), UNO_QUERY ); + ::pq_sdbc_driver::alterColumnByDescriptor( + extractStringProperty( this, getStatics().SCHEMA_NAME ), + extractStringProperty( this, getStatics().NAME ), + m_pSettings, + m_conn->createStatement(), + column, + descriptor ); + m_pColumns->refresh(); +} + +Sequence<Type > Table::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<css::sdbcx::XIndexesSupplier>::get(), + cppu::UnoType<css::sdbcx::XKeysSupplier>::get(), + cppu::UnoType<css::sdbcx::XColumnsSupplier>::get(), + cppu::UnoType<css::sdbcx::XRename>::get(), + cppu::UnoType<css::sdbcx::XAlterTable>::get(), + ReflectionBase::getTypes()); + + return collection.getTypes(); +} + +Sequence< sal_Int8> Table::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any Table::queryInterface( const Type & reqType ) +{ + Any ret = ReflectionBase::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< css::sdbcx::XIndexesSupplier * > ( this ), + static_cast< css::sdbcx::XKeysSupplier * > ( this ), + static_cast< css::sdbcx::XColumnsSupplier * > ( this ), + static_cast< css::sdbcx::XRename * > ( this ), + static_cast< css::sdbcx::XAlterTable * > ( this ) + ); + return ret; +} + +OUString Table::getName( ) +{ + Statics & st = getStatics(); + return concatQualified( + extractStringProperty( this, st.SCHEMA_NAME ), + extractStringProperty( this, st.NAME ) ); +} + +void Table::setName( const OUString& aName ) +{ + rename( aName ); +} + + +TableDescriptor::TableDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings) + : ReflectionBase( + getStatics().refl.tableDescriptor.implName, + getStatics().refl.tableDescriptor.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.tableDescriptor.pProps ) +{ +} + +Reference< XNameAccess > TableDescriptor::getColumns( ) +{ + if( ! m_columns.is() ) + { + m_columns = new ColumnDescriptors(m_xMutex, m_conn, m_pSettings ); + } + return m_columns; +} + +Reference< XNameAccess > TableDescriptor::getIndexes() +{ + if( ! m_indexes.is() ) + { + m_indexes = ::pq_sdbc_driver::IndexDescriptors::create( + m_xMutex, + m_conn, + m_pSettings); + } + return m_indexes; +} + +Reference< XIndexAccess > TableDescriptor::getKeys( ) +{ + if( ! m_keys.is() ) + { + m_keys = ::pq_sdbc_driver::KeyDescriptors::create( + m_xMutex, + m_conn, + m_pSettings ); + } + return m_keys; +} + + +Sequence<Type > TableDescriptor::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<css::sdbcx::XIndexesSupplier>::get(), + cppu::UnoType<css::sdbcx::XKeysSupplier>::get(), + cppu::UnoType<css::sdbcx::XColumnsSupplier>::get(), + ReflectionBase::getTypes()); + + return collection.getTypes(); +} + +Sequence< sal_Int8> TableDescriptor::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any TableDescriptor::queryInterface( const Type & reqType ) +{ + Any ret = ReflectionBase::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< css::sdbcx::XIndexesSupplier * > ( this ), + static_cast< css::sdbcx::XKeysSupplier * > ( this ), + static_cast< css::sdbcx::XColumnsSupplier * > ( this )); + return ret; +} + + +Reference< XPropertySet > TableDescriptor::createDataDescriptor( ) +{ + TableDescriptor * pTable = new TableDescriptor( + m_xMutex, m_conn, m_pSettings ); + + // TODO: deep copies + pTable->m_values = m_values; + + return Reference< XPropertySet > ( pTable ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xtable.hxx b/connectivity/source/drivers/postgresql/pq_xtable.hxx new file mode 100644 index 000000000..69e9d6435 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xtable.hxx @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XTABLE_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XTABLE_HXX + +#include <cppuhelper/component.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XIndexesSupplier.hpp> +#include <com/sun/star/sdbcx/XKeysSupplier.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <com/sun/star/sdbcx/XAlterTable.hpp> +#include <com/sun/star/container/XNamed.hpp> + +#include "pq_xbase.hxx" + +namespace pq_sdbc_driver +{ + +class Columns; + +class Table : public ReflectionBase, + public css::sdbcx::XColumnsSupplier, + public css::sdbcx::XIndexesSupplier, + public css::sdbcx::XKeysSupplier, + public css::sdbcx::XRename, + public css::sdbcx::XAlterTable +{ + css::uno::Reference< css::container::XNameAccess > m_columns; + css::uno::Reference< css::container::XIndexAccess > m_keys; + css::uno::Reference< css::container::XNameAccess > m_indexes; + Columns *m_pColumns; + +public: + Table( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + + // XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + + // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + + // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + + // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL + getColumns( ) override; + + // XIndexesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL + getIndexes( ) override; + + // XKeysSupplier + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL + getKeys( ) override; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + // XAlterTable + virtual void SAL_CALL alterColumnByName( + const OUString& colName, + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + + virtual void SAL_CALL alterColumnByIndex( + sal_Int32 index, + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; +}; + + +class TableDescriptor + : public ReflectionBase, + public css::sdbcx::XColumnsSupplier, + public css::sdbcx::XIndexesSupplier, + public css::sdbcx::XKeysSupplier +{ + css::uno::Reference< css::container::XNameAccess > m_columns; + css::uno::Reference< css::container::XIndexAccess > m_keys; + css::uno::Reference< css::container::XNameAccess > m_indexes; + +public: + TableDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + +public: // XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + +public: // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + +public: // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL + getColumns( ) override; + +public: // XIndexesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL + getIndexes( ) override; + +public: // XKeysSupplier + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL + getKeys( ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; +}; + + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xtables.cxx b/connectivity/source/drivers/postgresql/pq_xtables.cxx new file mode 100644 index 000000000..83758208b --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xtables.cxx @@ -0,0 +1,369 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include "pq_xtables.hxx" +#include "pq_xviews.hxx" +#include "pq_xtable.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; + +using com::sun::star::container::XEnumerationAccess; +using com::sun::star::container::XEnumeration; + +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XResultSet; +using com::sun::star::sdbc::XDatabaseMetaData; +using com::sun::star::sdbcx::XColumnsSupplier; +using com::sun::star::sdbcx::XKeysSupplier; + +namespace pq_sdbc_driver +{ +Tables::Tables( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ) + : Container( refMutex, origin, pSettings, getStatics().TABLE ) +{} + +Tables::~Tables() +{} + +void Tables::refresh() +{ + try + { + osl::MutexGuard guard( m_xMutex->GetMutex() ); + Statics & st = getStatics(); + + Reference< XDatabaseMetaData > meta = m_origin->getMetaData(); + + Reference< XResultSet > rs = + meta->getTables( Any(), st.cPERCENT,st.cPERCENT, Sequence< OUString > () ); + + Reference< XRow > xRow( rs , UNO_QUERY ); + + String2IntMap map; + + m_values.clear(); + sal_Int32 tableIndex = 0; + while( rs->next() ) + { + // if creating all these tables turns out to have too bad performance, we might + // instead offer a factory interface + Table * pTable = + new Table( m_xMutex, m_origin, m_pSettings ); + Reference< css::beans::XPropertySet > prop = pTable; + + OUString name = xRow->getString( TABLE_INDEX_NAME+1); + OUString schema = xRow->getString( TABLE_INDEX_SCHEMA+1); + pTable->setPropertyValue_NoBroadcast_public( + st.CATALOG_NAME , makeAny(xRow->getString( TABLE_INDEX_CATALOG+1) ) ); + pTable->setPropertyValue_NoBroadcast_public( st.NAME , makeAny( name ) ); + pTable->setPropertyValue_NoBroadcast_public( st.SCHEMA_NAME , makeAny( schema )); + pTable->setPropertyValue_NoBroadcast_public( + st.TYPE , makeAny( xRow->getString( TABLE_INDEX_TYPE+1) ) ); + pTable->setPropertyValue_NoBroadcast_public( + st.DESCRIPTION , makeAny( xRow->getString( TABLE_INDEX_REMARKS+1) ) ); + pTable->setPropertyValue_NoBroadcast_public( + st.PRIVILEGES , + makeAny( sal_Int32( css::sdbcx::Privilege::SELECT | + css::sdbcx::Privilege::INSERT | + css::sdbcx::Privilege::UPDATE | + css::sdbcx::Privilege::DELETE | + css::sdbcx::Privilege::READ | + css::sdbcx::Privilege::CREATE | + css::sdbcx::Privilege::ALTER | + css::sdbcx::Privilege::REFERENCE | + css::sdbcx::Privilege::DROP ) ) ); + + { + m_values.push_back( makeAny( prop ) ); + map[ schema + "." + name ] = tableIndex; + ++tableIndex; + } + } + m_name2index.swap( map ); + } + catch ( const css::sdbc::SQLException & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } + + fire( RefreshedBroadcaster( *this ) ); +} + + +static void appendColumnList( + OUStringBuffer &buf, const Reference< XColumnsSupplier > & columnSupplier, ConnectionSettings *settings ) +{ + if( !columnSupplier.is() ) + return; + + Reference< XEnumerationAccess > columns( columnSupplier->getColumns(),UNO_QUERY ); + if( !columns.is() ) + return; + + Reference< XEnumeration > xEnum( columns->createEnumeration() ); + bool first = true; + Statics & st = getStatics(); + + while( xEnum.is() && xEnum->hasMoreElements() ) + { + if( first ) + { + first = false; + } + else + { + buf.append( ", " ); + } + Reference< XPropertySet > column( xEnum->nextElement(), UNO_QUERY ); + OUString name = extractStringProperty( column, st.NAME ); + OUString defaultValue = extractStringProperty( column, st.DEFAULT_VALUE ); + bool isNullable = extractBoolProperty( column, st.IS_NULLABLE ); + bool isAutoIncrement = extractBoolProperty( column, st.IS_AUTO_INCREMENT ); + + bufferQuoteIdentifier( buf, name, settings ); + + OUString type = sqltype2string( column ); + if( isAutoIncrement ) + { + sal_Int32 dataType = 0; + column->getPropertyValue( st.TYPE ) >>= dataType; + if( css::sdbc::DataType::INTEGER == dataType ) + { + buf.append( " serial "); + isNullable = false; + } + else if( css::sdbc::DataType::BIGINT == dataType ) + { + buf.append( " serial8 " ); + isNullable = false; + } + else + buf.append( type ); + } + else + { + buf.append( type ); + } + if( !defaultValue.isEmpty() ) + { + bufferQuoteConstant( buf, defaultValue, settings ); + } + + if( ! isNullable ) + buf.append( " NOT NULL " ); + + } +} + +static void appendKeyList( + OUStringBuffer & buf, const Reference< XKeysSupplier > &keySupplier, ConnectionSettings *settings ) +{ + if( !keySupplier.is() ) + return; + + Reference< XEnumerationAccess > keys( keySupplier->getKeys(), UNO_QUERY ); + if(keys.is() ) + { + Reference< XEnumeration > xEnum = keys->createEnumeration(); + while( xEnum.is() && xEnum->hasMoreElements() ) + { + buf.append( ", " ); + Reference< XPropertySet > key( xEnum->nextElement(), UNO_QUERY ); + bufferKey2TableConstraint( buf, key, settings ); + } + } +} + +void Tables::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + Reference< XStatement > stmt = + m_origin->createStatement(); + + Statics &st = getStatics(); + OUString name,schema; + descriptor->getPropertyValue( st.SCHEMA_NAME ) >>= schema; + descriptor->getPropertyValue( st.NAME ) >>= name; + + TransactionGuard transaction( stmt ); + + OUStringBuffer buf( 128 ); + buf.append( "CREATE TABLE" ); + bufferQuoteQualifiedIdentifier( buf, schema, name , m_pSettings); + buf.append( "(" ); + + // columns + Reference< XColumnsSupplier > supplier( descriptor, UNO_QUERY ); + appendColumnList( buf, supplier, m_pSettings ); + + appendKeyList( buf, Reference< XKeysSupplier >( descriptor, UNO_QUERY ), m_pSettings ); + + buf.append( ") " ); + // execute the creation ! + transaction.executeUpdate( buf.makeStringAndClear() ); + + // description... + OUString description = extractStringProperty( descriptor, st.DESCRIPTION ); + if( !description.isEmpty() ) + { + buf.truncate(); + buf.append( "COMMENT ON TABLE" ); + bufferQuoteQualifiedIdentifier( buf, schema, name, m_pSettings ); + buf.append( "IS " ); + bufferQuoteConstant( buf, description, m_pSettings); + + transaction.executeUpdate( buf.makeStringAndClear() ); + } + + // column descriptions + if( supplier.is() ) + { + Reference< XEnumerationAccess > columns( supplier->getColumns(),UNO_QUERY ); + if( columns.is() ) + { + Reference< XEnumeration > xEnum( columns->createEnumeration() ); + while( xEnum.is() && xEnum->hasMoreElements() ) + { + Reference< XPropertySet > column( xEnum->nextElement(), UNO_QUERY ); + description = extractStringProperty( column,st.DESCRIPTION ); + if( !description.isEmpty() ) + { + buf.truncate(); + buf.append( "COMMENT ON COLUMN " ); + bufferQuoteQualifiedIdentifier( + buf, schema, name, extractStringProperty( column, st.NAME ), m_pSettings ); + buf.append( "IS " ); + bufferQuoteConstant( buf, description, m_pSettings ); + transaction.executeUpdate( buf.makeStringAndClear() ); + } + } + } + } + + transaction.commit(); + + disposeNoThrow( stmt ); + // TODO: cheaper recalculate +// Container::append( concatQualified( schema, name ), descriptor ); // maintain the lists + refresh(); +} + +void Tables::dropByIndex( sal_Int32 index ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + if( index < 0 || index >= static_cast<sal_Int32>(m_values.size()) ) + { + throw css::lang::IndexOutOfBoundsException( + "TABLES: Index out of range (allowed 0 to " + OUString::number(m_values.size() -1) + + ", got " + OUString::number( index ) + ")", + *this ); + } + + Reference< XPropertySet > set; + m_values[index] >>= set; + Statics &st = getStatics(); + OUString name,schema; + set->getPropertyValue( st.SCHEMA_NAME ) >>= schema; + set->getPropertyValue( st.NAME ) >>= name; + if( extractStringProperty( set, st.TYPE ) == st.VIEW && m_pSettings->views.is() ) + { + m_pSettings->pViewsImpl->dropByName( concatQualified( schema, name ) ); + } + else + { + OUStringBuffer update( 128 ); + update.append( "DROP " ); + if( extractStringProperty( set, st.TYPE ) == st.VIEW ) + update.append( "VIEW " ); + else + update.append( "TABLE " ); + bufferQuoteQualifiedIdentifier( update, schema, name, m_pSettings ); + Reference< XStatement > stmt = m_origin->createStatement( ); + DisposeGuard dispGuard( stmt ); + stmt->executeUpdate( update.makeStringAndClear() ); + } + + Container::dropByIndex( index ); +} + + +css::uno::Reference< css::beans::XPropertySet > Tables::createDataDescriptor() +{ + return new TableDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +Reference< css::container::XNameAccess > Tables::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + Tables **ppTables) +{ + *ppTables = new Tables( refMutex, origin, pSettings ); + Reference< css::container::XNameAccess > ret = *ppTables; + (*ppTables)->refresh(); + + return ret; +} + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xtables.hxx b/connectivity/source/drivers/postgresql/pq_xtables.hxx new file mode 100644 index 000000000..33eab3a5b --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xtables.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XTABLES_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XTABLES_HXX + +#include "pq_xcontainer.hxx" + +namespace pq_sdbc_driver +{ + +class Tables : public Container +{ + +public: // instances Tables 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + Tables ** ppTables); + +protected: + Tables( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + + virtual ~Tables() override; + +public: // XAppend + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + +public: // XDrop +// virtual void SAL_CALL dropByName( const OUString& elementName ) +// throw (css::sdbc::SQLException, +// css::container::NoSuchElementException, +// css::uno::RuntimeException); + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + +public: // XRefreshable + virtual void SAL_CALL refresh( ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; + +protected: + using Container::disposing; +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xuser.cxx b/connectivity/source/drivers/postgresql/pq_xuser.cxx new file mode 100644 index 000000000..bc06c5410 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xuser.cxx @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> + +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <com/sun/star/sdbc/SQLException.hpp> + +#include "pq_xuser.hxx" +#include "pq_tools.hxx" +#include "pq_statics.hxx" + +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Any; +using com::sun::star::uno::Type; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::SQLException; + +namespace pq_sdbc_driver +{ + +User::User( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings ) + : ReflectionBase( + getStatics().refl.user.implName, + getStatics().refl.user.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.user.pProps ) +{} + +Reference< XPropertySet > User::createDataDescriptor( ) +{ + UserDescriptor * pUser = new UserDescriptor( m_xMutex, m_conn, m_pSettings ); + pUser->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pUser ); +} + + +Sequence<Type > User::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<css::sdbcx::XUser>::get(), + ReflectionBase::getTypes()); + + return collection.getTypes(); +} + +Sequence< sal_Int8> User::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any User::queryInterface( const Type & reqType ) +{ + Any ret = ReflectionBase::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< css::sdbcx::XUser * > ( this ) ); + return ret; +} + + +void User::changePassword( + const OUString&, const OUString& newPassword ) +{ + OUStringBuffer buf(128); + buf.append( "ALTER USER " ); + bufferQuoteIdentifier( buf, extractStringProperty( this, getStatics().NAME ), m_pSettings ); + buf.append( " PASSWORD " ); + bufferQuoteConstant( buf, newPassword, m_pSettings ); + Reference< XStatement > stmt = m_conn->createStatement(); + DisposeGuard guard( stmt ); + stmt->executeUpdate( buf.makeStringAndClear() ); +} + +sal_Int32 User::getPrivileges( const OUString& objName, sal_Int32 objType ) +{ + SAL_INFO("connectivity.postgresql", "User::getPrivileges[\"Name\"] got called for " << objName << "(type=" << objType << ")"); + // all privileges + return 0xffffffff; +} + +sal_Int32 User::getGrantablePrivileges( const OUString&, sal_Int32 ) +{ + // all privileges + return 0xffffffff; +} + +void User::grantPrivileges( const OUString&, sal_Int32, sal_Int32 ) +{ + throw css::sdbc::SQLException("pq_driver: privilege change not implemented yet", + *this, OUString(), 1, Any() ); +} + +void User::revokePrivileges( const OUString&, sal_Int32, sal_Int32 ) +{ + throw css::sdbc::SQLException("pq_driver: privilege change not implemented yet", + *this, OUString(), 1, Any() ); +} + + +UserDescriptor::UserDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings ) + : ReflectionBase( + getStatics().refl.userDescriptor.implName, + getStatics().refl.userDescriptor.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.userDescriptor.pProps ) +{} + +Reference< XPropertySet > UserDescriptor::createDataDescriptor( ) +{ + UserDescriptor * pUser = new UserDescriptor( m_xMutex, m_conn, m_pSettings ); + pUser->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pUser ); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xuser.hxx b/connectivity/source/drivers/postgresql/pq_xuser.hxx new file mode 100644 index 000000000..950cd998b --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xuser.hxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XUSER_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XUSER_HXX + +#include <cppuhelper/component.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XUser.hpp> + +#include "pq_xbase.hxx" + +namespace pq_sdbc_driver +{ + +class User : public ReflectionBase, + public css::sdbcx::XUser +{ + +public: + User( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + + // XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + + // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + + // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + + // XUser : XAuthorizable + virtual sal_Int32 SAL_CALL getPrivileges( const OUString& objName, sal_Int32 objType ) override; + virtual sal_Int32 SAL_CALL getGrantablePrivileges( const OUString& objName, sal_Int32 objType ) override; + virtual void SAL_CALL grantPrivileges( const OUString& objName, sal_Int32 objType, sal_Int32 objPrivileges ) override; + virtual void SAL_CALL revokePrivileges( const OUString& objName, sal_Int32 objType, sal_Int32 objPrivileges ) override; + virtual void SAL_CALL changePassword( const OUString& oldPassword, const OUString& newPassword ) override; +}; + +class UserDescriptor : public ReflectionBase +{ +public: + UserDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; +}; + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xusers.cxx b/connectivity/source/drivers/postgresql/pq_xusers.cxx new file mode 100644 index 000000000..a6fe3489f --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xusers.cxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include "pq_xusers.hxx" +#include "pq_xuser.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::uno::makeAny; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Reference; + +using com::sun::star::container::NoSuchElementException; + +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XResultSet; + +namespace pq_sdbc_driver +{ +Users::Users( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ) + : Container( refMutex, origin, pSettings, getStatics().USER ) +{} + +Users::~Users() +{} + +void Users::refresh() +{ + try + { + osl::MutexGuard guard( m_xMutex->GetMutex() ); + Statics & st = getStatics(); + + Reference< XStatement > stmt = m_origin->createStatement(); + + Reference< XResultSet > rs = stmt->executeQuery( "SELECT usename FROM pg_shadow" ); + + Reference< XRow > xRow( rs , UNO_QUERY ); + + String2IntMap map; + + m_values.clear(); + sal_Int32 tableIndex = 0; + while( rs->next() ) + { + User * pUser = + new User( m_xMutex, m_origin, m_pSettings ); + Reference< css::beans::XPropertySet > prop = pUser; + + OUString name = xRow->getString( 1); + pUser->setPropertyValue_NoBroadcast_public( + st.NAME , makeAny(xRow->getString( TABLE_INDEX_CATALOG+1) ) ); + + { + m_values.push_back( makeAny( prop ) ); + map[ name ] = tableIndex; + ++tableIndex; + } + } + m_name2index.swap( map ); + } + catch ( css::sdbc::SQLException & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } + + fire( RefreshedBroadcaster( *this ) ); +} + + +void Users::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + + OUStringBuffer update( 128 ); + update.append( "CREATE USER " ); + bufferQuoteIdentifier( update, extractStringProperty( descriptor, getStatics().NAME ), m_pSettings ); + update.append( " PASSWORD " ); + bufferQuoteConstant( update, extractStringProperty( descriptor, getStatics().PASSWORD ), m_pSettings ); + + Reference< XStatement > stmt = m_origin->createStatement( ); + DisposeGuard disposeGuard( stmt ); + stmt->executeUpdate( update.makeStringAndClear() ); +} + +void Users::dropByName( const OUString& elementName ) +{ + String2IntMap::const_iterator ii = m_name2index.find( elementName ); + if( ii == m_name2index.end() ) + { + throw css::container::NoSuchElementException( + "User " + elementName + " is unknown, so it can't be dropped", + *this ); + } + dropByIndex( ii->second ); +} + +void Users::dropByIndex( sal_Int32 index ) +{ + + osl::MutexGuard guard( m_xMutex->GetMutex() ); + if( index < 0 || index >= static_cast<sal_Int32>(m_values.size()) ) + { + throw css::lang::IndexOutOfBoundsException( + "USERS: Index out of range (allowed 0 to " + + OUString::number( m_values.size() -1 ) + + ", got " + OUString::number( index ) + + ")", + *this ); + } + + Reference< XPropertySet > set; + m_values[index] >>= set; + OUString name; + set->getPropertyValue( getStatics().NAME ) >>= name; + + OUStringBuffer update( 128 ); + update.append( "DROP USER " ); + bufferQuoteIdentifier( update, name, m_pSettings ); + + Reference< XStatement > stmt = m_origin->createStatement( ); + DisposeGuard disposeGuard( stmt ); + stmt->executeUpdate( update.makeStringAndClear() ); +} + + +css::uno::Reference< css::beans::XPropertySet > Users::createDataDescriptor() +{ + return new UserDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +Reference< css::container::XNameAccess > Users::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ) +{ + Users *pUsers = new Users( refMutex, origin, pSettings ); + Reference< css::container::XNameAccess > ret = pUsers; + pUsers->refresh(); + + return ret; +} + +void Users::disposing() +{ +} + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xusers.hxx b/connectivity/source/drivers/postgresql/pq_xusers.hxx new file mode 100644 index 000000000..359b97dd1 --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xusers.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XUSERS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XUSERS_HXX + +#include "pq_xcontainer.hxx" + +namespace pq_sdbc_driver +{ + +class Users final : public Container +{ + +public: // instances Tables 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + +private: + Users( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ); + + virtual ~Users() override; + +public: + // XAppend + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + + // XDrop + virtual void SAL_CALL dropByName( const OUString& elementName ) override; + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + + // XRefreshable + virtual void SAL_CALL refresh( ) override; + + // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; + +private: + virtual void SAL_CALL disposing() override; +}; + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xview.cxx b/connectivity/source/drivers/postgresql/pq_xview.cxx new file mode 100644 index 000000000..1b00b9b5d --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xview.cxx @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <rtl/ustrbuf.hxx> + +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <com/sun/star/sdbc/SQLException.hpp> + +#include "pq_xview.hxx" +#include "pq_xviews.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + +using com::sun::star::uno::Reference; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::Any; +using com::sun::star::uno::makeAny; +using com::sun::star::uno::Type; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::SQLException; + +namespace pq_sdbc_driver +{ + +View::View( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings) + : ReflectionBase( + getStatics().refl.view.implName, + getStatics().refl.view.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.view.pProps ) +{} + +Reference< XPropertySet > View::createDataDescriptor( ) +{ + ViewDescriptor * pView = new ViewDescriptor( + m_xMutex, m_conn, m_pSettings ); + pView->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pView ); +} + +void View::rename( const OUString& newName ) +{ + MutexGuard guard( m_xMutex->GetMutex() ); + + Statics & st = getStatics(); + + OUString oldName = extractStringProperty(this,st.NAME ); + OUString schema = extractStringProperty(this,st.SCHEMA_NAME ); + OUString fullOldName = concatQualified( schema, oldName ); + + OUString newTableName; + OUString newSchemaName; + // OOo2.0 passes schema + dot + new-table-name while + // OO1.1.x passes new Name without schema + // in case name contains a dot, it is interpreted as schema.tablename + if( newName.indexOf( '.' ) >= 0 ) + { + splitConcatenatedIdentifier( newName, &newSchemaName, &newTableName ); + } + else + { + newTableName = newName; + newSchemaName = schema; + } + OUString fullNewName = concatQualified( newSchemaName, newTableName ); + + if( schema != newSchemaName ) + { + try + { + OUStringBuffer buf(128); + buf.append( "ALTER TABLE" ); + bufferQuoteQualifiedIdentifier(buf, schema, oldName, m_pSettings ); + buf.append( "SET SCHEMA" ); + bufferQuoteIdentifier( buf, newSchemaName, m_pSettings ); + Reference< XStatement > statement = m_conn->createStatement(); + statement->executeUpdate( buf.makeStringAndClear() ); + setPropertyValue_NoBroadcast_public( st.SCHEMA_NAME, makeAny(newSchemaName) ); + disposeNoThrow( statement ); + schema = newSchemaName; + } + catch( css::sdbc::SQLException &e ) + { + OUString buf( e.Message + "(NOTE: Only postgresql server >= V8.1 support changing a table's schema)" ); + e.Message = buf; + throw; + } + + } + if( oldName != newTableName ) + { + OUStringBuffer buf(128); + buf.append( "ALTER TABLE" ); + bufferQuoteQualifiedIdentifier( buf, schema, oldName, m_pSettings ); + buf.append( "RENAME TO" ); + bufferQuoteIdentifier( buf, newTableName, m_pSettings ); + Reference< XStatement > statement = m_conn->createStatement(); + statement->executeUpdate( buf.makeStringAndClear() ); + setPropertyValue_NoBroadcast_public( st.NAME, makeAny(newTableName) ); + } + + // inform the container of the name change ! + if( m_pSettings->views.is() ) + { + m_pSettings->pViewsImpl->rename( fullOldName, fullNewName ); + } +} + +Sequence<Type > View::getTypes() +{ + static cppu::OTypeCollection collection( + cppu::UnoType<css::sdbcx::XRename>::get(), + ReflectionBase::getTypes()); + + return collection.getTypes(); +} + +Sequence< sal_Int8> View::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any View::queryInterface( const Type & reqType ) +{ + Any ret = ReflectionBase::queryInterface( reqType ); + if( ! ret.hasValue() ) + ret = ::cppu::queryInterface( + reqType, + static_cast< css::sdbcx::XRename * > ( this ) + ); + return ret; +} + +OUString View::getName( ) +{ + Statics & st = getStatics(); + return concatQualified( + extractStringProperty( this, st.SCHEMA_NAME ), + extractStringProperty( this, st.NAME ) ); +} + +void View::setName( const OUString& aName ) +{ + rename( aName ); +} + + +ViewDescriptor::ViewDescriptor( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings) + : ReflectionBase( + getStatics().refl.viewDescriptor.implName, + getStatics().refl.viewDescriptor.serviceNames, + refMutex, + connection, + pSettings, + * getStatics().refl.viewDescriptor.pProps ) +{} + +Reference< XPropertySet > ViewDescriptor::createDataDescriptor( ) +{ + ViewDescriptor * pView = new ViewDescriptor( + m_xMutex, m_conn, m_pSettings ); + pView->copyValuesFrom( this ); + + return Reference< XPropertySet > ( pView ); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xview.hxx b/connectivity/source/drivers/postgresql/pq_xview.hxx new file mode 100644 index 000000000..d5c86981f --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xview.hxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XVIEW_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XVIEW_HXX + +#include <cppuhelper/component.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <com/sun/star/container/XNamed.hpp> + +#include "pq_xbase.hxx" + +namespace pq_sdbc_driver +{ +class View : public ReflectionBase, + public css::sdbcx::XRename +{ +public: + View( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + + // XInterface + virtual void SAL_CALL acquire() throw() override { ReflectionBase::acquire(); } + virtual void SAL_CALL release() throw() override { ReflectionBase::release(); } + virtual css::uno::Any SAL_CALL queryInterface( + const css::uno::Type & reqType ) override; + + // XTypeProvider, first implemented by OPropertySetHelper + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8> SAL_CALL getImplementationId() override; + + // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + +}; + + +class ViewDescriptor : public ReflectionBase +{ +public: + ViewDescriptor( const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & connection, + ConnectionSettings *pSettings); + + // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL + createDataDescriptor( ) override; +}; + +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xviews.cxx b/connectivity/source/drivers/postgresql/pq_xviews.cxx new file mode 100644 index 000000000..ac684a16c --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xviews.cxx @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#include "pq_xviews.hxx" +#include "pq_xview.hxx" +#include "pq_xtables.hxx" +#include "pq_statics.hxx" +#include "pq_tools.hxx" + +using osl::MutexGuard; + +using com::sun::star::beans::XPropertySet; + +using com::sun::star::uno::makeAny; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Reference; + +using com::sun::star::container::NoSuchElementException; + +using com::sun::star::sdbc::XRow; +using com::sun::star::sdbc::XStatement; +using com::sun::star::sdbc::XResultSet; + + +namespace pq_sdbc_driver +{ +Views::Views( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings ) + : Container( refMutex, origin, pSettings, getStatics().VIEW ) +{} + +Views::~Views() +{} + +void Views::refresh() +{ + try + { + osl::MutexGuard guard( m_xMutex->GetMutex() ); + Statics & st = getStatics(); + + Reference< XStatement > stmt = m_origin->createStatement(); + + Reference< XResultSet > rs = stmt->executeQuery("SELECT " + "DISTINCT ON( pg_namespace.nspname, relname) " // needed because of duplicates + "pg_namespace.nspname," // 1 + "relname," // 2 + "pg_get_viewdef(ev_class) " // 3 + "FROM pg_namespace, pg_class, pg_rewrite " + "WHERE pg_namespace.oid = relnamespace " + "AND pg_class.oid = ev_class " + "AND relkind=\'v\'" ); + + Reference< XRow > xRow( rs , UNO_QUERY ); + + m_values.clear(); + String2IntMap map; + sal_Int32 viewIndex = 0; + + while( rs->next() ) + { + OUString table, schema, command; + schema = xRow->getString( 1 ); + table = xRow->getString( 2 ); + command = xRow->getString( 3 ); + + View *pView = new View (m_xMutex, m_origin, m_pSettings ); + Reference< css::beans::XPropertySet > prop = pView; + + pView->setPropertyValue_NoBroadcast_public(st.NAME , makeAny(table) ); + pView->setPropertyValue_NoBroadcast_public(st.SCHEMA_NAME, makeAny(schema) ); + pView->setPropertyValue_NoBroadcast_public(st.COMMAND, makeAny(command) ); + + { + m_values.push_back( makeAny( prop ) ); + map[ schema + "." + table ] = viewIndex; + ++viewIndex; + } + } + m_name2index.swap( map ); + } + catch ( css::sdbc::SQLException & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } + fire( RefreshedBroadcaster( *this ) ); +} + + +void Views::appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + + Statics &st = getStatics(); + OUString name,schema,command; + descriptor->getPropertyValue( st.SCHEMA_NAME ) >>= schema; + descriptor->getPropertyValue( st.NAME ) >>= name; + descriptor->getPropertyValue( st.COMMAND ) >>= command; + + Reference< XStatement > stmt = m_origin->createStatement(); + + OUStringBuffer buf( 128 ); + + buf.append( "CREATE VIEW "); + bufferQuoteQualifiedIdentifier( buf, schema, name, m_pSettings ); + buf.append(" AS " ).append( command ); + + stmt->executeUpdate( buf.makeStringAndClear() ); + + disposeNoThrow( stmt ); + refresh(); + if( m_pSettings->tables.is() ) + { + m_pSettings->pTablesImpl->refresh(); + } +} + +void Views::dropByName( const OUString& elementName ) +{ + String2IntMap::const_iterator ii = m_name2index.find( elementName ); + if( ii == m_name2index.end() ) + { + throw css::container::NoSuchElementException( + "View " + elementName + " is unknown, so it can't be dropped", *this ); + } + dropByIndex( ii->second ); +} + +void Views::dropByIndex( sal_Int32 index ) +{ + osl::MutexGuard guard( m_xMutex->GetMutex() ); + if( index < 0 || index >= static_cast<sal_Int32>(m_values.size()) ) + { + throw css::lang::IndexOutOfBoundsException( + "VIEWS: Index out of range (allowed 0 to " + OUString::number(m_values.size() -1) + + ", got " + OUString::number( index ) + ")", + *this ); + } + + Reference< XPropertySet > set; + m_values[index] >>= set; + Statics &st = getStatics(); + OUString name,schema; + set->getPropertyValue( st.SCHEMA_NAME ) >>= schema; + set->getPropertyValue( st.NAME ) >>= name; + + Reference< XStatement > stmt = m_origin->createStatement( ); + + stmt->executeUpdate( "DROP VIEW \"" + schema + "\".\"" + name + "\"" ); +} + + +css::uno::Reference< css::beans::XPropertySet > Views::createDataDescriptor() +{ + return new ViewDescriptor( m_xMutex, m_origin, m_pSettings ); +} + +Reference< css::container::XNameAccess > Views::create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + Views **ppViews) +{ + *ppViews = new Views( refMutex, origin, pSettings ); + Reference< css::container::XNameAccess > ret = *ppViews; + (*ppViews)->refresh(); + + return ret; +} + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/postgresql/pq_xviews.hxx b/connectivity/source/drivers/postgresql/pq_xviews.hxx new file mode 100644 index 000000000..5ce5b879b --- /dev/null +++ b/connectivity/source/drivers/postgresql/pq_xviews.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * Effective License of whole file: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Parts "Copyright by Sun Microsystems, Inc" prior to August 2011: + * + * The Contents of this file are made available subject to the terms of + * the GNU Lesser General Public License Version 2.1 + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * Contributor(s): Joerg Budischewski + * + * All parts contributed on or after August 2011: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + ************************************************************************/ + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XVIEWS_HXX +#define INCLUDED_CONNECTIVITY_SOURCE_DRIVERS_POSTGRESQL_PQ_XVIEWS_HXX + +#include "pq_xcontainer.hxx" + +namespace pq_sdbc_driver +{ + +class Views : public Container +{ + +public: // instances Views 'exception safe' + static css::uno::Reference< css::container::XNameAccess > create( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings, + Views **ppViews ); + +protected: + Views( + const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex, + const css::uno::Reference< css::sdbc::XConnection > & origin, + ConnectionSettings *pSettings); + + virtual ~Views() override; + +public: // XAppend + virtual void SAL_CALL appendByDescriptor( + const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + +public: // XDrop + virtual void SAL_CALL dropByName( const OUString& elementName ) override; + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + +public: // XRefreshable + virtual void SAL_CALL refresh( ) override; + +public: // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; + +protected: + using Container::disposing; + +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/writer/WCatalog.cxx b/connectivity/source/drivers/writer/WCatalog.cxx new file mode 100644 index 000000000..8671af7bd --- /dev/null +++ b/connectivity/source/drivers/writer/WCatalog.cxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <writer/WCatalog.hxx> +#include <writer/WTables.hxx> + +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> + +#include <connectivity/sdbcx/VCollection.hxx> + +#include <writer/WConnection.hxx> + +using namespace ::com::sun::star; + +namespace connectivity::writer +{ +OWriterCatalog::OWriterCatalog(OWriterConnection* pConnection) + : file::OFileCatalog(pConnection) +{ +} + +void OWriterCatalog::refreshTables() +{ + ::std::vector<OUString> aVector; + uno::Sequence<OUString> aTypes; + OWriterConnection::ODocHolder aDocHolder(static_cast<OWriterConnection*>(m_pConnection)); + uno::Reference<sdbc::XResultSet> xResult = m_xMetaData->getTables(uno::Any(), "%", "%", aTypes); + + if (xResult.is()) + { + uno::Reference<sdbc::XRow> xRow(xResult, uno::UNO_QUERY); + while (xResult->next()) + aVector.push_back(xRow->getString(3)); + } + if (m_pTables) + m_pTables->reFill(aVector); + else + m_pTables = std::make_unique<OWriterTables>(m_xMetaData, *this, m_aMutex, aVector); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/writer/WConnection.cxx b/connectivity/source/drivers/writer/WConnection.cxx new file mode 100644 index 000000000..c4cc699e4 --- /dev/null +++ b/connectivity/source/drivers/writer/WConnection.cxx @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <writer/WConnection.hxx> +#include <writer/WDatabaseMetaData.hxx> +#include <writer/WCatalog.hxx> +#include <writer/WDriver.hxx> +#include <resource/sharedresources.hxx> +#include <strings.hrc> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <tools/urlobj.hxx> +#include <sal/log.hxx> +#include <component/CPreparedStatement.hxx> +#include <component/CStatement.hxx> +#include <unotools/pathoptions.hxx> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star; + +using OConnection_BASE = connectivity::file::OConnection; + +namespace connectivity::writer +{ +OWriterConnection::OWriterConnection(ODriver* _pDriver) + : OConnection(_pDriver) + , m_nDocCount(0) +{ +} + +OWriterConnection::~OWriterConnection() = default; + +void OWriterConnection::construct(const OUString& rURL, + const uno::Sequence<beans::PropertyValue>& rInfo) +{ + // open file + + sal_Int32 nLen = rURL.indexOf(':'); + nLen = rURL.indexOf(':', nLen + 1); + OUString aDSN(rURL.copy(nLen + 1)); + + m_aFileName = aDSN; + INetURLObject aURL; + aURL.SetSmartProtocol(INetProtocol::File); + { + SvtPathOptions aPathOptions; + m_aFileName = aPathOptions.SubstituteVariable(m_aFileName); + } + aURL.SetSmartURL(m_aFileName); + if (aURL.GetProtocol() == INetProtocol::NotValid) + { + // don't pass invalid URL to loadComponentFromURL + throw sdbc::SQLException(); + } + m_aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + + m_sPassword.clear(); + const char pPwd[] = "password"; + + const beans::PropertyValue* pIter = rInfo.getConstArray(); + const beans::PropertyValue* pEnd = pIter + rInfo.getLength(); + for (; pIter != pEnd; ++pIter) + { + if (pIter->Name == pPwd) + { + pIter->Value >>= m_sPassword; + break; + } + } // for(;pIter != pEnd;++pIter) + ODocHolder aDocHolder(this); // just to test that the doc can be loaded + acquireDoc(); +} + +uno::Reference<text::XTextDocument> const& OWriterConnection::acquireDoc() +{ + if (m_xDoc.is()) + { + osl_atomic_increment(&m_nDocCount); + return m_xDoc; + } + // open read-only as long as updating isn't implemented + uno::Sequence<beans::PropertyValue> aArgs(2); + aArgs[0].Name = "Hidden"; + aArgs[0].Value <<= true; + aArgs[1].Name = "ReadOnly"; + aArgs[1].Value <<= true; + + if (!m_sPassword.isEmpty()) + { + const sal_Int32 nPos = aArgs.getLength(); + aArgs.realloc(nPos + 1); + aArgs[nPos].Name = "Password"; + aArgs[nPos].Value <<= m_sPassword; + } + + uno::Reference<frame::XDesktop2> xDesktop + = frame::Desktop::create(getDriver()->getComponentContext()); + uno::Reference<lang::XComponent> xComponent; + uno::Any aLoaderException; + try + { + xComponent = xDesktop->loadComponentFromURL(m_aFileName, "_blank", 0, aArgs); + } + catch (const uno::Exception&) + { + aLoaderException = ::cppu::getCaughtException(); + } + + m_xDoc.set(xComponent, uno::UNO_QUERY); + + // if the URL is not a text document, throw the exception here + // instead of at the first access to it + if (!m_xDoc.is()) + { + if (aLoaderException.hasValue()) + { + uno::Exception aLoaderError; + OSL_VERIFY(aLoaderException >>= aLoaderError); + + SAL_WARN("connectivity.writer", + "empty m_xDoc, " << exceptionToString(aLoaderException)); + } + + const OUString sError(m_aResources.getResourceStringWithSubstitution( + STR_COULD_NOT_LOAD_FILE, "$filename$", m_aFileName)); + ::dbtools::throwGenericSQLException(sError, *this); + } + osl_atomic_increment(&m_nDocCount); + m_xCloseVetoButTerminateListener.set(new CloseVetoButTerminateListener); + m_xCloseVetoButTerminateListener->start(m_xDoc, xDesktop); + return m_xDoc; +} + +void OWriterConnection::releaseDoc() +{ + if (osl_atomic_decrement(&m_nDocCount) == 0) + { + if (m_xCloseVetoButTerminateListener.is()) + { + m_xCloseVetoButTerminateListener->stop(); // dispose m_xDoc + m_xCloseVetoButTerminateListener.clear(); + } + m_xDoc.clear(); + } +} + +void OWriterConnection::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + m_nDocCount = 0; + if (m_xCloseVetoButTerminateListener.is()) + { + m_xCloseVetoButTerminateListener->stop(); // dispose m_xDoc + m_xCloseVetoButTerminateListener.clear(); + } + m_xDoc.clear(); + + OConnection::disposing(); +} + +// XServiceInfo + +IMPLEMENT_SERVICE_INFO(OWriterConnection, "com.sun.star.sdbc.drivers.writer.Connection", + "com.sun.star.sdbc.Connection") + +uno::Reference<sdbc::XDatabaseMetaData> SAL_CALL OWriterConnection::getMetaData() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + uno::Reference<sdbc::XDatabaseMetaData> xMetaData = m_xMetaData; + if (!xMetaData.is()) + { + xMetaData = new OWriterDatabaseMetaData(this); + m_xMetaData = xMetaData; + } + + return xMetaData; +} + +css::uno::Reference<css::sdbcx::XTablesSupplier> OWriterConnection::createCatalog() +{ + ::osl::MutexGuard aGuard(m_aMutex); + uno::Reference<css::sdbcx::XTablesSupplier> xTab = m_xCatalog; + if (!xTab.is()) + { + auto pCat = new OWriterCatalog(this); + xTab = pCat; + m_xCatalog = xTab; + } + return xTab; +} + +uno::Reference<sdbc::XStatement> SAL_CALL OWriterConnection::createStatement() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + uno::Reference<sdbc::XStatement> xReturn = new component::OComponentStatement(this); + m_aStatements.push_back(uno::WeakReferenceHelper(xReturn)); + return xReturn; +} + +uno::Reference<sdbc::XPreparedStatement> + SAL_CALL OWriterConnection::prepareStatement(const OUString& sql) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + auto pStmt = new component::OComponentPreparedStatement(this); + uno::Reference<sdbc::XPreparedStatement> xHoldAlive = pStmt; + pStmt->construct(sql); + m_aStatements.push_back(uno::WeakReferenceHelper(*pStmt)); + return pStmt; +} + +uno::Reference<sdbc::XPreparedStatement> + SAL_CALL OWriterConnection::prepareCall(const OUString& /*sql*/) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(OConnection_BASE::rBHelper.bDisposed); + + ::dbtools::throwFeatureNotImplementedSQLException("XConnection::prepareCall", *this); + return nullptr; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/writer/WDatabaseMetaData.cxx b/connectivity/source/drivers/writer/WDatabaseMetaData.cxx new file mode 100644 index 000000000..2e55a46d2 --- /dev/null +++ b/connectivity/source/drivers/writer/WDatabaseMetaData.cxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <writer/WDatabaseMetaData.hxx> +#include <writer/WConnection.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextTablesSupplier.hpp> + +using namespace ::com::sun::star; + +namespace connectivity::writer +{ +OWriterDatabaseMetaData::OWriterDatabaseMetaData(file::OConnection* pConnection) + : OComponentDatabaseMetaData(pConnection) +{ +} + +OWriterDatabaseMetaData::~OWriterDatabaseMetaData() = default; + +OUString SAL_CALL OWriterDatabaseMetaData::getURL() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + return "sdbc:writer:" + m_pConnection->getURL(); +} + +uno::Reference<sdbc::XResultSet> SAL_CALL OWriterDatabaseMetaData::getTables( + const uno::Any& /*catalog*/, const OUString& /*schemaPattern*/, + const OUString& tableNamePattern, const uno::Sequence<OUString>& types) +{ + ::osl::MutexGuard aGuard(m_aMutex); + + auto pResult = new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTables); + uno::Reference<sdbc::XResultSet> xRef = pResult; + + // check if ORowSetValue type is given + // when no types are given then we have to return all tables e.g. TABLE + + OUString aTable("TABLE"); + + bool bTableFound = true; + sal_Int32 nLength = types.getLength(); + if (nLength) + { + bTableFound = false; + + const OUString* pIter = types.getConstArray(); + const OUString* pEnd = pIter + nLength; + for (; pIter != pEnd; ++pIter) + { + if (*pIter == aTable) + { + bTableFound = true; + break; + } + } + } + if (!bTableFound) + return xRef; + + // get the table names from the document + + OWriterConnection::ODocHolder aDocHolder(static_cast<OWriterConnection*>(m_pConnection)); + uno::Reference<text::XTextTablesSupplier> xDoc(aDocHolder.getDoc(), uno::UNO_QUERY); + if (!xDoc.is()) + throw sdbc::SQLException(); + uno::Reference<container::XNameAccess> xTables = xDoc->getTextTables(); + if (!xTables.is()) + throw sdbc::SQLException(); + uno::Sequence<OUString> aTableNames = xTables->getElementNames(); + + ODatabaseMetaDataResultSet::ORows aRows; + sal_Int32 nTableCount = aTableNames.getLength(); + for (sal_Int32 nTable = 0; nTable < nTableCount; nTable++) + { + OUString aName = aTableNames[nTable]; + if (match(tableNamePattern, aName, '\0')) + { + ODatabaseMetaDataResultSet::ORow aRow{ nullptr, nullptr, nullptr }; + aRow.reserve(6); + aRow.push_back(new ORowSetValueDecorator(aName)); + aRow.push_back(new ORowSetValueDecorator(aTable)); + aRow.push_back(ODatabaseMetaDataResultSet::getEmptyValue()); + aRows.push_back(aRow); + } + } + + pResult->setRows(aRows); + + return xRef; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/writer/WDriver.cxx b/connectivity/source/drivers/writer/WDriver.cxx new file mode 100644 index 000000000..63dc1f3f4 --- /dev/null +++ b/connectivity/source/drivers/writer/WDriver.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 <writer/WDriver.hxx> +#include <writer/WConnection.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <connectivity/dbexception.hxx> +#include <resource/sharedresources.hxx> +#include <strings.hrc> +#include <comphelper/processfactory.hxx> + +using namespace connectivity::file; +using namespace ::com::sun::star; + +namespace connectivity::writer +{ +OUString ODriver::getImplementationName_Static() { return "com.sun.star.comp.sdbc.writer.ODriver"; } + +OUString SAL_CALL ODriver::getImplementationName() { return getImplementationName_Static(); } + +uno::Reference<css::uno::XInterface> +ODriver_CreateInstance(const uno::Reference<lang::XMultiServiceFactory>& _rxFactory) +{ + return *(new ODriver(comphelper::getComponentContext(_rxFactory))); +} + +uno::Reference<sdbc::XConnection> + SAL_CALL ODriver::connect(const OUString& url, const uno::Sequence<beans::PropertyValue>& info) +{ + ::osl::MutexGuard aGuard(m_aMutex); + if (ODriver_BASE::rBHelper.bDisposed) + throw lang::DisposedException(); + + if (!acceptsURL(url)) + return nullptr; + + auto pCon = new OWriterConnection(this); + pCon->construct(url, info); + uno::Reference<sdbc::XConnection> xCon = pCon; + m_xConnections.push_back(uno::WeakReferenceHelper(*pCon)); + + return xCon; +} + +sal_Bool SAL_CALL ODriver::acceptsURL(const OUString& url) +{ + return url.startsWith("sdbc:writer:"); +} + +uno::Sequence<sdbc::DriverPropertyInfo> SAL_CALL +ODriver::getPropertyInfo(const OUString& url, const uno::Sequence<beans::PropertyValue>& /*info*/) +{ + if (!acceptsURL(url)) + { + SharedResources aResources; + const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR); + ::dbtools::throwGenericSQLException(sMessage, *this); + } + return uno::Sequence<sdbc::DriverPropertyInfo>(); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/writer/WTable.cxx b/connectivity/source/drivers/writer/WTable.cxx new file mode 100644 index 000000000..7236df247 --- /dev/null +++ b/connectivity/source/drivers/writer/WTable.cxx @@ -0,0 +1,253 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <writer/WTable.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextTable.hpp> +#include <com/sun/star/text/XTextTablesSupplier.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <writer/WConnection.hxx> +#include <connectivity/sdbcx/VColumn.hxx> +#include <sal/log.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> + +namespace com::sun::star::text +{ +class XTextDocument; +} + +using namespace ::com::sun::star; + +static void lcl_GetDataArea(const uno::Reference<text::XTextTable>& xTable, sal_Int32& rColumnCount, + sal_Int32& rRowCount) +{ + uno::Reference<container::XIndexAccess> xColumns = xTable->getColumns(); + if (xColumns.is()) + rColumnCount = xColumns->getCount(); + + uno::Reference<container::XIndexAccess> xRows = xTable->getRows(); + if (xRows.is()) + rRowCount = xRows->getCount() - 1; // first row (headers) is not counted +} + +static void lcl_GetColumnInfo(const uno::Reference<text::XTextTable>& xTable, sal_Int32 nDocColumn, + bool bHasHeaders, OUString& rName, sal_Int32& rDataType, + bool& rCurrency) +{ + uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY); + // get column name from first row, if range contains headers + if (bHasHeaders) + { + uno::Reference<text::XText> xHeaderText( + xCellRange->getCellByPosition(nDocColumn, /*nStartRow*/ 0), uno::UNO_QUERY); + if (xHeaderText.is()) + rName = xHeaderText->getString(); + } + + rCurrency = false; + rDataType = sdbc::DataType::VARCHAR; +} + +static void lcl_SetValue(connectivity::ORowSetValue& rValue, + const uno::Reference<text::XTextTable>& xTable, sal_Int32 nStartCol, + bool bHasHeaders, sal_Int32 nDBRow, sal_Int32 nDBColumn) +{ + sal_Int32 nDocColumn = nStartCol + nDBColumn - 1; // database counts from 1 + sal_Int32 nDocRow = nDBRow - 1; + if (bHasHeaders) + ++nDocRow; + + uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY); + uno::Reference<table::XCell> xCell; + try + { + xCell = xCellRange->getCellByPosition(nDocColumn, nDocRow); + } + catch (const lang::IndexOutOfBoundsException& /*rException*/) + { + SAL_WARN("connectivity.writer", + "getCellByPosition(" << nDocColumn << ", " << nDocRow << ") failed"); + rValue = OUString(); + } + + if (xCell.is()) + { + const uno::Reference<text::XText> xText(xCell, uno::UNO_QUERY); + if (xText.is()) + rValue = xText->getString(); + } +} + +namespace connectivity::writer +{ +void OWriterTable::fillColumns() +{ + if (!m_xTable.is()) + throw sdbc::SQLException(); + + OUString aTypeName; + ::comphelper::UStringMixEqual aCase( + m_pConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()); + const bool bStoresMixedCaseQuotedIdentifiers + = getConnection()->getMetaData()->supportsMixedCaseQuotedIdentifiers(); + + for (sal_Int32 i = 0; i < m_nDataCols; i++) + { + OUString aColumnName; + sal_Int32 eType = sdbc::DataType::OTHER; + bool bCurrency = false; + + lcl_GetColumnInfo(m_xTable, m_nStartCol + i, m_bHasHeaders, aColumnName, eType, bCurrency); + + sal_Int32 nPrecision = 0; //! ... + sal_Int32 nDecimals = 0; //! ... + + switch (eType) + { + case sdbc::DataType::VARCHAR: + aTypeName = "VARCHAR"; + break; + case sdbc::DataType::DECIMAL: + aTypeName = "DECIMAL"; + break; + case sdbc::DataType::BIT: + aTypeName = "BOOL"; + break; + case sdbc::DataType::DATE: + aTypeName = "DATE"; + break; + case sdbc::DataType::TIME: + aTypeName = "TIME"; + break; + case sdbc::DataType::TIMESTAMP: + aTypeName = "TIMESTAMP"; + break; + default: + SAL_WARN("connectivity.writer", "missing type name"); + aTypeName.clear(); + } + + // check if the column name already exists + OUString aAlias = aColumnName; + auto aFind = connectivity::find(m_aColumns->begin(), m_aColumns->end(), aAlias, aCase); + sal_Int32 nExprCnt = 0; + while (aFind != m_aColumns->end()) + { + aAlias = aColumnName + OUString::number(++nExprCnt); + aFind = connectivity::find(m_aColumns->begin(), m_aColumns->end(), aAlias, aCase); + } + + auto pColumn = new sdbcx::OColumn( + aAlias, aTypeName, OUString(), OUString(), sdbc::ColumnValue::NULLABLE, nPrecision, + nDecimals, eType, false, false, bCurrency, bStoresMixedCaseQuotedIdentifiers, + m_CatalogName, getSchema(), getName()); + uno::Reference<XPropertySet> xCol = pColumn; + m_aColumns->push_back(xCol); + } +} + +OWriterTable::OWriterTable(sdbcx::OCollection* _pTables, OWriterConnection* _pConnection, + const OUString& Name, const OUString& Type) + : OWriterTable_BASE(_pTables, _pConnection, Name, Type, OUString() /*Description*/, + OUString() /*SchemaName*/, OUString() /*CatalogName*/) + , m_pWriterConnection(_pConnection) + , m_nStartCol(0) + , m_nDataCols(0) + , m_bHasHeaders(false) +{ +} + +void OWriterTable::construct() +{ + uno::Reference<text::XTextDocument> xDoc = m_pWriterConnection->acquireDoc(); + if (xDoc.is()) + { + uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(xDoc, uno::UNO_QUERY); + uno::Reference<container::XNameAccess> xTables = xTextTablesSupplier->getTextTables(); + if (xTables.is() && xTables->hasByName(m_Name)) + { + m_xTable.set(xTables->getByName(m_Name), uno::UNO_QUERY); + if (m_xTable.is()) + { + lcl_GetDataArea(m_xTable, m_nDataCols, m_nDataRows); + m_bHasHeaders = true; + } + } + } + + fillColumns(); + + refreshColumns(); +} + +void SAL_CALL OWriterTable::disposing() +{ + OFileTable::disposing(); + ::osl::MutexGuard aGuard(m_aMutex); + m_aColumns = nullptr; + if (m_pWriterConnection) + m_pWriterConnection->releaseDoc(); + m_pWriterConnection = nullptr; +} + +uno::Sequence<sal_Int8> OWriterTable::getUnoTunnelId() +{ + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); +} + +sal_Int64 OWriterTable::getSomething(const uno::Sequence<sal_Int8>& rId) +{ + return (isUnoTunnelId<OWriterTable>(rId)) ? reinterpret_cast<sal_Int64>(this) + : OWriterTable_BASE::getSomething(rId); +} + +bool OWriterTable::fetchRow(OValueRefRow& _rRow, const OSQLColumns& _rCols, bool bRetrieveData) +{ + // read the bookmark + + _rRow->setDeleted(false); + *(*_rRow)[0] = m_nFilePos; + + if (!bRetrieveData) + return true; + + // fields + + const OValueRefVector::size_type nCount = std::min(_rRow->size(), _rCols.size() + 1); + for (OValueRefVector::size_type i = 1; i < nCount; i++) + { + if ((*_rRow)[i]->isBound()) + { + lcl_SetValue((*_rRow)[i]->get(), m_xTable, m_nStartCol, m_bHasHeaders, m_nFilePos, i); + } + } + return true; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/writer/WTables.cxx b/connectivity/source/drivers/writer/WTables.cxx new file mode 100644 index 000000000..15dc9e0c1 --- /dev/null +++ b/connectivity/source/drivers/writer/WTables.cxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <writer/WTables.hxx> + +#include <sal/config.h> + +#include <writer/WConnection.hxx> +#include <file/FCatalog.hxx> +#include <writer/WTable.hxx> + +using namespace ::com::sun::star; + +namespace connectivity::writer +{ +sdbcx::ObjectType OWriterTables::createObject(const OUString& rName) +{ + auto pTable = new OWriterTable(this, + static_cast<OWriterConnection*>( + static_cast<file::OFileCatalog&>(m_rParent).getConnection()), + rName, "TABLE"); + sdbcx::ObjectType xRet = pTable; + pTable->construct(); + return xRet; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/writer/Wservices.cxx b/connectivity/source/drivers/writer/Wservices.cxx new file mode 100644 index 000000000..7f997d7d1 --- /dev/null +++ b/connectivity/source/drivers/writer/Wservices.cxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <writer/WDriver.hxx> +#include <cppuhelper/factory.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +using namespace com::sun::star; + +using createFactoryFunc = uno::Reference<lang::XSingleServiceFactory> (*)( + const uno::Reference<lang::XMultiServiceFactory>& rServiceManager, + const OUString& rComponentName, ::cppu::ComponentInstantiation pCreateFunction, + const uno::Sequence<OUString>& rServiceNames, rtl_ModuleCount*); + +namespace +{ +struct ProviderRequest +{ +private: + uno::Reference<lang::XSingleServiceFactory> xRet; + uno::Reference<lang::XMultiServiceFactory> const xServiceManager; + OUString const sImplementationName; + +public: + ProviderRequest(void* pServiceManager, char const* pImplementationName) + : xServiceManager(static_cast<lang::XMultiServiceFactory*>(pServiceManager)) + , sImplementationName(OUString::createFromAscii(pImplementationName)) + { + } + + bool CREATE_PROVIDER(const OUString& Implname, const uno::Sequence<OUString>& Services, + ::cppu::ComponentInstantiation Factory, createFactoryFunc creator) + { + if (!xRet.is() && (Implname == sImplementationName)) + { + try + { + xRet = creator(xServiceManager, sImplementationName, Factory, Services, nullptr); + } + catch (...) + { + } + } + return xRet.is(); + } + + uno::XInterface* getProvider() const { return xRet.get(); } +}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* +connectivity_writer_component_getFactory(const char* pImplementationName, void* pServiceManager, + void* /*pRegistryKey*/) +{ + void* pRet = nullptr; + if (pServiceManager) + { + ProviderRequest aReq(pServiceManager, pImplementationName); + + aReq.CREATE_PROVIDER(connectivity::writer::ODriver::getImplementationName_Static(), + connectivity::writer::ODriver::getSupportedServiceNames_Static(), + connectivity::writer::ODriver_CreateInstance, + ::cppu::createSingleFactory); + + if (aReq.getProvider()) + aReq.getProvider()->acquire(); + + pRet = aReq.getProvider(); + } + + return pRet; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/writer/writer.component b/connectivity/source/drivers/writer/writer.component new file mode 100644 index 000000000..3bf9d6e79 --- /dev/null +++ b/connectivity/source/drivers/writer/writer.component @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="connectivity_writer" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.sdbc.writer.ODriver"> + <service name="com.sun.star.sdbc.Driver"/> + <service name="com.sun.star.sdbcx.Driver"/> + </implementation> +</component> |