Skip to content
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

NH-1285 - Drop/Create script with default_schema/default_catalog fix(SqlServer) #448

Merged
merged 2 commits into from
Jan 11, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
NH-1285 - Drop/Create script with default_schema/default_catalog fix(…
…SqlServer)

NH-2288 - Test updated (fully qualified name)
Typo fixed
feedbacks, new test, comments
previous version restored(failing tests)

Refactor quoting out of Configuration.

Adjust obsoletes, avoid breaking changes.

Missing async regen.

Refactor get schema/catalog.
lnu committed Jan 5, 2018
commit df8b0c75bb9fdd2a23886f355c14094702c5acfe
2 changes: 1 addition & 1 deletion src/NHibernate.Test/Async/NHSpecificTest/NH2288/Fixture.cs
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ private static void CheckDialect(Configuration configuration)
var sb = new StringBuilder(500);
await (su.ExecuteAsync(x => sb.AppendLine(x), false, false, cancellationToken));
string script = sb.ToString();
Assert.That(script, Does.Contain("if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'dbo.[Aclasses_Id_FK]') AND parent_object_id = OBJECT_ID('dbo.Aclass'))"));
Assert.That(script, Does.Contain("if exists (select 1 from nhibernate.sys.objects where object_id = OBJECT_ID(N'nhibernate.dbo.[Aclasses_Id_FK]') and parent_object_id = OBJECT_ID(N'nhibernate.dbo.Aclass'))"));
}

[Test]
178 changes: 135 additions & 43 deletions src/NHibernate.Test/DialectTest/MsSql2005DialectFixture.cs
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ public void GetLimitString()
{
var d = new MsSql2005Dialect();

SqlString str = d.GetLimitString(new SqlString("select distinct c.Contact_Id as Contact1_19_0_, c.Rating as Rating2_19_0_, c.Last_Name as Last_Name3_19_0, c.First_Name as First_Name4_19_0 from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"), new SqlString("111"), new SqlString("222"));
var str = d.GetLimitString(new SqlString("select distinct c.Contact_Id as Contact1_19_0_, c.Rating as Rating2_19_0_, c.Last_Name as Last_Name3_19_0, c.First_Name as First_Name4_19_0 from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"), new SqlString("111"), new SqlString("222"));
Assert.AreEqual(
"SELECT TOP (222) Contact1_19_0_, Rating2_19_0_, Last_Name3_19_0, First_Name4_19_0 FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY q_.Rating2_19_0_ DESC, q_.Last_Name3_19_0, q_.First_Name4_19_0) as __hibernate_sort_row FROM (select distinct c.Contact_Id as Contact1_19_0_, c.Rating as Rating2_19_0_, c.Last_Name as Last_Name3_19_0, c.First_Name as First_Name4_19_0 from dbo.Contact c where COALESCE(c.Rating, 0) > 0) as q_) as query WHERE query.__hibernate_sort_row > 111 ORDER BY query.__hibernate_sort_row",
str.ToString());
@@ -74,19 +74,19 @@ public void OnlyOffsetLimit()
{
var d = new MsSql2005Dialect();

SqlString str = d.GetLimitString(new SqlString("select distinct c.Contact_Id as Contact1_19_0_, c._Rating as Rating2_19_0_ from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"), null, new SqlString("10"));
var str = d.GetLimitString(new SqlString("select distinct c.Contact_Id as Contact1_19_0_, c._Rating as Rating2_19_0_ from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"), null, new SqlString("10"));
Assert.That(str.ToString(), Is.EqualTo("select distinct TOP (10) c.Contact_Id as Contact1_19_0_, c._Rating as Rating2_19_0_ from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"));
}

[Test]
public void NH1187()
{
MsSql2005Dialect d = new MsSql2005Dialect();
SqlString result = d.GetLimitString(new SqlString("select concat(a.Description,', ', a.Description) as desc from Animal a"), new SqlString("111"), new SqlString("222"));
var d = new MsSql2005Dialect();
var result = d.GetLimitString(new SqlString("select concat(a.Description,', ', a.Description) as desc from Animal a"), new SqlString("111"), new SqlString("222"));
Assert.AreEqual("SELECT TOP (222) desc FROM (select concat(a.Description,', ', a.Description) as desc, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row from Animal a) as query WHERE query.__hibernate_sort_row > 111 ORDER BY query.__hibernate_sort_row", result.ToString());

// The test use the function "cast" because cast need the keyWork "as" too
SqlString str = d.GetLimitString(new SqlString("SELECT fish.id, cast('astring, with,comma' as string) as bar FROM fish"), new SqlString("111"), new SqlString("222"));
var str = d.GetLimitString(new SqlString("SELECT fish.id, cast('astring, with,comma' as string) as bar FROM fish"), new SqlString("111"), new SqlString("222"));
Assert.AreEqual(
"SELECT TOP (222) id, bar FROM (SELECT fish.id, cast('astring, with,comma' as string) as bar, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM fish) as query WHERE query.__hibernate_sort_row > 111 ORDER BY query.__hibernate_sort_row",
str.ToString());
@@ -97,7 +97,7 @@ public void NH2809()
{
var d = new MsSql2005Dialect();

string t = d.GetTypeName(new BinarySqlType());
var t = d.GetTypeName(new BinarySqlType());
Assert.That(t, Is.EqualTo("VARBINARY(MAX)"));

t = d.GetTypeName(new BinarySqlType(), SqlClientDriver.MaxSizeForLengthLimitedBinary - 1, 0, 0);
@@ -113,18 +113,18 @@ public void NH2809()
[Test]
public void QuotedAndParenthesisStringTokenizerTests_WithComma_InQuotes()
{
MsSql2005Dialect.QuotedAndParenthesisStringTokenizer tokenizier =
new MsSql2005Dialect.QuotedAndParenthesisStringTokenizer(
var tokenizier =
new Dialect.Dialect.QuotedAndParenthesisStringTokenizer(
new SqlString("select concat(a.Description,', ', a.Description) from Animal a"));
string[] expected = new string[]
var expected = new string[]
{
"select",
"concat(a.Description,', ', a.Description)",
"from",
"Animal",
"a"
};
int current = 0;
var current = 0;
foreach (SqlString token in tokenizier)
{
Assert.AreEqual(expected[current], token.ToString());
@@ -136,10 +136,10 @@ public void QuotedAndParenthesisStringTokenizerTests_WithComma_InQuotes()
[Test]
public void QuotedAndParenthesisStringTokenizerTests_WithFunctionCallContainingComma()
{
MsSql2005Dialect.QuotedAndParenthesisStringTokenizer tokenizier =
new MsSql2005Dialect.QuotedAndParenthesisStringTokenizer(
var tokenizier =
new Dialect.Dialect.QuotedAndParenthesisStringTokenizer(
new SqlString("SELECT fish.id, cast('astring, with,comma' as string) as bar, f FROM fish"));
string[] expected = new string[]
var expected = new string[]
{
"SELECT",
"fish.id",
@@ -152,9 +152,9 @@ public void QuotedAndParenthesisStringTokenizerTests_WithFunctionCallContainingC
"FROM",
"fish"
};
int current = 0;
IList<SqlString> tokens = tokenizier.GetTokens();
foreach (SqlString token in tokens)
var current = 0;
var tokens = tokenizier.GetTokens();
foreach (var token in tokens)
{
Assert.AreEqual(expected[current], token.ToString());
current += 1;
@@ -165,10 +165,10 @@ public void QuotedAndParenthesisStringTokenizerTests_WithFunctionCallContainingC
[Test]
public void QuotedStringTokenizerTests()
{
MsSql2005Dialect.QuotedAndParenthesisStringTokenizer tokenizier =
new MsSql2005Dialect.QuotedAndParenthesisStringTokenizer(
var tokenizier =
new Dialect.Dialect.QuotedAndParenthesisStringTokenizer(
new SqlString("SELECT fish.\"id column\", fish.'fish name' as 'bar\\' column', f FROM fish"));
string[] expected = new string[]
var expected = new string[]
{
"SELECT",
"fish.\"id column\"",
@@ -181,41 +181,104 @@ public void QuotedStringTokenizerTests()
"FROM",
"fish"
};
int current = 0;
IList<SqlString> tokens = tokenizier.GetTokens();
foreach (SqlString token in tokens)
var current = 0;
var tokens = tokenizier.GetTokens();
foreach (var token in tokens)
{
Assert.AreEqual(expected[current], token.ToString());
current += 1;
}
Assert.AreEqual(current, expected.Length);
Assert.That(expected, Has.Length.EqualTo(current));
}

[Test]
public void GetIfExistsDropConstraintTest_without_catalog_without_schema()
{
var dialect = new MsSql2005Dialect();

const string expected = "if exists (select 1 from sys.objects" +
" where object_id = OBJECT_ID(N'[Bar]')" +
" and parent_object_id = OBJECT_ID(N'Foo'))";
var ifExistsDropConstraint = dialect.GetIfExistsDropConstraint(null, null, "Foo", "Bar");
Assert.That(ifExistsDropConstraint, Is.EqualTo(expected));
}

[Test]
public void GetIfExistsDropConstraintTest_without_catalog_without_schema_with_quoted_table()
{
var dialect = new MsSql2005Dialect();

const string expected = "if exists (select 1 from sys.objects" +
" where object_id = OBJECT_ID(N'[Bar]')" +
" and parent_object_id = OBJECT_ID(N'[Foo]'))";
var ifExistsDropConstraint = dialect.GetIfExistsDropConstraint(null, null, "[Foo]", "Bar");
Assert.That(ifExistsDropConstraint, Is.EqualTo(expected));
}

[Test]
public void GetIfExistsDropConstraintTest_with_schema()
{
var dialect = new MsSql2005Dialect();
const string expected = "if exists (select 1 from sys.objects" +
" where object_id = OBJECT_ID(N'Other.[Bar]')" +
" and parent_object_id = OBJECT_ID(N'Other.Foo'))";
var ifExistsDropConstraint = dialect.GetIfExistsDropConstraint(null, "Other", "Foo", "Bar");
Assert.That(ifExistsDropConstraint, Is.EqualTo(expected));
}

[Test]
public void GetIfExistsDropConstraintTest_with_quoted_schema()
{
var dialect = new MsSql2005Dialect();
const string expected = "if exists (select 1 from sys.objects" +
" where object_id = OBJECT_ID(N'[Other].[Bar]')" +
" and parent_object_id = OBJECT_ID(N'[Other].Foo'))";
var ifExistsDropConstraint = dialect.GetIfExistsDropConstraint(null, "[Other]", "Foo", "Bar");
Assert.That(ifExistsDropConstraint, Is.EqualTo(expected));
}

[Test]
public void GetIfExistsDropConstraintTest_with_catalog_without_schema()
{
var dialect = new MsSql2005Dialect();
const string expected = "if exists (select 1 from Catalog.sys.objects" +
" where object_id = OBJECT_ID(N'Catalog..[Bar]')" +
" and parent_object_id = OBJECT_ID(N'Catalog..Foo'))";
var ifExistsDropConstraint = dialect.GetIfExistsDropConstraint("Catalog", null, "Foo", "Bar");
Assert.That(ifExistsDropConstraint, Is.EqualTo(expected));
}

[Test]
public void GetIfExistsDropConstraintTest_with_quoted_catalog_without_schema()
{
var dialect = new MsSql2005Dialect();
const string expected = "if exists (select 1 from [Catalog].sys.objects" +
" where object_id = OBJECT_ID(N'[Catalog]..[Bar]')" +
" and parent_object_id = OBJECT_ID(N'[Catalog]..Foo'))";
var ifExistsDropConstraint = dialect.GetIfExistsDropConstraint("[Catalog]", null, "Foo", "Bar");
Assert.That(ifExistsDropConstraint, Is.EqualTo(expected));
}

[Test]
public void GetIfExistsDropConstraintTest_without_schema()
public void GetIfExistsDropConstraintTest_with_catalog_with_schema()
{
MsSql2005Dialect dialect = new MsSql2005Dialect();
Table foo = new Table("Foo");
string expected = "if exists (select 1 from sys.objects" +
" where object_id = OBJECT_ID(N'[Bar]')" +
" AND parent_object_id = OBJECT_ID('Foo'))";
string ifExistsDropConstraint = dialect.GetIfExistsDropConstraint(foo, "Bar");
System.Console.WriteLine(ifExistsDropConstraint);
Assert.AreEqual(expected, ifExistsDropConstraint);
var dialect = new MsSql2005Dialect();
const string expected = "if exists (select 1 from Catalog.sys.objects" +
" where object_id = OBJECT_ID(N'Catalog.Schema.[Bar]')" +
" and parent_object_id = OBJECT_ID(N'Catalog.Schema.Foo'))";
var ifExistsDropConstraint = dialect.GetIfExistsDropConstraint("Catalog", "Schema", "Foo", "Bar");
Assert.That(ifExistsDropConstraint, Is.EqualTo(expected));
}

[Test]
public void GetIfExistsDropConstraintTest_For_Schema_other_than_dbo()
public void GetIfExistsDropConstraintTest_with_quoted_catalog_quoted_schema_quoted_table()
{
MsSql2005Dialect dialect = new MsSql2005Dialect();
Table foo = new Table("Foo");
foo.Schema = "Other";
string expected = "if exists (select 1 from sys.objects" +
" where object_id = OBJECT_ID(N'Other.[Bar]')" +
" AND parent_object_id = OBJECT_ID('Other.Foo'))";
string ifExistsDropConstraint = dialect.GetIfExistsDropConstraint(foo, "Bar");
System.Console.WriteLine(ifExistsDropConstraint);
Assert.AreEqual(expected, ifExistsDropConstraint);
var dialect = new MsSql2005Dialect();
const string expected = "if exists (select 1 from [Catalog].sys.objects" +
" where object_id = OBJECT_ID(N'[Catalog].[Schema].[Bar]')" +
" and parent_object_id = OBJECT_ID(N'[Catalog].[Schema].[Foo]'))";
var ifExistsDropConstraint = dialect.GetIfExistsDropConstraint("[Catalog]", "[Schema]", "[Foo]", "Bar");
Assert.That(ifExistsDropConstraint, Is.EqualTo(expected));
}

[Test]
@@ -302,5 +365,34 @@ private static void VerifyLimitStringForStoredProcedureCalls(string sql)
limitSql = d.GetLimitString(new SqlString(sql), new SqlString("10"), new SqlString("2"));
Assert.That(limitSql, Is.Null, "Limit and Offset: {0}", sql);
}

[Test]
public void QualifyTableWithCatalogAndWithoutSchema()
{
var d = new MsSql2005Dialect();

var t = new Table
{
Name = "name",
Catalog = "catalog"
};

Assert.That(t.GetQualifiedName(d), Is.EqualTo("catalog..name"));
Assert.That(d.Qualify("catalog", null, "table"), Is.EqualTo("catalog..table"));
}

[Test]
public void QualifyTableWithoutCatalogAndWithoutSchema()
{
var d = new MsSql2005Dialect();

var t = new Table
{
Name = "name"
};

Assert.That(t.GetQualifiedName(d), Is.EqualTo("name"));
Assert.That(d.Qualify(null, null, "table"), Is.EqualTo("table"));
}
}
}
}
2 changes: 1 addition & 1 deletion src/NHibernate.Test/NHSpecificTest/NH2288/Fixture.cs
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ private static void AssertThatCheckOnTableExistenceIsCorrect(Configuration confi
var sb = new StringBuilder(500);
su.Execute(x => sb.AppendLine(x), false, false);
string script = sb.ToString();
Assert.That(script, Does.Contain("if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'dbo.[Aclasses_Id_FK]') AND parent_object_id = OBJECT_ID('dbo.Aclass'))"));
Assert.That(script, Does.Contain("if exists (select 1 from nhibernate.sys.objects where object_id = OBJECT_ID(N'nhibernate.dbo.[Aclasses_Id_FK]') and parent_object_id = OBJECT_ID(N'nhibernate.dbo.Aclass'))"));
}

[Test]
196 changes: 106 additions & 90 deletions src/NHibernate/Cfg/Configuration.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/NHibernate/Cfg/Mappings.cs
Original file line number Diff line number Diff line change
@@ -630,7 +630,7 @@ private string GetLogicalTableName(string schema, string catalog, string physica

public string GetLogicalTableName(Table table)
{
return GetLogicalTableName(table.GetQuotedSchema(), table.Catalog, table.GetQuotedName());
return GetLogicalTableName(table.GetQuotedSchema(), table.GetQuotedCatalog(), table.GetQuotedName());
}

public ResultSetMappingDefinition GetResultSetMapping(string name)
177 changes: 167 additions & 10 deletions src/NHibernate/Dialect/Dialect.cs
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ public abstract partial class Dialect

/// <summary> Characters used for closing quoted sql identifiers </summary>
public const string PossibleClosedQuoteChars = "`'\"]";

private readonly TypeNames _typeNames = new TypeNames();
private readonly TypeNames _hibernateTypeNames = new TypeNames();
private readonly IDictionary<string, string> _properties = new Dictionary<string, string>();
@@ -390,14 +390,14 @@ public virtual string GetAddForeignKeyConstraintString(string constraintName, st
res.Append(" constraint ")
.Append(constraintName)
.Append(" foreign key (")
.Append(StringHelper.Join(StringHelper.CommaSpace, foreignKey))
.Append(string.Join(StringHelper.CommaSpace, foreignKey))
.Append(") references ")
.Append(referencedTable);

if (!referencesPrimaryKey)
{
res.Append(" (")
.Append(StringHelper.Join(StringHelper.CommaSpace, primaryKey))
.Append(string.Join(StringHelper.CommaSpace, primaryKey))
.Append(')');
}

@@ -758,15 +758,22 @@ public virtual string GetDropForeignKeyConstraintString(string constraintName)
return " drop constraint " + constraintName;
}


/// <summary>
/// The syntax that is used to check if a constraint does not exists before creating it
/// </summary>
/// <param name="table">The table.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
// Since v5.1
[Obsolete("Can cause issues when a custom schema is defined (https://nhibernate.jira.com/browse/NH-1285). The new overload with the defaultSchema parameter should be used instead")]
public virtual string GetIfNotExistsCreateConstraint(Table table, string name)
{
return "";
var catalog = table.GetQuotedCatalog(this, null);
var schema = table.GetQuotedSchema(this, null);
var tableName = table.GetQuotedName(this);

return GetIfNotExistsCreateConstraint(catalog, schema, tableName, name);
Copy link
Member

Choose a reason for hiding this comment

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

Since the called method is public too, it will have to check if handling quoting is required too, for cases where it is called by some other code. So quoting here seems redundant. Same for next methods.

Copy link
Member

Choose a reason for hiding this comment

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

Since the called method is public too, it will have to check if handling quoting is required too, for cases where it is called by some other code. So quoting here seems redundant. Same for next methods.

Unless arguments are always supposed to already be with adequate quoting. It seems handled that way.

}

/// <summary>
@@ -776,9 +783,15 @@ public virtual string GetIfNotExistsCreateConstraint(Table table, string name)
/// <param name="table">The table.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
// Since v5.1
[Obsolete("Can cause issues when a custom schema is defined (https://nhibernate.jira.com/browse/NH-1285). The new overload with the defaultSchema parameter should be used instead")]
public virtual string GetIfNotExistsCreateConstraintEnd(Table table, string name)
{
return "";
var catalog = table.GetQuotedCatalog(this, null);
var schema = table.GetQuotedSchema(this, null);
var tableName = table.GetQuotedName(this);

return GetIfNotExistsCreateConstraintEnd(catalog, schema, tableName, name);
}

/// <summary>
@@ -787,9 +800,15 @@ public virtual string GetIfNotExistsCreateConstraintEnd(Table table, string name
/// <param name="table">The table.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
// Since v5.1
[Obsolete("Can cause issues when a custom schema is defined (https://nhibernate.jira.com/browse/NH-1285). The new overload with the defaultSchema parameter should be used instead")]
public virtual string GetIfExistsDropConstraint(Table table, string name)
{
return "";
var catalog = table.GetQuotedCatalog(this, null);
var schema = table.GetQuotedSchema(this, null);
var tableName = table.GetQuotedName(this);

return GetIfExistsDropConstraint(catalog, schema, tableName, name);
}

/// <summary>
@@ -799,7 +818,67 @@ public virtual string GetIfExistsDropConstraint(Table table, string name)
/// <param name="table">The table.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
// Since v5.1
[Obsolete("Can cause issues when a custom schema is defined (https://nhibernate.jira.com/browse/NH-1285). The new overload with the defaultSchema parameter should be used instead")]
public virtual string GetIfExistsDropConstraintEnd(Table table, string name)
{
var catalog = table.GetQuotedCatalog(this, null);
var schema = table.GetQuotedSchema(this, null);
var tableName = table.GetQuotedName(this);

return GetIfExistsDropConstraintEnd(catalog, schema, tableName, name);
}

/// <summary>
/// The syntax that is used to check if a constraint does not exists before creating it
/// </summary>
/// <param name="catalog">The catalog.</param>
/// <param name="schema">The schema.</param>
/// <param name="table">The table.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
public virtual string GetIfNotExistsCreateConstraint(string catalog, string schema, string table, string name)
{
return "";
Copy link
Member

Choose a reason for hiding this comment

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

Here you need to call GetIfExistsDropConstraintEnd(table, name), for example

Copy link
Member

Choose a reason for hiding this comment

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

I realize a bit late that we have a breaking change here.
virtual string GetIfNotExistsCreateConstraint(Table table, string name) has been obsoleted in favor of this new virtual string GetIfNotExistsCreateConstraint(string catalog, string schema, string table, string name) method, which is now used instead of the old in NHibernate code base.

So dialects overriding the obsolete method will cease working as expected, because their override will now just be ignored. It means that such dialects will return an empty string for the new GetIfNotExistsCreateConstraint, since they do not override it. And so such dialects will no more be able to create constraints.

So when obsoleting overridable methods, we cause a breaking change by ceasing using them.

In the present case, since the new default implementation is indeed "no-op", we could try avoiding the breaking change by calling the old obsolete method instead. The old method should then, of course, be reverted to its old "no-op" behavior instead of calling the new implementation. So furthermore all its removed overrides would have to be put in place again for avoiding another breaking change.

But this does not stops here. If we do that, how should we then consider overrides of the new method? These overrides would then be somewhat breaking change too, since they would cause another dialect further down the inheritance chain and overriding itself the obsolete method to cease achieving its override...
But in the case of this PR, removing these new overrides cannot be done without undoing the PR.

So either we fully revert this PR to avoid this kind of breaking change, or we add this as possible breaking change in 5.1, or we bump version to 6.0 (and still add this as possible breaking change).

An additional conclusion to this is that obsoleting overridable methods is in most cases a breaking change. (It could be done without breaking changes only if the new method call the old one and is not overridden in an exposed descendant, unless the new override also call the old method.)

}

/// <summary>
/// The syntax that is used to close the if for a constraint exists check, used
/// for dialects that requires begin/end for ifs
/// </summary>
/// <param name="catalog">The catalog.</param>
/// <param name="schema">The schema.</param>
/// <param name="table">The table.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
public virtual string GetIfNotExistsCreateConstraintEnd(string catalog, string schema, string table, string name)
{
return "";
}

/// <summary>
/// The syntax that is used to check if a constraint exists before dropping it
/// </summary>
/// <param name="catalog">The catalog.</param>
/// <param name="schema">The schema.</param>
/// <param name="table">The table.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
public virtual string GetIfExistsDropConstraint(string catalog, string schema, string table, string name)
{
return "";
}

/// <summary>
/// The syntax that is used to close the if for a constraint exists check, used
/// for dialects that requires begin/end for ifs
/// </summary>
/// <param name="catalog">The catalog.</param>
/// <param name="schema">The schema.</param>
/// <param name="table">The table.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
public virtual string GetIfExistsDropConstraintEnd(string catalog, string schema, string table, string name)
{
return "";
}
@@ -1429,7 +1508,7 @@ IEnumerator<SqlString> IEnumerable<SqlString>.GetEnumerator()

public IEnumerator GetEnumerator()
{
return ((IEnumerable<SqlString>)this).GetEnumerator();
return ((IEnumerable<SqlString>) this).GetEnumerator();
}

public enum TokenizerState
@@ -1641,7 +1720,7 @@ public virtual bool IsQuoted(string name)
return (name[0] == OpenQuote && name[name.Length - 1] == CloseQuote);
}

public virtual string Qualify(string catalog, string schema, string table)
public virtual string Qualify(string catalog, string schema, string name)
{
StringBuilder qualifiedName = new StringBuilder();

@@ -1653,7 +1732,7 @@ public virtual string Qualify(string catalog, string schema, string table)
{
qualifiedName.Append(schema).Append(StringHelper.Dot);
}
return qualifiedName.Append(table).ToString();
return qualifiedName.Append(name).ToString();
}

/// <summary>
@@ -1670,7 +1749,7 @@ public virtual string Qualify(string catalog, string schema, string table)
/// </remarks>
protected virtual string Quote(string name)
{
string quotedName = name.Replace(OpenQuote.ToString(), new string(OpenQuote, 2));
var quotedName = name.Replace(OpenQuote.ToString(), new string(OpenQuote, 2));

// in some dbs the Open and Close Quote are the same chars - if they are
// then we don't have to escape the Close Quote char because we already
@@ -1757,6 +1836,24 @@ public virtual string QuoteForSchemaName(string schemaName)
return IsQuoted(schemaName) ? schemaName : Quote(schemaName);
}

/// <summary>
/// Quotes a name for being used as a catalogname
/// </summary>
/// <param name="catalogName">Name of the catalog</param>
/// <returns>A Quoted name in the format of OpenQuote + catalogName + CloseQuote</returns>
/// <remarks>
/// <p>
/// If the catalogName is already enclosed in the OpenQuote and CloseQuote then this
/// method will return the catalogName that was passed in without going through any
/// Quoting process. So if catalogName is passed in already Quoted make sure that
/// you have escaped all of the chars according to your DataBase's specifications.
/// </p>
/// </remarks>
public virtual string QuoteForCatalogName(string catalogName)
{
return IsQuoted(catalogName) ? catalogName : Quote(catalogName);
}
Copy link
Member

Choose a reason for hiding this comment

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

This looks like a general quoting implementation. I definitely will have to check with local sources what is going on with quoting.


/// <summary>
/// Unquotes and unescapes an already quoted name
/// </summary>
@@ -1829,6 +1926,66 @@ public virtual string[] UnQuote(string[] quoted)
return unquoted;
}

/// <summary>
/// Convert back-tilt quotes in a name for being used as an aliasname.
/// </summary>
/// <param name="aliasName">Name of the alias.</param>
/// <returns>A name with back-tilt quotes converted if any.</returns>
public virtual string ConvertQuotesForAliasName(string aliasName)
Copy link
Member

Choose a reason for hiding this comment

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

Having X methods doing the same isn't necessary.

Copy link
Member

Choose a reason for hiding this comment

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

This is done for consistency with the QuoteFor series, which does exactly the same. I think the idea is to allow a dialect to override some but not all of them with a different implementation, in case a database may have different quoting rules depending of the quoted object type.

If we consider this as not necessary, then we should obsolete the QuoteFor series by the way and keep only a general purpose QuoteFor.

We may improve the pattern by calling the corresponding QuoteFor from ConvertQuotesFor, in order to allow to override only one method instead of both. (But this will cause the convert to do the additional check done in quote for, which it does not need.)

{
return StringHelper.IsBackticksEnclosed(aliasName)
? Quote(StringHelper.PurgeBackticksEnclosing(aliasName))
: aliasName;
}

/// <summary>
/// Convert back-tilt quotes in a name for being used as a columnname.
/// </summary>
/// <param name="columnName">Name of the column.</param>
/// <returns>A name with back-tilt quotes converted if any.</returns>
public virtual string ConvertQuotesForColumnName(string columnName)
{
return StringHelper.IsBackticksEnclosed(columnName)
? Quote(columnName)
: columnName;
}

/// <summary>
/// Convert back-tilt quotes in a name for being used as a tablename.
/// </summary>
/// <param name="tableName">Name of the table.</param>
/// <returns>A name with back-tilt quotes converted if any.</returns>
public virtual string ConvertQuotesForTableName(string tableName)
{
return StringHelper.IsBackticksEnclosed(tableName)
? Quote(tableName)
: tableName;
}

/// <summary>
/// Convert back-tilt quotes in a name for being used as a schemaname.
/// </summary>
/// <param name="schemaName">Name of the schema.</param>
/// <returns>A name with back-tilt quotes converted if any.</returns>
public virtual string ConvertQuotesForSchemaName(string schemaName)
{
return StringHelper.IsBackticksEnclosed(schemaName)
? Quote(schemaName)
: schemaName;
}

/// <summary>
/// Convert back-tilt quotes in a name for being used as a catalogname.
/// </summary>
/// <param name="catalogName">Name of the catalog.</param>
/// <returns>A name with back-tilt quotes converted if any.</returns>
public virtual string ConvertQuotesForCatalogName(string catalogName)
{
return StringHelper.IsBackticksEnclosed(catalogName)
? Quote(catalogName)
: catalogName;
}

#endregion

#region Union subclass support
59 changes: 46 additions & 13 deletions src/NHibernate/Dialect/MsSql2000Dialect.cs
Original file line number Diff line number Diff line change
@@ -554,6 +554,19 @@ public override bool DropTemporaryTableAfterUse()
return true;
}

public override string Qualify(string catalog, string schema, string name)
{
if (!string.IsNullOrEmpty(catalog))
{
return string.Join(".", catalog, schema, name);
}
if (!string.IsNullOrEmpty(schema))
{
return string.Join(".", schema, name);
}
return name;
}

/// <summary />
/// <param name="name"></param>
/// <returns></returns>
@@ -621,25 +634,45 @@ public override long TimestampResolutionInTicks
}
}

public override string GetIfExistsDropConstraint(Table table, string name)
public override string GetIfExistsDropConstraint(string catalog, string schema, string tableName, string name)
{
string selectExistingObject = GetSelectExistingObject(name, table);
string selectExistingObject = GetSelectExistingObject(catalog, schema, tableName, name);
return string.Format(@"if exists ({0})", selectExistingObject);
}

public override string GetIfNotExistsCreateConstraint(string catalog, string schema, string table, string name)
{
string selectExistingObject = GetSelectExistingObject(catalog, schema, table, name);
return string.Format(@"if not exists ({0})", selectExistingObject);
}

// Since v5.1
[Obsolete("Please use overload with catalog and schema parameters")]
protected virtual string GetSelectExistingObject(string name, Table table)
Copy link
Member

Choose a reason for hiding this comment

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

Breaking change, the method must still be there but obsoleted, otherwise it will have to go into 6.0 only.

{
string objName = table.GetQuotedSchemaName(this) + Quote(name);
return string.Format("select 1 from sysobjects where id = OBJECT_ID(N'{0}') AND parent_obj = OBJECT_ID('{1}')",
objName, table.GetQuotedName(this));
var catalog = table.GetQuotedCatalog(this, null);
var schema = table.GetQuotedSchema(this, null);
return GetSelectExistingObject(catalog, schema, table.GetQuotedName(), name);
}

public override string GetIfNotExistsCreateConstraint(Table table, string name)
/// <summary>
/// Returns a string containing the query to check if an object exists
/// </summary>
/// <param name="catalog">The catalong name</param>
/// <param name="schema">The schema name</param>
/// <param name="table">The table name</param>
/// <param name="name">The name of the object</param>
/// <returns></returns>
protected virtual string GetSelectExistingObject(string catalog, string schema, string table, string name)
{
string selectExistingObject = GetSelectExistingObject(name, table);
return string.Format(@"if not exists ({0})", selectExistingObject);
return
string.Format(
"select 1 from {0} where id = OBJECT_ID(N'{1}') and parent_obj = OBJECT_ID(N'{2}')",
Qualify(catalog, "dbo", "sysobjects"),
Qualify(catalog, schema, Quote(name)),
Qualify(catalog, schema, table));
}

[Serializable]
protected class CountBigQueryFunction : ClassicAggregateFunction
{
@@ -733,7 +766,7 @@ public LockHintAppender(MsSql2000Dialect dialect, IDictionary<string, LockMode>
// in various kinds of "FROM table1 alias1, table2 alias2".
_matchRegex = new Regex(" (" + aliasesPattern + ")([, ]|$)");
_unionSubclassRegex = new Regex(@"from\s+\(((?:.|\r|\n)*)\)(?:\s+as)?\s+(?<alias>" + aliasesPattern + ")", RegexOptions.IgnoreCase | RegexOptions.Multiline);
}
}

public SqlString AppendLockHint(SqlString sql)
{
@@ -743,11 +776,11 @@ public SqlString AppendLockHint(SqlString sql)
{
if (part == Parameter.Placeholder)
{
result.Add((Parameter)part);
result.Add((Parameter) part);
continue;
}
}

result.Add(ProcessUnionSubclassCase((string)part) ?? _matchRegex.Replace((string)part, ReplaceMatch));
result.Add(ProcessUnionSubclassCase((string) part) ?? _matchRegex.Replace((string) part, ReplaceMatch));
}

return result.ToSqlString();
36 changes: 10 additions & 26 deletions src/NHibernate/Dialect/MsSql2005Dialect.cs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
using NHibernate.Driver;
using NHibernate.Mapping;
using NHibernate.SqlCommand;
using NHibernate.Util;

namespace NHibernate.Dialect
{
@@ -43,50 +44,33 @@ public override SqlString GetLimitString(SqlString queryString, SqlString offset
/// functionality.
/// </summary>
/// <value><c>true</c></value>
public override bool SupportsLimit
{
get { return true; }
}
public override bool SupportsLimit => true;

/// <summary>
/// Sql Server 2005 supports a query statement that provides <c>LIMIT</c>
/// functionality with an offset.
/// </summary>
/// <value><c>true</c></value>
public override bool SupportsLimitOffset
{
get { return true; }
}
public override bool SupportsLimitOffset => true;

public override bool SupportsVariableLimit
{
get { return true; }
}
public override bool SupportsVariableLimit => true;

protected override string GetSelectExistingObject(string name, Table table)
protected override string GetSelectExistingObject(string catalog, string schema, string table, string name)
{
string schema = table.GetQuotedSchemaName(this);
if (schema != null)
{
schema += ".";
}
string objName = string.Format("{0}{1}", schema, Quote(name));
string parentName = string.Format("{0}{1}", schema, table.GetQuotedName(this));
return
string.Format(
"select 1 from sys.objects where object_id = OBJECT_ID(N'{0}') AND parent_object_id = OBJECT_ID('{1}')", objName,
parentName);
"select 1 from {0} where object_id = OBJECT_ID(N'{1}') and parent_object_id = OBJECT_ID(N'{2}')",
Qualify(catalog, "sys", "objects"),
Qualify(catalog, schema, Quote(name)),
Qualify(catalog, schema, table));
}

/// <summary>
/// Sql Server 2005 supports a query statement that provides <c>LIMIT</c>
/// functionality with an offset.
/// </summary>
/// <value><c>false</c></value>
public override bool UseMaxForLimit
{
get { return false; }
}
public override bool UseMaxForLimit => false;

public override string AppendLockHint(LockMode lockMode, string tableName)
{
41 changes: 21 additions & 20 deletions src/NHibernate/Mapping/Constraint.cs
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@ public int ColumnSpan

public IList<Column> Columns
{
get{return columns;}
get { return columns; }
}

/// <summary>
@@ -99,19 +99,23 @@ public Table Table
/// </returns>
public virtual string SqlDropString(Dialect.Dialect dialect, string defaultCatalog, string defaultSchema)
{
if (IsGenerated(dialect))
{
string ifExists = dialect.GetIfExistsDropConstraint(Table, Name);
string drop =
string.Format("alter table {0} drop constraint {1}", Table.GetQualifiedName(dialect, defaultCatalog, defaultSchema), Name);
string end = dialect.GetIfExistsDropConstraintEnd(Table, Name);

return ifExists + System.Environment.NewLine + drop + System.Environment.NewLine + end;
}
else
if (!IsGenerated(dialect))
{
return null;
}

var catalog = Table.GetQuotedCatalog(dialect, defaultCatalog);
var schema = Table.GetQuotedSchema(dialect, defaultSchema);
var tableName = Table.GetQuotedName(dialect);

return new StringBuilder()
.AppendLine(dialect.GetIfExistsDropConstraint(catalog, schema, tableName, Name))
.Append("alter table ")
.Append(Table.GetQualifiedName(dialect, defaultCatalog, defaultSchema))
.Append(" drop constraint ")
.AppendLine(Name)
.Append(dialect.GetIfExistsDropConstraintEnd(catalog, schema, tableName, Name))
.ToString();
}

/// <summary>
@@ -126,18 +130,15 @@ public virtual string SqlDropString(Dialect.Dialect dialect, string defaultCatal
/// </returns>
public virtual string SqlCreateString(Dialect.Dialect dialect, IMapping p, string defaultCatalog, string defaultSchema)
{
if (IsGenerated(dialect))
{
string constraintString = SqlConstraintString(dialect, Name, defaultCatalog, defaultSchema);
StringBuilder buf = new StringBuilder("alter table ")
.Append(Table.GetQualifiedName(dialect, defaultCatalog, defaultSchema))
.Append(constraintString);
return buf.ToString();
}
else
if (!IsGenerated(dialect))
{
return null;
}

StringBuilder buf = new StringBuilder("alter table ")
.Append(Table.GetQualifiedName(dialect, defaultCatalog, defaultSchema))
.Append(SqlConstraintString(dialect, Name, defaultCatalog, defaultSchema));
return buf.ToString();
}

#endregion
19 changes: 13 additions & 6 deletions src/NHibernate/Mapping/ForeignKey.cs
Original file line number Diff line number Diff line change
@@ -86,11 +86,18 @@ public bool CascadeDeleteEnabled
/// </returns>
public override string SqlDropString(Dialect.Dialect dialect, string defaultCatalog, string defaultSchema)
{
string ifExists = dialect.GetIfExistsDropConstraint(Table, Name);
string drop = string.Format("alter table {0} {1}", Table.GetQualifiedName(dialect, defaultCatalog, defaultSchema),
dialect.GetDropForeignKeyConstraintString(Name));
string end = dialect.GetIfExistsDropConstraintEnd(Table, Name);
return ifExists + System.Environment.NewLine + drop + System.Environment.NewLine + end;
var catalog = Table.GetQuotedCatalog(dialect, defaultCatalog);
var schema = Table.GetQuotedSchema(dialect, defaultSchema);
var quotedName = Table.GetQuotedName(dialect);

return new StringBuilder()
.AppendLine(dialect.GetIfExistsDropConstraint(catalog, schema, quotedName, Name))
.AppendFormat("alter table ")
.Append(Table.GetQualifiedName(dialect, defaultCatalog, defaultSchema))
.Append(" ")
.AppendLine(dialect.GetDropForeignKeyConstraintString(Name))
.Append(dialect.GetIfExistsDropConstraintEnd(catalog, schema, quotedName, Name))
.ToString();
}

#endregion
@@ -190,7 +197,7 @@ public override string ToString()
result.Append(GetType().FullName)
.Append('(')
.Append(Table.Name)
.Append(StringHelper.Join(", " , Columns))
.Append(StringHelper.Join(", ", Columns))
.Append(" ref-columns:")
.Append('(')
.Append(StringHelper.Join(", ", ReferencedColumns))
16 changes: 11 additions & 5 deletions src/NHibernate/Mapping/Index.cs
Original file line number Diff line number Diff line change
@@ -44,10 +44,16 @@ public static string BuildSqlCreateIndexString(Dialect.Dialect dialect, string n

public static string BuildSqlDropIndexString(Dialect.Dialect dialect, Table table, string name, string defaultCatalog, string defaultSchema)
{
string ifExists = dialect.GetIfExistsDropConstraint(table, name);
string drop = string.Format("drop index {0}", StringHelper.Qualify(table.GetQualifiedName(dialect, defaultCatalog, defaultSchema), name));
string end = dialect.GetIfExistsDropConstraintEnd(table, name);
return ifExists + Environment.NewLine + drop + Environment.NewLine + end;
var catalog = table.GetQuotedCatalog(dialect, defaultCatalog);
var schema = table.GetQuotedSchema(dialect, defaultSchema);
var tableName = table.GetQuotedName(dialect);

return new StringBuilder()
.AppendLine(dialect.GetIfExistsDropConstraint(catalog, schema, tableName, name))
.Append("drop index ")
.AppendLine(StringHelper.Qualify(table.GetQualifiedName(dialect, defaultCatalog, defaultSchema), name))
.Append(dialect.GetIfExistsDropConstraintEnd(catalog, schema, tableName, name))
.ToString();
}

/// <summary>
@@ -148,4 +154,4 @@ public override string ToString()
return GetType().FullName + "(" + Name + ")";
}
}
}
}
17 changes: 12 additions & 5 deletions src/NHibernate/Mapping/PrimaryKey.cs
Original file line number Diff line number Diff line change
@@ -75,12 +75,19 @@ public override string SqlConstraintString(Dialect.Dialect d, string constraintN
/// </returns>
public override string SqlDropString(Dialect.Dialect dialect, string defaultCatalog, string defaultSchema)
{
string ifExists = dialect.GetIfExistsDropConstraint(Table, Name);
string drop = string.Format("alter table {0}{1}", Table.GetQualifiedName(dialect, defaultCatalog, defaultSchema), dialect.GetDropPrimaryKeyConstraintString(Name));
string end = dialect.GetIfExistsDropConstraintEnd(Table, Name);
return ifExists + Environment.NewLine + drop + Environment.NewLine + end;
var catalog = Table.GetQuotedCatalog(dialect, defaultCatalog);
var schema = Table.GetQuotedSchema(dialect, defaultSchema);
var tableName = Table.GetQuotedName(dialect);

return new StringBuilder()
.AppendLine(dialect.GetIfExistsDropConstraint(catalog, schema, tableName, Name))
.Append("alter table ")
.Append(Table.GetQualifiedName(dialect, defaultCatalog, defaultSchema))
.AppendLine(dialect.GetDropPrimaryKeyConstraintString(Name))
.Append(dialect.GetIfExistsDropConstraintEnd(catalog, schema, tableName, Name))
.ToString();
}

#endregion
}
}
}
56 changes: 49 additions & 7 deletions src/NHibernate/Mapping/Table.cs
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ public class Table : IRelationalModel
private IKeyValue idValue;
private bool isAbstract;
private bool isSchemaQuoted;
private bool isCatalogQuoted;
private string name;
private bool quoted;
private string schema;
@@ -274,7 +275,18 @@ public bool HasPrimaryKey
public string Catalog
{
get { return catalog; }
set { catalog = value; }
set
{
if (value != null && value[0] == '`')
{
isCatalogQuoted = true;
catalog = value.Substring(1, value.Length - 2);
}
else
{
catalog = value;
Copy link
Member

Choose a reason for hiding this comment

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

For correctness, isCatalogQuoted should be set to false here. But neither Name nor Schema do it, so not doing it is consistent with them. I guess it does not cause issues because normally those properties are only set once per instance.

I am tempted to changed that for all of them, by setting their flag to false in the else. What is your opinion?

}
}
}

public string Comment
@@ -318,6 +330,11 @@ public bool IsSchemaQuoted
get { return isSchemaQuoted; }
}

