Skip to content

Commit 719538d

Browse files
fixup! Fix SQLite validation failure on tables with a schema
Do the same for SQL-Server CE
1 parent 9d83b0c commit 719538d

File tree

5 files changed

+112
-83
lines changed

5 files changed

+112
-83
lines changed

src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/SchemaValidateTableWithSchemaFixture.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Data;
32
using NHibernate.Dialect;
43
using NHibernate.Util;
54
using NUnit.Framework;
@@ -20,8 +19,16 @@ protected override bool AppliesTo(Dialect.Dialect dialect)
2019
case MsSql2000Dialect _:
2120
case PostgreSQLDialect _:
2221
case SQLiteDialect _:
22+
case MsSqlCeDialect _:
2323
return true;
2424
default:
25+
// Firebird does not support schema. Its current dialect leave table name being schema prefixed,
26+
// which causes SQL parse errors. It has a "create schema" command but instead creates a new
27+
// database.
28+
// MySql does not truly support schema. Its current dialect leave table name being schema prefixed,
29+
// which is interpreted as a database name. It has a "create schema" command but instead creates
30+
// a new database.
31+
// Oracle tightly bounds schema to users.
2532
return false;
2633
}
2734
}
@@ -61,7 +68,7 @@ protected override void CreateSchema()
6168

