From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- connectivity/source/drivers/odbc/OResultSet.cxx | 1847 +++++++++++++++++++++++ 1 file changed, 1847 insertions(+) create mode 100644 connectivity/source/drivers/odbc/OResultSet.cxx (limited to 'connectivity/source/drivers/odbc/OResultSet.cxx') diff --git a/connectivity/source/drivers/odbc/OResultSet.cxx b/connectivity/source/drivers/odbc/OResultSet.cxx new file mode 100644 index 000000000..9e68cd176 --- /dev/null +++ b/connectivity/source/drivers/odbc/OResultSet.cxx @@ -0,0 +1,1847 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(SQL_ATTR_ROW_STATUS_PTR, m_pRowStatusArray.get()); + } + catch(const Exception&) + { // we don't want our result destroy here + } + + try + { + SQLULEN nCurType = getStmtOption(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(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::get(), + cppu::UnoType::get(), + cppu::UnoType::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(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(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(); + auto const & row = m_aRow[columnIndex]; + if constexpr ( std::is_same_v ) + return row.getTime(); + else if constexpr ( std::is_same_v ) + return row.getDateTime(); + else if constexpr ( std::is_same_v ) + return row.getDate(); + else if constexpr ( std::is_same_v ) + return row.getString(); + else if constexpr ( std::is_same_v ) + return row.getLong(); + else if constexpr ( std::is_same_v ) + return row.getInt32(); + else if constexpr ( std::is_same_v ) + return row.getInt16(); + else if constexpr ( std::is_same_v ) + return row.getInt8(); + else if constexpr ( std::is_same_v ) + return row.getFloat(); + else if constexpr ( std::is_same_v ) + return row.getDouble(); + else if constexpr ( std::is_same_v ) + return row.getBool(); + else + return row; +} + +sal_Bool SAL_CALL OResultSet::getBoolean( sal_Int32 columnIndex ) +{ + return getValue( columnIndex ); +} + +sal_Int8 SAL_CALL OResultSet::getByte( sal_Int32 columnIndex ) +{ + return getValue( 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].getSequence(); + break; + default: + { + OUString const & sRet = m_aRow[columnIndex].getString(); + nRet = Sequence(reinterpret_cast(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(reinterpret_cast(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( columnIndex ); +} + + +double SAL_CALL OResultSet::getDouble( sal_Int32 columnIndex ) +{ + return getValue( columnIndex ); +} + + +float SAL_CALL OResultSet::getFloat( sal_Int32 columnIndex ) +{ + return getValue( columnIndex ); +} + +sal_Int16 SAL_CALL OResultSet::getShort( sal_Int32 columnIndex ) +{ + return getValue( columnIndex ); +} + +sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 columnIndex ) +{ + return getValue( columnIndex ); +} + +sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 columnIndex ) +{ + return getValue( columnIndex ); +} +sal_Int64 OResultSet::impl_getLong( sal_Int32 columnIndex ) +{ + try + { + return impl_getValue(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( 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( 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