diff --git a/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/SchemaValidateTableWithSchemaFixture.cs b/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/SchemaValidateTableWithSchemaFixture.cs
new file mode 100644
index 00000000000..3e719891bfe
--- /dev/null
+++ b/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/SchemaValidateTableWithSchemaFixture.cs
@@ -0,0 +1,120 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using NHibernate.Dialect;
+using NHibernate.Util;
+using NUnit.Framework;
+
+namespace NHibernate.Test.Tools.hbm2ddl.SchemaValidator
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class SchemaValidateTableWithSchemaFixtureAsync : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override string[] Mappings => new[] { "Tools.hbm2ddl.SchemaValidator.VersionWithSchema.hbm.xml" };
+
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ switch (Dialect)
+ {
+ case MsSql2000Dialect _:
+ case PostgreSQLDialect _:
+ case SQLiteDialect _:
+ case MsSqlCeDialect _:
+ return true;
+ default:
+ // Firebird does not support schema. Its current dialect leave table name being schema prefixed,
+ // which causes SQL parse errors. It has a "create schema" command but instead creates a new
+ // database.
+ // MySql does not truly support schema. Its current dialect leave table name being schema prefixed,
+ // which is interpreted as a database name. It has a "create schema" command but instead creates
+ // a new database.
+ // Oracle tightly bounds schema to users.
+ return false;
+ }
+ }
+
+ protected override void CreateSchema()
+ {
+ switch (Dialect)
+ {
+ case MsSql2000Dialect _:
+ case PostgreSQLDialect _:
+ // Must handle the schema manually: mapped database-objects are handled too late.
+ var cnx = Sfi.ConnectionProvider.GetConnection();
+ try
+ {
+ using (var cmd = cnx.CreateCommand())
+ {
+ cmd.CommandText = "create schema Test";
+ cmd.ExecuteNonQuery();
+ }
+ }
+ catch (Exception ex)
+ {
+ // Unfortunateley Assert.Warn and Console.WriteLine at this place seems to be ignored in Rider
+ // viewer.
+ Assert.Warn("Creating the schema failed, assuming it already exists. {0}", ex);
+ Console.WriteLine("Creating the schema failed, assuming it already exists.");
+ Console.WriteLine(ex);
+ }
+ finally
+ {
+ Sfi.ConnectionProvider.CloseConnection(cnx);
+ }
+ break;
+ }
+ base.CreateSchema();
+ }
+
+ protected override void DropSchema()
+ {
+ // SQL-Server does not need this call, but Postgres does not accept dropping a schema carrying objects.
+ base.DropSchema();
+
+ switch (Dialect)
+ {
+ case MsSql2000Dialect _:
+ case PostgreSQLDialect _:
+ var cnx = Sfi.ConnectionProvider.GetConnection();
+ try
+ {
+ using (var cmd = cnx.CreateCommand())
+ {
+ cmd.CommandText = "drop schema Test";
+ cmd.ExecuteNonQuery();
+ }
+ }
+ finally
+ {
+ Sfi.ConnectionProvider.CloseConnection(cnx);
+ }
+ break;
+ }
+ }
+
+ [Test]
+ public async Task ShouldVerifyAsync()
+ {
+ var validator = new Tool.hbm2ddl.SchemaValidator(cfg);
+ try
+ {
+ await (validator.ValidateAsync());
+ }
+ catch (SchemaValidationException sve)
+ {
+ Assert.Fail("Validation failed: {0}.\n{1}", StringHelper.CollectionToString(sve.ValidationErrors), sve);
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/SchemaValidateTableWithSchemaFixture.cs b/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/SchemaValidateTableWithSchemaFixture.cs
new file mode 100644
index 00000000000..87afcd89625
--- /dev/null
+++ b/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/SchemaValidateTableWithSchemaFixture.cs
@@ -0,0 +1,109 @@
+using System;
+using NHibernate.Dialect;
+using NHibernate.Util;
+using NUnit.Framework;
+
+namespace NHibernate.Test.Tools.hbm2ddl.SchemaValidator
+{
+ [TestFixture]
+ public class SchemaValidateTableWithSchemaFixture : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override string[] Mappings => new[] { "Tools.hbm2ddl.SchemaValidator.VersionWithSchema.hbm.xml" };
+
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ switch (Dialect)
+ {
+ case MsSql2000Dialect _:
+ case PostgreSQLDialect _:
+ case SQLiteDialect _:
+ case MsSqlCeDialect _:
+ return true;
+ default:
+ // Firebird does not support schema. Its current dialect leave table name being schema prefixed,
+ // which causes SQL parse errors. It has a "create schema" command but instead creates a new
+ // database.
+ // MySql does not truly support schema. Its current dialect leave table name being schema prefixed,
+ // which is interpreted as a database name. It has a "create schema" command but instead creates
+ // a new database.
+ // Oracle tightly bounds schema to users.
+ return false;
+ }
+ }
+
+ protected override void CreateSchema()
+ {
+ switch (Dialect)
+ {
+ case MsSql2000Dialect _:
+ case PostgreSQLDialect _:
+ // Must handle the schema manually: mapped database-objects are handled too late.
+ var cnx = Sfi.ConnectionProvider.GetConnection();
+ try
+ {
+ using (var cmd = cnx.CreateCommand())
+ {
+ cmd.CommandText = "create schema Test";
+ cmd.ExecuteNonQuery();
+ }
+ }
+ catch (Exception ex)
+ {
+ // Unfortunateley Assert.Warn and Console.WriteLine at this place seems to be ignored in Rider
+ // viewer.
+ Assert.Warn("Creating the schema failed, assuming it already exists. {0}", ex);
+ Console.WriteLine("Creating the schema failed, assuming it already exists.");
+ Console.WriteLine(ex);
+ }
+ finally
+ {
+ Sfi.ConnectionProvider.CloseConnection(cnx);
+ }
+ break;
+ }
+ base.CreateSchema();
+ }
+
+ protected override void DropSchema()
+ {
+ // SQL-Server does not need this call, but Postgres does not accept dropping a schema carrying objects.
+ base.DropSchema();
+
+ switch (Dialect)
+ {
+ case MsSql2000Dialect _:
+ case PostgreSQLDialect _:
+ var cnx = Sfi.ConnectionProvider.GetConnection();
+ try
+ {
+ using (var cmd = cnx.CreateCommand())
+ {
+ cmd.CommandText = "drop schema Test";
+ cmd.ExecuteNonQuery();
+ }
+ }
+ finally
+ {
+ Sfi.ConnectionProvider.CloseConnection(cnx);
+ }
+ break;
+ }
+ }
+
+ [Test]
+ public void ShouldVerify()
+ {
+ var validator = new Tool.hbm2ddl.SchemaValidator(cfg);
+ try
+ {
+ validator.Validate();
+ }
+ catch (SchemaValidationException sve)
+ {
+ Assert.Fail("Validation failed: {0}.\n{1}", StringHelper.CollectionToString(sve.ValidationErrors), sve);
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/VersionWithSchema.hbm.xml b/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/VersionWithSchema.hbm.xml
new file mode 100644
index 00000000000..fe73b2f166e
--- /dev/null
+++ b/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/VersionWithSchema.hbm.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ uid_table
+ next_hi_value_column
+
+
+
+
+
+
+
diff --git a/src/NHibernate/Dialect/MsSqlCeDialect.cs b/src/NHibernate/Dialect/MsSqlCeDialect.cs
index 88c1fa21be1..86407ba2530 100644
--- a/src/NHibernate/Dialect/MsSqlCeDialect.cs
+++ b/src/NHibernate/Dialect/MsSqlCeDialect.cs
@@ -263,7 +263,7 @@ public override bool SupportsLimitOffset
public override IDataBaseSchema GetDataBaseSchema(DbConnection connection)
{
- return new MsSqlCeDataBaseSchema(connection);
+ return new MsSqlCeDataBaseSchema(connection, this);
}
public override SqlString GetLimitString(SqlString querySqlString, SqlString offset, SqlString limit)
diff --git a/src/NHibernate/Dialect/SQLiteDialect.cs b/src/NHibernate/Dialect/SQLiteDialect.cs
index 77d027987f7..802ef14eb6e 100644
--- a/src/NHibernate/Dialect/SQLiteDialect.cs
+++ b/src/NHibernate/Dialect/SQLiteDialect.cs
@@ -190,7 +190,7 @@ protected virtual void RegisterDefaultProperties()
public override Schema.IDataBaseSchema GetDataBaseSchema(DbConnection connection)
{
- return new Schema.SQLiteDataBaseMetaData(connection);
+ return new Schema.SQLiteDataBaseMetaData(connection, this);
}
public override string AddColumnString
diff --git a/src/NHibernate/Dialect/Schema/AbstractDataBaseSchema.cs b/src/NHibernate/Dialect/Schema/AbstractDataBaseSchema.cs
index 5ae17dbd564..dc7caa8e925 100644
--- a/src/NHibernate/Dialect/Schema/AbstractDataBaseSchema.cs
+++ b/src/NHibernate/Dialect/Schema/AbstractDataBaseSchema.cs
@@ -15,20 +15,27 @@ namespace NHibernate.Dialect.Schema
///
public abstract class AbstractDataBaseSchema : IDataBaseSchema
{
- private readonly DbConnection connection;
+ private readonly Dialect _dialect;
- protected AbstractDataBaseSchema(DbConnection connection)
- {
- this.connection = connection;
- }
+ protected AbstractDataBaseSchema(DbConnection connection) : this(connection, null) {}
- protected DbConnection Connection
+ protected AbstractDataBaseSchema(DbConnection connection, Dialect dialect)
{
- get { return connection; }
+ Connection = connection;
+ _dialect = dialect;
}
+ protected DbConnection Connection { get; }
+
public virtual bool IncludeDataTypesInReservedWords => true;
+ ///
+ /// Should be used for searching tables instead of using separately
+ /// the table, schema and catalog names? If , dialect must be provided
+ /// with .
+ ///
+ public virtual bool UseDialectQualifyInsteadOfTableName => false;
+
#region IDataBaseSchema Members
public virtual bool StoresMixedCaseQuotedIdentifiers
@@ -58,8 +65,36 @@ public virtual bool StoresLowerCaseIdentifiers
public virtual DataTable GetTables(string catalog, string schemaPattern, string tableNamePattern, string[] types)
{
+ if (UseDialectQualifyInsteadOfTableName)
+ {
+ var actualTablePattern = GetActualTableName(catalog, schemaPattern, tableNamePattern);
+ var tables = Connection.GetSchema("Tables", new[] { null, null, actualTablePattern });
+
+ // Caller may check the table name of yielded results, we need to patch them
+ foreach (DataRow tableRow in tables.Rows)
+ {
+ var tableName = Convert.ToString(tableRow[ColumnNameForTableName]);
+ if (tableName.Equals(actualTablePattern, StringComparison.InvariantCultureIgnoreCase))
+ {
+ tableRow[ColumnNameForTableName] = tableNamePattern;
+ // Columns are looked-up according to the row table name, and schema and catalog data.
+ // We need to patch schema and catalog for being able to reconstruct the adequate table name.
+ if (!string.IsNullOrEmpty(catalog))
+ {
+ tableRow["TABLE_CATALOG"] = catalog;
+ }
+ if (!string.IsNullOrEmpty(schemaPattern))
+ {
+ tableRow["TABLE_SCHEMA"] = schemaPattern;
+ }
+ }
+ }
+
+ return tables;
+ }
+
var restrictions = new[] { catalog, schemaPattern, tableNamePattern };
- return connection.GetSchema("Tables", restrictions);
+ return Connection.GetSchema("Tables", restrictions);
}
public virtual string ColumnNameForTableName
@@ -72,32 +107,56 @@ public virtual string ColumnNameForTableName
public virtual DataTable GetColumns(string catalog, string schemaPattern, string tableNamePattern,
string columnNamePattern)
{
+ if (UseDialectQualifyInsteadOfTableName)
+ {
+ var actualTablePattern = GetActualTableName(catalog, schemaPattern, tableNamePattern);
+ return Connection.GetSchema("Columns", new[] {null, null, actualTablePattern, columnNamePattern});
+ }
+
var restrictions = new[] {catalog, schemaPattern, tableNamePattern, columnNamePattern};
- return connection.GetSchema("Columns", restrictions);
+ return Connection.GetSchema("Columns", restrictions);
}
public virtual DataTable GetIndexInfo(string catalog, string schemaPattern, string tableName)
{
+ if (UseDialectQualifyInsteadOfTableName)
+ {
+ var actualTableName = GetActualTableName(catalog, schemaPattern, tableName);
+ return Connection.GetSchema("Indexes", new[] {null, null, actualTableName, null});
+ }
+
var restrictions = new[] {catalog, schemaPattern, tableName, null};
- return connection.GetSchema("Indexes", restrictions);
+ return Connection.GetSchema("Indexes", restrictions);
}
public virtual DataTable GetIndexColumns(string catalog, string schemaPattern, string tableName, string indexName)
{
+ if (UseDialectQualifyInsteadOfTableName)
+ {
+ var actualTableName = GetActualTableName(catalog, schemaPattern, tableName);
+ return Connection.GetSchema("IndexColumns", new[] {null, null, actualTableName, indexName, null});
+ }
+
var restrictions = new[] {catalog, schemaPattern, tableName, indexName, null};
- return connection.GetSchema("IndexColumns", restrictions);
+ return Connection.GetSchema("IndexColumns", restrictions);
}
public virtual DataTable GetForeignKeys(string catalog, string schema, string table)
{
+ if (UseDialectQualifyInsteadOfTableName)
+ {
+ var actualTableName = GetActualTableName(catalog, schema, table);
+ return Connection.GetSchema(ForeignKeysSchemaName, new[] {null, null, actualTableName, null});
+ }
+
var restrictions = new[] {catalog, schema, table, null};
- return connection.GetSchema(ForeignKeysSchemaName, restrictions);
+ return Connection.GetSchema(ForeignKeysSchemaName, restrictions);
}
public virtual ISet GetReservedWords()
{
var result = new HashSet(StringComparer.OrdinalIgnoreCase);
- DataTable dtReservedWords = connection.GetSchema(DbMetaDataCollectionNames.ReservedWords);
+ DataTable dtReservedWords = Connection.GetSchema(DbMetaDataCollectionNames.ReservedWords);
foreach (DataRow row in dtReservedWords.Rows)
{
result.Add(row["ReservedWord"].ToString());
@@ -105,7 +164,7 @@ public virtual ISet GetReservedWords()
if (IncludeDataTypesInReservedWords)
{
- DataTable dtTypes = connection.GetSchema(DbMetaDataCollectionNames.DataTypes);
+ DataTable dtTypes = Connection.GetSchema(DbMetaDataCollectionNames.DataTypes);
foreach (DataRow row in dtTypes.Rows)
{
result.Add(row["TypeName"].ToString());
@@ -120,6 +179,16 @@ protected virtual string ForeignKeysSchemaName
get { return "ForeignKeys"; }
}
+ private string GetActualTableName(string catalog, string schemaPattern, string tableNamePattern)
+ {
+ if (_dialect == null)
+ throw new InvalidOperationException($"{this}: cannot qualify table name without the dialect");
+
+ // _dialect is supposed to concatenate catalog and schema with the table name as an
+ // unqualified name instead of actually qualifying it.
+ return _dialect.Qualify(catalog, schemaPattern, tableNamePattern);
+ }
+
#endregion
}
}
diff --git a/src/NHibernate/Dialect/Schema/MsSqlCeMetaData.cs b/src/NHibernate/Dialect/Schema/MsSqlCeMetaData.cs
index 12671a58d67..0269314eda4 100644
--- a/src/NHibernate/Dialect/Schema/MsSqlCeMetaData.cs
+++ b/src/NHibernate/Dialect/Schema/MsSqlCeMetaData.cs
@@ -6,7 +6,16 @@ namespace NHibernate.Dialect.Schema
{
public class MsSqlCeDataBaseSchema: AbstractDataBaseSchema
{
- public MsSqlCeDataBaseSchema(DbConnection connection) : base(connection) {}
+ // Since v5.2
+ [Obsolete("Use overload with dialect argument.")]
+ public MsSqlCeDataBaseSchema(DbConnection connection) : this(connection, Dialect.GetDialect()) {}
+
+ public MsSqlCeDataBaseSchema(DbConnection connection, Dialect dialect) : base(connection, dialect)
+ {
+ UseDialectQualifyInsteadOfTableName = dialect is MsSqlCeDialect;
+ }
+
+ public override bool UseDialectQualifyInsteadOfTableName { get; }
public override ITableMetadata GetTableMetadata(DataRow rs, bool extras)
{
diff --git a/src/NHibernate/Dialect/Schema/SQLiteMetaData.cs b/src/NHibernate/Dialect/Schema/SQLiteMetaData.cs
index 8be4242acce..493e4aa7d07 100644
--- a/src/NHibernate/Dialect/Schema/SQLiteMetaData.cs
+++ b/src/NHibernate/Dialect/Schema/SQLiteMetaData.cs
@@ -6,7 +6,35 @@ namespace NHibernate.Dialect.Schema
{
public class SQLiteDataBaseMetaData : AbstractDataBaseSchema
{
- public SQLiteDataBaseMetaData(DbConnection connection) : base(connection) {}
+ // Since v5.2
+ [Obsolete("Use overload with dialect argument.")]
+ public SQLiteDataBaseMetaData(DbConnection connection) : this(connection, Dialect.GetDialect()) {}
+
+ public SQLiteDataBaseMetaData(DbConnection connection, Dialect dialect) : base(connection, dialect)
+ {
+ UseDialectQualifyInsteadOfTableName = dialect is SQLiteDialect;
+ }
+
+ public override bool UseDialectQualifyInsteadOfTableName { get; }
+
+ public override DataTable GetTables(string catalog, string schemaPattern, string tableNamePattern, string[] types)
+ {
+ var tables = base.GetTables(catalog, schemaPattern, tableNamePattern, types);
+ if (UseDialectQualifyInsteadOfTableName)
+ {
+ foreach (DataRow tableRow in tables.Rows)
+ {
+ var tableName = Convert.ToString(tableRow[ColumnNameForTableName]);
+ if (tableName.Equals(tableNamePattern, StringComparison.InvariantCultureIgnoreCase))
+ {
+ // SQLite indicates "main" here by default, wrecking the other Get methods.
+ tableRow["TABLE_CATALOG"] = string.Empty;
+ }
+ }
+ }
+
+ return tables;
+ }
public override ITableMetadata GetTableMetadata(DataRow rs, bool extras)
{
@@ -93,4 +121,4 @@ public SQLiteForeignKeyMetaData(DataRow rs) : base(rs)
Name = Convert.ToString(rs["CONSTRAINT_NAME"]);
}
}
-}
\ No newline at end of file
+}