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

tdf#104734 Firebird: Add Binary (fix) type

There is no explicit binary type in Firebird. It can be accomplished
using the CHAR type with a special character set, which tells the
database that it is binary data and there is no collation. (called
OCTETS).

Because of that, we also need the character set to decide the exact
column type.

And also refactor some parts of the driver:
- Create class to determine internal type from firebird type, subtype,
scale and character set.
- Use internal type (DataType::XXX) in XDatabaseMetaData::getTypeInfo()
indirectly. (We want to return a Firebird type for each internal type,
not in the opposite direction.

Change-Id: Ica56a84d89253e11936e7012086fe1d9f61a65f0
Reviewed-on: https://gerrit.libreoffice.org/47091Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarTamás Bunth <btomi96@gmail.com>
üst 3c567e55
......@@ -39,6 +39,39 @@ OResultSetMetaData::~OResultSetMetaData()
{
}
OUString OResultSetMetaData::getCharacterSet( sal_Int32 nIndex )
{
OUString sTable = getTableName( nIndex );
if( !sTable.isEmpty() )
{
OUString sColumnName = getColumnName( nIndex );
OUString sSql = "SELECT charset.RDB$CHARACTER_SET_NAME "
"FROM RDB$CHARACTER_SETS charset "
"JOIN RDB$FIELDS fields "
"ON (fields.RDB$CHARACTER_SET_ID = charset.RDB$CHARACTER_SET_ID) "
"JOIN RDB$RELATION_FIELDS relfields "
"ON (fields.RDB$FIELD_NAME = relfields.RDB$FIELD_SOURCE) "
"WHERE relfields.RDB$RELATION_NAME = '"
+ escapeWith(sTable, '\'', '\'') + "' AND "
"relfields.RDB$FIELD_NAME = '"+ escapeWith(sColumnName, '\'', '\'') +"'";
Reference<XStatement> xStmt= m_pConnection->createStatement();
Reference<XResultSet> xRes =
xStmt->executeQuery(sSql);
Reference<XRow> xRow ( xRes, UNO_QUERY);
if(xRes->next())
{
OUString sCharset = xRow->getString(1).trim();
return sCharset;
}
}
return OUString();
}
void OResultSetMetaData::verifyValidColumn(sal_Int32 column)
{
if (column>getColumnCount() || column < 1)
......@@ -60,11 +93,20 @@ sal_Int32 SAL_CALL OResultSetMetaData::getColumnType(sal_Int32 column)
{
verifyValidColumn(column);
short aType = m_pSqlda->sqlvar[column-1].sqltype;
short aSubType = m_pSqlda->sqlvar[column-1].sqlsubtype;
short aScale = m_pSqlda->sqlvar[column-1].sqlscale;
short aType = m_pSqlda->sqlvar[column-1].sqltype & ~1;
OUString sCharset;
if(aType == SQL_TEXT)
{
sCharset = getCharacterSet(column);
}
ColumnTypeInfo aInfo( m_pSqlda->sqlvar[column-1].sqltype,
m_pSqlda->sqlvar[column-1].sqlsubtype,
m_pSqlda->sqlvar[column-1].sqlscale,
sCharset );
return getColumnTypeFromFBType(aType, aSubType, aScale);
return aInfo.getSdbcType();
}
sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive(sal_Int32)
......@@ -109,11 +151,11 @@ OUString SAL_CALL OResultSetMetaData::getColumnTypeName(sal_Int32 column)
{
verifyValidColumn(column);
short aType = m_pSqlda->sqlvar[column-1].sqltype;
short aSubType = m_pSqlda->sqlvar[column-1].sqlsubtype;
short aScale = m_pSqlda->sqlvar[column-1].sqlscale;
ColumnTypeInfo aInfo( m_pSqlda->sqlvar[column-1].sqltype,
m_pSqlda->sqlvar[column-1].sqlsubtype,
m_pSqlda->sqlvar[column-1].sqlscale );
return getColumnTypeNameFromFBType(aType, aSubType, aScale);
return aInfo.getColumnTypeName();
}
OUString SAL_CALL OResultSetMetaData::getColumnLabel(sal_Int32 column)
......
......@@ -45,6 +45,7 @@ namespace connectivity
/// @throws css::sdbc::SQLException
void verifyValidColumn(sal_Int32 column);
OUString getCharacterSet(sal_Int32 nIndex);
public:
// a constructor, which is required for returning objects:
OResultSetMetaData(Connection* pConnection,
......
......@@ -92,6 +92,21 @@ OUString Tables::createStandardColumnPart(const Reference< XPropertySet >& xColP
aSql.append(dbtools::createStandardTypePart(xColProp, _xConnection));
// Add character set for BINARY (fix) type:
// BINARY is distinguished from other CHAR types by its character set.
// Octets is a special character set for binary data.
if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(
PROPERTY_ID_TYPE)) )
{
sal_Int32 aType = 0;
xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE))
>>= aType;
if(aType == DataType::BINARY)
{
aSql.append(" ");
aSql.append("CHARACTER SET OCTETS");
}
}
if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty())
{
......
......@@ -17,6 +17,8 @@ using namespace ::com::sun::star;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
using namespace firebird;
OUString firebird::sanitizeIdentifier(const OUString& rIdentifier)
{
OUString sRet = rIdentifier.trim();
......@@ -64,41 +66,76 @@ void firebird::evaluateStatusVector(const ISC_STATUS_ARRAY& rStatusVector,
}
}
sal_Int32 firebird::getColumnTypeFromFBType(short aType, short aSubType, short aScale)
sal_Int32 lcl_getNumberType( short aType, NumberSubType aSubType )
{
switch(aSubType)
{
case NumberSubType::Numeric:
return DataType::NUMERIC;
case NumberSubType::Decimal:
return DataType::DECIMAL;
default:
switch(aType)
{
case SQL_SHORT:
return DataType::SMALLINT;
case SQL_LONG:
return DataType::INTEGER;
case SQL_DOUBLE:
return DataType::DOUBLE;
case SQL_INT64:
return DataType::BIGINT;
default:
assert(false); // not a number
return 0;
}
}
}
sal_Int32 lcl_getCharColumnType( short aType, const OUString& sCharset )
{
switch(aType)
{
case SQL_TEXT:
if( sCharset == "OCTETS")
return DataType::BINARY;
else
return DataType::CHAR;
case SQL_VARYING: // TODO VARBINARY
return DataType::VARCHAR;
default:
assert(false);
return 0;
}
}
sal_Int32 firebird::ColumnTypeInfo::getSdbcType() const
{
aType &= ~1; // Remove last bit -- it is used to denote whether column
short aType = m_aType & ~1; // Remove last bit -- it is used to denote whether column
// can store Null, not needed for type determination
short aSubType = m_aSubType;
if( m_nScale > 0 )
{
// scale makes sense only for decimal and numeric types
assert(aType == SQL_SHORT || aType == SQL_LONG || aType == SQL_DOUBLE
|| aType == SQL_INT64);
// if scale is set without subtype then imply numeric
if(aSubType == 0 && aScale < 0)
aSubType = 1;
// if scale is set without subtype then imply numeric
if( static_cast<NumberSubType>(aSubType) == NumberSubType::Other )
aSubType = static_cast<short>(NumberSubType::Numeric);
}
switch (aType)
{
case SQL_TEXT:
return DataType::CHAR;
case SQL_VARYING:
return DataType::VARCHAR;
return lcl_getCharColumnType(aType, m_sCharsetName);
case SQL_SHORT:
if(aSubType == 1)
return DataType::NUMERIC;
if(aSubType == 2)
return DataType::DECIMAL;
return DataType::SMALLINT;
case SQL_LONG:
if(aSubType == 1)
return DataType::NUMERIC;
if(aSubType == 2)
return DataType::DECIMAL;
return DataType::INTEGER;
case SQL_DOUBLE:
case SQL_INT64:
return lcl_getNumberType(aType, static_cast<NumberSubType>(aSubType) );
case SQL_FLOAT:
return DataType::FLOAT;
case SQL_DOUBLE:
if(aSubType == 1)
return DataType::NUMERIC;
if(aSubType == 2)
return DataType::DECIMAL;
return DataType::DOUBLE;
case SQL_D_FLOAT:
return DataType::DOUBLE;
case SQL_TIMESTAMP:
......@@ -121,12 +158,6 @@ sal_Int32 firebird::getColumnTypeFromFBType(short aType, short aSubType, short a
return DataType::TIME;
case SQL_TYPE_DATE:
return DataType::DATE;
case SQL_INT64:
if(aSubType == 1)
return DataType::NUMERIC;
if(aSubType == 2)
return DataType::DECIMAL;
return DataType::BIGINT;
case SQL_NULL:
return DataType::SQLNULL;
case SQL_QUAD: // Is a "Blob ID" according to the docs
......@@ -139,68 +170,48 @@ sal_Int32 firebird::getColumnTypeFromFBType(short aType, short aSubType, short a
}
}
OUString firebird::getColumnTypeNameFromFBType(short aType, short aSubType, short aScale)
OUString firebird::ColumnTypeInfo::getColumnTypeName() const
{
aType &= ~1; // Remove last bit -- it is used to denote whether column
short aType = m_aType & ~1; // Remove last bit -- it is used to denote whether column
// can store Null, not needed for type determination
// if scale is set without subtype than imply numeric
if(aSubType == 0 && aScale < 0)
aSubType = 1;
switch (aType)
switch (aType)
{
case SQL_TEXT:
return OUString("SQL_TEXT");
case SQL_VARYING:
return OUString("SQL_VARYING");
case SQL_SHORT:
if(aSubType == 1)
return OUString("SQL_NUMERIC");
if(aSubType == 2)
return OUString("SQL_DECIMAL");
return OUString("SQL_SHORT");
case SQL_LONG:
if(aSubType == 1)
return OUString("SQL_NUMERIC");
if(aSubType == 2)
return OUString("SQL_DECIMAL");
return OUString("SQL_LONG");
case SQL_FLOAT:
return OUString("SQL_FLOAT");
case SQL_DOUBLE:
if(aSubType == 1)
return OUString("SQL_NUMERIC");
if(aSubType == 2)
return OUString("SQL_DECIMAL");
return OUString("SQL_DOUBLE");
case SQL_D_FLOAT:
return OUString("SQL_D_FLOAT");
case SQL_TIMESTAMP:
return OUString("SQL_TIMESTAMP");
case SQL_BLOB:
return OUString("SQL_BLOB");
case SQL_ARRAY:
return OUString("SQL_ARRAY");
case SQL_TYPE_TIME:
return OUString("SQL_TYPE_TIME");
case SQL_TYPE_DATE:
return OUString("SQL_TYPE_DATE");
case SQL_INT64:
if(aSubType == 1)
return OUString("SQL_NUMERIC");
if(aSubType == 2)
return OUString("SQL_DECIMAL");
return OUString("SQL_INT64");
case SQL_NULL:
return OUString("SQL_NULL");
case SQL_QUAD:
return OUString("SQL_QUAD");
case SQL_BOOLEAN:
return OUString("SQL_BOOLEAN");
default:
assert(false); // Should never happen
return OUString();
case SQL_TEXT:
return OUString("SQL_TEXT");
case SQL_VARYING:
return OUString("SQL_VARYING");
case SQL_SHORT:
return OUString("SQL_SHORT");
case SQL_LONG:
return OUString("SQL_LONG");
case SQL_FLOAT:
return OUString("SQL_FLOAT");
case SQL_DOUBLE:
return OUString("SQL_DOUBLE");
case SQL_D_FLOAT:
return OUString("SQL_D_FLOAT");
case SQL_TIMESTAMP:
return OUString("SQL_TIMESTAMP");
case SQL_BLOB:
return OUString("SQL_BLOB");
case SQL_ARRAY:
return OUString("SQL_ARRAY");
case SQL_TYPE_TIME:
return OUString("SQL_TYPE_TIME");
case SQL_TYPE_DATE:
return OUString("SQL_TYPE_DATE");
case SQL_INT64:
return OUString("SQL_INT64");
case SQL_NULL:
return OUString("SQL_NULL");
case SQL_QUAD:
return OUString("SQL_QUAD");
case SQL_BOOLEAN:
return OUString("SQL_BOOLEAN");
default:
assert(false); // Should never happen
return OUString();
}
}
......
......@@ -33,6 +33,42 @@ namespace connectivity
Clob = 1
};
// Numeric and decimal types can be identified by their subtype
// 1 for NUMERIC, 2 for DECIMAL
enum class NumberSubType {
Other = 0,
Numeric = 1,
Decimal = 2
};
class ColumnTypeInfo {
private:
short m_aType;
short m_aSubType;
short m_nScale;
OUString m_sCharsetName;
public:
explicit ColumnTypeInfo( short aType, short aSubType = 0,
short nScale = 0, const OUString& sCharset = OUString() )
: m_aType(aType)
, m_aSubType(aSubType)
, m_nScale(nScale)
, m_sCharsetName(sCharset) {}
explicit ColumnTypeInfo( short aType, const OUString& sCharset )
: m_aType(aType)
, m_aSubType(0)
, m_nScale(0)
, m_sCharsetName(sCharset) {}
short getType() const { return m_aType; }
short getSubType() const { return m_aSubType; }
short getScale() const { return m_nScale; }
OUString getCharacterSet() const { return m_sCharsetName; }
sal_Int32 getSdbcType() const;
::rtl::OUString getColumnTypeName() const;
};
/**
* Make sure an identifier is safe to use within the database. Currently
* firebird seems to return identifiers with 93 character (instead of
......@@ -62,9 +98,6 @@ namespace connectivity
const ::rtl::OUString& aCause,
const css::uno::Reference< css::uno::XInterface >& _rxContext);
sal_Int32 getColumnTypeFromFBType(short aType, short aSubType, short aScale);
::rtl::OUString getColumnTypeNameFromFBType(short aType, short aSubType, short aScale);
/**
* Internally (i.e. in RDB$FIELD_TYPE) firebird stores the data type
* for a column as defined in blr_*, however in the firebird
......
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