Skip to content

Commit 6bca2b0

Browse files
authored
Merge pull request #1415 from fredericDelaporte/879
Correct MaxAliasLength for various dialects
2 parents a6f97d0 + 0b20a3b commit 6bca2b0

18 files changed

+108
-24
lines changed

src/NHibernate.Test/MappingTest/ColumnFixture.cs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public void StringSqlType()
4040
Assert.AreEqual("NVARCHAR(100)", column.GetSqlType(_dialect, null));
4141
}
4242

43+
// Should be kept synchronized with Column same constant.
44+
private const int _charactersLeftCount = 4;
4345

4446
[TestCase("xxxxyyyyz")]
4547
[TestCase("xxxxyyyyzz")]
@@ -54,16 +56,15 @@ public void GetAliasRespectsMaxAliasLength(string columnName)
5456
{
5557
var dialect = new GenericDialect();
5658

57-
// Verify test case assumption.
58-
Assert.That(dialect.MaxAliasLength, Is.EqualTo(10));
59+
// Test case is meant for a max length of 10, adjusts name if it is more.
60+
columnName = AdjustColumnNameToMaxLength(columnName, dialect, 10);
5961

6062
var column = new Column(columnName);
6163
string generatedAlias = column.GetAlias(dialect);
6264

63-
Assert.That(generatedAlias, Has.Length.LessThanOrEqualTo(dialect.MaxAliasLength));
65+
Assert.That(generatedAlias, Has.Length.LessThanOrEqualTo(dialect.MaxAliasLength - _charactersLeftCount));
6466
}
6567

66-
6768
[TestCase("xxxxyyyyz")]
6869
[TestCase("xxxxyyyyzz")]
6970
[TestCase("xxxxyyyyzzz")]
@@ -77,15 +78,24 @@ public void GetAliasWithTableSuffixRespectsMaxAliasLength(string columnName)
7778
{
7879
var dialect = new GenericDialect();
7980

80-
// Verify test case assumption.
81-
Assert.That(dialect.MaxAliasLength, Is.EqualTo(10));
81+
// Test case is meant for a max length of 10, adjusts name if it is more.
82+
columnName = AdjustColumnNameToMaxLength(columnName, dialect, 10);
8283

8384
var table = new Table();
8485
var column = new Column(columnName);
8586

8687
string generatedAlias = column.GetAlias(dialect, table);
8788

88-
Assert.That(generatedAlias, Has.Length.LessThanOrEqualTo(dialect.MaxAliasLength));
89+
Assert.That(generatedAlias, Has.Length.LessThanOrEqualTo(dialect.MaxAliasLength - _charactersLeftCount));
90+
}
91+
92+
private static string AdjustColumnNameToMaxLength(string columnName, GenericDialect dialect, int referenceMaxLength)
93+
{
94+
if (dialect.MaxAliasLength > referenceMaxLength)
95+
columnName = new string('w', dialect.MaxAliasLength - referenceMaxLength) + columnName;
96+
else if (dialect.MaxAliasLength < referenceMaxLength)
97+
Assert.Fail("Dialect max alias length is too short for the test.");
98+
return columnName;
8999
}
90100
}
91101
}

src/NHibernate.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
2020
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;</s:String>
2121
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
22+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
2223
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
2324
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
2425
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>

src/NHibernate/Dialect/DB2Dialect.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ public override string ForUpdateString
272272
get { return " for read only with rs"; }
273273
}
274274

275+
// As of DB2 9.5 documentation, limit is 128 bytes which with Unicode names could mean only 32 characters.
276+
/// <inheritdoc />
277+
public override int MaxAliasLength => 32;
278+
275279
#region Overridden informational metadata
276280

277281
public override bool SupportsEmptyInList => false;

src/NHibernate/Dialect/Dialect.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,10 +2282,11 @@ public virtual string LowercaseFunction
22822282
get { return "lower"; }
22832283
}
22842284

2285-
public virtual int MaxAliasLength
2286-
{
2287-
get { return 10; }
2288-
}
2285+
// 18 is the smallest of all dialects we handle.
2286+
/// <summary>
2287+
/// The maximum length a SQL alias can have.
2288+
/// </summary>
2289+
public virtual int MaxAliasLength => 18;
22892290

22902291
/// <summary>
22912292
/// The syntax used to add a column to a table. Note this is deprecated

src/NHibernate/Dialect/FirebirdDialect.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,12 @@ private void RegisterTrigonometricFunctions()
520520
RegisterFunction("tanh", new StandardSQLFunction("tanh", NHibernateUtil.Double));
521521
}
522522

523+
// As of Firebird 2.5 documentation, limit is 30/31 (not all source are concordant), with some
524+
// cases supporting more but considered as bugs and no more tolerated in v3.
525+
// It seems it may be extended to 63 for Firebird v4.
526+
/// <inheritdoc />
527+
public override int MaxAliasLength => 30;
528+
523529
#region Informational metadata
524530

