diff --git a/src/NHibernate.Test/MappingTest/ColumnFixture.cs b/src/NHibernate.Test/MappingTest/ColumnFixture.cs
index 94f6a8c4c12..3363fb1675c 100644
--- a/src/NHibernate.Test/MappingTest/ColumnFixture.cs
+++ b/src/NHibernate.Test/MappingTest/ColumnFixture.cs
@@ -40,6 +40,8 @@ public void StringSqlType()
Assert.AreEqual("NVARCHAR(100)", column.GetSqlType(_dialect, null));
}
+ // Should be kept synchronized with Column same constant.
+ private const int _charactersLeftCount = 4;
[TestCase("xxxxyyyyz")]
[TestCase("xxxxyyyyzz")]
@@ -54,16 +56,15 @@ public void GetAliasRespectsMaxAliasLength(string columnName)
{
var dialect = new GenericDialect();
- // Verify test case assumption.
- Assert.That(dialect.MaxAliasLength, Is.EqualTo(10));
+ // Test case is meant for a max length of 10, adjusts name if it is more.
+ columnName = AdjustColumnNameToMaxLength(columnName, dialect, 10);
var column = new Column(columnName);
string generatedAlias = column.GetAlias(dialect);
- Assert.That(generatedAlias, Has.Length.LessThanOrEqualTo(dialect.MaxAliasLength));
+ Assert.That(generatedAlias, Has.Length.LessThanOrEqualTo(dialect.MaxAliasLength - _charactersLeftCount));
}
-
[TestCase("xxxxyyyyz")]
[TestCase("xxxxyyyyzz")]
[TestCase("xxxxyyyyzzz")]
@@ -77,15 +78,24 @@ public void GetAliasWithTableSuffixRespectsMaxAliasLength(string columnName)
{
var dialect = new GenericDialect();
- // Verify test case assumption.
- Assert.That(dialect.MaxAliasLength, Is.EqualTo(10));
+ // Test case is meant for a max length of 10, adjusts name if it is more.
+ columnName = AdjustColumnNameToMaxLength(columnName, dialect, 10);
var table = new Table();
var column = new Column(columnName);
string generatedAlias = column.GetAlias(dialect, table);
- Assert.That(generatedAlias, Has.Length.LessThanOrEqualTo(dialect.MaxAliasLength));
+ Assert.That(generatedAlias, Has.Length.LessThanOrEqualTo(dialect.MaxAliasLength - _charactersLeftCount));
+ }
+
+ private static string AdjustColumnNameToMaxLength(string columnName, GenericDialect dialect, int referenceMaxLength)
+ {
+ if (dialect.MaxAliasLength > referenceMaxLength)
+ columnName = new string('w', dialect.MaxAliasLength - referenceMaxLength) + columnName;
+ else if (dialect.MaxAliasLength < referenceMaxLength)
+ Assert.Fail("Dialect max alias length is too short for the test.");
+ return columnName;
}
}
}
diff --git a/src/NHibernate.sln.DotSettings b/src/NHibernate.sln.DotSettings
index 918ecb514b5..a38b0a203a1 100644
--- a/src/NHibernate.sln.DotSettings
+++ b/src/NHibernate.sln.DotSettings
@@ -19,6 +19,7 @@
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ True
True
True
True
diff --git a/src/NHibernate/Dialect/DB2Dialect.cs b/src/NHibernate/Dialect/DB2Dialect.cs
index fc578dcc6df..b9b785a2025 100644
--- a/src/NHibernate/Dialect/DB2Dialect.cs
+++ b/src/NHibernate/Dialect/DB2Dialect.cs
@@ -272,6 +272,10 @@ public override string ForUpdateString
get { return " for read only with rs"; }
}
+ // As of DB2 9.5 documentation, limit is 128 bytes which with Unicode names could mean only 32 characters.
+ ///
+ public override int MaxAliasLength => 32;
+
#region Overridden informational metadata
public override bool SupportsEmptyInList => false;
diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs
index 0cb0d53c605..0a07ed0134c 100644
--- a/src/NHibernate/Dialect/Dialect.cs
+++ b/src/NHibernate/Dialect/Dialect.cs
@@ -2282,10 +2282,11 @@ public virtual string LowercaseFunction
get { return "lower"; }
}
- public virtual int MaxAliasLength
- {
- get { return 10; }
- }
+ // 18 is the smallest of all dialects we handle.
+ ///
+ /// The maximum length a SQL alias can have.
+ ///
+ public virtual int MaxAliasLength => 18;
///
/// The syntax used to add a column to a table. Note this is deprecated
diff --git a/src/NHibernate/Dialect/FirebirdDialect.cs b/src/NHibernate/Dialect/FirebirdDialect.cs
index 564ba59ddab..e84436b5a08 100644
--- a/src/NHibernate/Dialect/FirebirdDialect.cs
+++ b/src/NHibernate/Dialect/FirebirdDialect.cs
@@ -520,6 +520,12 @@ private void RegisterTrigonometricFunctions()
RegisterFunction("tanh", new StandardSQLFunction("tanh", NHibernateUtil.Double));
}
+ // As of Firebird 2.5 documentation, limit is 30/31 (not all source are concordant), with some
+ // cases supporting more but considered as bugs and no more tolerated in v3.
+ // It seems it may be extended to 63 for Firebird v4.
+ ///
+ public override int MaxAliasLength => 30;
+
#region Informational metadata
///
diff --git a/src/NHibernate/Dialect/InformixDialect.cs b/src/NHibernate/Dialect/InformixDialect.cs
index 94b174b5e64..4bedf931856 100644
--- a/src/NHibernate/Dialect/InformixDialect.cs
+++ b/src/NHibernate/Dialect/InformixDialect.cs
@@ -457,6 +457,10 @@ public override string GetAddForeignKeyConstraintString(string constraintName, s
return res.ToString();
}
+
+ // Informix 7 is said on Internet to be limited to 18. (http://www.justskins.com/forums/length-of-columns-names-143294.html)
+ ///
+ public override int MaxAliasLength => 18;
}
public class IfxViolatedConstraintExtracter : TemplatedViolatedConstraintNameExtracter
diff --git a/src/NHibernate/Dialect/InformixDialect0940.cs b/src/NHibernate/Dialect/InformixDialect0940.cs
index f80876c0b43..59ef5b7a6e6 100644
--- a/src/NHibernate/Dialect/InformixDialect0940.cs
+++ b/src/NHibernate/Dialect/InformixDialect0940.cs
@@ -146,5 +146,8 @@ public override bool SupportsLimitOffset
get { return false; }
}
+ // Informix 9 is said on Internet to be limited to 128. (http://www.justskins.com/forums/length-of-columns-names-143294.html)
+ ///
+ public override int MaxAliasLength => 128;
};
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Dialect/IngresDialect.cs b/src/NHibernate/Dialect/IngresDialect.cs
index 06395686aba..9741642ccde 100644
--- a/src/NHibernate/Dialect/IngresDialect.cs
+++ b/src/NHibernate/Dialect/IngresDialect.cs
@@ -53,6 +53,15 @@ public IngresDialect()
DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.IngresDriver";
}
+ // Ingres 10.2 supports 256 bytes (so worst unicode case would mean 64 characters), but I am unable to find
+ // the limit for older versions, excepted many various sites mention a 32 length limit.
+ // https://unifaceinfo.com/docs/0906/Uniface_Library_HTML/ulibrary/INS_NAMING_RULES_8EEFC1A489331BF969D2A8AA36AF2832.html
+ // There are traces of a ticket for increasing this in version 10: http://lists.ingres.com/pipermail/bugs/2010-May/000052.html
+ // This dialect seems to target version below 9, since there is Ingres9Dialect deriving from it.
+ // So sticking to 32.
+ ///
+ public override int MaxAliasLength => 32;
+
#region Overridden informational metadata
public override bool SupportsEmptyInList => false;
diff --git a/src/NHibernate/Dialect/MsSql2000Dialect.cs b/src/NHibernate/Dialect/MsSql2000Dialect.cs
index 1418beccbda..9fbadeeda95 100644
--- a/src/NHibernate/Dialect/MsSql2000Dialect.cs
+++ b/src/NHibernate/Dialect/MsSql2000Dialect.cs
@@ -677,6 +677,11 @@ public override bool SupportsSqlBatches
get { return true; }
}
+ // Was 30 in "earlier version", without telling to which version the document apply.
+ // https://msdn.microsoft.com/en-us/library/ms191240.aspx#Anchor_3
+ ///
+ public override int MaxAliasLength => 30;
+
#region Overridden informational metadata
public override bool SupportsEmptyInList => false;
diff --git a/src/NHibernate/Dialect/MsSql2005Dialect.cs b/src/NHibernate/Dialect/MsSql2005Dialect.cs
index 4e3cd731dde..a53b2248de8 100644
--- a/src/NHibernate/Dialect/MsSql2005Dialect.cs
+++ b/src/NHibernate/Dialect/MsSql2005Dialect.cs
@@ -103,6 +103,10 @@ public override string AppendLockHint(LockMode lockMode, string tableName)
return tableName;
}
+ // SQL Server 2005 supports 128.
+ ///
+ public override int MaxAliasLength => 128;
+
#region Overridden informational metadata
///
diff --git a/src/NHibernate/Dialect/MsSqlCeDialect.cs b/src/NHibernate/Dialect/MsSqlCeDialect.cs
index 257c71186a1..4b4f4bb9e1b 100644
--- a/src/NHibernate/Dialect/MsSqlCeDialect.cs
+++ b/src/NHibernate/Dialect/MsSqlCeDialect.cs
@@ -344,6 +344,10 @@ public override long TimestampResolutionInTicks
}
}
+ // SQL Server 3.5 supports 128.
+ ///
+ public override int MaxAliasLength => 128;
+
#region Informational metadata
///
diff --git a/src/NHibernate/Dialect/MySQL5Dialect.cs b/src/NHibernate/Dialect/MySQL5Dialect.cs
index 0a7c73b0018..56724403e53 100644
--- a/src/NHibernate/Dialect/MySQL5Dialect.cs
+++ b/src/NHibernate/Dialect/MySQL5Dialect.cs
@@ -61,5 +61,10 @@ public override bool SupportsInsertSelectIdentity
{
get { return true; }
}
+
+ // At least MySQL 5 is said to support 64 characters for columns, but 5.7 supports 256 for aliases.
+ // 64 seems quite good enough, being conservative.
+ ///
+ public override int MaxAliasLength => 64;
}
}
diff --git a/src/NHibernate/Dialect/Oracle12cDialect.cs b/src/NHibernate/Dialect/Oracle12cDialect.cs
index 38d7c99512b..0603ed43c19 100644
--- a/src/NHibernate/Dialect/Oracle12cDialect.cs
+++ b/src/NHibernate/Dialect/Oracle12cDialect.cs
@@ -3,7 +3,7 @@
namespace NHibernate.Dialect
{
///
- /// A dialect specifically for use with Oracle 10g.
+ /// A dialect specifically for use with Oracle 12c.
///
///
/// The main difference between this dialect and
@@ -38,5 +38,10 @@ public override SqlString GetLimitString(SqlString querySqlString, SqlString off
return result.ToSqlString();
}
+
+ // 128 since 12.2. https://stackoverflow.com/a/756569/1178314, will
+ // have to do a 12-2c dialect for exploiting it, or wait for 13.
+ // /
+ //public override int MaxAliasLength => 128;
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Dialect/Oracle8iDialect.cs b/src/NHibernate/Dialect/Oracle8iDialect.cs
index ab629fd8e8e..45e15891cc9 100644
--- a/src/NHibernate/Dialect/Oracle8iDialect.cs
+++ b/src/NHibernate/Dialect/Oracle8iDialect.cs
@@ -533,6 +533,10 @@ public override long TimestampResolutionInTicks
}
}
+ // 30 before 12.1. https://stackoverflow.com/a/756569/1178314
+ ///
+ public override int MaxAliasLength => 30;
+
#region Overridden informational metadata
public override bool SupportsEmptyInList
diff --git a/src/NHibernate/Dialect/PostgreSQL81Dialect.cs b/src/NHibernate/Dialect/PostgreSQL81Dialect.cs
index 05c9592d339..c131396f646 100644
--- a/src/NHibernate/Dialect/PostgreSQL81Dialect.cs
+++ b/src/NHibernate/Dialect/PostgreSQL81Dialect.cs
@@ -116,5 +116,9 @@ public override bool SupportsInsertSelectIdentity
///
public override bool SupportsDateTimeScale => true;
+
+ // Said to be 63 bytes at least since v8.
+ ///
+ public override int MaxAliasLength => 63;
}
}
diff --git a/src/NHibernate/Dialect/SQLiteDialect.cs b/src/NHibernate/Dialect/SQLiteDialect.cs
index e7dbc859aee..03748e0b022 100644
--- a/src/NHibernate/Dialect/SQLiteDialect.cs
+++ b/src/NHibernate/Dialect/SQLiteDialect.cs
@@ -395,6 +395,10 @@ public override bool SupportsForeignKeyConstraintInAlterTable
///
public override bool SupportsConcurrentWritingConnections => false;
+ // Said to be unlimited. http://sqlite.1065341.n5.nabble.com/Max-limits-on-the-following-td37859.html
+ ///
+ public override int MaxAliasLength => 128;
+
[Serializable]
protected class SQLiteCastFunction : CastFunction
{
@@ -407,4 +411,4 @@ protected override bool CastingIsRequired(string sqlType)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Mapping/Column.cs b/src/NHibernate/Mapping/Column.cs
index 9ec4e8c18bf..d807125c547 100644
--- a/src/NHibernate/Mapping/Column.cs
+++ b/src/NHibernate/Mapping/Column.cs
@@ -115,10 +115,14 @@ public string GetQuotedName(Dialect.Dialect d)
return IsQuoted ? d.QuoteForColumnName(_name) : _name;
}
+ // Accommodate the one character suffix appended in AbstractCollectionPersister and
+ // the SelectFragment suffix up to 99 joins.
+ private const int _charactersLeftCount = 4;
///
/// For any column name, generate an alias that is unique to that
/// column name, and also take Dialect.MaxAliasLength into account.
+ /// It keeps four characters left for accommodating additional suffixes.
///
public string GetAlias(Dialect.Dialect dialect)
{
@@ -127,6 +131,7 @@ public string GetAlias(Dialect.Dialect dialect)
private string GetAlias(int maxAliasLength)
{
+ var usableLength = maxAliasLength - _charactersLeftCount;
var name = CanonicalName;
string alias = name;
string suffix = UniqueInteger.ToString() + StringHelper.Underscore;
@@ -148,25 +153,27 @@ private string GetAlias(int maxAliasLength)
// reason, the checks for "_quoted" and "rowid" looks redundant. If you remove
// those checks, then the double checks for total length can be reduced to one.
// But I will leave it like this for now to make it look similar. /Oskar 2016-08-20
- bool useRawName = name.Length + suffix.Length <= maxAliasLength &&
+ bool useRawName = name.Length + suffix.Length <= usableLength &&
!_quoted &&
!StringHelper.EqualsCaseInsensitive(name, "rowid");
if (!useRawName)
{
- if (suffix.Length >= maxAliasLength)
+ if (suffix.Length >= usableLength)
{
throw new MappingException(
- string.Format(
- "Unique suffix {0} length must be less than maximum {1} characters.",
- suffix,
- maxAliasLength));
+ $"Unique suffix {suffix} length must be less than maximum {usableLength} characters.");
}
- if (alias.Length + suffix.Length > maxAliasLength)
- alias = alias.Substring(0, maxAliasLength - suffix.Length);
+ if (alias.Length + suffix.Length > usableLength)
+ alias = alias.Substring(0, usableLength - suffix.Length);
}
return alias + suffix;
}
+ ///
+ /// For any column name, generate an alias that is unique to that
+ /// column name and table, and also take Dialect.MaxAliasLength into account.
+ /// It keeps four characters left for accommodating additional suffixes.
+ ///
public string GetAlias(Dialect.Dialect dialect, Table table)
{
string suffix = table.UniqueInteger.ToString() + StringHelper.Underscore;
diff --git a/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs b/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs
index 528d4230517..42611366c74 100644
--- a/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs
+++ b/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs
@@ -248,7 +248,11 @@ public AbstractCollectionPersister(Mapping.Collection collection, ICacheConcurre
foreach (Column col in collection.Owner.Key.ColumnIterator)
{
keyColumnNames[k] = col.GetQuotedName(dialect);
- keyColumnAliases[k] = col.GetAlias(dialect) + "_owner_"; // Force the alias to be unique in case it conflicts with an alias in the entity
+ // Force the alias to be unique in case it conflicts with an alias in the entity
+ // As per Column.GetAlias, we have 3 characters left for SelectFragment suffix and one for here.
+ // Since suffixes are composed of digits and '_', and GetAlias is already suffixed, adding any other
+ // letter will avoid collision.
+ keyColumnAliases[k] = col.GetAlias(dialect) + "o";
k++;
}
joinColumnNames = new string[collection.Key.ColumnSpan];