From 19a914eb5c5619b5b6423b8664e13087ca9b6dcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <fredericdelaporte@free.fr>
Date: Wed, 4 Oct 2017 18:16:38 +0200
Subject: [PATCH 1/3] NH-4088 - Fix TypeNames.Get for decimal capacity

 * Compare capacity against precision for precision based types
 * Fix dialects declarations for max precision
 * Fix dialects declarations for precision based types which were using length as precision or scale

It is a prerequisite to the fix of GetTypeCastName.
---
 src/NHibernate/Dialect/DB2Dialect.cs          |  3 +-
 src/NHibernate/Dialect/Dialect.cs             | 18 +-----
 src/NHibernate/Dialect/FirebirdDialect.cs     | 31 +---------
 src/NHibernate/Dialect/GenericDialect.cs      | 22 ++++---
 src/NHibernate/Dialect/InformixDialect.cs     |  9 ++-
 src/NHibernate/Dialect/IngresDialect.cs       |  5 +-
 src/NHibernate/Dialect/MsSql2000Dialect.cs    |  3 +-
 src/NHibernate/Dialect/MsSqlCeDialect.cs      |  3 +-
 src/NHibernate/Dialect/MySQL5Dialect.cs       |  7 ++-
 src/NHibernate/Dialect/Oracle8iDialect.cs     |  5 +-
 src/NHibernate/Dialect/OracleLiteDialect.cs   |  3 +-
 src/NHibernate/Dialect/PostgreSQLDialect.cs   |  3 +-
 src/NHibernate/Dialect/SybaseASA9Dialect.cs   |  5 +-
 .../Dialect/SybaseSQLAnywhere10Dialect.cs     |  3 +-
 src/NHibernate/Dialect/TypeNames.cs           | 58 ++++++++++++++-----
 15 files changed, 94 insertions(+), 84 deletions(-)

diff --git a/src/NHibernate/Dialect/DB2Dialect.cs b/src/NHibernate/Dialect/DB2Dialect.cs
index 26d658f9140..fc578dcc6df 100644
--- a/src/NHibernate/Dialect/DB2Dialect.cs
+++ b/src/NHibernate/Dialect/DB2Dialect.cs
@@ -39,7 +39,8 @@ public DB2Dialect()
 			RegisterColumnType(DbType.Date, "DATE");
 			RegisterColumnType(DbType.DateTime, "TIMESTAMP");
 			RegisterColumnType(DbType.Decimal, "DECIMAL(19,5)");
-			RegisterColumnType(DbType.Decimal, 19, "DECIMAL(19, $l)");
+			// DB2 max precision is 31, but .Net is 28-29 anyway.
+			RegisterColumnType(DbType.Decimal, 28, "DECIMAL($p, $s)");
 			RegisterColumnType(DbType.Double, "DOUBLE");
 			RegisterColumnType(DbType.Int16, "SMALLINT");
 			RegisterColumnType(DbType.Int32, "INTEGER");
diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs
index a85a330b4c9..ff97bce3220 100644
--- a/src/NHibernate/Dialect/Dialect.cs
+++ b/src/NHibernate/Dialect/Dialect.cs
@@ -215,17 +215,10 @@ public virtual string GetTypeName(SqlType sqlType)
 		{
 			if (sqlType.LengthDefined || sqlType.PrecisionDefined || sqlType.ScaleDefined)
 			{
-				string resultWithLength = _typeNames.Get(sqlType.DbType, sqlType.Length, sqlType.Precision, sqlType.Scale);
-				if (resultWithLength != null) return resultWithLength;
+				return _typeNames.Get(sqlType.DbType, sqlType.Length, sqlType.Precision, sqlType.Scale);
 			}
 
-			string result = _typeNames.Get(sqlType.DbType);
-			if (result == null)
-			{
-				throw new HibernateException(string.Format("No default type mapping for SqlType {0}", sqlType));
-			}
-
-			return result;
+			return _typeNames.Get(sqlType.DbType);
 		}
 
 		/// <summary>
@@ -239,12 +232,7 @@ public virtual string GetTypeName(SqlType sqlType)
 		/// <returns>The database type name used by ddl.</returns>
 		public virtual string GetTypeName(SqlType sqlType, int length, int precision, int scale)
 		{
-			string result = _typeNames.Get(sqlType.DbType, length, precision, scale);
-			if (result == null)
-			{
-				throw new HibernateException(string.Format("No type mapping for SqlType {0} of length {1}", sqlType, length));
-			}
-			return result;
+			return _typeNames.Get(sqlType.DbType, length, precision, scale);
 		}
 
 		/// <summary>
diff --git a/src/NHibernate/Dialect/FirebirdDialect.cs b/src/NHibernate/Dialect/FirebirdDialect.cs
index 5cd56a4d08a..564ba59ddab 100644
--- a/src/NHibernate/Dialect/FirebirdDialect.cs
+++ b/src/NHibernate/Dialect/FirebirdDialect.cs
@@ -6,7 +6,6 @@
 using NHibernate.Dialect.Schema;
 using NHibernate.Engine;
 using NHibernate.SqlCommand;
-using NHibernate.SqlTypes;
 using NHibernate.Type;
 using Environment = NHibernate.Cfg.Environment;
 
