|
15 | 15 |
|
16 | 16 | //$Authors = Jiri Cincura (jiri@cincura.net) |
17 | 17 |
|
| 18 | +using System; |
18 | 19 | using System.Linq; |
19 | 20 | using System.Threading.Tasks; |
| 21 | +using FirebirdSql.Data.FirebirdClient; |
| 22 | +using FirebirdSql.EntityFrameworkCore.Firebird.Metadata; |
| 23 | +using FirebirdSql.EntityFrameworkCore.Firebird.Metadata.Internal; |
20 | 24 | using FirebirdSql.EntityFrameworkCore.Firebird.Scaffolding.Internal; |
21 | 25 | using Microsoft.EntityFrameworkCore.Scaffolding; |
| 26 | +using Microsoft.EntityFrameworkCore.Scaffolding.Metadata; |
22 | 27 | using NUnit.Framework; |
23 | 28 |
|
24 | 29 | namespace FirebirdSql.EntityFrameworkCore.Firebird.Tests.Scaffolding; |
25 | 30 | #pragma warning disable EF1001 |
26 | 31 | public class ScaffoldingTests : EntityFrameworkCoreTestsBase |
27 | 32 | { |
| 33 | + public override async Task SetUp() |
| 34 | + { |
| 35 | + await base.SetUp(); |
| 36 | + |
| 37 | + await CreateScaffoldingObjectsAsync(); |
| 38 | + } |
| 39 | + |
28 | 40 | [Test] |
29 | 41 | public void JustCanRun() |
30 | 42 | { |
@@ -107,6 +119,208 @@ public async Task ReadsCorrectFieldType(string dataType) |
107 | 119 | Assert.That(column.StoreType, Is.EqualTo(dataType)); |
108 | 120 | } |
109 | 121 |
|
| 122 | + [Test] |
| 123 | + public void CanScaffoldPrimaryKey() |
| 124 | + { |
| 125 | + var modelFactory = GetModelFactory(); |
| 126 | + var databaseModel = modelFactory.Create(Connection, new DatabaseModelFactoryOptions()); |
| 127 | + var testTable = databaseModel.Tables.Where(t => t.Name.Equals("TEST")).First(); |
| 128 | + |
| 129 | + Assert.NotNull(testTable.PrimaryKey); |
| 130 | + Assert.AreEqual("INT_FIELD", testTable.PrimaryKey.Columns[0].Name); |
| 131 | + } |
| 132 | + |
| 133 | + [Test] |
| 134 | + public void CanScaffoldGeneratedByIdentities() |
| 135 | + { |
| 136 | + var modelFactory = GetModelFactory(); |
| 137 | + var databaseModel = modelFactory.Create(Connection, new DatabaseModelFactoryOptions()); |
| 138 | + var testTable = databaseModel.Tables.Where(t => t.Name == "SCAFFOLD_TEST").First(); |
| 139 | + Assert.NotNull(testTable); |
| 140 | + |
| 141 | + var idDefaultColumn = testTable.Columns.Where(c => c.Name == "ID_DEFAULT").First(); |
| 142 | + Assert.AreEqual(FbIdentityType.GeneratedByDefault, (FbIdentityType)(idDefaultColumn.GetAnnotation(FbAnnotationNames.IdentityType).Value)); |
| 143 | + if (FbTestsSetup.ServerVersionAtLeast(ServerVersion, new Version(4, 0, 0, 0))) |
| 144 | + { |
| 145 | + Assert.IsNull(idDefaultColumn.FindAnnotation(FbAnnotationNames.IdentityStart)); |
| 146 | + Assert.IsNull(idDefaultColumn.FindAnnotation(FbAnnotationNames.IdentityIncrement)); |
| 147 | + |
| 148 | + var testTableFirebird4 = databaseModel.Tables.Where(t => t.Name == "SCAFFOLD_NEW_FB4_TYPES").First(); |
| 149 | + Assert.NotNull(testTableFirebird4); |
| 150 | + |
| 151 | + var idAlwaysColumn = testTableFirebird4.Columns.Where(c => c.Name == "ID_ALWAYS").First(); |
| 152 | + Assert.AreEqual(FbIdentityType.GeneratedAlways, (FbIdentityType)idAlwaysColumn.GetAnnotation(FbAnnotationNames.IdentityType).Value); |
| 153 | + Assert.AreEqual(2, Convert.ToInt32(idAlwaysColumn.GetAnnotation(FbAnnotationNames.IdentityStart).Value)); |
| 154 | + Assert.AreEqual(3, Convert.ToInt32(idAlwaysColumn.GetAnnotation(FbAnnotationNames.IdentityIncrement).Value)); |
| 155 | + } |
| 156 | + } |
| 157 | + |
| 158 | + [Test] |
| 159 | + public void CanScaffoldColumns() |
| 160 | + { |
| 161 | + var modelFactory = GetModelFactory(); |
| 162 | + var databaseModel = modelFactory.Create(Connection, new DatabaseModelFactoryOptions()); |
| 163 | + var testTable = databaseModel.Tables.Where(t => t.Name == "TEST").First(); |
| 164 | + Assert.NotNull(testTable); |
| 165 | + |
| 166 | + var intColumn = testTable.Columns.Where(c => c.Name == "INT_FIELD").First(); |
| 167 | + Assert.AreEqual("INTEGER", intColumn.StoreType); |
| 168 | + Assert.AreEqual("0", intColumn.DefaultValueSql); |
| 169 | + Assert.IsNull(intColumn.FindAnnotation(FbAnnotationNames.IdentityType)); |
| 170 | + |
| 171 | + var charColumn = testTable.Columns.Where(c => c.Name == "CHAR_FIELD").First(); |
| 172 | + Assert.AreEqual("CHAR(30)", charColumn.StoreType); |
| 173 | + |
| 174 | + var varcharColumn = testTable.Columns.Where(c => c.Name == "VARCHAR_FIELD").First(); |
| 175 | + Assert.AreEqual("VARCHAR(100)", varcharColumn.StoreType); |
| 176 | + |
| 177 | + var numericColumn = testTable.Columns.Where(c => c.Name == "NUMERIC_FIELD").First(); |
| 178 | + Assert.AreEqual("NUMERIC(15,2)", numericColumn.StoreType); |
| 179 | + |
| 180 | + var decimalColumn = testTable.Columns.Where(c => c.Name == "DECIMAL_FIELD").First(); |
| 181 | + Assert.AreEqual("DECIMAL(15,2)", decimalColumn.StoreType); |
| 182 | + |
| 183 | + var blobColumn = testTable.Columns.Where(c => c.Name == "BLOB_FIELD").First(); |
| 184 | + Assert.AreEqual("BLOB SUB_TYPE BINARY", blobColumn.StoreType); |
| 185 | + Assert.AreEqual(80, Convert.ToInt32(blobColumn.GetAnnotation(FbAnnotationNames.BlobSegmentSize).Value)); |
| 186 | + |
| 187 | + var clobColumn = testTable.Columns.Where(c => c.Name == "CLOB_FIELD").First(); |
| 188 | + Assert.AreEqual("BLOB SUB_TYPE TEXT", clobColumn.StoreType); |
| 189 | + Assert.AreEqual(80, Convert.ToInt32(clobColumn.GetAnnotation(FbAnnotationNames.BlobSegmentSize).Value)); |
| 190 | + |
| 191 | + var exprColumn = testTable.Columns.Where(c => c.Name == "EXPR_FIELD").First(); |
| 192 | + Assert.AreEqual("(smallint_field * 1000)", exprColumn.ComputedColumnSql); |
| 193 | + |
| 194 | + var csColumn = testTable.Columns.Where(c => c.Name == "CS_FIELD").First(); |
| 195 | + Assert.AreEqual("CHAR(1)", csColumn.StoreType); |
| 196 | + Assert.AreEqual("UNICODE_FSS", csColumn.Collation); |
| 197 | + Assert.AreEqual("UNICODE_FSS", csColumn.GetAnnotation(FbAnnotationNames.CharacterSet).Value.ToString()); |
| 198 | + } |
| 199 | + |
| 200 | + [Test] |
| 201 | + public void CanScaffoldFirebird4DataTypes() |
| 202 | + { |
| 203 | + if (!EnsureServerVersionAtLeast(new Version(4, 0, 0, 0))) |
| 204 | + return; |
| 205 | + |
| 206 | + var modelFactory = GetModelFactory(); |
| 207 | + var databaseModel = modelFactory.Create(Connection, new DatabaseModelFactoryOptions()); |
| 208 | + var testTable = databaseModel.Tables.Where(t => t.Name == "SCAFFOLD_NEW_FB4_TYPES").First(); |
| 209 | + Assert.NotNull(testTable); |
| 210 | + |
| 211 | + var int128Column = testTable.Columns.Where(c => c.Name == "INT128_FIELD").First(); |
| 212 | + Assert.AreEqual("INT128", int128Column.StoreType); |
| 213 | + |
| 214 | + var decFloat16Column = testTable.Columns.Where(c => c.Name == "DECFLOAT_16_FIELD").First(); |
| 215 | + Assert.AreEqual("DECFLOAT(16)", decFloat16Column.StoreType); |
| 216 | + |
| 217 | + var decFloat34Column = testTable.Columns.Where(c => c.Name == "DECFLOAT_34_FIELD").First(); |
| 218 | + Assert.AreEqual("DECFLOAT(34)", decFloat34Column.StoreType); |
| 219 | + |
| 220 | + var timeWithTimeZoneColumn = testTable.Columns.Where(c => c.Name == "TWTZ_FIELD").First(); |
| 221 | + Assert.AreEqual("TIME WITH TIME ZONE", timeWithTimeZoneColumn.StoreType); |
| 222 | + |
| 223 | + var timestampWithTimeZoneColumn = testTable.Columns.Where(c => c.Name == "TSWTZ_FIELD").First(); |
| 224 | + Assert.AreEqual("TIMESTAMP WITH TIME ZONE", timestampWithTimeZoneColumn.StoreType); |
| 225 | + } |
| 226 | + |
| 227 | + async Task CreateScaffoldingObjectsAsync() |
| 228 | + { |
| 229 | + await ExecuteDdlAsync(Connection, "DROP TABLE SCAFFOLD_NEW_FB4_TYPES", true); |
| 230 | + |
| 231 | + await ExecuteDdlAsync(Connection, "DROP TABLE SCAFFOLD_TEST", true); |
| 232 | + |
| 233 | + if (FbTestsSetup.ServerVersionAtLeast(ServerVersion, new Version(4, 0, 0, 0))) |
| 234 | + { |
| 235 | + await ExecuteDdlAsync(Connection, """ |
| 236 | + CREATE TABLE SCAFFOLD_TEST ( |
| 237 | + ID_DEFAULT INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1 INCREMENT BY 1) |
| 238 | + ) |
| 239 | + """ |
| 240 | + ); |
| 241 | + |
| 242 | + await ExecuteDdlAsync(Connection, """ |
| 243 | + CREATE TABLE SCAFFOLD_NEW_FB4_TYPES ( |
| 244 | + ID_ALWAYS INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 2 INCREMENT BY 3), |
| 245 | + INT128_FIELD INT128, |
| 246 | + DECFLOAT_16_FIELD DECFLOAT(16), |
| 247 | + DECFLOAT_34_FIELD DECFLOAT(34), |
| 248 | + TWTZ_FIELD TIME WITH TIME ZONE, |
| 249 | + TSWTZ_FIELD TIMESTAMP WITH TIME ZONE |
| 250 | + ) |
| 251 | + """); |
| 252 | + } |
| 253 | + else |
| 254 | + { |
| 255 | + await ExecuteDdlAsync(Connection, """ |
| 256 | + CREATE TABLE SCAFFOLD_TEST ( |
| 257 | + ID_DEFAULT INTEGER GENERATED BY DEFAULT AS IDENTITY |
| 258 | + ) |
| 259 | + """ |
| 260 | + ); |
| 261 | + } |
| 262 | + } |
| 263 | + |
| 264 | + static async Task ExecuteDdlAsync(FbConnection connection, string ddlScript, bool ignoreErrors = false) |
| 265 | + { |
| 266 | + try |
| 267 | + { |
| 268 | + await using var command = new FbCommand(ddlScript, connection); |
| 269 | + await command.ExecuteNonQueryAsync(); |
| 270 | + } |
| 271 | + catch when (ignoreErrors) |
| 272 | + { |
| 273 | + } |
| 274 | + } |
| 275 | + |
| 276 | + [Test] |
| 277 | + public async Task ExpressionIndexDoesNotBreakScaffolding() |
| 278 | + { |
| 279 | + var tableName = "TEST_EXPR_IDX"; |
| 280 | + |
| 281 | + using var commandTable = Connection.CreateCommand(); |
| 282 | + commandTable.CommandText = $"recreate table {tableName} (ID INTEGER NOT NULL, DATA VARCHAR(100))"; |
| 283 | + await commandTable.ExecuteNonQueryAsync(); |
| 284 | + |
| 285 | + using var commandIndex = Connection.CreateCommand(); |
| 286 | + commandIndex.CommandText = $"create index IDX_EXPR on {tableName} computed by (upper(DATA))"; |
| 287 | + await commandIndex.ExecuteNonQueryAsync(); |
| 288 | + |
| 289 | + var modelFactory = GetModelFactory(); |
| 290 | + var model = modelFactory.Create(Connection.ConnectionString, new DatabaseModelFactoryOptions(new string[] { tableName })); |
| 291 | + var table = model.Tables.Single(x => x.Name == tableName); |
| 292 | + |
| 293 | + Assert.That(table.Indexes, Has.None.Matches<Microsoft.EntityFrameworkCore.Scaffolding.Metadata.DatabaseIndex>(x => x.Name == "IDX_EXPR")); |
| 294 | + } |
| 295 | + |
| 296 | + [Test] |
| 297 | + public async Task RegularIndexScaffoldedAlongsideExpressionIndex() |
| 298 | + { |
| 299 | + var tableName = "TEST_MIX_IDX"; |
| 300 | + |
| 301 | + using var commandTable = Connection.CreateCommand(); |
| 302 | + commandTable.CommandText = $"recreate table {tableName} (ID INTEGER NOT NULL, DATA VARCHAR(100))"; |
| 303 | + await commandTable.ExecuteNonQueryAsync(); |
| 304 | + |
| 305 | + using var commandRegularIndex = Connection.CreateCommand(); |
| 306 | + commandRegularIndex.CommandText = $"create index IDX_REGULAR on {tableName} (DATA)"; |
| 307 | + await commandRegularIndex.ExecuteNonQueryAsync(); |
| 308 | + |
| 309 | + using var commandExprIndex = Connection.CreateCommand(); |
| 310 | + commandExprIndex.CommandText = $"create index IDX_EXPR_MIX on {tableName} computed by (upper(DATA))"; |
| 311 | + await commandExprIndex.ExecuteNonQueryAsync(); |
| 312 | + |
| 313 | + var modelFactory = GetModelFactory(); |
| 314 | + var model = modelFactory.Create(Connection.ConnectionString, new DatabaseModelFactoryOptions(new string[] { tableName })); |
| 315 | + var table = model.Tables.Single(x => x.Name == tableName); |
| 316 | + |
| 317 | + Assert.Multiple(() => |
| 318 | + { |
| 319 | + Assert.That(table.Indexes, Has.Some.Matches<Microsoft.EntityFrameworkCore.Scaffolding.Metadata.DatabaseIndex>(x => x.Name == "IDX_REGULAR")); |
| 320 | + Assert.That(table.Indexes, Has.None.Matches<Microsoft.EntityFrameworkCore.Scaffolding.Metadata.DatabaseIndex>(x => x.Name == "IDX_EXPR_MIX")); |
| 321 | + }); |
| 322 | + } |
| 323 | + |
110 | 324 | static IDatabaseModelFactory GetModelFactory() |
111 | 325 | { |
112 | 326 | return new FbDatabaseModelFactory(); |
|
0 commit comments