Skip to content

DB2 dialect enhancements #2058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,10 @@ public void DialectShouldUseCorrectSubstringImplementation(System.Type dialectTy
case SybaseASE15Dialect _:
Assert.That(substringFunction, Is.TypeOf<EmulatedLengthSubstringFunction>());
break;
case DB2Dialect _:
Assert.That(substringFunction, Is.TypeOf<SQLFunctionTemplate>());
break;
case SybaseSQLAnywhere10Dialect _:
Assert.That(substringFunction, Is.TypeOf<VarArgsSQLFunction>());
break;
case DB2Dialect _:
case Oracle8iDialect _:
case SQLiteDialect _:
case HanaDialectBase _:
Expand All @@ -56,7 +54,6 @@ public void DialectShouldUseCorrectSubstringImplementation(System.Type dialectTy
Assert.That(substringFunction, Is.TypeOf<AnsiSubstringFunction>());
break;
}

}
}
}
23 changes: 20 additions & 3 deletions src/NHibernate/Dialect/DB2Dialect.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Data;
using System.Data.Common;
using System.Text;
using NHibernate.Cfg;
using NHibernate.Dialect.Function;
using NHibernate.Dialect.Schema;
using NHibernate.SqlCommand;

namespace NHibernate.Dialect
Expand Down Expand Up @@ -53,6 +55,7 @@ public DB2Dialect()
RegisterColumnType(DbType.String, 8000, "VARCHAR($l)");
RegisterColumnType(DbType.String, 2147483647, "CLOB");
RegisterColumnType(DbType.Time, "TIME");
RegisterColumnType(DbType.Guid, "CHAR(16) FOR BIT DATA");

RegisterFunction("abs", new StandardSQLFunction("abs"));
RegisterFunction("absval", new StandardSQLFunction("absval"));
Expand All @@ -61,7 +64,7 @@ public DB2Dialect()
RegisterFunction("ceiling", new StandardSQLFunction("ceiling"));
RegisterFunction("ceil", new StandardSQLFunction("ceil"));
RegisterFunction("floor", new StandardSQLFunction("floor"));
RegisterFunction("round", new StandardSQLFunction("round"));
RegisterFunction("round", new StandardSQLFunctionWithRequiredParameters("round", new object[] { null, "0" }));

RegisterFunction("acos", new StandardSQLFunction("acos", NHibernateUtil.Double));
RegisterFunction("asin", new StandardSQLFunction("asin", NHibernateUtil.Double));
Expand Down Expand Up @@ -127,8 +130,15 @@ public DB2Dialect()

RegisterFunction("mod", new StandardSQLFunction("mod", NHibernateUtil.Int32));

// DB2 does not support ANSI substring syntax.
RegisterFunction("substring", new SQLFunctionTemplate(NHibernateUtil.String, "substring(?1, ?2, ?3)"));
RegisterFunction("substring", new StandardSQLFunction("substr", NHibernateUtil.String));

// Bitwise operations
RegisterFunction("band", new Function.BitwiseFunctionOperation("bitand"));
RegisterFunction("bor", new Function.BitwiseFunctionOperation("bitor"));
RegisterFunction("bxor", new Function.BitwiseFunctionOperation("bitxor"));
RegisterFunction("bnot", new Function.BitwiseFunctionOperation("bitnot"));

RegisterFunction("current_timestamp", new NoArgSQLFunction("current_timestamp", NHibernateUtil.DateTime, false));

DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.DB2Driver";
}
Expand Down Expand Up @@ -193,6 +203,11 @@ public override string GetDropSequenceString(string sequenceName)
return string.Concat("drop sequence ", sequenceName, " restrict");
}

public override IDataBaseSchema GetDataBaseSchema(DbConnection connection)
{
return new DB2MetaData(connection);
}

