Kaydet (Commit) ee33372f authored tarafından Tamas Bunth's avatar Tamas Bunth Kaydeden (comit) Tamás Bunth

mysqlc: Fix result set metadata related issue

In order to allow fetching result of multiple result sets at time same
time, all the data is fetched and copied on demand from the mysql result
set. The mysql result set (MYSQL_RES) is freed afterwards.

That means we need a copy of the meta information as well. Now all the
meta data is stored in the driver for each result set, so it does not
depend on the MYSQL_RES structure anymore.

Also add test case for invoking some meta data queries before and after
fetching the result set.

Change-Id: Ie8bf993926ebe89cd362ab0b311d1f3d164b84df
Reviewed-on: https://gerrit.libreoffice.org/65717
Tested-by: Jenkins
Reviewed-by: 's avatarTamás Bunth <btomi96@gmail.com>
üst f2ee5326
......@@ -13,7 +13,10 @@
#include <com/sun/star/sdbc/XColumnLocate.hpp>
#include <com/sun/star/sdbc/XConnection.hpp>
#include <com/sun/star/sdbc/XResultSet.hpp>
#include <com/sun/star/sdbc/XResultSetMetaData.hpp>
#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/sdbc/XParameters.hpp>
#include <com/sun/star/sdbc/XStatement.hpp>
#include <com/sun/star/sdbc/XDriver.hpp>
......@@ -46,12 +49,14 @@ public:
void testIntegerInsertAndQuery();
void testDBPositionChange();
void testMultipleResultsets();
void testDBMetaData();
CPPUNIT_TEST_SUITE(MysqlTestDriver);
CPPUNIT_TEST(testDBConnection);
CPPUNIT_TEST(testCreateAndDropTable);
CPPUNIT_TEST(testIntegerInsertAndQuery);
CPPUNIT_TEST(testMultipleResultsets);
CPPUNIT_TEST(testDBMetaData);
CPPUNIT_TEST_SUITE_END();
};
......@@ -289,6 +294,43 @@ void MysqlTestDriver::testMultipleResultsets()
xStatement->executeUpdate("DROP TABLE otherTable");
}
void MysqlTestDriver::testDBMetaData()
{
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");
auto nUpdateCount = xStatement->executeUpdate(
"CREATE TABLE myTestTable (id INTEGER PRIMARY KEY, name VARCHAR(20))");
Reference<XPreparedStatement> xPrepared
= xConnection->prepareStatement(OUString{ "INSERT INTO myTestTable VALUES (?, ?)" });
Reference<XParameters> xParams(xPrepared, UNO_QUERY);
constexpr int ROW_COUNT = 3;
for (int i = 0; i < ROW_COUNT; ++i)
{
xParams->setLong(1, i);
xParams->setString(2, "lorem");
xPrepared->executeUpdate();
}
Reference<XResultSet> xResultSet = xStatement->executeQuery("SELECT * from myTestTable");
Reference<XResultSetMetaDataSupplier> xMetaDataSupplier(xResultSet, UNO_QUERY);
Reference<XResultSetMetaData> xMetaData = xMetaDataSupplier->getMetaData();
CPPUNIT_ASSERT_EQUAL(OUString{ "id" }, xMetaData->getColumnName(1));
CPPUNIT_ASSERT_EQUAL(OUString{ "name" }, xMetaData->getColumnName(2));
CPPUNIT_ASSERT(!xMetaData->isAutoIncrement(1));
CPPUNIT_ASSERT(!xMetaData->isCaseSensitive(2)); // default collation should be case insensitive
xResultSet->next(); // use it
// test that meta data is usable even after fetching result set
CPPUNIT_ASSERT_EQUAL(OUString{ "name" }, xMetaData->getColumnName(2));
CPPUNIT_ASSERT_THROW_MESSAGE("exception expected when indexing out of range",
xMetaData->getColumnName(3), sdbc::SQLException);
nUpdateCount = xStatement->executeUpdate("DROP TABLE myTestTable");
}
CPPUNIT_TEST_SUITE_REGISTRATION(MysqlTestDriver);
CPPUNIT_PLUGIN_IMPLEMENT();
......
......@@ -225,11 +225,13 @@ sal_Int32 mysqlStrToOOOType(const OUString& sType)
return css::sdbc::DataType::VARCHAR;
}
OUString mysqlTypeToStr(MYSQL_FIELD* field)
OUString mysqlTypeToStr(MYSQL_FIELD* field) { return mysqlTypeToStr(field->type, field->flags); }
OUString mysqlTypeToStr(unsigned type, unsigned flags)
{
bool isUnsigned = (field->flags & UNSIGNED_FLAG) != 0;
bool isZerofill = (field->flags & ZEROFILL_FLAG) != 0;
switch (field->type)
bool isUnsigned = (flags & UNSIGNED_FLAG) != 0;
bool isZerofill = (flags & ZEROFILL_FLAG) != 0;
switch (type)
{
case MYSQL_TYPE_BIT:
return OUString{ "BIT" };
......@@ -294,21 +296,21 @@ OUString mysqlTypeToStr(MYSQL_FIELD* field)
}
case MYSQL_TYPE_VARCHAR:
case MYSQL_TYPE_VAR_STRING:
if (field->flags & ENUM_FLAG)
if (flags & ENUM_FLAG)
{
return OUString{ "ENUM" };
}
if (field->flags & SET_FLAG)
if (flags & SET_FLAG)
{
return OUString{ "SET" };
}
return OUString{ "VARCHAR" };
case MYSQL_TYPE_STRING:
if (field->flags & ENUM_FLAG)
if (flags & ENUM_FLAG)
{
return OUString{ "ENUM" };
}
if (field->flags & SET_FLAG)
if (flags & SET_FLAG)
{
return OUString{ "SET" };
}
......
......@@ -92,12 +92,10 @@ void resetSqlVar(void** target, T* pValue, enum_field_types type, sal_Int32 nSiz
void allocateSqlVar(void** mem, enum_field_types eType, unsigned nSize = 0);
/// @throws css::sdbc::SQLException
void throwFeatureNotImplementedException(
const sal_Char* _pAsciiFeatureName,
const css::uno::Reference<css::uno::XInterface>& _rxContext);
/// @throws css::sdbc::SQLException
void throwInvalidArgumentException(const sal_Char* _pAsciiFeatureName,
const css::uno::Reference<css::uno::XInterface>& _rxContext);
......@@ -109,6 +107,8 @@ sal_Int32 mysqlToOOOType(int eType, int charsetnr) noexcept;
OUString mysqlTypeToStr(MYSQL_FIELD* pField);
OUString mysqlTypeToStr(unsigned mysql_type, unsigned mysql_flags);
sal_Int32 mysqlStrToOOOType(const OUString& sType);
OUString convert(const ::std::string& _string, const rtl_TextEncoding encoding);
......
......@@ -23,41 +23,61 @@
#include <com/sun/star/sdbc/XRow.hpp>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
using namespace connectivity::mysqlc;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::sdbc;
MYSQL_FIELD* OResultSetMetaData::getField(sal_Int32 column) const
OResultSetMetaData::OResultSetMetaData(OConnection& rConn, MYSQL_RES* pResult)
: m_rConnection(rConn)
{
return mysql_fetch_field_direct(m_pRes, column - 1);
MYSQL_FIELD* fields = mysql_fetch_field(pResult);
unsigned nFieldCount = mysql_num_fields(pResult);
for (unsigned i = 0; i < nFieldCount; ++i)
{
MySqlFieldInfo fieldInfo{
OUString{ fields[i].name, static_cast<sal_Int32>(fields[i].name_length),
m_rConnection.getConnectionEncoding() }, // column name
static_cast<sal_Int32>(fields[i].length), // length
mysqlc_sdbc_driver::mysqlToOOOType(fields[i].type, fields[i].charsetnr), // type
fields[i].type, // mysql_type
fields[i].charsetnr, // charset number
fields[i].flags,
OUString{ fields[i].db, static_cast<sal_Int32>(fields[i].db_length),
m_rConnection.getConnectionEncoding() }, // schema name
OUString{ fields[i].table, static_cast<sal_Int32>(fields[i].table_length),
m_rConnection.getConnectionEncoding() }, // table name
OUString{ fields[i].catalog, static_cast<sal_Int32>(fields[i].catalog_length),
m_rConnection.getConnectionEncoding() }, // catalog
static_cast<sal_Int32>(fields[i].decimals),
static_cast<sal_Int32>(fields[i].max_length)
};
m_fields.push_back(std::move(fieldInfo));
}
}
sal_Int32 SAL_CALL OResultSetMetaData::getColumnDisplaySize(sal_Int32 column)
{
MYSQL_FIELD* pField = getField(column);
return pField->length;
checkColumnIndex(column);
return m_fields.at(column - 1).length;
}
sal_Int32 SAL_CALL OResultSetMetaData::getColumnType(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return mysqlc_sdbc_driver::mysqlToOOOType(pField->type, pField->charsetnr);
return m_fields.at(column - 1).type;
}
sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount() { return mysql_num_fields(m_pRes); }
sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount() { return m_fields.size(); }
sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive(sal_Int32 column)
{
checkColumnIndex(column);
// MYSQL_FIELD::charsetnr is the collation identifier
// _ci postfix means it's insensitive
MYSQL_FIELD* pField = getField(column);
OUStringBuffer sql{ "SHOW COLLATION WHERE Id =" };
sql.append(OUString::number(pField->charsetnr));
sql.append(OUString::number(m_fields.at(column - 1).charsetNumber));
Reference<XStatement> stmt = m_rConnection.createStatement();
Reference<XResultSet> rs = stmt->executeQuery(sql.makeStringAndClear());
......@@ -74,54 +94,43 @@ sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive(sal_Int32 column)
OUString SAL_CALL OResultSetMetaData::getSchemaName(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return OStringToOUString(pField->db, m_rConnection.getConnectionEncoding());
return m_fields.at(column - 1).schemaName;
}
OUString SAL_CALL OResultSetMetaData::getColumnName(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return OStringToOUString(pField->name, m_rConnection.getConnectionEncoding());
return m_fields.at(column - 1).columnName;
}
OUString SAL_CALL OResultSetMetaData::getTableName(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return OStringToOUString(pField->table, m_rConnection.getConnectionEncoding());
return m_fields.at(column - 1).tableName;
}
OUString SAL_CALL OResultSetMetaData::getCatalogName(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return OStringToOUString(pField->catalog, m_rConnection.getConnectionEncoding());
return m_fields.at(column - 1).catalogName;
}
OUString SAL_CALL OResultSetMetaData::getColumnTypeName(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return mysqlc_sdbc_driver::mysqlTypeToStr(pField);
return mysqlc_sdbc_driver::mysqlTypeToStr(m_fields.at(column - 1).mysql_type,
m_fields.at(column - 1).flags);
}
OUString SAL_CALL OResultSetMetaData::getColumnLabel(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return OStringToOUString(pField->name, m_rConnection.getConnectionEncoding());
return getColumnName(column);
}
OUString SAL_CALL OResultSetMetaData::getColumnServiceName(sal_Int32 column)
OUString SAL_CALL OResultSetMetaData::getColumnServiceName(sal_Int32 /*column*/)
{
checkColumnIndex(column);
OUString aRet = OUString();
return aRet;
return OUString{};
}
sal_Bool SAL_CALL OResultSetMetaData::isCurrency(sal_Int32 /*column*/)
......@@ -132,47 +141,43 @@ sal_Bool SAL_CALL OResultSetMetaData::isCurrency(sal_Int32 /*column*/)
sal_Bool SAL_CALL OResultSetMetaData::isAutoIncrement(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return (pField->flags & AUTO_INCREMENT_FLAG) != 0;
return (m_fields.at(column - 1).flags & AUTO_INCREMENT_FLAG) != 0;
}
sal_Bool SAL_CALL OResultSetMetaData::isSigned(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return !(pField->flags & UNSIGNED_FLAG);
return !(m_fields.at(column - 1).flags & UNSIGNED_FLAG);
}
sal_Int32 SAL_CALL OResultSetMetaData::getPrecision(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return pField->max_length - pField->decimals;
return m_fields.at(column - 1).max_length - m_fields.at(column - 1).decimals;
}
sal_Int32 SAL_CALL OResultSetMetaData::getScale(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return pField->decimals;
return m_fields.at(column - 1).decimals;
}
sal_Int32 SAL_CALL OResultSetMetaData::isNullable(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return (pField->flags & NOT_NULL_FLAG) ? 0 : 1;
return (m_fields.at(column - 1).flags & NOT_NULL_FLAG) ? 0 : 1;
}
sal_Bool SAL_CALL OResultSetMetaData::isSearchable(sal_Int32 /*column*/) { return true; }
sal_Bool SAL_CALL OResultSetMetaData::isSearchable(sal_Int32 column)
{
checkColumnIndex(column);
return true;
}
sal_Bool SAL_CALL OResultSetMetaData::isReadOnly(sal_Int32 column)
{
checkColumnIndex(column);
MYSQL_FIELD* pField = getField(column);
return !(pField->db && strlen(pField->db));
return m_fields.at(column - 1).schemaName.isEmpty();
}
sal_Bool SAL_CALL OResultSetMetaData::isDefinitelyWritable(sal_Int32 column)
......@@ -189,7 +194,7 @@ sal_Bool SAL_CALL OResultSetMetaData::isWritable(sal_Int32 column)
void OResultSetMetaData::checkColumnIndex(sal_Int32 columnIndex)
{
unsigned nColCount = mysql_num_fields(m_pRes);
auto nColCount = m_fields.size();
if (columnIndex < 1 || columnIndex > static_cast<sal_Int32>(nColCount))
{
OUStringBuffer buf;
......
......@@ -37,6 +37,21 @@ using ::com::sun::star::sdbc::SQLException;
using ::com::sun::star::uno::RuntimeException;
using ::com::sun::star::uno::RuntimeException;
struct MySqlFieldInfo
{
OUString columnName;
sal_Int32 length = 0;
sal_Int32 type = 0;
unsigned mysql_type = 0;
unsigned charsetNumber = 0;
unsigned flags = 0;
OUString schemaName;
OUString tableName;
OUString catalogName;
sal_Int32 decimals;
sal_Int32 max_length;
};
//************ Class: ResultSetMetaData
typedef ::cppu::WeakImplHelper1<css::sdbc::XResultSetMetaData> OResultSetMetaData_BASE;
......@@ -45,17 +60,13 @@ class OResultSetMetaData final : public OResultSetMetaData_BASE
{
private:
OConnection& m_rConnection;
MYSQL_RES* m_pRes;
std::vector<MySqlFieldInfo> m_fields;
void checkColumnIndex(sal_Int32 columnIndex);
virtual ~OResultSetMetaData() override = default;
MYSQL_FIELD* getField(sal_Int32 column) const;
public:
OResultSetMetaData(OConnection& rConn, MYSQL_RES* pResult)
: m_rConnection(rConn)
, m_pRes(pResult)
{
}
OResultSetMetaData(OConnection& rConn, MYSQL_RES* pResult);
sal_Int32 SAL_CALL getColumnCount() override;
......@@ -89,10 +100,6 @@ public:
sal_Bool SAL_CALL isDefinitelyWritable(sal_Int32 column) override;
OUString SAL_CALL getColumnServiceName(sal_Int32 column) override;
/// @throws SQLException
/// @throws RuntimeException
void checkColumnIndex(sal_Int32 columnIndex);
};
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment