SQL Server 2019 执行无法访问的代码

Hei*_*nzi 34 sql-server functions sql-server-2019

[更新:此问题描述了SQL Server 2019 累积更新 5 中已修复的错误。]


考虑以下重现示例(fiddle):

CREATE FUNCTION dbo.Repro (@myYear int)
RETURNS datetime
AS
BEGIN
    IF @myYear <> 1990
    BEGIN
        RETURN NULL
    END

    DECLARE @firstOfYear datetime;
    SET @firstOfYear = DATEFROMPARTS(@myYear, 1, 1);
    
    IF DATEDIFF(day, @firstOfYear, @firstOfYear) <> 0
    BEGIN
        RETURN NULL
    END

    RETURN @firstOfYear
END
SELECT dbo.Repro(0);
Run Code Online (Sandbox Code Playgroud)

显然,如果输入是 ,该函数应该返回 1990 年 1 月的第一天1990NULL否则返回。是的,我知道这DATEDIFF(day, @firstOfYear, @firstOfYear) <> 0是一个荒谬的操作。这是一个演示潜在错误的mcve,而不是生产代码。

现在让我们SELECT dbo.Repro(0)在 SQL Server 2017 和 SQL Server 2019 上执行。

预期结果NULL

SQL Server 2017 的实际结果NULL

SQL Server 2019 的实际结果

消息 289 级别 16 状态 1 第 1 行
无法构造数据类型日期,某些参数的值无效。

显然,SQL Server 2019IF @myYear <> 1990即使不应该执行初始保护子句 ( )下的一些代码。

我的问题:

  • 这是预期的行为,还是我在 SQL Server 2019 中发现了错误?
  • 如果这是预期行为,我如何正确编写验证输入参数的保护子句?

Mar*_*ith 42

这是标量 UDF 内联的错误(或者可能是查询优化器的错误,标量 UDF 内联更多地暴露了该错误)。您可以使用WITH INLINE = OFF关闭该函数的内联。

使用变量而不是常量显示更多细节

declare @myYear int = 0

SELECT dbo.Repro(@myYear);
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • 节点 5 定义 Expr1000 = CASE WHEN [@myYear]<>(1990) THEN (1) ELSE (0) END
  • 节点 2 定义 [Expr1003] = Scalar Operator(CONVERT_IMPLICIT(datetime,datefromparts([@myYear],(1),(1)),0))

当分别使用文字0to1和时,这些表达式得到了简化CONVERT_IMPLICIT(datetime,datefromparts((0),(1),(1)),0)

datefromparts(0计算时将抛出错误。

  • 节点 6 定义 Expr1002 = CASE WHEN [Expr1000] = (1) THEN (1) ELSE (0) END

并且Expr1002用作嵌套循环连接(节点 3)上的传递谓词。在嵌套循环内部,常量扫描(节点 7)不返回任何列。

因此,这看起来与此处的答案相同其中受 passthru 谓词保护的嵌套循环内部的表达式被移出到未受保护的区域中。

  • 仅供参考 Microsoft 知道并且应该在下一个累积更新中提供修复程序。感谢您报告它,很抱歉您遇到了这个问题。 (21认同)