summaryrefslogtreecommitdiff
path: root/connectivity
diff options
context:
space:
mode:
authorTamas Bunth <tamas.bunth@collabora.co.uk>2019-04-22 19:09:24 +0200
committerAndras Timar <andras.timar@collabora.com>2019-04-24 17:20:00 +0200
commit930074a919da821813fd4e9e11a3b74b2822b1b6 (patch)
tree8cf6e5fbd2f3deb90c002da15cfbd65df0c081f1 /connectivity
parent07aecc1d2d089e97a6d73c8b648611cda168ba39 (diff)
mysqlc: Allow conversions between different types
Change-Id: I54c1f438a755267db0896637c79f915de9113f83 Reviewed-on: https://gerrit.libreoffice.org/71246 Reviewed-by: Andras Timar <andras.timar@collabora.com> Tested-by: Andras Timar <andras.timar@collabora.com>
Diffstat (limited to 'connectivity')
-rw-r--r--connectivity/Library_mysqlc.mk1
-rw-r--r--connectivity/qa/connectivity/mysql/mysql.cxx32
-rw-r--r--connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx353
-rw-r--r--connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx5
4 files changed, 210 insertions, 181 deletions
diff --git a/connectivity/Library_mysqlc.mk b/connectivity/Library_mysqlc.mk
index eac56916c479..817dbb689d3e 100644
--- a/connectivity/Library_mysqlc.mk
+++ b/connectivity/Library_mysqlc.mk
@@ -35,6 +35,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 115347e64f42..98e068b65f2a 100644
--- a/connectivity/qa/connectivity/mysql/mysql.cxx
+++ b/connectivity/qa/connectivity/mysql/mysql.cxx
@@ -52,6 +52,7 @@ public:
void testMultipleResultsets();
void testDBMetaData();
void testTimestampField();
+ void testNumericConversionPrepared();
CPPUNIT_TEST_SUITE(MysqlTestDriver);
CPPUNIT_TEST(testDBConnection);
@@ -60,6 +61,7 @@ public:
CPPUNIT_TEST(testMultipleResultsets);
CPPUNIT_TEST(testDBMetaData);
CPPUNIT_TEST(testTimestampField);
+ CPPUNIT_TEST(testNumericConversionPrepared);
CPPUNIT_TEST_SUITE_END();
};
@@ -358,6 +360,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 e9f07f8947c0..10cc65122dfa 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));
+ }
+}
+}
rtl::OUString SAL_CALL OPreparedResultSet::getImplementationName()
{
@@ -129,146 +176,169 @@ sal_Int32 SAL_CALL OPreparedResultSet::findColumn(const rtl::OUString& columnNam
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 *reinterpret_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 *reinterpret_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>(reinterpret_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);
+
+ OUString sReturn = OUString(sStr, *m_aData[column - 1].length, m_encoding);
+ return sReturn;
+}
- if (*m_aData[column - 1].is_null)
+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 = reinterpret_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 double{};
- }
- 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 *reinterpret_cast<float*>(m_aData[column - 1].buffer);
+Date SAL_CALL OPreparedResultSet::getDate(sal_Int32 column)
+{
+ return safelyRetrieveValue<Date>(column);
+}
- return *reinterpret_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 float{};
- }
- m_bWasNull = false;
-
- return *reinterpret_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 sal_Int32{};
- }
- m_bWasNull = false;
-
- return *reinterpret_cast<sal_Int32*>(m_aData[column - 1].buffer);
+ return safelyRetrieveValue<sal_Int32>(column);
}
sal_Int32 SAL_CALL OPreparedResultSet::getRow()
@@ -281,14 +351,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 sal_Int64{};
-
- return *reinterpret_cast<sal_Int64*>(m_aData[column - 1].buffer);
+ return safelyRetrieveValue<sal_Int64>(column);
}
uno::Reference<XResultSetMetaData> SAL_CALL OPreparedResultSet::getMetaData()
@@ -357,94 +420,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 sal_Int16{};
- }
- m_bWasNull = false;
-
- return *reinterpret_cast<sal_Int16*>(m_aData[column - 1].buffer);
+ return safelyRetrieveValue<sal_Int16>(column);
}
rtl::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 (*reinterpret_cast<sal_Int8*>(m_aData[column - 1].buffer) != 0)
- return OUString{ "YES" };
- return OUString{ "NO" };
- }
-
- const char* sStr = reinterpret_cast<const char*>(m_aData[column - 1].buffer);
-
- OUString sReturn = rtl::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 = reinterpret_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 = reinterpret_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 93690d194fdf..e89345ba0196 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>
@@ -92,6 +93,10 @@ class OPreparedResultSet final : public OBase_Mutex,
void SAL_CALL getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const SAL_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;