@@ -30,12 +29,6 @@ namespace NHibernate.Dialect
 	/// </remarks>
 	public class FirebirdDialect : Dialect
 	{
-		#region Fields
-
-		private const int MAX_DECIMAL_PRECISION = 18;
-
-		#endregion
-
 		public FirebirdDialect()
 		{
 			RegisterKeywords();
@@ -49,23 +42,6 @@ public override string AddColumnString
 			get { return "add"; }
 		}
 
-		public override string GetTypeName(SqlType sqlType)
-		{
-			if (IsUnallowedDecimal(sqlType.DbType, sqlType.Precision))
-				return base.GetTypeName(new SqlType(sqlType.DbType, MAX_DECIMAL_PRECISION, sqlType.Scale));
-
-			return base.GetTypeName(sqlType);
-		}
-
-		public override string GetTypeName(SqlType sqlType, int length, int precision, int scale)
-		{
-			var fbDecimalPrecision = precision;
-			if (IsUnallowedDecimal(sqlType.DbType, precision))
-				fbDecimalPrecision = MAX_DECIMAL_PRECISION;
-
-			return base.GetTypeName(sqlType, length, fbDecimalPrecision, scale);
-		}
-
 		public override string GetSelectSequenceNextValString(string sequenceName)
 		{
 			return string.Format("gen_id({0}, 1 )", sequenceName);
@@ -544,11 +520,6 @@ private void RegisterTrigonometricFunctions()
 			RegisterFunction("tanh", new StandardSQLFunction("tanh", NHibernateUtil.Double));
 		}
 
-		private static bool IsUnallowedDecimal(DbType dbType, int precision)
-		{
-			return dbType == DbType.Decimal && precision > MAX_DECIMAL_PRECISION;
-		}
-
 		#region Informational metadata
 
 		/// <summary>
@@ -562,4 +533,4 @@ private static bool IsUnallowedDecimal(DbType dbType, int precision)
 
 		#endregion
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Dialect/GenericDialect.cs b/src/NHibernate/Dialect/GenericDialect.cs
index 545981b6249..a55c638d55e 100644
--- a/src/NHibernate/Dialect/GenericDialect.cs
+++ b/src/NHibernate/Dialect/GenericDialect.cs
@@ -9,25 +9,31 @@ namespace NHibernate.Dialect
 	public class GenericDialect : Dialect
 	{
 		/// <summary></summary>
-		public GenericDialect() : base()
+		public GenericDialect()
 		{
-			RegisterColumnType(DbType.AnsiStringFixedLength, "CHAR($l)");
-			RegisterColumnType(DbType.AnsiString, "VARCHAR($l)");
-			RegisterColumnType(DbType.Binary, "VARBINARY($l)");
+			RegisterColumnType(DbType.AnsiStringFixedLength, "CHAR(255)");
+			RegisterColumnType(DbType.AnsiStringFixedLength, 8000, "CHAR($l)");
+			RegisterColumnType(DbType.AnsiString, "VARCHAR(255)");
+			RegisterColumnType(DbType.AnsiString, 8000, "VARCHAR($l)");
+			RegisterColumnType(DbType.Binary, "VARBINARY(255)");
+			RegisterColumnType(DbType.Binary, 8000, "VARBINARY($l)");
 			RegisterColumnType(DbType.Boolean, "BIT");
 			RegisterColumnType(DbType.Byte, "TINYINT");
 			RegisterColumnType(DbType.Currency, "MONEY");
 			RegisterColumnType(DbType.Date, "DATE");
 			RegisterColumnType(DbType.DateTime, "DATETIME");
-			RegisterColumnType(DbType.Decimal, "DECIMAL(19, $l)");
+			RegisterColumnType(DbType.Decimal, "DECIMAL(19, 5)");
+			RegisterColumnType(DbType.Decimal, 19, "DECIMAL($p, $s)");
 			RegisterColumnType(DbType.Double, "DOUBLE PRECISION");
 			RegisterColumnType(DbType.Guid, "UNIQUEIDENTIFIER");
 			RegisterColumnType(DbType.Int16, "SMALLINT");
 			RegisterColumnType(DbType.Int32, "INT");
 			RegisterColumnType(DbType.Int64, "BIGINT");
 			RegisterColumnType(DbType.Single, "REAL");
-			RegisterColumnType(DbType.StringFixedLength, "NCHAR($l)");
-			RegisterColumnType(DbType.String, "NVARCHAR($l)");
+			RegisterColumnType(DbType.StringFixedLength, "NCHAR(255)");
+			RegisterColumnType(DbType.StringFixedLength, 4000, "NCHAR($l)");
+			RegisterColumnType(DbType.String, "NVARCHAR(255)");
+			RegisterColumnType(DbType.String, 4000, "NVARCHAR($l)");
 			RegisterColumnType(DbType.Time, "TIME");
 		}
 
@@ -37,4 +43,4 @@ public override string AddColumnString
 			get { return "add column"; }
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Dialect/InformixDialect.cs b/src/NHibernate/Dialect/InformixDialect.cs
index 4ab647b632b..94b174b5e64 100644
--- a/src/NHibernate/Dialect/InformixDialect.cs
+++ b/src/NHibernate/Dialect/InformixDialect.cs
@@ -36,7 +36,8 @@ public partial class InformixDialect : Dialect
 		/// <summary></summary>
 		public InformixDialect()
 		{
-			RegisterColumnType(DbType.AnsiStringFixedLength, "CHAR($l)");
+			RegisterColumnType(DbType.AnsiStringFixedLength, "CHAR(255)");
+			RegisterColumnType(DbType.AnsiStringFixedLength, 255, "CHAR($l)");
 			RegisterColumnType(DbType.AnsiString, 255, "VARCHAR($l)");
 			RegisterColumnType(DbType.AnsiString, 32739, "LVARCHAR($l)");
 			RegisterColumnType(DbType.AnsiString, 2147483647, "TEXT");
@@ -49,14 +50,16 @@ public InformixDialect()
 			RegisterColumnType(DbType.Date, "DATE");
 			RegisterColumnType(DbType.DateTime, "datetime year to fraction(5)");
 			RegisterColumnType(DbType.Decimal, "DECIMAL(19, 5)");
-			RegisterColumnType(DbType.Decimal, 19, "DECIMAL($p, $s)");
+			// Informix max precision is 32, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 28, "DECIMAL($p, $s)");
 			RegisterColumnType(DbType.Double, "DOUBLE");
 			RegisterColumnType(DbType.Int16, "SMALLINT");
 			RegisterColumnType(DbType.Int32, "INTEGER");
 			RegisterColumnType(DbType.Int64, "BIGINT");
 			RegisterColumnType(DbType.Single, "SmallFloat");
 			RegisterColumnType(DbType.Time, "datetime hour to second");
-			RegisterColumnType(DbType.StringFixedLength, "CHAR($l)");
+			RegisterColumnType(DbType.StringFixedLength, "CHAR(255)");
+			RegisterColumnType(DbType.StringFixedLength, 255, "CHAR($l)");
 			RegisterColumnType(DbType.String, 255, "VARCHAR($l)");
 			RegisterColumnType(DbType.String, 32739, "LVARCHAR($l)");
 			RegisterColumnType(DbType.String, 2147483647, "TEXT");
diff --git a/src/NHibernate/Dialect/IngresDialect.cs b/src/NHibernate/Dialect/IngresDialect.cs
index 616d7cde29b..06395686aba 100644
--- a/src/NHibernate/Dialect/IngresDialect.cs
+++ b/src/NHibernate/Dialect/IngresDialect.cs
@@ -36,7 +36,8 @@ public IngresDialect()
 			RegisterColumnType(DbType.Date, "date");
 			RegisterColumnType(DbType.DateTime, "timestamp");
 			RegisterColumnType(DbType.Decimal, "decimal(19,5)");
-			RegisterColumnType(DbType.Decimal, 19, "decimal(18, $l)");
+			// Ingres max precision is 31, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 28, "decimal($p, $s)");
 			RegisterColumnType(DbType.Double, "float8");
 			RegisterColumnType(DbType.Int16, "int2");
 			RegisterColumnType(DbType.Int32, "int4");
@@ -64,4 +65,4 @@ public IngresDialect()
 
 		#endregion
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Dialect/MsSql2000Dialect.cs b/src/NHibernate/Dialect/MsSql2000Dialect.cs
index 0e86557107b..1418beccbda 100644
--- a/src/NHibernate/Dialect/MsSql2000Dialect.cs
+++ b/src/NHibernate/Dialect/MsSql2000Dialect.cs
@@ -373,7 +373,8 @@ protected virtual void RegisterNumericTypeMappings()
 			RegisterColumnType(DbType.Byte, "TINYINT");
 			RegisterColumnType(DbType.Currency, "MONEY");
 			RegisterColumnType(DbType.Decimal, "DECIMAL(19,5)");
-			RegisterColumnType(DbType.Decimal, 19, "DECIMAL($p, $s)");
+			// SQL Server max precision is 38, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 28, "DECIMAL($p, $s)");
 			RegisterColumnType(DbType.Double, "FLOAT(53)");
 			RegisterColumnType(DbType.Int16, "SMALLINT");
 			RegisterColumnType(DbType.Int32, "INT");
diff --git a/src/NHibernate/Dialect/MsSqlCeDialect.cs b/src/NHibernate/Dialect/MsSqlCeDialect.cs
index 4b39f2b87de..257c71186a1 100644
--- a/src/NHibernate/Dialect/MsSqlCeDialect.cs
+++ b/src/NHibernate/Dialect/MsSqlCeDialect.cs
@@ -150,7 +150,8 @@ protected virtual void RegisterTypeMapping()
 			RegisterColumnType(DbType.Date, "DATETIME");
 			RegisterColumnType(DbType.DateTime, "DATETIME");
 			RegisterColumnType(DbType.Decimal, "NUMERIC(19,5)");
-			RegisterColumnType(DbType.Decimal, 19, "NUMERIC($p, $s)");
+			// SQL Server CE max precision is 38, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 28, "NUMERIC($p, $s)");
 			RegisterColumnType(DbType.Double, "FLOAT");
 			RegisterColumnType(DbType.Guid, "UNIQUEIDENTIFIER");
 			RegisterColumnType(DbType.Int16, "SMALLINT");
diff --git a/src/NHibernate/Dialect/MySQL5Dialect.cs b/src/NHibernate/Dialect/MySQL5Dialect.cs
index aa76e16b3ac..0a7c73b0018 100644
--- a/src/NHibernate/Dialect/MySQL5Dialect.cs
+++ b/src/NHibernate/Dialect/MySQL5Dialect.cs
@@ -8,7 +8,8 @@ public class MySQL5Dialect : MySQLDialect
 		public MySQL5Dialect()
 		{
 			RegisterColumnType(DbType.Decimal, "DECIMAL(19,5)");
-			RegisterColumnType(DbType.Decimal, 19, "DECIMAL($p, $s)");
+			// My SQL supports precision up to 65, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 28, "DECIMAL($p, $s)");
 			RegisterColumnType(DbType.Guid, "BINARY(16)");
 		}
 
@@ -17,7 +18,7 @@ protected override void RegisterCastTypes() {
 			// MySql 5 also supports DECIMAL as a cast type target
 			// http://dev.mysql.com/doc/refman/5.0/en/cast-functions.html
 			RegisterCastType(DbType.Decimal, "DECIMAL(19,5)");
-			RegisterCastType(DbType.Decimal, 19, "DECIMAL($p, $s)");
+			RegisterCastType(DbType.Decimal, 28, "DECIMAL($p, $s)");
 			RegisterCastType(DbType.Double, "DECIMAL(19,5)");
 			RegisterCastType(DbType.Single, "DECIMAL(19,5)");
 			RegisterCastType(DbType.Guid, "BINARY(16)");
@@ -61,4 +62,4 @@ public override bool SupportsInsertSelectIdentity
 			get { return true; }
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Dialect/Oracle8iDialect.cs b/src/NHibernate/Dialect/Oracle8iDialect.cs
index fb20ded0cf4..0c1cff94616 100644
--- a/src/NHibernate/Dialect/Oracle8iDialect.cs
+++ b/src/NHibernate/Dialect/Oracle8iDialect.cs
@@ -185,9 +185,10 @@ protected virtual void RegisterNumericTypeMappings()
 			RegisterColumnType(DbType.Currency, "NUMBER(20,2)");
 			RegisterColumnType(DbType.Single, "FLOAT(24)");
 			RegisterColumnType(DbType.Double, "DOUBLE PRECISION");
-			RegisterColumnType(DbType.Double, 19, "NUMBER($p,$s)");
+			// Oracle max precision is 39-40, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Double, 28, "NUMBER($p,$s)");
 			RegisterColumnType(DbType.Decimal, "NUMBER(19,5)");
-			RegisterColumnType(DbType.Decimal, 19, "NUMBER($p,$s)");
+			RegisterColumnType(DbType.Decimal, 28, "NUMBER($p,$s)");
 		}
 
 		protected virtual void RegisterDateTimeTypeMappings()
diff --git a/src/NHibernate/Dialect/OracleLiteDialect.cs b/src/NHibernate/Dialect/OracleLiteDialect.cs
index 707736fb166..d0945678fe9 100644
--- a/src/NHibernate/Dialect/OracleLiteDialect.cs
+++ b/src/NHibernate/Dialect/OracleLiteDialect.cs
@@ -43,7 +43,8 @@ public OracleLiteDialect()
 			RegisterColumnType(DbType.Date, "DATE");
 			RegisterColumnType(DbType.DateTime, "TIMESTAMP(4)");
 			RegisterColumnType(DbType.Decimal, "NUMBER(19,5)");
-			RegisterColumnType(DbType.Decimal, 19, "NUMBER(19, $l)");
+			// Oracle max precision is 39-40, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 28, "NUMBER($p, $s)");
 			// having problems with both ODP and OracleClient from MS not being able
 			// to read values out of a field that is DOUBLE PRECISION
 			RegisterColumnType(DbType.Double, "DOUBLE PRECISION"); //"FLOAT(53)" );
diff --git a/src/NHibernate/Dialect/PostgreSQLDialect.cs b/src/NHibernate/Dialect/PostgreSQLDialect.cs
index f45ff7040de..cb1f5086ab2 100644
--- a/src/NHibernate/Dialect/PostgreSQLDialect.cs
+++ b/src/NHibernate/Dialect/PostgreSQLDialect.cs
@@ -42,7 +42,8 @@ public PostgreSQLDialect()
 			RegisterColumnType(DbType.Byte, "int2");
 			RegisterColumnType(DbType.Currency, "decimal(16,4)");
 			RegisterColumnType(DbType.Decimal, "decimal(19,5)");
-			RegisterColumnType(DbType.Decimal, 19, "decimal($p, $s)");
+			// PostgreSQL max precision is unlimited, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 28, "decimal($p, $s)");
 			RegisterColumnType(DbType.Double, "float8");
 			RegisterColumnType(DbType.Int16, "int2");
 			RegisterColumnType(DbType.Int32, "int4");
diff --git a/src/NHibernate/Dialect/SybaseASA9Dialect.cs b/src/NHibernate/Dialect/SybaseASA9Dialect.cs
index 94745a76329..699a5430433 100644
--- a/src/NHibernate/Dialect/SybaseASA9Dialect.cs
+++ b/src/NHibernate/Dialect/SybaseASA9Dialect.cs
@@ -51,7 +51,8 @@ public SybaseASA9Dialect()
 			RegisterColumnType(DbType.Date, "DATE");
 			RegisterColumnType(DbType.DateTime, "TIMESTAMP");
 			RegisterColumnType(DbType.Decimal, "DECIMAL(18,5)"); // NUMERIC(18,5) is equivalent to DECIMAL(18,5)
-			RegisterColumnType(DbType.Decimal, 18, "DECIMAL(18,$l)");
+			// Sybase max precision is 38, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 28, "DECIMAL($p,$s)");
 			RegisterColumnType(DbType.Double, "DOUBLE");
 			RegisterColumnType(DbType.Guid, "CHAR(16)");
 			RegisterColumnType(DbType.Int16, "SMALLINT");
@@ -185,4 +186,4 @@ private static int GetAfterSelectInsertPoint(SqlString sql)
 			return 0;
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs b/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs
index a45cfda6775..7b40051ef65 100644
--- a/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs
+++ b/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs
@@ -91,7 +91,8 @@ protected virtual void RegisterNumericTypeMappings()
 			RegisterColumnType(DbType.Single, "REAL");
 			RegisterColumnType(DbType.Double, "DOUBLE");
 			RegisterColumnType(DbType.Decimal, "NUMERIC(19,5)"); // Precision ranges from 0-127
-			RegisterColumnType(DbType.Decimal, 19, "NUMERIC($p, $s)"); // Precision ranges from 0-127
+			// Anywhere max precision is 127, but .Net is limited to 28-29.
+			RegisterColumnType(DbType.Decimal, 38, "NUMERIC($p, $s)"); // Precision ranges from 0-127
 		}
 
 		protected virtual void RegisterDateTimeTypeMappings()
diff --git a/src/NHibernate/Dialect/TypeNames.cs b/src/NHibernate/Dialect/TypeNames.cs
index 4ceb8a49a88..7c25a211461 100644
--- a/src/NHibernate/Dialect/TypeNames.cs
+++ b/src/NHibernate/Dialect/TypeNames.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Data;
+using System.Linq;
 using NHibernate.Util;
 
 namespace NHibernate.Dialect
@@ -56,10 +57,9 @@ public class TypeNames
 		/// <returns>the default type name associated with the specified key</returns>
 		public string Get(DbType typecode)
 		{
-			string result;
-			if (!defaults.TryGetValue(typecode, out result))
+			if (!defaults.TryGetValue(typecode, out var result))
 			{
-				throw new ArgumentException("Dialect does not support DbType." + typecode, "typecode");
+				throw new ArgumentException("Dialect does not support DbType." + typecode, nameof(typecode));
 			}
 			return result;
 		}
@@ -72,15 +72,18 @@ public string Get(DbType typecode)
 		/// <param name="scale">the SQL scale </param>
 		/// <param name="precision">the SQL precision </param>
 		/// <returns>
-		/// The associated name with smallest capacity >= size (or scale for date time types) if available,
-		/// otherwise the default type name.
+		/// The associated name with smallest capacity >= size (or precision for decimal, or scale for date time types)
+		/// if available, otherwise the default type name.
 		/// </returns>
 		public string Get(DbType typecode, int size, int precision, int scale)
 		{
 			weighted.TryGetValue(typecode, out var map);
 			if (map != null && map.Count > 0)
 			{
-				var requiredCapacity = IsScaleType(typecode) ? scale : size;
+				var isPrecisionType = IsPrecisionType(typecode);
+				var requiredCapacity = isPrecisionType
+					? precision
+					: IsScaleType(typecode) ? scale : size;
 				foreach (var entry in map)
 				{
 					if (requiredCapacity <= entry.Key)
@@ -88,14 +91,27 @@ public string Get(DbType typecode, int size, int precision, int scale)
 						return Replace(entry.Value, size, precision, scale);
 					}
 				}
+				if (isPrecisionType && precision != 0)
+				{
+					// The default is usually not the max for precision type, fallback to last entry instead.
+					var maxEntry = map.Last();
+					var adjustedPrecision = maxEntry.Key;
+					// Reduce the scale (most databases restrict scale to be less or equal to precision)
+					// For a proportionnal reduction, we could use
+					// Math.Min((int) Math.Round(scale * adjustedPrecision / (double) precision), adjustedPrecision);
+					// But if the type is used for storing amounts, this may cause losing the ability to store cents...
+					// So better just reduce as few as possible.
+					var adjustedScale = Math.Min(scale, adjustedPrecision);
+					return Replace(maxEntry.Value, size, adjustedPrecision, adjustedScale);
+				}
 			}
 			//Could not find a specific type for the capacity, using the default
-			return Replace(Get(typecode), size, precision, scale);
+			return Get(typecode);
 		}
 
 		/// <summary>
-		/// For types with a simple length (or scale for date time types), this method returns the definition
-		/// for the longest registered type.
+		/// For types with a simple length (or precision for decimal, or scale for date time types), this method
+		/// returns the definition for the longest registered type.
 		/// </summary>
 		/// <param name="typecode"></param>
 		/// <returns></returns>
@@ -104,18 +120,32 @@ public string GetLongest(DbType typecode)
 			weighted.TryGetValue(typecode, out var map);
 			if (map != null && map.Count > 0)
 			{
+				var isPrecisionType = IsPrecisionType(typecode);
 				var isScaleType = IsScaleType(typecode);
+				var isSizeType = !isPrecisionType && !isScaleType;
 				var capacity = map.Keys[map.Count - 1];
 				return Replace(
 					map.Values[map.Count - 1],
-					isScaleType ? 0 : capacity,
-					0,
+					isSizeType ? capacity : 0,
+					isPrecisionType ? capacity : 0,
 					isScaleType ? capacity : 0);
 			}
 
 			return Get(typecode);
 		}
 
