Sql-Server

SQL Server 2019 執行無法訪問的程式碼

  • August 17, 2020

[更新:此問題描述了已在SQL Server 2019 的累積更新 5 中修復的錯誤。]


考慮以下複製範例(小提琴):

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);

顯然,如果輸入為1990,則該函式應返回 1990 年 1 月的第一天,NULL否則返回。是的,我知道這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 2019 會執行初始保護子句 ( IF @myYear <> 1990) 下面的一些程式碼,即使它不應該執行。

我的問題:

  • 這是預期的行為,還是我在 SQL Server 2019 中發現了錯誤?
  • 如果這是預期的行為,我該如何正確編寫驗證輸入參數的保護子句?

這是標量 UDF 內聯的錯誤(或者可能是標量 UDF 內聯更多地暴露的查詢優化器的錯誤)。您可以使用WITH INLINE = OFF該函式關閉內聯。

使用變數而不是常量可以顯示更多細節

declare @myYear int = 0

SELECT dbo.Repro(@myYear);

在此處輸入圖像描述

  • 節點 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)不返回任何列。

因此,這看起來與此處的答案相同的基本問題,即受通路謂詞保護的嵌套循環內部的表達式被移出不受保護的區域。

引用自:https://dba.stackexchange.com/questions/265912