From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- .../drivers/mysqlc/mysqlc_prepared_resultset.cxx | 1084 ++++++++++++++++++++ 1 file changed, 1084 insertions(+) create mode 100644 connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx (limited to 'connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx') 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 +#include +#include +#include +#include +#include +#include +#include + +using namespace rtl; + +#include + +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 + +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 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(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 SAL_CALL OPreparedResultSet::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::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 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(nColumnIndex); +} + +template T OPreparedResultSet::retrieveValue(sal_Int32 nColumnIndex) +{ + if (getTypeFromMysqlType(m_aFields[nColumnIndex - 1].type) == std::type_index(typeid(T))) + return *static_cast(m_aData[nColumnIndex - 1].buffer); + else + return getRowSetValue(nColumnIndex); +} + +template <> uno::Sequence OPreparedResultSet::retrieveValue(sal_Int32 column) +{ + // TODO make conversion possible + return uno::Sequence(static_cast(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(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(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(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(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 SAL_CALL OPreparedResultSet::getBinaryStream(sal_Int32 /*column*/) +{ + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getBinaryStream", + *this); + return nullptr; +} + +uno::Reference 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(column); +} + +sal_Int8 SAL_CALL OPreparedResultSet::getByte(sal_Int32 column) +{ + return safelyRetrieveValue(column); +} + +uno::Sequence SAL_CALL OPreparedResultSet::getBytes(sal_Int32 column) +{ + return safelyRetrieveValue>(column); +} + +Date SAL_CALL OPreparedResultSet::getDate(sal_Int32 column) +{ + return safelyRetrieveValue(column); +} + +double SAL_CALL OPreparedResultSet::getDouble(sal_Int32 column) +{ + return safelyRetrieveValue(column); +} + +float SAL_CALL OPreparedResultSet::getFloat(sal_Int32 column) +{ + return safelyRetrieveValue(column); +} + +sal_Int32 SAL_CALL OPreparedResultSet::getInt(sal_Int32 column) +{ + return safelyRetrieveValue(column); +} + +sal_Int32 SAL_CALL OPreparedResultSet::getRow() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); + + return static_cast(mysql_field_tell(m_pResult)); +} + +sal_Int64 SAL_CALL OPreparedResultSet::getLong(sal_Int32 column) +{ + return safelyRetrieveValue(column); +} + +uno::Reference 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 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 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 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 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& /* 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(column); +} + +OUString SAL_CALL OPreparedResultSet::getString(sal_Int32 column) +{ + return safelyRetrieveValue(column); +} + +Time SAL_CALL OPreparedResultSet::getTime(sal_Int32 column) +{ + return safelyRetrieveValue