+		private static bool IsPrecisionType(DbType typecode)
+		{
+			switch (typecode)
+			{
+				case DbType.Decimal:
+				// Oracle dialect defines precision and scale for double, because it uses number instead of binary_double.
+				case DbType.Double:
+					return true;
+			}
+			return false;
+		}
+
 		private static bool IsScaleType(DbType typecode)
 		{
 			switch (typecode)
@@ -140,10 +170,12 @@ private static string Replace(string type, int size, int precision, int scale)
 		/// Set a type name for specified type key and capacity
 		/// </summary>
 		/// <param name="typecode">the type key</param>
-		/// <param name="capacity">the (maximum) type size/length or scale</param>
+		/// <param name="capacity">the (maximum) type size/length, precision or scale</param>
 		/// <param name="value">The associated name</param>
 		public void Put(DbType typecode, int capacity, string value)
 		{
+			if (value == null)
+				throw new ArgumentNullException(nameof(value));
 			SortedList<int, string> map;
 			if (!weighted.TryGetValue(typecode, out map))
 			{
@@ -160,7 +192,7 @@ public void Put(DbType typecode, int capacity, string value)
 		/// <param name="value"></param>
 		public void Put(DbType typecode, string value)
 		{
-			defaults[typecode] = value;
+			defaults[typecode] = value ?? throw new ArgumentNullException(nameof(value));
 		}
 	}
 }

From bbd7772cd257b09bfdfb75cb897a7b33e42c0b89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <fredericdelaporte@free.fr>
Date: Wed, 4 Oct 2017 15:55:32 +0200
Subject: [PATCH 2/3] NH-4088 - Fix GetCastTypeName

 * Use type length/precision/scale when defined
 * Use maximal capacity types otherwise when it makes sens
 * Use configurable default length/precision/scale otherwise
---
 .../Async/Criteria/ProjectionsTest.cs         | 55 +++++++++++++++++++
 .../Async/DialectTest/DialectFixture.cs       |  3 +-
 .../Criteria/ProjectionsTest.cs               | 55 +++++++++++++++++++
 .../DialectTest/DialectFixture.cs             | 26 ++++++++-
 .../DriverTest/FirebirdClientDriverFixture.cs |  6 +-
 src/NHibernate/Cfg/Environment.cs             | 18 ++++++
 src/NHibernate/Dialect/Dialect.cs             | 40 +++++++++++++-
 src/NHibernate/Dialect/MySQLDialect.cs        | 16 ++----
 src/NHibernate/Driver/FirebirdClientDriver.cs | 38 +++++++++----
 src/NHibernate/nhibernate-configuration.xsd   | 24 ++++++++
 10 files changed, 250 insertions(+), 31 deletions(-)

diff --git a/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs b/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs
index 71623f356a0..7b00a0b4bd3 100644
--- a/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs
+++ b/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs
@@ -8,10 +8,12 @@
 //------------------------------------------------------------------------------
 
 
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using NHibernate.Criterion;
 using NHibernate.Dialect;
+using NHibernate.Type;
 using NUnit.Framework;
 
 namespace NHibernate.Test.Criteria
@@ -100,6 +102,59 @@ public async Task UsingSqlFunctions_Concat_WithCastAsync()
 			}
 		}
 
