diff options
author | Tamas Bunth <tamas.bunth@collabora.co.uk> | 2019-04-22 19:09:24 +0200 |
---|---|---|
committer | Andras Timar <andras.timar@collabora.com> | 2019-04-25 15:47:38 +0200 |
commit | 406aef41d1368a46d36757072592e93efd9ae08e (patch) | |
tree | 8678ca1aedae07fcaf02fba6b5fef9c0a7012074 /connectivity | |
parent | 7ec3baef938d62d16558b7ee7fd84eb02b206551 (diff) |
mysqlc: Allow conversions between different types
Change-Id: I54c1f438a755267db0896637c79f915de9113f83
Reviewed-on: https://gerrit.libreoffice.org/71074
Tested-by: Jenkins
Reviewed-by: Andras Timar <andras.timar@collabora.com>
Diffstat (limited to 'connectivity')
4 files changed, 210 insertions, 181 deletions
diff --git a/connectivity/Library_mysqlc.mk b/connectivity/Library_mysqlc.mk index 9dba7769a9a2..455f9b2224df 100644 --- a/connectivity/Library_mysqlc.mk +++ b/connectivity/Library_mysqlc.mk @@ -31,6 +31,7 @@ $(eval $(call gb_Library_use_sdk_api,mysqlc)) $(eval $(call gb_Library_use_libraries,mysqlc,\ cppu \ + dbtools \ sal \ salhelper \ comphelper \ diff --git a/connectivity/qa/connectivity/mysql/mysql.cxx b/connectivity/qa/connectivity/mysql/mysql.cxx index 1cbe34790421..00ff9423aa4a 100644 --- a/connectivity/qa/connectivity/mysql/mysql.cxx +++ b/connectivity/qa/connectivity/mysql/mysql.cxx @@ -53,6 +53,7 @@ public: void testMultipleResultsets(); void testDBMetaData(); void testTimestampField(); + void testNumericConversionPrepared(); CPPUNIT_TEST_SUITE(MysqlTestDriver); CPPUNIT_TEST(testDBConnection); @@ -61,6 +62,7 @@ public: CPPUNIT_TEST(testMultipleResultsets); CPPUNIT_TEST(testDBMetaData); CPPUNIT_TEST(testTimestampField); + CPPUNIT_TEST(testNumericConversionPrepared); CPPUNIT_TEST_SUITE_END(); }; @@ -373,6 +375,36 @@ void MysqlTestDriver::testTimestampField() xStatement->executeUpdate("DROP TABLE myTestTable"); } +/** + * Test getting value from a decimal type column from a result set of a + * prepared statement, getting as a tinyint, string, short, int, long. + */ +void MysqlTestDriver::testNumericConversionPrepared() +{ + Reference<XConnection> xConnection = m_xDriver->connect(m_sUrl, m_infos); + if (!xConnection.is()) + CPPUNIT_ASSERT_MESSAGE("cannot connect to data source!", xConnection.is()); + uno::Reference<XStatement> xStatement = xConnection->createStatement(); + CPPUNIT_ASSERT(xStatement.is()); + xStatement->executeUpdate("DROP TABLE IF EXISTS myTestTable"); + + xStatement->executeUpdate("CREATE TABLE myTestTable (myDecimal DECIMAL(4,2))"); + xStatement->executeUpdate("INSERT INTO myTestTable VALUES (11.22)"); + Reference<XPreparedStatement> xPrepared + = xConnection->prepareStatement("SELECT * from myTestTable"); + Reference<XResultSet> xResultSet = xPrepared->executeQuery(); + xResultSet->next(); // use it + Reference<XRow> xRow(xResultSet, UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("11.22"), xRow->getString(1)); + // converting to integer types results in rounding down the number + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int8>(11), xRow->getByte(1)); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(11), xRow->getShort(1)); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(11), xRow->getInt(1)); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int64>(11), xRow->getLong(1)); + + xStatement->executeUpdate("DROP TABLE myTestTable"); +} + CPPUNIT_TEST_SUITE_REGISTRATION(MysqlTestDriver); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx index a3e45937b610..b3630072c143 100644 --- a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx +++ b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx @@ -38,6 +38,7 @@ 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; @@ -51,6 +52,52 @@ using namespace ::comphelper; using ::osl::MutexGuard; #include <stdio.h> +#include <typeinfo> +#include <typeindex> + +namespace +{ +const 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: + 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_INT24: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_NULL: + default: + return std::type_index(typeid(nullptr)); + } +} +} OUString SAL_CALL OPreparedResultSet::getImplementationName() { @@ -128,146 +175,169 @@ sal_Int32 SAL_CALL OPreparedResultSet::findColumn(const OUString& columnName) Any()); } -uno::Reference<XInputStream> SAL_CALL OPreparedResultSet::getBinaryStream(sal_Int32 column) +template <typename T> T OPreparedResultSet::safelyRetrieveValue(sal_Int32 nColumnIndex) { MutexGuard aGuard(m_aMutex); checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); + checkColumnIndex(nColumnIndex); + if (*m_aData[nColumnIndex - 1].is_null) + { + m_bWasNull = true; + return T(); + } + m_bWasNull = false; - mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getBinaryStream", - *this); - return nullptr; + return retrieveValue<T>(nColumnIndex); } -uno::Reference<XInputStream> SAL_CALL OPreparedResultSet::getCharacterStream(sal_Int32 column) +template <typename T> T OPreparedResultSet::retrieveValue(sal_Int32 nColumnIndex) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); + 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); +} - mysqlc_sdbc_driver::throwFeatureNotImplementedException( - "OPreparedResultSet::getCharacterStream", *this); - return nullptr; +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); } -sal_Bool SAL_CALL OPreparedResultSet::getBoolean(sal_Int32 column) +template <> Date OPreparedResultSet::retrieveValue(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(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); - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return false; - } - m_bWasNull = false; - return *static_cast<bool*>(m_aData[column - 1].buffer); + Date d; + d.Year = pTime->year; + d.Month = pTime->month; + d.Day = pTime->day; + return d; } -sal_Int8 SAL_CALL OPreparedResultSet::getByte(sal_Int32 column) +template <> Time OPreparedResultSet::retrieveValue(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(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); - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return 0; - } - m_bWasNull = false; - return *static_cast<sal_Int8*>(m_aData[column - 1].buffer); + Time t; + t.Hours = pTime->hour; + t.Minutes = pTime->minute; + t.Seconds = pTime->second; + return t; } -uno::Sequence<sal_Int8> SAL_CALL OPreparedResultSet::getBytes(sal_Int32 column) +template <> DateTime OPreparedResultSet::retrieveValue(sal_Int32 column) { - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - MutexGuard aGuard(m_aMutex); - - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return uno::Sequence<sal_Int8>(); - } - m_bWasNull = false; + 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); - return uno::Sequence<sal_Int8>(static_cast<sal_Int8 const*>(m_aData[column - 1].buffer), - *m_aData[column - 1].length); + 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; } -Date SAL_CALL OPreparedResultSet::getDate(sal_Int32 column) +template <> OUString OPreparedResultSet::retrieveValue(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); + if (getTypeFromMysqlType(m_aFields[column - 1].type) != std::type_index(typeid(OUString))) + return getRowSetValue(column); + const char* sStr = static_cast<const char*>(m_aData[column - 1].buffer); - if (*m_aData[column - 1].is_null) + OUString sReturn = OUString(sStr, *m_aData[column - 1].length, m_encoding); + return sReturn; +} + +ORowSetValue OPreparedResultSet::getRowSetValue(sal_Int32 nColumnIndex) +{ + switch (m_aFields[nColumnIndex - 1].type) { - m_bWasNull = true; - return Date{}; // TODO init + case MYSQL_TYPE_TINY: + return getByte(nColumnIndex); + case MYSQL_TYPE_SHORT: + return getShort(nColumnIndex); + case MYSQL_TYPE_LONG: + return getInt(nColumnIndex); + case MYSQL_TYPE_LONGLONG: + return getLong(nColumnIndex); + case MYSQL_TYPE_FLOAT: + 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); + default: + mysqlc_sdbc_driver::throwFeatureNotImplementedException( + "OPreparedResultSet::getRowSetValue", *this); + return ORowSetValue(); } - m_bWasNull = false; +} - const MYSQL_TIME* pTime = static_cast<MYSQL_TIME*>(m_aData[column - 1].buffer); +uno::Reference<XInputStream> SAL_CALL OPreparedResultSet::getBinaryStream(sal_Int32 /*column*/) +{ + mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::getBinaryStream", + *this); + return nullptr; +} - assert(pTime != nullptr); +uno::Reference<XInputStream> SAL_CALL OPreparedResultSet::getCharacterStream(sal_Int32 /*column*/) +{ + mysqlc_sdbc_driver::throwFeatureNotImplementedException( + "OPreparedResultSet::getCharacterStream", *this); + return nullptr; +} - Date d; - d.Year = pTime->year; - d.Month = pTime->month; - d.Day = pTime->day; - return d; +sal_Bool SAL_CALL OPreparedResultSet::getBoolean(sal_Int32 column) +{ + return safelyRetrieveValue<bool>(column); } -double SAL_CALL OPreparedResultSet::getDouble(sal_Int32 column) +sal_Int8 SAL_CALL OPreparedResultSet::getByte(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); + return safelyRetrieveValue<sal_Int8>(column); +} - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return 0; - } - m_bWasNull = false; +uno::Sequence<sal_Int8> SAL_CALL OPreparedResultSet::getBytes(sal_Int32 column) +{ + return safelyRetrieveValue<uno::Sequence<sal_Int8>>(column); +} - if (m_aFields[column - 1].type == MYSQL_TYPE_FLOAT) - return *static_cast<float*>(m_aData[column - 1].buffer); +Date SAL_CALL OPreparedResultSet::getDate(sal_Int32 column) +{ + return safelyRetrieveValue<Date>(column); +} - return *static_cast<double*>(m_aData[column - 1].buffer); +double SAL_CALL OPreparedResultSet::getDouble(sal_Int32 column) +{ + return safelyRetrieveValue<double>(column); } float SAL_CALL OPreparedResultSet::getFloat(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); - - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return 0; - } - m_bWasNull = false; - - return *static_cast<float*>(m_aData[column - 1].buffer); + return safelyRetrieveValue<float>(column); } sal_Int32 SAL_CALL OPreparedResultSet::getInt(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return 0; - } - m_bWasNull = false; - - return *static_cast<sal_Int32*>(m_aData[column - 1].buffer); + return safelyRetrieveValue<sal_Int32>(column); } sal_Int32 SAL_CALL OPreparedResultSet::getRow() @@ -280,14 +350,7 @@ sal_Int32 SAL_CALL OPreparedResultSet::getRow() sal_Int64 SAL_CALL OPreparedResultSet::getLong(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); - - if (*m_aData[column - 1].is_null) - return 0; - - return *static_cast<sal_Int64*>(m_aData[column - 1].buffer); + return safelyRetrieveValue<sal_Int64>(column); } uno::Reference<XResultSetMetaData> SAL_CALL OPreparedResultSet::getMetaData() @@ -356,94 +419,22 @@ Any SAL_CALL OPreparedResultSet::getObject(sal_Int32 column, sal_Int16 SAL_CALL OPreparedResultSet::getShort(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); - - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return 0; - } - m_bWasNull = false; - - return *static_cast<sal_Int16*>(m_aData[column - 1].buffer); + return safelyRetrieveValue<sal_Int16>(column); } OUString SAL_CALL OPreparedResultSet::getString(sal_Int32 column) { - MutexGuard aGuard(m_aMutex); - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - checkColumnIndex(column); - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return OUString{}; - } - m_bWasNull = false; - - if (m_aFields[column - 1].type == MYSQL_TYPE_BIT) - { - if (*static_cast<sal_Int8*>(m_aData[column - 1].buffer) != 0) - return OUString{ "YES" }; - return OUString{ "NO" }; - } - - const char* sStr = static_cast<const char*>(m_aData[column - 1].buffer); - - OUString sReturn = OUString(sStr, *m_aData[column - 1].length, m_encoding); - return sReturn; + return safelyRetrieveValue<OUString>(column); } Time SAL_CALL OPreparedResultSet::getTime(sal_Int32 column) { - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - MutexGuard aGuard(m_aMutex); - checkColumnIndex(column); - - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return Time{}; // TODO init - } - m_bWasNull = false; - - const MYSQL_TIME* pTime = static_cast<MYSQL_TIME*>(m_aData[column - 1].buffer); - - assert(pTime != nullptr); - - Time t; - t.Hours = pTime->hour; - t.Minutes = pTime->minute; - t.Seconds = pTime->second; - return t; + return safelyRetrieveValue<Time>(column); } DateTime SAL_CALL OPreparedResultSet::getTimestamp(sal_Int32 column) { - checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed); - MutexGuard aGuard(m_aMutex); - checkColumnIndex(column); - - if (*m_aData[column - 1].is_null) - { - m_bWasNull = true; - return DateTime{}; - } - m_bWasNull = false; - - const MYSQL_TIME* pTime = static_cast<MYSQL_TIME*>(m_aData[column - 1].buffer); - - assert(pTime != nullptr); - - 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; + return safelyRetrieveValue<DateTime>(column); } sal_Bool SAL_CALL OPreparedResultSet::isBeforeFirst() diff --git a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx index 89e302eb08c7..5c8c231c44d8 100644 --- a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx +++ b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx @@ -36,6 +36,7 @@ #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> @@ -91,6 +92,10 @@ class OPreparedResultSet final : public OBase_Mutex, 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); + // you can't delete objects of this type virtual ~OPreparedResultSet() override = default; |