/// <summary></summary>
public override bool SupportsSequences
{
Expand Down Expand Up @@ -278,6 +293,8 @@ public override string ForUpdateString
/// <inheritdoc />
public override int MaxAliasLength => 32;

public override long TimestampResolutionInTicks => 10L; // Microseconds.

#region Overridden informational metadata

public override bool SupportsEmptyInList => false;
Expand Down
118 changes: 118 additions & 0 deletions src/NHibernate/Dialect/Schema/DB2MetaData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;

namespace NHibernate.Dialect.Schema
{
public class DB2MetaData : AbstractDataBaseSchema
{
public DB2MetaData(DbConnection connection) : base(connection)
{
}

public override ITableMetadata GetTableMetadata(DataRow rs, bool extras)
{
return new DB2TableMetaData(rs, this, extras);
}

public override DataTable GetIndexColumns(string catalog, string schemaPattern, string tableName, string indexName)
{
var restrictions = new[] {tableName, indexName};
return Connection.GetSchema("Indexes", restrictions);
}

public override ISet<string> GetReservedWords()
{
var result = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var dtReservedWords = Connection.GetSchema(DbMetaDataCollectionNames.ReservedWords);
foreach (DataRow row in dtReservedWords.Rows)
{
result.Add(row["ReservedWord"].ToString());
}

if (IncludeDataTypesInReservedWords)
{
var dtTypes = Connection.GetSchema(DbMetaDataCollectionNames.DataTypes);
foreach (DataRow row in dtTypes.Rows)
{
result.Add(row["SQL_TYPE_NAME"].ToString());
}
}

return result;
}
}

public class DB2TableMetaData : AbstractTableMetadata
{
public DB2TableMetaData(DataRow rs, IDataBaseSchema meta, bool extras) : base(rs, meta, extras)
{
}

protected override IColumnMetadata GetColumnMetadata(DataRow rs)
{
return new DB2ColumnMetaData(rs);
}

protected override string GetColumnName(DataRow rs)
{
return Convert.ToString(rs["COLUMN_NAME"]);
}

protected override string GetConstraintName(DataRow rs)
{
return Convert.ToString(rs["FK_NAME"]);
}

protected override IForeignKeyMetadata GetForeignKeyMetadata(DataRow rs)
{
return new DB2ForeignKeyMetaData(rs);
}

protected override IIndexMetadata GetIndexMetadata(DataRow rs)
{
return new DB2IndexMetaData(rs);
}

protected override string GetIndexName(DataRow rs)
{
return Convert.ToString(rs["INDEX_NAME"]);
}

protected override void ParseTableInfo(DataRow rs)
{
Catalog = Convert.ToString(rs["TABLE_CATALOG"]);
Schema = Convert.ToString(rs["TABLE_SCHEMA"]);
if (string.IsNullOrEmpty(Catalog)) Catalog = null;
if (string.IsNullOrEmpty(Schema)) Schema = null;
Name = Convert.ToString(rs["TABLE_NAME"]);
}
}

public class DB2ColumnMetaData : AbstractColumnMetaData
{
public DB2ColumnMetaData(DataRow rs) : base(rs)
{
Name = Convert.ToString(rs["COLUMN_NAME"]);
Nullable = Convert.ToString(rs["IS_NULLABLE"]);
TypeName = Convert.ToString(rs["DATA_TYPE_NAME"]);
}
}

public class DB2IndexMetaData : AbstractIndexMetadata
{
public DB2IndexMetaData(DataRow rs) : base(rs)
{
Name = Convert.ToString(rs["INDEX_NAME"]);
}
}

public class DB2ForeignKeyMetaData : AbstractForeignKeyMetadata
{
public DB2ForeignKeyMetaData(DataRow rs) : base(rs)
{
Name = Convert.ToString(rs["FK_NAME"]);
}
}
}
20 changes: 20 additions & 0 deletions src/NHibernate/Driver/DB2DriverBase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using System;
using System.Data;
using System.Data.Common;
using NHibernate.Engine;
using NHibernate.SqlTypes;

namespace NHibernate.Driver
{
Expand Down Expand Up @@ -41,5 +45,21 @@ public override IResultSetsCommand GetResultSetsCommand(ISessionImplementor sess
{
return new BasicResultSetsCommand(session);
}

protected override void InitializeParameter(DbParameter dbParam, string name, SqlType sqlType)
{
switch (sqlType.DbType)
{
case DbType.Guid:
dbParam.DbType = DbType.Binary;
break;
case DbType.Byte:
dbParam.DbType = DbType.Int16;
break;
default:
dbParam.DbType = sqlType.DbType;
break;
}
}
}
}
5 changes: 3 additions & 2 deletions src/NHibernate/Type/ByteType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ public override System.Type PrimitiveClass

public override void Set(DbCommand cmd, object value, int index, ISessionImplementor session)
{
cmd.Parameters[index].Value = Convert.ToByte(value);
}
var dp = cmd.Parameters[index];
dp.Value = dp.DbType == DbType.Int16 ? Convert.ToInt16(value) : Convert.ToByte(value);
Copy link
Member Author

@hazzik hazzik Mar 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DB2 does not support byte native (as many other RDBMS) and has a problem handling it. If the value is of type byte the provider remaps the parameter as DbType.Binary. Then it throws InvalidCastException trying to cast the value to byte[].


}
public override string Name
{
get { return "Byte"; }
Expand Down