+		[Test]
+		public async Task CastWithLengthAsync()
+		{
+			using (var s = OpenSession())
+			{
+				try
+				{
+					var shortName = await (s
+						.CreateCriteria<Student>()
+						.SetProjection(
+							Projections.Cast(
+								TypeFactory.GetStringType(3),
+								Projections.Property("Name")))
+						.UniqueResultAsync<string>());
+					Assert.That(shortName, Is.EqualTo("aye"));
+				}
+				catch (Exception e)
+				{
+					if (!e.Message.Contains("truncation") && 
+						(e.InnerException == null || !e.InnerException.Message.Contains("truncation")))
+						throw;
+				}
+			}
+		}
+
+		[Test]
+		public async Task CastWithPrecisionScaleAsync()
+		{
+			if (TestDialect.HasBrokenDecimalType)
+				Assert.Ignore("Dialect does not correctly handle decimal.");
+
+			using (var s = OpenSession())
+			{
+				var value = await (s
+					.CreateCriteria<Student>()
+					.SetProjection(
+						Projections.Cast(
+							TypeFactory.Basic("decimal(18,9)"),
+							Projections.Constant(123456789.123456789m, TypeFactory.Basic("decimal(18,9)"))))
+					.UniqueResultAsync<decimal>());
+				Assert.That(value, Is.EqualTo(123456789.123456789m), "Same type cast");
+
+				value = await (s
+					.CreateCriteria<Student>()
+					.SetProjection(
+						Projections.Cast(
+							TypeFactory.Basic("decimal(18,7)"),
+							Projections.Constant(123456789.987654321m, TypeFactory.Basic("decimal(18,9)"))))
+					.UniqueResultAsync<decimal>());
+				Assert.That(value, Is.EqualTo(123456789.9876543m), "Reduced scale cast");
+			}
+		}
+
 		[Test]
 		public async Task CanUseParametersWithProjectionsAsync()
 		{
diff --git a/src/NHibernate.Test/Async/DialectTest/DialectFixture.cs b/src/NHibernate.Test/Async/DialectTest/DialectFixture.cs
index fed9479e9fd..e822ea63452 100644
--- a/src/NHibernate.Test/Async/DialectTest/DialectFixture.cs
+++ b/src/NHibernate.Test/Async/DialectTest/DialectFixture.cs
@@ -11,7 +11,6 @@
 using System;
 using System.Collections.Generic;
 using System.Data;
-using System.Data.Common;
 using NHibernate.Dialect;
 using NHibernate.Driver;
 using NHibernate.Engine;
@@ -79,4 +78,4 @@ public async Task CurrentTimestampSelectionAsync()
 			}
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate.Test/Criteria/ProjectionsTest.cs b/src/NHibernate.Test/Criteria/ProjectionsTest.cs
index eb553ce00c2..165c12715cb 100644
--- a/src/NHibernate.Test/Criteria/ProjectionsTest.cs
+++ b/src/NHibernate.Test/Criteria/ProjectionsTest.cs
@@ -1,7 +1,9 @@
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using NHibernate.Criterion;
 using NHibernate.Dialect;
+using NHibernate.Type;
 using NUnit.Framework;
 
 namespace NHibernate.Test.Criteria
@@ -89,6 +91,59 @@ public void UsingSqlFunctions_Concat_WithCast()
 			}
 		}
 
+		[Test]
+		public void CastWithLength()
+		{
+			using (var s = OpenSession())
+			{
+				try
+				{
+					var shortName = s
+						.CreateCriteria<Student>()
+						.SetProjection(
+							Projections.Cast(
+								TypeFactory.GetStringType(3),
+								Projections.Property("Name")))
+						.UniqueResult<string>();
+					Assert.That(shortName, Is.EqualTo("aye"));
+				}
+				catch (Exception e)
+				{
+					if (!e.Message.Contains("truncation") && 
+						(e.InnerException == null || !e.InnerException.Message.Contains("truncation")))
+						throw;
+				}
+			}
+		}
+
+		[Test]
+		public void CastWithPrecisionScale()
+		{
+			if (TestDialect.HasBrokenDecimalType)
+				Assert.Ignore("Dialect does not correctly handle decimal.");
+
+			using (var s = OpenSession())
+			{
+				var value = s
+					.CreateCriteria<Student>()
+					.SetProjection(
+						Projections.Cast(
+							TypeFactory.Basic("decimal(18,9)"),
+							Projections.Constant(123456789.123456789m, TypeFactory.Basic("decimal(18,9)"))))
+					.UniqueResult<decimal>();
+				Assert.That(value, Is.EqualTo(123456789.123456789m), "Same type cast");
+
+				value = s
+					.CreateCriteria<Student>()
+					.SetProjection(
+						Projections.Cast(
+							TypeFactory.Basic("decimal(18,7)"),
+							Projections.Constant(123456789.987654321m, TypeFactory.Basic("decimal(18,9)"))))
+					.UniqueResult<decimal>();
+				Assert.That(value, Is.EqualTo(123456789.9876543m), "Reduced scale cast");
+			}
+		}
+
 		[Test]
 		public void CanUseParametersWithProjections()
 		{
diff --git a/src/NHibernate.Test/DialectTest/DialectFixture.cs b/src/NHibernate.Test/DialectTest/DialectFixture.cs
index 50a9aa12661..1895f9be13b 100644
--- a/src/NHibernate.Test/DialectTest/DialectFixture.cs
+++ b/src/NHibernate.Test/DialectTest/DialectFixture.cs
@@ -1,7 +1,6 @@
 using System;
 using System.Collections.Generic;
 using System.Data;
-using System.Data.Common;
 using NHibernate.Dialect;
 using NHibernate.Driver;
 using NHibernate.Engine;
@@ -171,5 +170,28 @@ public void CurrentTimestampSelection()
 				}
 			}
 		}
