Sql-Server
SQL Server 2019 執行無法訪問的程式碼
[更新:此問題描述了已在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))
分別使用字面量
0
to1
和時,這些表達式被簡化了CONVERT_IMPLICIT(datetime,datefromparts((0),(1),(1)),0)
。
datefromparts(0
評估時將拋出錯誤。
- 節點 6 定義
Expr1002 = CASE WHEN [Expr1000] = (1) THEN (1) ELSE (0) END
並且
Expr1002
用作嵌套循環連接(節點 3)上的通路謂詞。在該嵌套循環的內部,常量掃描(節點 7)不返回任何列。因此,這看起來與此處的答案相同的基本問題,即受通路謂詞保護的嵌套循環內部的表達式被移出不受保護的區域。