Skip to content

Commit

Permalink
sql: enable support for udfs calling other udfs
Browse files Browse the repository at this point in the history
Previously, we blocked the ability of one UDF to call another UDF due to
a lack of dependency tracking. This patch removes this limitation by
allowing user-defined functions to reference other user-defined functions
in their bodies.

Informs: cockroachdb#88198
Release note: None
  • Loading branch information
fqazi authored and jasminejsun committed Mar 18, 2024
1 parent 34bf59d commit 8b7ec1c
Show file tree
Hide file tree
Showing 18 changed files with 131 additions and 23 deletions.
7 changes: 7 additions & 0 deletions pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/sql/logictest/testdata/logic_test/procedure
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ CREATE OR REPLACE PROCEDURE p2() LANGUAGE SQL AS $$
CALL p();
$$

statement error pgcode 42883 unknown function: p\(\)
statement error pgcode 42809 p\(\) is a procedure
CREATE FUNCTION err(i INT) RETURNS VOID LANGUAGE SQL AS 'SELECT p()'

statement error pgcode 0A000 unimplemented: CALL usage inside a function definition
Expand Down
20 changes: 20 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/udf_calling_udf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
statement ok
CREATE FUNCTION lower_hello() RETURNS STRING LANGUAGE SQL AS $$ SELECT lower('hello') $$;

statement ok
CREATE FUNCTION upper_hello() RETURNS STRING LANGUAGE SQL AS $$ SELECT upper(lower_hello()) $$;

statement ok
CREATE FUNCTION nested_udf_for_from() RETURNS STRING LANGUAGE SQL AS $$ SELECT * FROM upper_hello()$$;

statement ok
CREATE FUNCTION concat_hello() RETURNS STRING LANGUAGE SQL AS $$ SELECT upper(lower_hello()) || upper_hello() || lower_hello() $$;

query TTTT
SELECT upper_hello(), nested_udf_for_from(), lower_hello(), concat_hello()
----
HELLO HELLO hello HELLOHELLOhello

# Validate recursion doesn't work today.
statement error pgcode 42883 unknown function: recursion_check\(\)
CREATE FUNCTION recursion_check() RETURNS STRING LANGUAGE SQL AS $$ SELECT recursion_check() $$;
12 changes: 8 additions & 4 deletions pkg/sql/logictest/testdata/logic_test/udf_oid_ref
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,21 @@ CREATE FUNCTION f_in_udf() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;
let $fn_oid
SELECT oid FROM pg_catalog.pg_proc WHERE proname = 'f_in_udf'

# TODO(chengxiong,mgartner): Fix this test when we enable support of calling UDFs from UDFs.
statement error pgcode 42883 pq: function \d+ not found
statement ok
CREATE FUNCTION f_using_udf() RETURNS INT LANGUAGE SQL AS $$ SELECT [FUNCTION $fn_oid]() $$;

query I
SELECT f_using_udf()
----
1

# 814 is the OID of builtin function "length" with signature, and it's ok to
# call it from a UDF.
statement ok
CREATE FUNCTION f_using_udf() RETURNS INT LANGUAGE SQL AS $$ SELECT [FUNCTION 814]('abc') $$;
CREATE FUNCTION f_using_udf_2() RETURNS INT LANGUAGE SQL AS $$ SELECT [FUNCTION 814]('abc') $$;

query I
SELECT f_using_udf();
SELECT f_using_udf_2();
----
3

Expand Down
21 changes: 21 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/udf_rewrite
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,24 @@ statement ok
DROP PROCEDURE p_rewrite();

subtest end

subtest rewrite_udf_calling_duf

statement ok
CREATE FUNCTION nested_func() RETURNS INT AS $$
SELECT 1;
$$ LANGUAGE SQL

statement ok
CREATE PROCEDURE p_rewrite() AS $$
SELECT nested_func();
SELECT * FROM nested_func();
$$ LANGUAGE SQL

# TODO(fqazi): Renaming function calls will break today until #120351 is completed.
query T
SELECT get_body_str('p_rewrite');
----
"SELECT public.nested_func();\nSELECT nested_func FROM ROWS FROM (public.nested_func());"

subtest end
13 changes: 4 additions & 9 deletions pkg/sql/logictest/testdata/logic_test/udf_unsupported
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,11 @@ ALTER TABLE test_tbl_t ALTER COLUMN b SET ON UPDATE (test_tbl_f());
subtest end


subtest disallow_udf_in_views_and_udf
subtest disallow_udf_in_views

statement ok
CREATE FUNCTION test_vf_f() RETURNS STRING LANGUAGE SQL AS $$ SELECT lower('hello') $$;

statement error pgcode 42883 pq: unknown function: test_vf_f\(\)\nHINT:.*intention was to use a user-defined function in the function body, which is currently not supported.
CREATE FUNCTION test_vf_g() RETURNS STRING LANGUAGE SQL AS $$ SELECT test_vf_f() $$;

statement ok
CREATE FUNCTION test_vf_g() RETURNS STRING LANGUAGE SQL AS $$ SELECT lower('hello') $$;

statement error pgcode 42883 pq: unknown function: test_vf_f\(\)\nHINT:.*intention was to use a user-defined function in the view query, which is currently not supported.
CREATE VIEW v AS SELECT test_vf_f();
Expand Down Expand Up @@ -84,10 +79,10 @@ CREATE FUNCTION f_cross_db(cross_db1.sc.workday) RETURNS INT LANGUAGE SQL AS $$
statement error pgcode 0A000 pq: cross database type references are not supported: cross_db1.sc.workday
CREATE FUNCTION f_cross_db() RETURNS cross_db1.sc.workday LANGUAGE SQL AS $$ SELECT 'MON'::cross_db1.sc.workday $$;

statement error pgcode 0A000 pq: the function cannot refer to other databases
statement error pgcode 0A000 pq: dependent relation tbl cannot be from another database
CREATE FUNCTION f_cross_db() RETURNS INT LANGUAGE SQL AS $$ SELECT a FROM cross_db1.sc.tbl $$;

statement error pgcode 0A000 pq: the function cannot refer to other databases
statement error pgcode 0A000 pq: dependent relation v cannot be from another database
CREATE FUNCTION f_cross_db() RETURNS INT LANGUAGE SQL AS $$ SELECT a FROM cross_db1.sc.v $$;

subtest end
Expand Down Expand Up @@ -139,7 +134,7 @@ CREATE FUNCTION rec(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT CASE i WHEN 0 THE
statement ok
CREATE FUNCTION other_udf() RETURNS INT LANGUAGE SQL AS 'SELECT 1'

statement error pgcode 42883 unknown function: other_udf()
statement ok
CREATE FUNCTION err() RETURNS INT LANGUAGE SQL AS 'SELECT other_udf()'

subtest end
Expand Down
7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist-disk/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local-mixed-23.1/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local-mixed-23.2/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions pkg/sql/opt/optbuilder/create_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,6 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o
schID := b.factory.Metadata().AddSchema(sch)
cf.Name.ObjectNamePrefix = resName

// TODO(#88198): this is a hack to disallow UDF usage in UDF and we will
// need to lift this hack when we plan to allow it.
preFuncResolver := b.semaCtx.FunctionResolver
b.semaCtx.FunctionResolver = nil

b.insideFuncDef = true
b.trackSchemaDeps = true
// Make sure datasource names are qualified.
Expand All @@ -70,7 +65,6 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o
b.evalCtx.Annotations = oldEvalCtxAnn
b.semaCtx.Annotations = oldSemaCtxAnn

b.semaCtx.FunctionResolver = preFuncResolver
switch recErr := recover().(type) {
case nil:
// No error.
Expand Down
5 changes: 5 additions & 0 deletions pkg/sql/schemachanger/comparator_generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,10 @@ func validateFunctionRelationReferences(
for _, id := range refProvider.ReferencedRelationIDs().Ordered() {
_, _, namespace := scpb.FindNamespace(b.QueryByID(id))
if namespace.DatabaseID != parentDBID {
name := tree.MakeTypeNameWithPrefix(b.NamePrefix(namespace), namespace.Name)
panic(pgerror.Newf(
pgcode.FeatureNotSupported,
"the function cannot refer to other databases",
name.String()))
"dependent relation %s cannot be from another database",
namespace.Name))
}
}
}
Expand Down

0 comments on commit 8b7ec1c

Please sign in to comment.