+
+		[Test]
+		public void GetDecimalTypeName()
+		{
+			var cfg = TestConfigurationHelper.GetDefaultConfiguration();
+			var dialect = Dialect.Dialect.GetDialect(cfg.Properties);
+
+			Assert.That(dialect.GetTypeName(SqlTypeFactory.GetSqlType(DbType.Decimal, 40, 40)), Does.Not.Contain("40"), "oversized decimal");
+			Assert.That(dialect.GetTypeName(SqlTypeFactory.GetSqlType(DbType.Decimal, 3, 2)), Does.Match(@"^[^(]*(\(\s*3\s*,\s*2\s*\))?\s*$"), "small decimal");
+		}
+
+		[Test]
+		public void GetTypeCastName()
+		{
+			var cfg = TestConfigurationHelper.GetDefaultConfiguration();
+			cfg.SetProperty(Environment.QueryDefaultCastLength, "20");
+			cfg.SetProperty(Environment.QueryDefaultCastPrecision, "10");
+			cfg.SetProperty(Environment.QueryDefaultCastScale, "3");
+			var dialect = Dialect.Dialect.GetDialect(cfg.Properties);
+
+			Assert.That(dialect.GetCastTypeName(SqlTypeFactory.Decimal), Does.Match(@"^[^(]*(\(\s*10\s*,\s*3\s*\))?\s*$"), "decimal");
+			Assert.That(dialect.GetCastTypeName(new SqlType(DbType.String)), Does.Match(@"^[^(]*(\(\s*20\s*\))?\s*$"), "string");
+		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate.Test/DriverTest/FirebirdClientDriverFixture.cs b/src/NHibernate.Test/DriverTest/FirebirdClientDriverFixture.cs
index 027fa2e9f77..efaa46b7d2e 100644
--- a/src/NHibernate.Test/DriverTest/FirebirdClientDriverFixture.cs
+++ b/src/NHibernate.Test/DriverTest/FirebirdClientDriverFixture.cs
@@ -76,7 +76,7 @@ public void ConnectionPooling_OpenThenCloseTwoAtTheSameTime_TowConnectionsArePoo
 		public void AdjustCommand_StringParametersWithinConditionalSelect_ThenParameterIsWrappedByAVarcharCastStatement()
 		{
 			MakeDriver();
-			var cmd = BuildSelectCaseCommand(SqlTypeFactory.GetString(0));
+			var cmd = BuildSelectCaseCommand(SqlTypeFactory.GetString(255));
 
 			_driver.AdjustCommand(cmd);
 
@@ -100,7 +100,7 @@ public void AdjustCommand_IntParametersWithinConditionalSelect_ThenParameterIsWr
 		public void AdjustCommand_ParameterWithinSelectConcat_ParameterIsCasted()
 		{
 			MakeDriver();
-			var cmd = BuildSelectConcatCommand(SqlTypeFactory.GetString(0));
+			var cmd = BuildSelectConcatCommand(SqlTypeFactory.GetString(255));
 
 			_driver.AdjustCommand(cmd);
 
@@ -112,7 +112,7 @@ public void AdjustCommand_ParameterWithinSelectConcat_ParameterIsCasted()
 		public void AdjustCommand_ParameterWithinSelectAddFunction_ParameterIsCasted()
 		{
 			MakeDriver();
-			var cmd = BuildSelectAddCommand(SqlTypeFactory.GetString(0));
+			var cmd = BuildSelectAddCommand(SqlTypeFactory.GetString(255));
 
 			_driver.AdjustCommand(cmd);
 
diff --git a/src/NHibernate/Cfg/Environment.cs b/src/NHibernate/Cfg/Environment.cs
index f46e6674cb1..efc0b6dcab4 100644
--- a/src/NHibernate/Cfg/Environment.cs
+++ b/src/NHibernate/Cfg/Environment.cs
@@ -203,6 +203,24 @@ public static string Version
 
 		public const string QueryModelRewriterFactory = "query.query_model_rewriter_factory";
 
+		/// <summary>
+		/// Set the default length used in casting when the target type is length bound and
+		/// does not specify it. <c>4000</c> by default, automatically trim down according to dialect type registration.
+		/// </summary>
+		public const string QueryDefaultCastLength = "query.default_cast_length";
+
+		/// <summary>
+		/// Set the default precision used in casting when the target type is decimal and
+		/// does not specify it. <c>28</c> by default, automatically trim down according to dialect type registration.
+		/// </summary>
+		public const string QueryDefaultCastPrecision = "query.default_cast_precision";
+
+		/// <summary>
+		/// Set the default scale used in casting when the target type is decimal and
+		/// does not specify it. <c>10</c> by default, automatically trim down according to dialect type registration.
+		/// </summary>
+		public const string QueryDefaultCastScale = "query.default_cast_scale";
+
 		/// <summary>
 		/// This may need to be set to 3 if you are using the OdbcDriver with MS SQL Server 2008+.
 		/// </summary>
diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs
index ff97bce3220..a485c766107 100644
--- a/src/NHibernate/Dialect/Dialect.cs
+++ b/src/NHibernate/Dialect/Dialect.cs
@@ -199,6 +199,9 @@ private static Dialect InstantiateDialect(string dialectName, IDictionary<string
 		/// <param name="settings">The configuration settings.</param>
 		public virtual void Configure(IDictionary<string, string> settings)
 		{
+			DefaultCastLength = PropertiesHelper.GetInt32(Environment.QueryDefaultCastLength, settings, 4000);
+			DefaultCastPrecision = PropertiesHelper.GetByte(Environment.QueryDefaultCastPrecision, settings, null) ?? 28;
+			DefaultCastScale = PropertiesHelper.GetByte(Environment.QueryDefaultCastScale, settings, null) ?? 10;
 		}
 
 		#endregion
@@ -245,15 +248,48 @@ public virtual string GetLongestTypeName(DbType dbType)
 			return _typeNames.GetLongest(dbType);
 		}
 
+		protected int DefaultCastLength { get; set; }
+		protected byte DefaultCastPrecision { get; set; }
+		protected byte DefaultCastScale { get; set; }
+
 		/// <summary> 
 		/// Get the name of the database type appropriate for casting operations
 		/// (via the CAST() SQL function) for the given <see cref="SqlType"/> typecode.
 		/// </summary>
 		/// <param name="sqlType">The <see cref="SqlType"/> typecode </param>
 		/// <returns> The database type name </returns>
-		public virtual string GetCastTypeName(SqlType sqlType)
+		public virtual string GetCastTypeName(SqlType sqlType) =>
+			GetCastTypeName(sqlType, _typeNames);
+
+		/// <summary> 
+		/// Get the name of the database type appropriate for casting operations
+		/// (via the CAST() SQL function) for the given <see cref="SqlType"/> typecode.
+		/// </summary>
+		/// <param name="sqlType">The <see cref="SqlType"/> typecode.</param>
+		/// <param name="castTypeNames">The source for type names.</param>
+		/// <returns>The database type name.</returns>
+		protected virtual string GetCastTypeName(SqlType sqlType, TypeNames castTypeNames)
 		{
-			return GetTypeName(sqlType, Column.DefaultLength, Column.DefaultPrecision, Column.DefaultScale);
+			if (sqlType.LengthDefined || sqlType.PrecisionDefined || sqlType.ScaleDefined)
+				return castTypeNames.Get(sqlType.DbType, sqlType.Length, sqlType.Precision, sqlType.Scale);
+			switch (sqlType.DbType)
+			{
+				case DbType.Decimal:
+					// We cannot know if the user needs its digit after or before the dot, so use a configurable
+					// default.
+					return castTypeNames.Get(DbType.Decimal, 0, DefaultCastPrecision, DefaultCastScale);
+				case DbType.DateTime:
+				case DbType.DateTime2:
+				case DbType.DateTimeOffset:
+				case DbType.Time:
+				case DbType.Currency:
+					// Use default for these, dialects are supposed to map them to max capacity
+					return castTypeNames.Get(sqlType.DbType);
+				default:
+					// Other types are either length bound or not length/precision/scale bound. Otherwise they need to be
+					// handled previously.
+					return castTypeNames.Get(sqlType.DbType, DefaultCastLength, 0, 0);
+			}
 		}
 
 		/// <summary>
diff --git a/src/NHibernate/Dialect/MySQLDialect.cs b/src/NHibernate/Dialect/MySQLDialect.cs
index da1954f6601..fb913a92320 100644
--- a/src/NHibernate/Dialect/MySQLDialect.cs
+++ b/src/NHibernate/Dialect/MySQLDialect.cs
@@ -4,7 +4,6 @@
 using System.Text;
 using NHibernate.Dialect.Function;
 using NHibernate.Dialect.Schema;
-using NHibernate.Mapping;
 using NHibernate.SqlCommand;
 using NHibernate.SqlTypes;
 using NHibernate.Util;
@@ -64,7 +63,7 @@ public MySQLDialect()
 			RegisterColumnType(DbType.String, 16777215, "MEDIUMTEXT");
 			//todo: future: add compatibility with decimal???
 			//An unpacked fixed-point number. Behaves like a CHAR column; 
-			//�unpacked� means the number is stored as a string, using one character for each digit of the value.
+			//“unpacked” means the number is stored as a string, using one character for each digit of the value.
 			//M is the total number of digits and D is the number of digits after the decimal point
 			//DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL]
 
@@ -494,15 +493,8 @@ protected void RegisterCastType(DbType code, int capacity, string name)
 		/// </summary>
 		/// <param name="sqlType">The <see cref="SqlType"/> typecode </param>
 		/// <returns> The database type name </returns>
-		public override string GetCastTypeName(SqlType sqlType)
-		{
-			string result = castTypeNames.Get(sqlType.DbType, Column.DefaultLength, Column.DefaultPrecision, Column.DefaultScale);
-			if (result == null)
-			{
-				throw new HibernateException(string.Format("No CAST() type mapping for SqlType {0}", sqlType));
-			}
-			return result;
-		}
+		public override string GetCastTypeName(SqlType sqlType) =>
+			GetCastTypeName(sqlType, castTypeNames);
 
 		public override long TimestampResolutionInTicks
 		{
@@ -552,4 +544,4 @@ public override long TimestampResolutionInTicks
 
 		#endregion
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Driver/FirebirdClientDriver.cs b/src/NHibernate/Driver/FirebirdClientDriver.cs
index 32fcb94853e..730d11a388d 100644
--- a/src/NHibernate/Driver/FirebirdClientDriver.cs
+++ b/src/NHibernate/Driver/FirebirdClientDriver.cs
@@ -40,6 +40,12 @@ public FirebirdClientDriver()
 
 		}
 
+		public override void Configure(IDictionary<string, string> settings)
+		{
+			base.Configure(settings);
+			_fbDialect.Configure(settings);
+		}
+
 		public override bool UseNamedPrefixInSql
 		{
 			get { return true; }
@@ -72,12 +78,13 @@ public override DbCommand GenerateCommand(CommandType type, SqlString sqlString,
 			if (!string.IsNullOrWhiteSpace(expWithParams))
 			{
 				var candidates = GetCastCandidates(expWithParams);
-				var castParams = from DbParameter p in command.Parameters
-								 where candidates.Contains(p.ParameterName)
-								 select p;
-				foreach (var param in castParams)
+
+				var index = 0;
+				foreach (DbParameter p in command.Parameters)
 				{
-					TypeCastParam(param, command);
+					if (candidates.Contains(p.ParameterName))
+						TypeCastParam(p, command, parameterTypes[index]);
+					index++;
 				}
 			}
 
@@ -99,15 +106,26 @@ private HashSet<string> GetCastCandidates(string statement)
 			return new HashSet<string>(candidates);
 		}
 
-		private void TypeCastParam(DbParameter param, DbCommand command)
+		private void TypeCastParam(DbParameter param, DbCommand command, SqlType sqlType)
 		{
-			var castType = GetFbTypeFromDbType(param.DbType);
-			command.CommandText = command.CommandText.ReplaceWholeWord(param.ParameterName, string.Format("cast({0} as {1})", param.ParameterName, castType));
+			var castType = GetFbTypeForParam(sqlType);
+			command.CommandText = command.CommandText.ReplaceWholeWord(
+				param.ParameterName,
+				$"cast({param.ParameterName} as {castType})");
 		}
 
-		private string GetFbTypeFromDbType(DbType dbType)
+		private string GetFbTypeForParam(SqlType sqlType)
 		{
-			return _fbDialect.GetCastTypeName(new SqlType(dbType));
+			if (sqlType.LengthDefined)
+				switch (sqlType.DbType)
+				{
+					case DbType.AnsiString:
+					case DbType.String:
+						// Use default length instead for supporting like expressions requiring longer length.
+						sqlType = new SqlType(sqlType.DbType);
+						break;
+				}
+			return _fbDialect.GetCastTypeName(sqlType);
 		}
 
 		private static volatile MethodInfo _clearPool;
diff --git a/src/NHibernate/nhibernate-configuration.xsd b/src/NHibernate/nhibernate-configuration.xsd
index 7a837e28909..261f29d2a2e 100644
--- a/src/NHibernate/nhibernate-configuration.xsd
+++ b/src/NHibernate/nhibernate-configuration.xsd
@@ -169,6 +169,30 @@
 										</xs:documentation>
 									</xs:annotation>
 								</xs:enumeration>
+								<xs:enumeration value="query.default_cast_length">
+									<xs:annotation>
+										<xs:documentation>
+											Set the default length used in casting when the target type is length bound and
+											does not specify it. 4000 by default, automatically trim down according to dialect type registration.
+										</xs:documentation>
+									</xs:annotation>
+								</xs:enumeration>
+								<xs:enumeration value="query.default_cast_precision">
+									<xs:annotation>
+										<xs:documentation>
+											Set the default precision used in casting when the target type is decimal and
+											does not specify it. 28 by default, automatically trim down according to dialect type registration.
+										</xs:documentation>
+									</xs:annotation>
+								</xs:enumeration>
+								<xs:enumeration value="query.default_cast_scale">
+									<xs:annotation>
+										<xs:documentation>
+											Set the default scale used in casting when the target type is decimal and
+											does not specify it. 10 by default, automatically trim down according to dialect type registration.
+										</xs:documentation>
+									</xs:annotation>
+								</xs:enumeration>
 							</xs:restriction>
 						</xs:simpleType>
 					</xs:attribute>

From 7b6ff8e57d5ceacc897c355be212d836f8cb3383 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <fredericdelaporte@free.fr>
Date: Thu, 5 Oct 2017 14:59:28 +0200
Subject: [PATCH 3/3] fixup !NH-4088 - Fix GetCastTypeName

 * Oracle double special case
 * More tests
 * Tests fixes
---
 .../Async/Criteria/ProjectionsTest.cs         |  10 +-
 .../DriverTest/FirebirdClientDriverFixture.cs | 130 +++++------
 .../Criteria/ProjectionsTest.cs               |  10 +-
 .../DialectTest/DialectFixture.cs             |   4 +
 .../DriverTest/FirebirdClientDriverFixture.cs | 216 +++++++++---------
 src/NHibernate/Dialect/Dialect.cs             |   4 +-
 .../Dialect/SybaseSQLAnywhere10Dialect.cs     |   2 +-
 7 files changed, 200 insertions(+), 176 deletions(-)

diff --git a/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs b/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs
index 7b00a0b4bd3..baacbc9fbb1 100644
--- a/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs
+++ b/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs
@@ -11,8 +11,10 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Text.RegularExpressions;
 using NHibernate.Criterion;
 using NHibernate.Dialect;
+using NHibernate.SqlTypes;
 using NHibernate.Type;
 using NUnit.Framework;
 
@@ -105,6 +107,11 @@ public async Task UsingSqlFunctions_Concat_WithCastAsync()
 		[Test]
 		public async Task CastWithLengthAsync()
 		{
+			if (Regex.IsMatch(Dialect.GetCastTypeName(SqlTypeFactory.GetString(3)), @"^[^(]*$"))
+			{
+				Assert.Ignore($"Dialect {Dialect} does not seem to handle string length in cast");
+			}
+
 			using (var s = OpenSession())
 			{
 				try
@@ -120,8 +127,7 @@ public async Task CastWithLengthAsync()
 				}
 				catch (Exception e)
 				{
-					if (!e.Message.Contains("truncation") && 
-						(e.InnerException == null || !e.InnerException.Message.Contains("truncation")))
+					if (e.InnerException == null || !e.InnerException.Message.Contains("truncation"))
 						throw;
 				}
 			}
diff --git a/src/NHibernate.Test/Async/DriverTest/FirebirdClientDriverFixture.cs b/src/NHibernate.Test/Async/DriverTest/FirebirdClientDriverFixture.cs
index bc2f4157f98..734866bda0c 100644
--- a/src/NHibernate.Test/Async/DriverTest/FirebirdClientDriverFixture.cs
+++ b/src/NHibernate.Test/Async/DriverTest/FirebirdClientDriverFixture.cs
@@ -26,73 +26,74 @@ public class FirebirdClientDriverFixtureAsync
 		private string _connectionString;
 		private FirebirdClientDriver _driver;
 
+		[OneTimeSetUp]
+		public void OneTimeSetup()
+		{
+			var cfg = TestConfigurationHelper.GetDefaultConfiguration();
+
+			var dlct = cfg.GetProperty("dialect");
+			if (!dlct.Contains("Firebird"))
+				Assert.Ignore("Applies only to Firebird");
+
+			_driver = new FirebirdClientDriver();
+			_driver.Configure(cfg.Properties);
+			_connectionString = cfg.GetProperty("connection.connection_string");
+		}
+
 		[Test]
 		public async Task ConnectionPooling_OpenThenCloseThenOpenAnotherOne_OnlyOneConnectionIsPooledAsync()
 		{
-			MakeDriver();
-
 			_driver.ClearPool(_connectionString);
 
 			var allreadyEstablished = await (GetEstablishedConnectionsAsync());
 
-			var connection1 = MakeConnection();
-			var connection2 = MakeConnection();
-
-			//open first connection
-			await (connection1.OpenAsync());
-			await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After first open"));
+			using (var connection1 = MakeConnection())
+			using (var connection2 = MakeConnection())
+			{
+				//open first connection
+				await (connection1.OpenAsync());
+				await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After first open"));
 
-			//return it to the pool
-			connection1.Close();
-			await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After first close"));
+				//return it to the pool
+				connection1.Close();
+				await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After first close"));
 
-			//open the second connection
-			await (connection2.OpenAsync());
-			await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After second open"));
+				//open the second connection
+				await (connection2.OpenAsync());
+				await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After second open"));
 
-			//return it to the pool
-			connection2.Close();
-			await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After second close"));
+				//return it to the pool
+				connection2.Close();
+				await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After second close"));
+			}
 		}
 
 		[Test]
 		public async Task ConnectionPooling_OpenThenCloseTwoAtTheSameTime_TowConnectionsArePooledAsync()
 		{
-			MakeDriver();
-
 			_driver.ClearPool(_connectionString);
 
 			var allreadyEstablished = await (GetEstablishedConnectionsAsync());
 
-			var connection1 = MakeConnection();
-			var connection2 = MakeConnection();
-
-			//open first connection
-			await (connection1.OpenAsync());
-			await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After first open"));
-
-			//open second one
-			await (connection2.OpenAsync());
-			await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 2, "After second open"));
-
-			//return connection1 to the pool
-			connection1.Close();
-			await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 2, "After first close"));
+			using (var connection1 = MakeConnection())
+			using (var connection2 = MakeConnection())
+			{
+				//open first connection
+				await (connection1.OpenAsync());
+				await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 1, "After first open"));
 
-			//return connection2 to the pool
-			connection2.Close();
-			await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 2, "After second close"));
-		}
+				//open second one
+				await (connection2.OpenAsync());
+				await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 2, "After second open"));
 