public bool IsCatalogQuoted
{
get { return isCatalogQuoted; }
}

#region IRelationalModel Members

/// <summary>
@@ -493,9 +510,9 @@ public virtual string GetQualifiedName(Dialect.Dialect dialect, string defaultCa
{
return "( " + subselect + " )";
}
string quotedName = GetQuotedName(dialect);
string usedSchema = schema == null ? defaultSchema : GetQuotedSchema(dialect);
string usedCatalog = catalog ?? defaultCatalog;
var quotedName = GetQuotedName(dialect);
var usedSchema = GetQuotedSchema(dialect, defaultSchema);
var usedCatalog = GetQuotedCatalog(dialect, defaultCatalog);
return dialect.Qualify(usedCatalog, usedSchema, quotedName);
}

@@ -528,19 +545,44 @@ public string GetQuotedSchema()

public string GetQuotedSchema(Dialect.Dialect dialect)
{
return IsSchemaQuoted ? dialect.OpenQuote + schema + dialect.CloseQuote : schema;
return IsSchemaQuoted ? dialect.QuoteForSchemaName(schema) : schema;
}

public string GetQuotedSchema(Dialect.Dialect dialect, string defaultQuotedSchema)
{
return schema == null ? defaultQuotedSchema :
GetQuotedSchema(dialect);
}

/// <summary> returns quoted name as it is in the mapping file.</summary>
public string GetQuotedCatalog()
{
return IsCatalogQuoted ? "`" + catalog + "`" : catalog;
}

public string GetQuotedCatalog(Dialect.Dialect dialect)
{
return IsCatalogQuoted ? dialect.QuoteForCatalogName(catalog) : catalog;
}

public string GetQuotedCatalog(Dialect.Dialect dialect, string defaultQuotedCatalog)
{
return catalog == null ? defaultQuotedCatalog :
GetQuotedCatalog(dialect);
}

/// <summary>
/// Gets the schema for this table in quoted form if it is necessary.
/// </summary>
/// <param name="dialect">
/// The <see cref="Dialect.Dialect" /> that knows how to quote the table name.
/// The <see cref="Dialect.Dialect" /> that knows how to quote the schema name.
/// </param>
/// <returns>
/// The schema name for this table in a form that is safe to use inside
/// of a SQL statement. Quoted if it needs to be, not quoted if it does not need to be.
/// </returns>
// Since v5.1; back-tilts are removed when storing schema: this thing is non-sens.
[Obsolete("This method is no-op and has no usages")]
public string GetQuotedSchemaName(Dialect.Dialect dialect)
{
if (schema == null)
@@ -727,7 +769,7 @@ public UniqueKey GetOrCreateUniqueKey(string keyName)
return uk;
}

public virtual void CreateForeignKeys() {}
public virtual void CreateForeignKeys() { }

public virtual ForeignKey CreateForeignKey(string keyName, IEnumerable<Column> keyColumns, string referencedEntityName)
{
4 changes: 2 additions & 2 deletions src/NHibernate/Mapping/UniqueKey.cs
Original file line number Diff line number Diff line change
@@ -107,10 +107,10 @@ public override bool IsGenerated(Dialect.Dialect dialect)
return true;
foreach (Column column in ColumnIterator)
{
if(column.IsNullable)
if (column.IsNullable)
return false;
}
return true;
}
}
}
}