SqlServer: preserve GO statements in scripted migrations#37632
SqlServer: preserve GO statements in scripted migrations#37632BurakBebek1 wants to merge 1 commit intodotnet:mainfrom
Conversation
|
Am not sure exactly how this relates to e.g. #37521, but the parser here definitely seems insufficient (e.g. when GO is within a string). @AndriySvyryd assigning to you as I think you have more knowledge in what we're doing in this area. |
|
Hi @roji @AndriySvyryd , thanks for the feedback! I’d like to clarify the scope of this PR: This PR fixes script migrations for SQL Server: GO and GO lines are now preserved as text when MigrationsSqlGenerationOptions.Script is used. Previously, script mode incorrectly split GO into multiple batches, causing errors for commands like CREATE VIEW. Normal migration batch parsing is unchanged; other edge cases (e.g., GO in strings or comments) are out of scope. Test added: Script_migration_does_not_treat_GO_as_batch_delimiter. Only SQL Server is affected, since GO is a SQL Server–specific batch separator. |
Summary
When generating script migrations on SQL Server,
GOstatements should be treated as plain SQL text rather than batch delimiters. Currently,GOandGO <count>are incorrectly split even in script mode.This PR ensures that
GOis not interpreted as a batch separator whenisScript == true.Why only SQL Server?
GOis a SQL Server–specific batch separator understood by tools likesqlcmdand SSMS, not by the SQL Server engine itself. Other EF Core providers do not support or recognizeGO, so the issue and fix are scoped to the SQL Server provider only.What changed?
isScriptGOandGO <count>behavior in script migrationsTests
Script_migration_does_not_treat_GO_as_batch_delimiterBehavior Before
CREATE TABLE T1 (Id int);
GO
INSERT INTO T1 VALUES (1);
GO 2
INSERT INTO T1 VALUES (2);
Was interpreted as multiple batches even in script mode, causing formatting differences and incorrect assumptions about execution behavior.
Behavior After
CREATE TABLE T1 (Id int);
GO
INSERT INTO T1 VALUES (1);
GO 2
INSERT INTO T1 VALUES (2);
GO is treated as text, not as a batch delimiter, matching SQL Server tooling expectations for scripts.
Before the Development
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v3.1.3+b1b99bdeb3 (64-bit .NET 11.0.0-preview.2.26080.101)
[xUnit.net 00:00:03.17] Discovering: Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests
[xUnit.net 00:00:11.28] Discovered: Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests
[xUnit.net 00:00:11.34] Starting: Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests
[xUnit.net 00:00:11.95] Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGeneratorTest.Script_migration_does_not_treat_GO_as_batch_delimiter [FAIL]
[xUnit.net 00:00:11.95] Assert.Equal() Failure: Strings differ
[xUnit.net 00:00:11.95] ↓ (pos 63)
[xUnit.net 00:00:11.95] Expected: ···"O T1 VALUES (1);\r\nGO 2\r\n\r\nINSERT INTO T1 "···
[xUnit.net 00:00:11.95] Actual: ···"O T1 VALUES (1);\r\nGO\r\n\r\nINSERT INTO T1 VA"···
[xUnit.net 00:00:11.95] ↑ (pos 63)
[xUnit.net 00:00:11.96] Stack Trace:
[xUnit.net 00:00:11.96] C:\Projects\efcore\test\EFCore.Relational.Specification.Tests\Migrations\MigrationsSqlGeneratorTestBase.cs(777,0): at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGeneratorTestBase.AssertSql(String expected)
[xUnit.net 00:00:11.96] C:\Projects\efcore\test\EFCore.SqlServer.FunctionalTests\Migrations\SqlServerMigrationsSqlGeneratorTest.cs(1823,0): at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGeneratorTest.Script_migration_does_not_treat_GO_as_batch_delimiter()
[xUnit.net 00:00:11.96] at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
[xUnit.net 00:00:11.96] at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
[xUnit.net 00:00:11.96] at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
[xUnit.net 00:00:11.96] Finished: Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests
EFCore.SqlServer.FunctionalTests test net11.0 failed with 1 error(s) (14.2s)
C:\Projects\efcore\test\EFCore.Relational.Specification.Tests\Migrations\MigrationsSqlGeneratorTestBase.cs(777): error TESTERROR:
Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGeneratorTest.Script_migration_does_not_treat_GO_as
_batch_delimiter (551ms): Error Message: Assert.Equal() Failure: Strings differ
↓ (pos 63)
Expected: ···"O T1 VALUES (1);\r\nGO 2\r\n\r\nINSERT INTO T1 "···
Actual: ···"O T1 VALUES (1);\r\nGO\r\n\r\nINSERT INTO T1 VA"···
↑ (pos 63)
Stack Trace:
at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGeneratorTestBase.AssertSql(String expected) in C:\Pro
jects\efcore\test\EFCore.Relational.Specification.Tests\Migrations\MigrationsSqlGeneratorTestBase.cs:line 777
at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGeneratorTest.Script_migration_does_not_treat
_GO_as_batch_delimiter() in C:\Projects\efcore\test\EFCore.SqlServer.FunctionalTests\Migrations\SqlServerMigration
sSqlGeneratorTest.cs:line 1823
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack si
g, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] para
meters, CultureInfo culture)
Test summary: total: 1, failed: 1, succeeded: 0, skipped: 0, duration: 14.1s
Build failed with 1 error(s) in 62.2s
Fixes #37628