-		private void MakeDriver()
-		{
-			var cfg = TestConfigurationHelper.GetDefaultConfiguration();
-			var dlct = cfg.GetProperty("dialect");
-			if (!dlct.Contains("Firebird"))
-				Assert.Ignore("Applies only to Firebird");
+				//return connection1 to the pool
+				connection1.Close();
+				await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 2, "After first close"));
 
-			_driver = new FirebirdClientDriver();
-			_connectionString = cfg.GetProperty("connection.connection_string");
+				//return connection2 to the pool
+				connection2.Close();
+				await (VerifyCountOfEstablishedConnectionsIsAsync(allreadyEstablished + 2, "After second close"));
+			}
 		}
 
 		private DbConnection MakeConnection()
@@ -125,14 +126,14 @@ private DbConnection MakeConnection()
 		private DbCommand BuildSelectCaseCommand(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
-					.Add("select (case when col = ")
-					.AddParameter()
-					.Add(" then ")
-					.AddParameter()
-					.Add(" else ")
-					.AddParameter()
-					.Add(" end) from table")
-					.ToSqlString();
+				.Add("select (case when col = ")
+				.AddParameter()
+				.Add(" then ")
+				.AddParameter()
+				.Add(" else ")
+				.AddParameter()
+				.Add(" end) from table")
+				.ToSqlString();
 
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType, paramType, paramType });
 		}
@@ -140,12 +141,12 @@ private DbCommand BuildSelectCaseCommand(SqlType paramType)
 		private DbCommand BuildSelectConcatCommand(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
-					.Add("select col || ")
-					.AddParameter()
-					.Add(" || ")
-					.Add("col ")
-					.Add("from table")
-					.ToSqlString();
+				.Add("select col || ")
+				.AddParameter()
+				.Add(" || ")
+				.Add("col ")
+				.Add("from table")
+				.ToSqlString();
 
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType });
 		}