525531
/// <summary>

src/NHibernate/Dialect/InformixDialect.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,10 @@ public override string GetAddForeignKeyConstraintString(string constraintName, s
457457

458458
return res.ToString();
459459
}
460+
461+
// Informix 7 is said on Internet to be limited to 18. (http://www.justskins.com/forums/length-of-columns-names-143294.html)
462+
/// <inheritdoc />
463+
public override int MaxAliasLength => 18;
460464
}
461465

462466
public class IfxViolatedConstraintExtracter : TemplatedViolatedConstraintNameExtracter

src/NHibernate/Dialect/InformixDialect0940.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,8 @@ public override bool SupportsLimitOffset
146146
get { return false; }
147147
}
148148

149+
// Informix 9 is said on Internet to be limited to 128. (http://www.justskins.com/forums/length-of-columns-names-143294.html)
150+
/// <inheritdoc />
151+
public override int MaxAliasLength => 128;
149152
};
150-
}
153+
}

src/NHibernate/Dialect/IngresDialect.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ public IngresDialect()
5353
DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.IngresDriver";
5454
}
5555

56+
// Ingres 10.2 supports 256 bytes (so worst unicode case would mean 64 characters), but I am unable to find
57+
// the limit for older versions, excepted many various sites mention a 32 length limit.
58+
// https://unifaceinfo.com/docs/0906/Uniface_Library_HTML/ulibrary/INS_NAMING_RULES_8EEFC1A489331BF969D2A8AA36AF2832.html
59+
// There are traces of a ticket for increasing this in version 10: http://lists.ingres.com/pipermail/bugs/2010-May/000052.html
60+
// This dialect seems to target version below 9, since there is Ingres9Dialect deriving from it.
61+
// So sticking to 32.
62+
/// <inheritdoc />
63+
public override int MaxAliasLength => 32;
64+
5665
#region Overridden informational metadata
5766

5867
public override bool SupportsEmptyInList => false;

src/NHibernate/Dialect/MsSql2000Dialect.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,11 @@ public override bool SupportsSqlBatches
677677
get { return true; }
678678
}
679679

680+
// Was 30 in "earlier version", without telling to which version the document apply.
681+
// https://msdn.microsoft.com/en-us/library/ms191240.aspx#Anchor_3
682+
/// <inheritdoc />
683+
public override int MaxAliasLength => 30;
684+
680685
#region Overridden informational metadata
681686

682687
public override bool SupportsEmptyInList => false;

src/NHibernate/Dialect/MsSql2005Dialect.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ public override string AppendLockHint(LockMode lockMode, string tableName)
103103
return tableName;
104104
}
105105

106+
// SQL Server 2005 supports 128.
107+
/// <inheritdoc />
108+
public override int MaxAliasLength => 128;
109+
106110
#region Overridden informational metadata
107111

108112
/// <summary>

src/NHibernate/Dialect/MsSqlCeDialect.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,10 @@ public override long TimestampResolutionInTicks
344344
}
345345
}
346346

347+
// SQL Server 3.5 supports 128.
348+
/// <inheritdoc />
349+
public override int MaxAliasLength => 128;
350+
347351
#region Informational metadata
348352

349353
/// <summary>

src/NHibernate/Dialect/MySQL5Dialect.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,10 @@ public override bool SupportsInsertSelectIdentity
6161
{
6262
get { return true; }
6363
}
64+
65+
// At least MySQL 5 is said to support 64 characters for columns, but 5.7 supports 256 for aliases.
66+
// 64 seems quite good enough, being conservative.
67+
/// <inheritdoc />
68+
public override int MaxAliasLength => 64;
6469
}
6570
}

src/NHibernate/Dialect/Oracle12cDialect.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace NHibernate.Dialect
44
{
55
/// <summary>
6-
/// A dialect specifically for use with Oracle 10g.
6+
/// A dialect specifically for use with Oracle 12c.
77
/// </summary>
88
/// <remarks>
99
/// The main difference between this dialect and <see cref="Oracle12cDialect"/>
@@ -38,5 +38,10 @@ public override SqlString GetLimitString(SqlString querySqlString, SqlString off
3838

3939
return result.ToSqlString();
4040
}
41+
42+
// 128 since 12.2. https://stackoverflow.com/a/756569/1178314, will
43+
// have to do a 12-2c dialect for exploiting it, or wait for 13.
44+
// / <inheritdoc />
45+
//public override int MaxAliasLength => 128;
4146
}
42-
}
47+
}

src/NHibernate/Dialect/Oracle8iDialect.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,10 @@ public override long TimestampResolutionInTicks
533533
}
534534
}
535535

536+
// 30 before 12.1. https://stackoverflow.com/a/756569/1178314
537+
/// <inheritdoc />
538+
public override int MaxAliasLength => 30;
539+
536540
#region Overridden informational metadata
537541

538542
public override bool SupportsEmptyInList

