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() ...@@ -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) void OResultSetMetaData::verifyValidColumn(sal_Int32 column)
{ {
if (column>getColumnCount() || column < 1) if (column>getColumnCount() || column < 1)
...@@ -60,11 +93,20 @@ sal_Int32 SAL_CALL OResultSetMetaData::getColumnType(sal_Int32 column) ...@@ -60,11 +93,20 @@ sal_Int32 SAL_CALL OResultSetMetaData::getColumnType(sal_Int32 column)
{ {
verifyValidColumn(column); verifyValidColumn(column);
short aType = m_pSqlda->sqlvar[column-1].sqltype; short aType = m_pSqlda->sqlvar[column-1].sqltype & ~1;
short aSubType = m_pSqlda->sqlvar[column-1].sqlsubtype; OUString sCharset;
short aScale = m_pSqlda->sqlvar[column-1].sqlscale;
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) sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive(sal_Int32)
...@@ -109,11 +151,11 @@ OUString SAL_CALL OResultSetMetaData::getColumnTypeName(sal_Int32 column) ...@@ -109,11 +151,11 @@ OUString SAL_CALL OResultSetMetaData::getColumnTypeName(sal_Int32 column)
{ {
verifyValidColumn(column); verifyValidColumn(column);
short aType = m_pSqlda->sqlvar[column-1].sqltype; ColumnTypeInfo aInfo( m_pSqlda->sqlvar[column-1].sqltype,
short aSubType = m_pSqlda->sqlvar[column-1].sqlsubtype; m_pSqlda->sqlvar[column-1].sqlsubtype,
short aScale = m_pSqlda->sqlvar[column-1].sqlscale; m_pSqlda->sqlvar[column-1].sqlscale );
return getColumnTypeNameFromFBType(aType, aSubType, aScale); return aInfo.getColumnTypeName();
} }
OUString SAL_CALL OResultSetMetaData::getColumnLabel(sal_Int32 column) OUString SAL_CALL OResultSetMetaData::getColumnLabel(sal_Int32 column)
......
...@@ -45,6 +45,7 @@ namespace connectivity ...@@ -45,6 +45,7 @@ namespace connectivity
/// @throws css::sdbc::SQLException /// @throws css::sdbc::SQLException
void verifyValidColumn(sal_Int32 column); void verifyValidColumn(sal_Int32 column);
OUString getCharacterSet(sal_Int32 nIndex);
public: public:
// a constructor, which is required for returning objects: // a constructor, which is required for returning objects:
OResultSetMetaData(Connection* pConnection, OResultSetMetaData(Connection* pConnection,
......
...@@ -92,6 +92,21 @@ OUString Tables::createStandardColumnPart(const Reference< XPropertySet >& xColP ...@@ -92,6 +92,21 @@ OUString Tables::createStandardColumnPart(const Reference< XPropertySet >& xColP
aSql.append(dbtools::createStandardTypePart(xColProp, _xConnection)); 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()) if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty())
{ {
......
...@@ -17,6 +17,8 @@ using namespace ::com::sun::star; ...@@ -17,6 +17,8 @@ using namespace ::com::sun::star;
using namespace ::com::sun::star::sdbc; using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno; using namespace ::com::sun::star::uno;
using namespace firebird;
OUString firebird::sanitizeIdentifier(const OUString& rIdentifier) OUString firebird::sanitizeIdentifier(const OUString& rIdentifier)
{ {
OUString sRet = rIdentifier.trim(); OUString sRet = rIdentifier.trim();
...@@ -64,41 +66,76 @@ void firebird::evaluateStatusVector(const ISC_STATUS_ARRAY& rStatusVector, ...@@ -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 // 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 scale is set without subtype then imply numeric
if(aSubType == 0 && aScale < 0) if( static_cast<NumberSubType>(aSubType) == NumberSubType::Other )
aSubType = 1; aSubType = static_cast<short>(NumberSubType::Numeric);
}
switch (aType) switch (aType)
{ {
case SQL_TEXT: case SQL_TEXT:
return DataType::CHAR;
case SQL_VARYING: case SQL_VARYING:
return DataType::VARCHAR; return lcl_getCharColumnType(aType, m_sCharsetName);
case SQL_SHORT: case SQL_SHORT:
if(aSubType == 1)
return DataType::NUMERIC;
if(aSubType == 2)
return DataType::DECIMAL;
return DataType::SMALLINT;
case SQL_LONG: case SQL_LONG:
if(aSubType == 1) case SQL_DOUBLE:
return DataType::NUMERIC; case SQL_INT64:
if(aSubType == 2) return lcl_getNumberType(aType, static_cast<NumberSubType>(aSubType) );
return DataType::DECIMAL;
return DataType::INTEGER;
case SQL_FLOAT: case SQL_FLOAT:
return DataType::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: case SQL_D_FLOAT:
return DataType::DOUBLE; return DataType::DOUBLE;
case SQL_TIMESTAMP: case SQL_TIMESTAMP:
...@@ -121,12 +158,6 @@ sal_Int32 firebird::getColumnTypeFromFBType(short aType, short aSubType, short a ...@@ -121,12 +158,6 @@ sal_Int32 firebird::getColumnTypeFromFBType(short aType, short aSubType, short a
return DataType::TIME; return DataType::TIME;
case SQL_TYPE_DATE: case SQL_TYPE_DATE:
return DataType::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: case SQL_NULL:
return DataType::SQLNULL; return DataType::SQLNULL;
case SQL_QUAD: // Is a "Blob ID" according to the docs 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 ...@@ -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 // can store Null, not needed for type determination
// if scale is set without subtype than imply numeric switch (aType)
if(aSubType == 0 && aScale < 0)
aSubType = 1;
switch (aType)
{ {
case SQL_TEXT: case SQL_TEXT:
return OUString("SQL_TEXT"); return OUString("SQL_TEXT");
case SQL_VARYING: case SQL_VARYING:
return OUString("SQL_VARYING"); return OUString("SQL_VARYING");
case SQL_SHORT: case SQL_SHORT:
if(aSubType == 1) return OUString("SQL_SHORT");
return OUString("SQL_NUMERIC"); case SQL_LONG:
if(aSubType == 2) return OUString("SQL_LONG");
return OUString("SQL_DECIMAL"); case SQL_FLOAT:
return OUString("SQL_SHORT"); return OUString("SQL_FLOAT");
case SQL_LONG: case SQL_DOUBLE:
if(aSubType == 1) return OUString("SQL_DOUBLE");
return OUString("SQL_NUMERIC"); case SQL_D_FLOAT:
if(aSubType == 2) return OUString("SQL_D_FLOAT");
return OUString("SQL_DECIMAL"); case SQL_TIMESTAMP:
return OUString("SQL_LONG"); return OUString("SQL_TIMESTAMP");
case SQL_FLOAT: case SQL_BLOB:
return OUString("SQL_FLOAT"); return OUString("SQL_BLOB");
case SQL_DOUBLE: case SQL_ARRAY:
if(aSubType == 1) return OUString("SQL_ARRAY");
return OUString("SQL_NUMERIC"); case SQL_TYPE_TIME:
if(aSubType == 2) return OUString("SQL_TYPE_TIME");
return OUString("SQL_DECIMAL"); case SQL_TYPE_DATE:
return OUString("SQL_DOUBLE"); return OUString("SQL_TYPE_DATE");
case SQL_D_FLOAT: case SQL_INT64:
return OUString("SQL_D_FLOAT"); return OUString("SQL_INT64");
case SQL_TIMESTAMP: case SQL_NULL:
return OUString("SQL_TIMESTAMP"); return OUString("SQL_NULL");
case SQL_BLOB: case SQL_QUAD:
return OUString("SQL_BLOB"); return OUString("SQL_QUAD");
case SQL_ARRAY: case SQL_BOOLEAN:
return OUString("SQL_ARRAY"); return OUString("SQL_BOOLEAN");
case SQL_TYPE_TIME: default:
return OUString("SQL_TYPE_TIME"); assert(false); // Should never happen
case SQL_TYPE_DATE: return OUString();
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();
} }
} }
......
...@@ -33,6 +33,42 @@ namespace connectivity ...@@ -33,6 +33,42 @@ namespace connectivity
Clob = 1 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 * Make sure an identifier is safe to use within the database. Currently
* firebird seems to return identifiers with 93 character (instead of * firebird seems to return identifiers with 93 character (instead of
...@@ -62,9 +98,6 @@ namespace connectivity ...@@ -62,9 +98,6 @@ namespace connectivity
const ::rtl::OUString& aCause, const ::rtl::OUString& aCause,
const css::uno::Reference< css::uno::XInterface >& _rxContext); 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 * Internally (i.e. in RDB$FIELD_TYPE) firebird stores the data type
* for a column as defined in blr_*, however in the firebird * 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