@@ -153,10 +154,10 @@ private DbCommand BuildSelectConcatCommand(SqlType paramType)
 		private DbCommand BuildSelectAddCommand(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
-					.Add("select col + ")
-					.AddParameter()
-					.Add(" from table")
-					.ToSqlString();
+				.Add("select col + ")
+				.AddParameter()
+				.Add(" from table")
+				.ToSqlString();
 
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType });
 		}
@@ -172,6 +173,7 @@ private DbCommand BuildInsertWithParamsInSelectCommand(SqlType paramType)
 
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType });
 		}
+
 		private DbCommand BuildInsertWithParamsInSelectCommandWithSelectInColumnName(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
@@ -184,7 +186,7 @@ private DbCommand BuildInsertWithParamsInSelectCommandWithSelectInColumnName(Sql
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType });
 		}
 
-        private DbCommand BuildInsertWithParamsInSelectCommandWithWhereInColumnName(SqlType paramType)
+		private DbCommand BuildInsertWithParamsInSelectCommandWithWhereInColumnName(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
 				.Add("insert into table1 (col1_where_aaa) ")
diff --git a/src/NHibernate.Test/Criteria/ProjectionsTest.cs b/src/NHibernate.Test/Criteria/ProjectionsTest.cs
index 165c12715cb..f4d928f6d9f 100644
--- a/src/NHibernate.Test/Criteria/ProjectionsTest.cs
+++ b/src/NHibernate.Test/Criteria/ProjectionsTest.cs
@@ -1,8 +1,10 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Text.RegularExpressions;
 using NHibernate.Criterion;
 using NHibernate.Dialect;
+using NHibernate.SqlTypes;
 using NHibernate.Type;
 using NUnit.Framework;
 
@@ -94,6 +96,11 @@ public void UsingSqlFunctions_Concat_WithCast()
 		[Test]
 		public void CastWithLength()
 		{
+			if (Regex.IsMatch(Dialect.GetCastTypeName(SqlTypeFactory.GetString(3)), @"^[^(]*$"))
+			{
+				Assert.Ignore($"Dialect {Dialect} does not seem to handle string length in cast");
+			}
+
 			using (var s = OpenSession())
 			{
 				try
@@ -109,8 +116,7 @@ public void CastWithLength()
 				}
 				catch (Exception e)
 				{
-					if (!e.Message.Contains("truncation") && 
-						(e.InnerException == null || !e.InnerException.Message.Contains("truncation")))
+					if (e.InnerException == null || !e.InnerException.Message.Contains("truncation"))
 						throw;
 				}
 			}
diff --git a/src/NHibernate.Test/DialectTest/DialectFixture.cs b/src/NHibernate.Test/DialectTest/DialectFixture.cs
index 1895f9be13b..17899c1e88f 100644
--- a/src/NHibernate.Test/DialectTest/DialectFixture.cs
+++ b/src/NHibernate.Test/DialectTest/DialectFixture.cs
@@ -178,6 +178,7 @@ public void GetDecimalTypeName()
 			var dialect = Dialect.Dialect.GetDialect(cfg.Properties);
 
 			Assert.That(dialect.GetTypeName(SqlTypeFactory.GetSqlType(DbType.Decimal, 40, 40)), Does.Not.Contain("40"), "oversized decimal");
+			// This regex tests wether the type is qualified with expected length/precision/scale or not qualified at all.
 			Assert.That(dialect.GetTypeName(SqlTypeFactory.GetSqlType(DbType.Decimal, 3, 2)), Does.Match(@"^[^(]*(\(\s*3\s*,\s*2\s*\))?\s*$"), "small decimal");
 		}
 
@@ -190,8 +191,11 @@ public void GetTypeCastName()
 			cfg.SetProperty(Environment.QueryDefaultCastScale, "3");
 			var dialect = Dialect.Dialect.GetDialect(cfg.Properties);
 
+			// Those regex test wether the type is qualified with expected length/precision/scale or not qualified at all.
 			Assert.That(dialect.GetCastTypeName(SqlTypeFactory.Decimal), Does.Match(@"^[^(]*(\(\s*10\s*,\s*3\s*\))?\s*$"), "decimal");
+			Assert.That(dialect.GetCastTypeName(SqlTypeFactory.GetSqlType(DbType.Decimal, 12, 4)), Does.Match(@"^[^(]*(\(\s*12\s*,\s*4\s*\))?\s*$"), "decimal(12,4)");
 			Assert.That(dialect.GetCastTypeName(new SqlType(DbType.String)), Does.Match(@"^[^(]*(\(\s*20\s*\))?\s*$"), "string");
+			Assert.That(dialect.GetCastTypeName(SqlTypeFactory.GetString(25)), Does.Match(@"^[^(]*(\(\s*25\s*\))?\s*$"), "string(25)");
 		}
 	}
 }
diff --git a/src/NHibernate.Test/DriverTest/FirebirdClientDriverFixture.cs b/src/NHibernate.Test/DriverTest/FirebirdClientDriverFixture.cs
index efaa46b7d2e..2bd4177e8db 100644
--- a/src/NHibernate.Test/DriverTest/FirebirdClientDriverFixture.cs
+++ b/src/NHibernate.Test/DriverTest/FirebirdClientDriverFixture.cs
@@ -14,157 +14,160 @@ public class FirebirdClientDriverFixture
 		private string _connectionString;
 		private FirebirdClientDriver _driver;
 
+		[OneTimeSetUp]
+		public void OneTimeSetup()
+		{
+			var cfg = TestConfigurationHelper.GetDefaultConfiguration();
+
+			var dlct = cfg.GetProperty("dialect");
+			if (!dlct.Contains("Firebird"))
+				Assert.Ignore("Applies only to Firebird");
+
+			_driver = new FirebirdClientDriver();
+			_driver.Configure(cfg.Properties);
+			_connectionString = cfg.GetProperty("connection.connection_string");
+		}
+
 		[Test]
 		public void ConnectionPooling_OpenThenCloseThenOpenAnotherOne_OnlyOneConnectionIsPooled()
 		{
-			MakeDriver();
-
 			_driver.ClearPool(_connectionString);
 
 			var allreadyEstablished = GetEstablishedConnections();
 
-			var connection1 = MakeConnection();
-			var connection2 = MakeConnection();
-
-			//open first connection
-			connection1.Open();
-			VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After first open");
+			using (var connection1 = MakeConnection())
+			using (var connection2 = MakeConnection())
+			{
+				//open first connection
+				connection1.Open();
+				VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After first open");
 
-			//return it to the pool
-			connection1.Close();
-			VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After first close");
+				//return it to the pool
+				connection1.Close();
+				VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After first close");
 
-			//open the second connection
-			connection2.Open();
-			VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After second open");
+				//open the second connection
+				connection2.Open();
+				VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After second open");
 
-			//return it to the pool
-			connection2.Close();
-			VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After second close");
+				//return it to the pool
+				connection2.Close();
+				VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After second close");
+			}
 		}
 
 		[Test]
 		public void ConnectionPooling_OpenThenCloseTwoAtTheSameTime_TowConnectionsArePooled()
 		{
-			MakeDriver();
-
 			_driver.ClearPool(_connectionString);
 
 			var allreadyEstablished = GetEstablishedConnections();
 
-			var connection1 = MakeConnection();
-			var connection2 = MakeConnection();
-
-			//open first connection
-			connection1.Open();
-			VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After first open");
+			using (var connection1 = MakeConnection())
+			using (var connection2 = MakeConnection())
+			{
+				//open first connection
+				connection1.Open();
+				VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 1, "After first open");
 
-			//open second one
-			connection2.Open();
-			VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 2, "After second open");
+				//open second one
+				connection2.Open();
+				VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 2, "After second open");
 
-			//return connection1 to the pool
-			connection1.Close();
-			VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 2, "After first close");
+				//return connection1 to the pool
+				connection1.Close();
+				VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 2, "After first close");
 