src/NHibernate/Dialect/PostgreSQL81Dialect.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,9 @@ public override bool SupportsInsertSelectIdentity
116116

117117
/// <inheritdoc />
118118
public override bool SupportsDateTimeScale => true;
119+
120+
// Said to be 63 bytes at least since v8.
121+
/// <inheritdoc />
122+
public override int MaxAliasLength => 63;
119123
}
120124
}

src/NHibernate/Dialect/SQLiteDialect.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,10 @@ public override bool SupportsForeignKeyConstraintInAlterTable
395395
/// </remarks>
396396
public override bool SupportsConcurrentWritingConnections => false;
397397

398+
// Said to be unlimited. http://sqlite.1065341.n5.nabble.com/Max-limits-on-the-following-td37859.html
399+
/// <inheritdoc />
400+
public override int MaxAliasLength => 128;
401+
398402
[Serializable]
399403
protected class SQLiteCastFunction : CastFunction
400404
{
@@ -407,4 +411,4 @@ protected override bool CastingIsRequired(string sqlType)
407411
}
408412
}
409413
}
410-
}
414+
}

src/NHibernate/Mapping/Column.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,14 @@ public string GetQuotedName(Dialect.Dialect d)
115115
return IsQuoted ? d.QuoteForColumnName(_name) : _name;
116116
}
117117

118+
// Accommodate the one character suffix appended in AbstractCollectionPersister and
119+
// the SelectFragment suffix up to 99 joins.
120+
private const int _charactersLeftCount = 4;
118121

119122
/// <summary>
120123
/// For any column name, generate an alias that is unique to that
121124
/// column name, and also take Dialect.MaxAliasLength into account.
125+
/// It keeps four characters left for accommodating additional suffixes.
122126
/// </summary>
123127
public string GetAlias(Dialect.Dialect dialect)
124128
{
@@ -127,6 +131,7 @@ public string GetAlias(Dialect.Dialect dialect)
127131

128132
private string GetAlias(int maxAliasLength)
129133
{
134+
var usableLength = maxAliasLength - _charactersLeftCount;
130135
var name = CanonicalName;
131136
string alias = name;
132137
string suffix = UniqueInteger.ToString() + StringHelper.Underscore;
@@ -148,25 +153,27 @@ private string GetAlias(int maxAliasLength)
148153
// reason, the checks for "_quoted" and "rowid" looks redundant. If you remove
149154
// those checks, then the double checks for total length can be reduced to one.
150155
// But I will leave it like this for now to make it look similar. /Oskar 2016-08-20
151-
bool useRawName = name.Length + suffix.Length <= maxAliasLength &&
156+
bool useRawName = name.Length + suffix.Length <= usableLength &&
152157
!_quoted &&
153158
!StringHelper.EqualsCaseInsensitive(name, "rowid");
154159
if (!useRawName)
155160
{
156-
if (suffix.Length >= maxAliasLength)
161+
if (suffix.Length >= usableLength)
157162
{
158163
throw new MappingException(
159-
string.Format(
160-
"Unique suffix {0} length must be less than maximum {1} characters.",
161-
suffix,
162-
maxAliasLength));
164+
$"Unique suffix {suffix} length must be less than maximum {usableLength} characters.");
163165
}
164-
if (alias.Length + suffix.Length > maxAliasLength)
165-
alias = alias.Substring(0, maxAliasLength - suffix.Length);
166+
if (alias.Length + suffix.Length > usableLength)
167+
alias = alias.Substring(0, usableLength - suffix.Length);
166168
}
167169
return alias + suffix;
168170
}
169171

172+
/// <summary>
173+
/// For any column name, generate an alias that is unique to that
174+
/// column name and table, and also take Dialect.MaxAliasLength into account.
175+
/// It keeps four characters left for accommodating additional suffixes.
176+
/// </summary>
170177
public string GetAlias(Dialect.Dialect dialect, Table table)
171178
{
172179
string suffix = table.UniqueInteger.ToString() + StringHelper.Underscore;

src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,11 @@ public AbstractCollectionPersister(Mapping.Collection collection, ICacheConcurre
248248
foreach (Column col in collection.Owner.Key.ColumnIterator)
249249
{
250250
keyColumnNames[k] = col.GetQuotedName(dialect);
251-
keyColumnAliases[k] = col.GetAlias(dialect) + "_owner_"; // Force the alias to be unique in case it conflicts with an alias in the entity
251+
// Force the alias to be unique in case it conflicts with an alias in the entity
252+
// As per Column.GetAlias, we have 3 characters left for SelectFragment suffix and one for here.
253+
// Since suffixes are composed of digits and '_', and GetAlias is already suffixed, adding any other
254+
// letter will avoid collision.
255+
keyColumnAliases[k] = col.GetAlias(dialect) + "o";
252256
k++;
253257
}
254258
joinColumnNames = new string[collection.Key.ColumnSpan];

0 commit comments

Comments
 (0)