Consider the following repro example ([fiddle](https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=a7d81b741dbe78aa6fb94b1edec1d78a)):
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);
Obviously, that function should return the first of January 1990 if the input is `1990`, and `NULL` otherwise. Yes, I know that `DATEDIFF(day, @firstOfYear, @firstOfYear) <> 0` is a nonsensical operation. This is a [mcve](https://stackoverflow.com/help/minimal-reproducible-example) to demonstrate a potential bug, not production code.
Now let's execute `SELECT dbo.Repro(0)` on SQL Server 2017 and SQL Server 2019.
**Expected result**: `NULL`.
**Actual result on SQL Server 2017**: `NULL`
**Actual result on SQL Server 2019**:
> Msg 289 Level 16 State 1 Line 1
> Cannot construct data type date, some of the arguments have values which are not valid.
Apparently, SQL Server 2019 executes some of the code below the initial guard clause (`IF @myYear <> 1990`) even if it shouldn't.
My questions:
* Is this expected behavior, or did I find a bug in SQL Server 2019?
* If this is expected behavior, how do I correctly write a guard clause validating input paramters?
This is a bug with the inlining of Scalar UDFs (or perhaps a bug with the query optimiser that is being exposed more by scalar UDF inlining). You can use `WITH INLINE = OFF` to turn off inlining for that function.
Using a variable instead of a constant shows a bit more detail
declare @myYear int = 0
SELECT dbo.Repro(@myYear);
[![enter image description here][1]][1]
- Node 5 defines `Expr1000 = CASE WHEN [@myYear]<>(1990) THEN (1) ELSE (0) END`
- Node 2 defines `[Expr1003] = Scalar Operator(CONVERT_IMPLICIT(datetime,datefromparts([@myYear],(1),(1)),0))`
These expressions are simplified when using the literal `0` to `1` and `CONVERT_IMPLICIT(datetime,datefromparts((0),(1),(1)),0)` respectively.
The `datefromparts(0` will throw the error when evaluated.
- Node 6 defines `Expr1002 = CASE WHEN [Expr1000] = (1) THEN (1) ELSE (0) END`
And `Expr1002` is used as a [passthru predicate][2] on a nested loops join (node 3). On the inside of that nested loops the constant scan (node 7) returns no columns.
So this looks like the same basic issue as
@@@ answer 199
where an expression on the inside of a nested loops protected by a passthru predicate is moved out into an unprotected region.
---
Conor Cunningham MSFT [replied](https://dba.stackexchange.com/questions/265912/sql-server-2019-executes-unreachable-code?noredirect=1&lq=1#comment523590_265917):
> Microsoft is aware and a fix should be available in the next Cumulative Update. Thanks for reporting it, and sorry you hit this issue.
[1]: https://i.stack.imgur.com/XKlAk.png
[2]: https://docs.microsoft.com/en-gb/archive/blogs/craigfr/subqueries-in-case-expressions