-			//return connection2 to the pool
-			connection2.Close();
-			VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 2, "After second close");
+				//return connection2 to the pool
+				connection2.Close();
+				VerifyCountOfEstablishedConnectionsIs(allreadyEstablished + 2, "After second close");
+			}
 		}
 
 		[Test]
 		public void AdjustCommand_StringParametersWithinConditionalSelect_ThenParameterIsWrappedByAVarcharCastStatement()
 		{
-			MakeDriver();
-			var cmd = BuildSelectCaseCommand(SqlTypeFactory.GetString(255));
-
-			_driver.AdjustCommand(cmd);
+			using (var cmd = BuildSelectCaseCommand(SqlTypeFactory.GetString(255)))
+			{
+				_driver.AdjustCommand(cmd);
 
-			var expectedCommandTxt = "select (case when col = @p0 then cast(@p1 as VARCHAR(255)) else cast(@p2 as VARCHAR(255)) end) from table";
-			Assert.That(cmd.CommandText, Is.EqualTo(expectedCommandTxt));
+				var expectedCommandTxt =
+					"select (case when col = @p0 then cast(@p1 as VARCHAR(4000)) else cast(@p2 as VARCHAR(4000)) end) from table";
+				Assert.That(cmd.CommandText, Is.EqualTo(expectedCommandTxt));
+			}
 		}
 
 		[Test]
 		public void AdjustCommand_IntParametersWithinConditionalSelect_ThenParameterIsWrappedByAnIntCastStatement()
 		{
-			MakeDriver();
-			var cmd = BuildSelectCaseCommand(SqlTypeFactory.Int32);
-
-			_driver.AdjustCommand(cmd);
+			using (var cmd = BuildSelectCaseCommand(SqlTypeFactory.Int32))
+			{
+				_driver.AdjustCommand(cmd);
 
-			var expectedCommandTxt = "select (case when col = @p0 then cast(@p1 as INTEGER) else cast(@p2 as INTEGER) end) from table";
-			Assert.That(cmd.CommandText, Is.EqualTo(expectedCommandTxt));
+				var expectedCommandTxt =
+					"select (case when col = @p0 then cast(@p1 as INTEGER) else cast(@p2 as INTEGER) end) from table";
+				Assert.That(cmd.CommandText, Is.EqualTo(expectedCommandTxt));
+			}
 		}
 
 		[Test]
 		public void AdjustCommand_ParameterWithinSelectConcat_ParameterIsCasted()
 		{
-			MakeDriver();
-			var cmd = BuildSelectConcatCommand(SqlTypeFactory.GetString(255));
-
-			_driver.AdjustCommand(cmd);
+			using (var cmd = BuildSelectConcatCommand(SqlTypeFactory.GetString(255)))
+			{
+				_driver.AdjustCommand(cmd);
 
-			var expected = "select col || cast(@p0 as VARCHAR(255)) || col from table";
-			Assert.That(cmd.CommandText, Is.EqualTo(expected));
+				var expected = "select col || cast(@p0 as VARCHAR(4000)) || col from table";
+				Assert.That(cmd.CommandText, Is.EqualTo(expected));
+			}
 		}
 
 		[Test]
 		public void AdjustCommand_ParameterWithinSelectAddFunction_ParameterIsCasted()
 		{
-			MakeDriver();
-			var cmd = BuildSelectAddCommand(SqlTypeFactory.GetString(255));
-
-			_driver.AdjustCommand(cmd);
+			using (var cmd = BuildSelectAddCommand(SqlTypeFactory.GetString(255)))
+			{
+				_driver.AdjustCommand(cmd);
 
-			var expected = "select col + cast(@p0 as VARCHAR(255)) from table";
-			Assert.That(cmd.CommandText, Is.EqualTo(expected));
+				var expected = "select col + cast(@p0 as VARCHAR(4000)) from table";
+				Assert.That(cmd.CommandText, Is.EqualTo(expected));
+			}
 		}
 
 		[Test]
 		public void AdjustCommand_InsertWithParamsInSelect_ParameterIsCasted()
 		{
-			MakeDriver();
-			var cmd = BuildInsertWithParamsInSelectCommand(SqlTypeFactory.Int32);
-
-			_driver.AdjustCommand(cmd);
+			using (var cmd = BuildInsertWithParamsInSelectCommand(SqlTypeFactory.Int32))
+			{
+				_driver.AdjustCommand(cmd);
 
-			var expected = "insert into table1 (col1, col2) select col1, cast(@p0 as INTEGER) from table2";
-			Assert.That(cmd.CommandText, Is.EqualTo(expected));
+				var expected = "insert into table1 (col1, col2) select col1, cast(@p0 as INTEGER) from table2";
+				Assert.That(cmd.CommandText, Is.EqualTo(expected));
+			}
 		}
 
 		[Test]
 		public void AdjustCommand_InsertWithParamsInSelect_ParameterIsNotCasted_WhenColumnNameContainsSelect()
 		{
-			MakeDriver();
-			var cmd = BuildInsertWithParamsInSelectCommandWithSelectInColumnName(SqlTypeFactory.Int32);
-
-			_driver.AdjustCommand(cmd);
+			using (var cmd = BuildInsertWithParamsInSelectCommandWithSelectInColumnName(SqlTypeFactory.Int32))
+			{
+				_driver.AdjustCommand(cmd);
 
-			var expected = "insert into table1 (col1_select_aaa) values(@p0) from table2";
-			Assert.That(cmd.CommandText, Is.EqualTo(expected));
+				var expected = "insert into table1 (col1_select_aaa) values(@p0) from table2";
+				Assert.That(cmd.CommandText, Is.EqualTo(expected));
+			}
 		}
 
 		[Test]
 		public void AdjustCommand_InsertWithParamsInSelect_ParameterIsNotCasted_WhenColumnNameContainsWhere()
 		{
-			MakeDriver();
-			var cmd = BuildInsertWithParamsInSelectCommandWithWhereInColumnName(SqlTypeFactory.Int32);
-
-			_driver.AdjustCommand(cmd);
-
-			var expected = "insert into table1 (col1_where_aaa) values(@p0) from table2";
-			Assert.That(cmd.CommandText, Is.EqualTo(expected));
-		}
-
-		private void MakeDriver()
-		{
-			var cfg = TestConfigurationHelper.GetDefaultConfiguration();
-			var dlct = cfg.GetProperty("dialect");
-			if (!dlct.Contains("Firebird"))
-				Assert.Ignore("Applies only to Firebird");
+			using (var cmd = BuildInsertWithParamsInSelectCommandWithWhereInColumnName(SqlTypeFactory.Int32))
+			{
+				_driver.AdjustCommand(cmd);
 
-			_driver = new FirebirdClientDriver();
-			_connectionString = cfg.GetProperty("connection.connection_string");
+				var expected = "insert into table1 (col1_where_aaa) values(@p0) from table2";
+				Assert.That(cmd.CommandText, Is.EqualTo(expected));
+			}
 		}
 
 		private DbConnection MakeConnection()
@@ -197,14 +200,14 @@ private int GetEstablishedConnections()
 		private DbCommand BuildSelectCaseCommand(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
-					.Add("select (case when col = ")
-					.AddParameter()
-					.Add(" then ")
-					.AddParameter()
-					.Add(" else ")
-					.AddParameter()
-					.Add(" end) from table")
-					.ToSqlString();
+				.Add("select (case when col = ")
+				.AddParameter()
+				.Add(" then ")
+				.AddParameter()
+				.Add(" else ")
+				.AddParameter()
+				.Add(" end) from table")
+				.ToSqlString();
 
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType, paramType, paramType });
 		}
@@ -212,12 +215,12 @@ private DbCommand BuildSelectCaseCommand(SqlType paramType)
 		private DbCommand BuildSelectConcatCommand(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
-					.Add("select col || ")
-					.AddParameter()
-					.Add(" || ")
-					.Add("col ")
-					.Add("from table")
-					.ToSqlString();
+				.Add("select col || ")
+				.AddParameter()
+				.Add(" || ")
+				.Add("col ")
+				.Add("from table")
+				.ToSqlString();
 
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType });
 		}
@@ -225,10 +228,10 @@ private DbCommand BuildSelectConcatCommand(SqlType paramType)
 		private DbCommand BuildSelectAddCommand(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
-					.Add("select col + ")
-					.AddParameter()
-					.Add(" from table")
-					.ToSqlString();
+				.Add("select col + ")
+				.AddParameter()
+				.Add(" from table")
+				.ToSqlString();
 
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType });
 		}
@@ -244,6 +247,7 @@ private DbCommand BuildInsertWithParamsInSelectCommand(SqlType paramType)
 
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType });
 		}
+
 		private DbCommand BuildInsertWithParamsInSelectCommandWithSelectInColumnName(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
@@ -256,7 +260,7 @@ private DbCommand BuildInsertWithParamsInSelectCommandWithSelectInColumnName(Sql
 			return _driver.GenerateCommand(CommandType.Text, sqlString, new[] { paramType });
 		}
 
-        private DbCommand BuildInsertWithParamsInSelectCommandWithWhereInColumnName(SqlType paramType)
+		private DbCommand BuildInsertWithParamsInSelectCommandWithWhereInColumnName(SqlType paramType)
 		{
 			var sqlString = new SqlStringBuilder()
 				.Add("insert into table1 (col1_where_aaa) ")
diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs
index a485c766107..0cb0d53c605 100644
--- a/src/NHibernate/Dialect/Dialect.cs
+++ b/src/NHibernate/Dialect/Dialect.cs
@@ -275,9 +275,11 @@ protected virtual string GetCastTypeName(SqlType sqlType, TypeNames castTypeName
 			switch (sqlType.DbType)
 			{
 				case DbType.Decimal:
+				// Oracle dialect defines precision and scale for double, because it uses number instead of binary_double.
+				case DbType.Double:
 					// We cannot know if the user needs its digit after or before the dot, so use a configurable
 					// default.
-					return castTypeNames.Get(DbType.Decimal, 0, DefaultCastPrecision, DefaultCastScale);
+					return castTypeNames.Get(sqlType.DbType, 0, DefaultCastPrecision, DefaultCastScale);
 				case DbType.DateTime:
 				case DbType.DateTime2:
 				case DbType.DateTimeOffset:
diff --git a/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs b/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs
index 7b40051ef65..5758cd5da31 100644
--- a/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs
+++ b/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs
@@ -92,7 +92,7 @@ protected virtual void RegisterNumericTypeMappings()
 			RegisterColumnType(DbType.Double, "DOUBLE");
 			RegisterColumnType(DbType.Decimal, "NUMERIC(19,5)"); // Precision ranges from 0-127
 			// Anywhere max precision is 127, but .Net is limited to 28-29.
-			RegisterColumnType(DbType.Decimal, 38, "NUMERIC($p, $s)"); // Precision ranges from 0-127
+			RegisterColumnType(DbType.Decimal, 28, "NUMERIC($p, $s)"); // Precision ranges from 0-127
 		}
 
 		protected virtual void RegisterDateTimeTypeMappings()