6269
protected override void DropSchema()
6370
{
64-
// SQL-Server do not need this call, but Postgres does not accept dropping a schema carrying objects.
71+
// SQL-Server does not need this call, but Postgres does not accept dropping a schema carrying objects.
6572
base.DropSchema();
6673

6774
switch (Dialect)

src/NHibernate/Dialect/MsSqlCeDialect.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ public override bool SupportsLimitOffset
263263

264264
public override IDataBaseSchema GetDataBaseSchema(DbConnection connection)
265265
{
266-
return new MsSqlCeDataBaseSchema(connection);
266+
return new MsSqlCeDataBaseSchema(connection, this);
267267
}
268268

269269
public override SqlString GetLimitString(SqlString querySqlString, SqlString offset, SqlString limit)

src/NHibernate/Dialect/Schema/AbstractDataBaseSchema.cs

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,27 @@ namespace NHibernate.Dialect.Schema
1515
/// <seealso cref="DbConnection.GetSchema()"/>
1616
public abstract class AbstractDataBaseSchema : IDataBaseSchema
1717
{
18-
private readonly DbConnection connection;
18+
private readonly Dialect _dialect;
1919

20-
protected AbstractDataBaseSchema(DbConnection connection)
21-
{
22-
this.connection = connection;
23-
}
20+
protected AbstractDataBaseSchema(DbConnection connection) : this(connection, null) {}
2421

25-
protected DbConnection Connection
22+
protected AbstractDataBaseSchema(DbConnection connection, Dialect dialect)
2623
{
27-
get { return connection; }
24+
Connection = connection;
25+
_dialect = dialect;
2826
}
2927

28+
protected DbConnection Connection { get; }
29+
3030
public virtual bool IncludeDataTypesInReservedWords => true;
3131

32+
/// <summary>
33+
/// Should <see cref="Dialect.Qualify"/> be used for searching tables instead of using separately
34+
/// the table, schema and catalog names? If <see langword="true" />, dialect must be provided
35+
/// with <see cref="AbstractDataBaseSchema(DbConnection, Dialect)"/>.
36+
/// </summary>
37+
public virtual bool UseDialectQualifyInsteadOfTableName => false;
38+
3239
#region IDataBaseSchema Members
3340

3441
public virtual bool StoresMixedCaseQuotedIdentifiers
@@ -58,8 +65,36 @@ public virtual bool StoresLowerCaseIdentifiers
5865

5966
public virtual DataTable GetTables(string catalog, string schemaPattern, string tableNamePattern, string[] types)
6067
{
68+
if (UseDialectQualifyInsteadOfTableName)
69+
{
70+
var actualTablePattern = GetActualTableName(catalog, schemaPattern, tableNamePattern);
71+
var tables = Connection.GetSchema("Tables", new[] { null, null, actualTablePattern });
72+
73+
// Caller may check the table name of yielded results, we need to patch them
74+
foreach (DataRow tableRow in tables.Rows)
75+
{
76+
var tableName = Convert.ToString(tableRow[ColumnNameForTableName]);
77+
if (tableName.Equals(actualTablePattern, StringComparison.InvariantCultureIgnoreCase))
78+
{
79+
tableRow[ColumnNameForTableName] = tableNamePattern;
80+
// Columns are looked-up according to the row table name, and schema and catalog data.
81+
// We need to patch schema and catalog for being able to reconstruct the adequate table name.
82+
if (!string.IsNullOrEmpty(catalog))
83+
{
84+
tableRow["TABLE_CATALOG"] = catalog;
85+
}
86+
if (!string.IsNullOrEmpty(schemaPattern))
87+
{
88+
tableRow["TABLE_SCHEMA"] = schemaPattern;
89+
}
90+
}
91+
}
92+
93+
return tables;
94+
}
95+
6196
var restrictions = new[] { catalog, schemaPattern, tableNamePattern };
62-
return connection.GetSchema("Tables", restrictions);
97+
return Connection.GetSchema("Tables", restrictions);
6398
}
6499

65100
public virtual string ColumnNameForTableName
@@ -72,40 +107,64 @@ public virtual string ColumnNameForTableName
72107
public virtual DataTable GetColumns(string catalog, string schemaPattern, string tableNamePattern,
73108
string columnNamePattern)
74109
{
110+
if (UseDialectQualifyInsteadOfTableName)
111+
{
112+
var actualTablePattern = GetActualTableName(catalog, schemaPattern, tableNamePattern);
113+
return Connection.GetSchema("Columns", new[] {null, null, actualTablePattern, columnNamePattern});
114+
}
115+
75116
var restrictions = new[] {catalog, schemaPattern, tableNamePattern, columnNamePattern};
76-
return connection.GetSchema("Columns", restrictions);
117+
return Connection.GetSchema("Columns", restrictions);
77118
}
78119

79120
public virtual DataTable GetIndexInfo(string catalog, string schemaPattern, string tableName)
80121
{
122+
if (UseDialectQualifyInsteadOfTableName)
123+
{
124+
var actualTableName = GetActualTableName(catalog, schemaPattern, tableName);
125+
return Connection.GetSchema("Indexes", new[] {null, null, actualTableName, null});
126+
}
127+
81128
var restrictions = new[] {catalog, schemaPattern, tableName, null};
82-
return connection.GetSchema("Indexes", restrictions);
129+
return Connection.GetSchema("Indexes", restrictions);
83130
}
84131

85132
public virtual DataTable GetIndexColumns(string catalog, string schemaPattern, string tableName, string indexName)
86133
{
134+
if (UseDialectQualifyInsteadOfTableName)
135+
{
136+
var actualTableName = GetActualTableName(catalog, schemaPattern, tableName);
137+
return Connection.GetSchema("IndexColumns", new[] {null, null, actualTableName, indexName, null});
138+
}
139+
87140
var restrictions = new[] {catalog, schemaPattern, tableName, indexName, null};
88-
return connection.GetSchema("IndexColumns", restrictions);
141+
return Connection.GetSchema("IndexColumns", restrictions);
89142
}
90143

91144
public virtual DataTable GetForeignKeys(string catalog, string schema, string table)
92145
{
146+
if (UseDialectQualifyInsteadOfTableName)
147+
{
148+
var actualTableName = GetActualTableName(catalog, schema, table);
149+
return Connection.GetSchema(ForeignKeysSchemaName, new[] {null, null, actualTableName, null});
150+
}
151+
93152
var restrictions = new[] {catalog, schema, table, null};
94-
return connection.GetSchema(ForeignKeysSchemaName, restrictions);
153+
return Connection.GetSchema(ForeignKeysSchemaName, restrictions);
95154
}
96155

97156
public virtual ISet<string> GetReservedWords()
98157
{
99158
var result = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
100-
DataTable dtReservedWords = connection.GetSchema(DbMetaDataCollectionNames.ReservedWords);
159+
DataTable dtReservedWords = Connection.GetSchema(DbMetaDataCollectionNames.ReservedWords);
101160
foreach (DataRow row in dtReservedWords.Rows)
102161
{
103162
result.Add(row["ReservedWord"].ToString());
104163
}
105164

106165
if (IncludeDataTypesInReservedWords)
107166
{
108-
DataTable dtTypes = connection.GetSchema(DbMetaDataCollectionNames.DataTypes);
167+
DataTable dtTypes = Connection.GetSchema(DbMetaDataCollectionNames.DataTypes);
109168
foreach (DataRow row in dtTypes.Rows)
110169
{
111170
result.Add(row["TypeName"].ToString());
@@ -120,6 +179,16 @@ protected virtual string ForeignKeysSchemaName
120179
get { return "ForeignKeys"; }
121180
}
122181

182+
private string GetActualTableName(string catalog, string schemaPattern, string tableNamePattern)
183+
{
184+
if (_dialect == null)
185+
throw new InvalidOperationException($"{this}: cannot qualify table name without the dialect");
186+
187+
// _dialect is supposed to concatenate catalog and schema with the table name as an
188+
// unqualified name instead of actually qualifying it.
189+
return _dialect.Qualify(catalog, schemaPattern, tableNamePattern);
190+
}
191+
123192
#endregion
124193
}
125194
}

src/NHibernate/Dialect/Schema/MsSqlCeMetaData.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,16 @@ namespace NHibernate.Dialect.Schema
66
{
77
public class MsSqlCeDataBaseSchema: AbstractDataBaseSchema
88
{
9-
public MsSqlCeDataBaseSchema(DbConnection connection) : base(connection) {}
9+
// Since v5.2
10+
[Obsolete("Use overload with dialect argument.")]
11+
public MsSqlCeDataBaseSchema(DbConnection connection) : this(connection, Dialect.GetDialect()) {}
12+
13+
public MsSqlCeDataBaseSchema(DbConnection connection, Dialect dialect) : base(connection, dialect)
14+
{
15+
UseDialectQualifyInsteadOfTableName = dialect is MsSqlCeDialect;
16+
}
17+
18+
public override bool UseDialectQualifyInsteadOfTableName { get; }
1019

1120
public override ITableMetadata GetTableMetadata(DataRow rs, bool extras)
1221
{

src/NHibernate/Dialect/Schema/SQLiteMetaData.cs

Lines changed: 9 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -10,86 +10,30 @@ public class SQLiteDataBaseMetaData : AbstractDataBaseSchema
1010
[Obsolete("Use overload with dialect argument.")]
1111
public SQLiteDataBaseMetaData(DbConnection connection) : this(connection, Dialect.GetDialect()) {}
1212

13-
public SQLiteDataBaseMetaData(DbConnection connection, Dialect dialect) : base(connection)
13+
public SQLiteDataBaseMetaData(DbConnection connection, Dialect dialect) : base(connection, dialect)
1414
{
15-
_dialect = dialect;
15+
UseDialectQualifyInsteadOfTableName = dialect is SQLiteDialect;
1616
}
1717

18-
private readonly Dialect _dialect;
18+
public override bool UseDialectQualifyInsteadOfTableName { get; }
1919

2020
public override DataTable GetTables(string catalog, string schemaPattern, string tableNamePattern, string[] types)
2121
{
22-
if (_dialect is SQLiteDialect)
22+
var tables = base.GetTables(catalog, schemaPattern, tableNamePattern, types);
23+
if (UseDialectQualifyInsteadOfTableName)
2324
{
24-
// SQLiteDialect concatenates catalog and schema to the table name.
25-
var actualTablePattern = _dialect.Qualify(catalog, schemaPattern, tableNamePattern);
26-
var tables = base.GetTables(null, null, actualTablePattern, types);
27-
// Caller may check the table name of yielded results, we need to patch them
2825
foreach (DataRow tableRow in tables.Rows)
2926
{
3027
var tableName = Convert.ToString(tableRow[ColumnNameForTableName]);
31-
if (tableName.Equals(actualTablePattern, StringComparison.InvariantCultureIgnoreCase))
28+
if (tableName.Equals(tableNamePattern, StringComparison.InvariantCultureIgnoreCase))
3229
{
33-
tableRow[ColumnNameForTableName] = tableNamePattern;
34-
// Columns are looked-up according to the row table name, and schema and catalog data.
35-
// We need to patch schema and catalog for being able to reconstruct the adequate table name.
36-
if (!string.IsNullOrEmpty(catalog))
37-
{
38-
tableRow["TABLE_CATALOG"] = catalog;
39-
}
40-
else
41-
{
42-
// SQLite indicates "main" here by default, wrecking the other Get method
43-
// overrides.
44-
tableRow["TABLE_CATALOG"] = string.Empty;
45-
}
46-
if (!string.IsNullOrEmpty(schemaPattern))
47-
{
48-
tableRow["TABLE_SCHEMA"] = schemaPattern;
49-
}
30+
// SQLite indicates "main" here by default, wrecking the other Get methods.
31+
tableRow["TABLE_CATALOG"] = string.Empty;
5032
}
5133
}
52-
53-
return tables;
54-
}
55-
56-
return base.GetTables(catalog, schemaPattern, tableNamePattern, types);
57-
}
58-
59-
public override DataTable GetColumns(string catalog, string schemaPattern, string tableNamePattern, string columnNamePattern)
60-
{
61-
if (_dialect is SQLiteDialect)
62-
{
63-
// SQLiteDialect concatenates catalog and schema to the table name.
64-
var actualTablePattern = _dialect.Qualify(catalog, schemaPattern, tableNamePattern);
65-
return base.GetColumns(null, null, actualTablePattern, columnNamePattern);
66-
}
67-
68-
return base.GetColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
69-
}
70-
71-
public override DataTable GetForeignKeys(string catalog, string schema, string table)
72-
{
73-
if (_dialect is SQLiteDialect)
74-
{
75-
// SQLiteDialect concatenates catalog and schema to the table name.
76-
var actualTable = _dialect.Qualify(catalog, schema, table);
77-
return base.GetForeignKeys(null, null, actualTable);
78-
}
79-
80-
return base.GetForeignKeys(catalog, schema, table);
81-
}
82-
83-
public override DataTable GetIndexColumns(string catalog, string schemaPattern, string tableName, string indexName)
84-
{
85-
if (_dialect is SQLiteDialect)
86-
{
87-
// SQLiteDialect concatenates catalog and schema to the table name.
88-
var actualTableName = _dialect.Qualify(catalog, schemaPattern, tableName);
89-
return base.GetIndexColumns(null, null, actualTableName, indexName);
9034
}
9135

92-
return base.GetIndexColumns(catalog, schemaPattern, tableName, indexName);
36+
return tables;
9337
}
9438

9539
public override ITableMetadata GetTableMetadata(DataRow rs, bool extras)

0 commit comments

Comments
 (0)