【发布时间】:2021-02-11 08:48:47
【问题描述】:
将 Flyway 与 Microsoft SQL Server 结合使用时,我们观察到this question 中描述的问题。
基本上,像这样的迁移脚本不会在另一部分失败时回滚成功的GO-delimited 批次:
BEGIN TRANSACTION
-- Create a table with two nullable columns
CREATE TABLE [dbo].[t1](
[id] [nvarchar](36) NULL,
[name] [nvarchar](36) NULL
)
-- add one row having one NULL column
INSERT INTO [dbo].[t1] VALUES(NEWID(), NULL)
-- set one column as NOT NULLABLE
-- this fails because of the previous insert
ALTER TABLE [dbo].[t1] ALTER COLUMN [name] [nvarchar](36) NOT NULL
GO
-- create a table as next action, so that we can test whether the rollback happened properly
CREATE TABLE [dbo].[t2](
[id] [nvarchar](36) NOT NULL
)
GO
COMMIT TRANSACTION
在上面的示例中,尽管前面的 ALTER TABLE 语句失败,但正在创建表 t2。
在链接的问题上,提出了以下方法(在飞行路线上下文之外):
-
一个多批处理脚本应该有一个错误处理程序范围,它在出错时回滚事务,并在最后提交。在 TSQL 中,您可以使用动态 sql 来做到这一点
- 动态 SQL 使脚本难以阅读,非常不方便
-
使用 SQLCMD,您可以使用
-b选项在出错时中止脚本- 这可以在 flyway 中使用吗?
-
或者滚动你自己的脚本运行器
- flyway 可能是这种情况吗?是否有特定于 flyway 的配置来启用正确的错误失败?
编辑:替代示例
给定:简单的数据库
BEGIN TRANSACTION
CREATE TABLE [a] (
[a_id] [nvarchar](36) NOT NULL,
[a_name] [nvarchar](100) NOT NULL
);
CREATE TABLE [b] (
[b_id] [nvarchar](36) NOT NULL,
[a_name] [nvarchar](100) NOT NULL
);
INSERT INTO [a] VALUES (NEWID(), 'name-1');
INSERT INTO [b] VALUES (NEWID(), 'name-1'), (NEWID(), 'name-2');
COMMIT TRANSACTION
迁移脚本 1(失败,没有 GO)
BEGIN TRANSACTION
ALTER TABLE [b] ADD [a_id] [nvarchar](36) NULL;
UPDATE [b] SET [a_id] = [a].[a_id] FROM [a] WHERE [a].[a_name] = [b].[a_name];
ALTER TABLE [b] ALTER COLUMN [a_id] [nvarchar](36) NOT NULL;
ALTER TABLE [b] DROP COLUMN [a_name];
COMMIT TRANSACTION
这会导致UPDATE 语句出现错误消息Invalid column name 'a_id'.。
可能的解决方案:在语句之间引入GO
迁移脚本 2(使用 GO:为“快乐情况”工作,但在出现错误时仅部分回滚)
BEGIN TRANSACTION
SET XACT_ABORT ON
GO
ALTER TABLE [b] ADD [a_id] [nvarchar](36) NULL;
GO
UPDATE [b] SET [a_id] = [a].[a_id] FROM [a] WHERE [a].[a_name] = [b].[a_name];
GO
ALTER TABLE [b] ALTER COLUMN [a_id] [nvarchar](36) NOT NULL;
GO
ALTER TABLE [b] DROP COLUMN [a_name];
GO
COMMIT TRANSACTION
- 只要表
[b]中的所有值在表[a]中都有匹配条目,就会执行所需的迁移。 - 在给定的示例中,情况并非如此。 IE。我们得到两个错误:
- 预期:
Cannot insert the value NULL into column 'a_id', table 'test.dbo.b'; column does not allow nulls. UPDATE fails. - 意外:
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION. -
可怕的是:最后一个
ALTER TABLE [b] DROP COLUMN [a_name]语句实际上已执行、提交且未回滚。即由于链接列丢失,之后无法修复此问题。
- 预期:
这种行为实际上是独立于flyway的,可以通过SSMS直接重现。
【问题讨论】: