TL;DR; Is it a bug that SQL Server allows a scalar UDF to recursively call itself when schema-bound, but only when altered to do so using the CREATE OR ALTER
syntax? Or is it a bug that other syntaxes are disallowed?
A trivial recursive scalar UDF can be constructed as follows
CREATE FUNCTION dbo.Try1 (@i int)
RETURNS int
AS BEGIN
RETURN IIF(@i = 0, 0, @i + dbo.Try1(@i - 1));
END;
As long as this is not schema-bound then this is allowed.
Let's try schema-binding it, we'll do
CREATE FUNCTION dbo.Try2 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN
RETURN IIF(@i = 0, 0, @i + dbo.Try2(@i - 1));
END;
Nope.
Cannot find either column "dbo" or the user-defined function
or aggregate "dbo.Try2", or the name is ambiguous
Create it without recursion then alter it
CREATE OR ALTER FUNCTION dbo.Try3 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN RETURN NULL; END;
ALTER FUNCTION dbo.Try3 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN
RETURN IIF(@i = 0, 0, @i + dbo.Try3(@i - 1));
END;
A different error this time:
Cannot schema bind function 'dbo.Try3' because name 'dbo.Try3' is invalid
for schema binding. Names must be in two-part format
and an object cannot reference itself.
Hmmm, an object cannot reference itself
who invented that rule? It's not in the docs.
Let's try with CREATE OR ALTER
CREATE OR ALTER FUNCTION dbo.Try4 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN
RETURN IIF(@i = 0, 0, @i + dbo.Try4(@i - 1));
END;
Still not.
But if we first do CREATE
without recursion, then do CREATE OR ALTER
(not ALTER
) with recursion then it works
CREATE OR ALTER FUNCTION dbo.Try5 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN RETURN NULL; END;
CREATE OR ALTER FUNCTION dbo.Try5 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN
RETURN IIF(@i = 0, 0, @i + dbo.Try5(@i - 1));
END;
The weird thing is: this doesn't make sense whichever way CREATE OR ALTER
works underneath. If it actually drops and re-creates then why don't we get the first error? And if it alter then why does this last option work?
The funniest bit: If we create it without schema-binding first, then do CREATE OR ALTER WITH SCHEMABINDING
then we get
Cannot schema bind function 'dbo.Try6'. 'dbo.Try6' is not schema bound.
Which is just utter nonsense.
CREATE OR ALTER FUNCTION dbo.Try5
does still go tosqllang.dll!ValidateObjForSchemaBinding
in the same way asALTER FUNCTION dbo.Try3
does but doesn't throw the error.Try6
?sqllang.dll!ValidateObjForSchemaBinding()
again i.stack.imgur.com/ljWLO.png