-
Notifications
You must be signed in to change notification settings - Fork 934
Generate stable sql objects names #1802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
9241c59
3323db7
e77d02f
9aa4b76
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Configuration; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Linq; | ||
|
@@ -1191,6 +1190,13 @@ private void SecondPassCompileForeignKeys(Table table, ISet<ForeignKey> done) | |
try | ||
{ | ||
fk.AddReferencedTable(referencedClass); | ||
|
||
if (string.IsNullOrEmpty(fk.Name)) | ||
{ | ||
fk.Name = Constraint.GenerateName( | ||
fk.GeneratedConstraintNamePrefix, table, fk.ReferencedTable, fk.Columns); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a heavily simplified port of the Hibernate code, which instead uses name policy generation and bothers with quoting/unquoting (like the things removed in #1703). |
||
} | ||
|
||
fk.AlignColumns(); | ||
} | ||
catch (MappingException me) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,57 @@ public IEnumerable<Column> ColumnIterator | |
get { return columns; } | ||
} | ||
|
||
/// <summary> | ||
/// Generate a name hopefully unique using the table and column names. | ||
/// Static so the name can be generated prior to creating the Constraint. | ||
/// They're cached, keyed by name, in multiple locations. | ||
/// </summary> | ||
/// <param name="prefix">A name prefix for the generated name.</param> | ||
/// <param name="table">The table for which the name is generated.</param> | ||
/// <param name="referencedTable">The referenced table, if any.</param> | ||
/// <param name="columns">The columns for which the name is generated.</param> | ||
/// <returns>The generated name.</returns> | ||
/// <remarks>Hybrid of Hibernate <c>Constraint.generateName</c> and | ||
/// <c>NamingHelper.generateHashedFkName</c>.</remarks> | ||
public static string GenerateName( | ||
string prefix, Table table, Table referencedTable, IEnumerable<Column> columns) | ||
{ | ||
// Use a concatenation that guarantees uniqueness, even if identical names | ||
// exist between all table and column identifiers. | ||
var sb = new StringBuilder("table`").Append(table.Name).Append("`"); | ||
if (referencedTable != null) | ||
sb.Append("references`").Append(referencedTable.Name).Append("`"); | ||
|
||
// Ensure a consistent ordering of columns, regardless of the order | ||
// they were bound. | ||
// Clone the list, as sometimes a set of order-dependent Column | ||
// bindings are given. | ||
var alphabeticalColumns = new List<Column>(columns); | ||
alphabeticalColumns.Sort(ColumnComparator.Instance); | ||
foreach (var column in alphabeticalColumns) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because I have ported the Java code without thinking about it. |
||
{ | ||
var columnName = column == null ? "" : column.Name; | ||
sb.Append("column`").Append(columnName).Append("`"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, here should be CanonicalName prob. |
||
} | ||
// Hash the generated name for avoiding collisions with user choosen names. | ||
// This is not 100% reliable, as hashing may still have a chance of generating | ||
// collisions. | ||
// Hibernate uses MD5 here, which .Net standrad implementation is rejected by | ||
// FIPS enabled machine. Better use a non-cryptographic hash. | ||
var name = prefix + Hasher.HashToString(sb.ToString()); | ||
|
||
return name; | ||
} | ||
|
||
private class ColumnComparator : IComparer<Column> | ||
{ | ||
public static readonly ColumnComparator Instance = new ColumnComparator(); | ||
|
||
public int Compare(Column col1, Column col2) { | ||
return StringComparer.Ordinal.Compare(col1?.Name, col2?.Name); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should compare on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it depends on database. SQL Server 2017 keeps respecting the collation of the database (in fact, of its metadata tables), quoted or not. Quoted identifier being case sensitive is an Oracle feature. So I think it is better taking the name "as is", relying on the user to map its objects in a consistent way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not talking about databases here, but about rules of NHibernate itself. Column has overridden Equals and GetHashCode operators which follow this rule. So we need to be consistent. |
||
} | ||
} | ||
|
||
/// <summary> | ||
/// Adds the <see cref="Column"/> to the <see cref="ICollection"/> of | ||
/// Columns that are part of the constraint. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/* | ||
* Derived from MurmurHash2Simple, | ||
* http://landman-code.blogspot.com/2009/02/c-superfasthash-and-murmurhash2.html | ||
*/ | ||
|
||
/***** BEGIN LICENSE BLOCK ***** | ||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
* | ||
* The contents of this file are subject to the Mozilla Public License Version | ||
* 1.1 (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* http://www.mozilla.org/MPL/ | ||
* | ||
* Software distributed under the License is distributed on an "AS IS" basis, | ||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
* for the specific language governing rights and limitations under the | ||
* License. | ||
* | ||
* The Original Code is HashTableHashing.MurmurHash2. | ||
* | ||
* The Initial Developer of the Original Code is | ||
* Davy Landman. | ||
* Portions created by the Initial Developer are Copyright (C) 2009 | ||
* the Initial Developer. All Rights Reserved. | ||
* | ||
* Contributor(s): | ||
* | ||
* | ||
* Alternatively, the contents of this file may be used under the terms of | ||
* either the GNU General Public License Version 2 or later (the "GPL"), or | ||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
* in which case the provisions of the GPL or the LGPL are applicable instead | ||
* of those above. If you wish to allow use of your version of this file only | ||
* under the terms of either the GPL or the LGPL, and not to allow others to | ||
* use your version of this file under the terms of the MPL, indicate your | ||
* decision by deleting the provisions above and replace them with the notice | ||
* and other provisions required by the GPL or the LGPL. If you do not delete | ||
* the provisions above, a recipient may use your version of this file under | ||
* the terms of any one of the MPL, the GPL or the LGPL. | ||
* | ||
* ***** END LICENSE BLOCK ***** */ | ||
|
||
using System; | ||
using System.Text; | ||
|
||
namespace NHibernate.Util | ||
{ | ||
/// <summary>A stable hasher using MurmurHash2 algorithm.</summary> | ||
internal static class Hasher | ||
{ | ||
internal static string HashToString(string input) | ||
{ | ||
var hash = Hash(input); | ||
return hash.ToString("X"); | ||
} | ||
|
||
internal static uint Hash(string input) | ||
{ | ||
return Hash(Encoding.UTF8.GetBytes(input)); | ||
} | ||
|
||
internal static uint Hash(byte[] data) | ||
{ | ||
return Hash(data, 0xc58f1a7b); | ||
} | ||
|
||
private const uint _m = 0x5bd1e995; | ||
private const int _r = 24; | ||
|
||
internal static uint Hash(byte[] data, uint seed) | ||
{ | ||
var length = data.Length; | ||
if (length == 0) | ||
return 0; | ||
var h = seed ^ (uint) length; | ||
var currentIndex = 0; | ||
while (length >= 4) | ||
{ | ||
var k = BitConverter.ToUInt32(data, currentIndex); | ||
k *= _m; | ||
k ^= k >> _r; | ||
k *= _m; | ||
|
||
h *= _m; | ||
h ^= k; | ||
currentIndex += 4; | ||
length -= 4; | ||
} | ||
|
||
switch (length) | ||
{ | ||
case 3: | ||
h ^= BitConverter.ToUInt16(data, currentIndex); | ||
h ^= (uint) data[currentIndex + 2] << 16; | ||
h *= _m; | ||
break; | ||
case 2: | ||
h ^= BitConverter.ToUInt16(data, currentIndex); | ||
h *= _m; | ||
break; | ||
case 1: | ||
h ^= data[currentIndex]; | ||
h *= _m; | ||
break; | ||
} | ||
|
||
// Do a few final mixes of the hash to ensure the last few | ||
// bytes are well-incorporated. | ||
|
||
h ^= h >> 13; | ||
h *= _m; | ||
h ^= h >> 15; | ||
|
||
return h; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixture is testing
Table.UniqueColumnString
with a non-null referencedTable parameter, case which was no more having any usage in NHibernate. So indeed these tests are obsolete since some times.