/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * 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_BIT: return std::type_index(typeid(bool)); case MYSQL_TYPE_TINY: return std::type_index(typeid(sal_Int8)); case MYSQL_TYPE_SHORT: case MYSQL_TYPE_YEAR: 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_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) { bool bIsBlobType = false; switch (m_aFields[i].type) { case MYSQL_TYPE_BLOB: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: bIsBlobType = true; break; default: bIsBlobType = false; } 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 = bIsBlobType ? 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(css::uno::Reference(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.clear(); 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 { auto const& row = getRowSetValue(nColumnIndex); 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.getDouble(); else if constexpr (std::is_same_v) return row.getFloat(); else if constexpr (std::is_same_v) return row.getBool(); else return row; } } 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).getDate(); 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).getTime(); 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).getDateTime(); 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 bool bIsBlobType = false; switch (m_aFields[column - 1].type) { case MYSQL_TYPE_BLOB: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: bIsBlobType = true; break; default: bIsBlobType = false; } if (getTypeFromMysqlType(m_aFields[column - 1].type) != std::type_index(typeid(OUString)) && !bIsBlobType) return getRowSetValue(column).getString(); 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: case MYSQL_TYPE_YEAR: return getShort(nColumnIndex); case MYSQL_TYPE_LONG: case MYSQL_TYPE_INT24: return getInt(nColumnIndex); case MYSQL_TYPE_BIT: return ORowSetValue(bool(getBoolean(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: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_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