Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: allow procedures to call other procedures #120674

Merged
merged 2 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.

92 changes: 92 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/proc_invokes_proc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# LogicTest: !local-mixed-23.1 !local-mixed-23.2

statement ok
CREATE PROCEDURE a() LANGUAGE SQL AS $$
SELECT 1;
$$

statement ok
CREATE PROCEDURE b() LANGUAGE SQL AS $$
CALL a();
$$

# Mutual recursion is not currently allowed.
statement error pgcode 42P13 cannot add dependency from descriptor \d+ to function b \(\d+\) because there will be a dependency cycle
CREATE OR REPLACE PROCEDURE a() LANGUAGE SQL AS $$
CALL b();
$$

statement error pgcode 2BP01 cannot drop function \"a\" because other objects \(\[test.public.b\]\) still depend on it
DROP PROCEDURE a

statement ok
DROP PROCEDURE b;
DROP PROCEDURE a;

statement ok
CREATE TYPE e AS ENUM ('foo', 'bar');

statement ok
CREATE PROCEDURE a() LANGUAGE SQL AS $$
SELECT 'foo'::e;
$$

statement ok
CREATE PROCEDURE b() LANGUAGE SQL AS $$
CALL a();
$$

statement error pgcode 2BP01 cannot drop type \"e\" because other objects \(\[test.public.a\]\) still depend on it
DROP TYPE e

statement ok
DROP PROCEDURE b;
DROP PROCEDURE a;
DROP TYPE e;

statement ok
CREATE TABLE ab (
a INT PRIMARY KEY,
b INT
)

statement ok
CREATE PROCEDURE ins_ab(new_a INT, new_b INT) LANGUAGE SQL AS $$
INSERT INTO ab VALUES (new_a, new_b);
$$

statement ok
CREATE PROCEDURE ins3() LANGUAGE SQL AS $$
CALL ins_ab(1, 10);
CALL ins_ab(2, 20);
CALL ins_ab(3, 30);
$$

statement ok
CALL ins_ab(4, 40)

statement ok
CALL ins3()

statement error pgcode 23505 duplicate key value violates unique constraint \"ab_pkey\"
CALL ins3()

query II rowsort
SELECT * FROM ab
----
1 10
2 20
3 30
4 40

# TODO(#102771): Add dependency tracking that causes this to error.
# statement error pgcode 2BP01 cannot drop table ab because other objects depend on it
# DROP TABLE ab;

statement error pgcode 2BP01 cannot drop function \"ins_ab\" because other objects \(\[test.public.ins3\]\) still depend on it
DROP PROCEDURE ins_ab

statement ok
DROP PROCEDURE ins3;
DROP PROCEDURE ins_ab;
DROP TABLE ab;
26 changes: 0 additions & 26 deletions pkg/sql/logictest/testdata/logic_test/procedure
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,9 @@ CALL p(foo())
statement error pgcode 42723 function "p" already exists with same argument types
CREATE FUNCTION p() RETURNS VOID LANGUAGE SQL AS ''

# TODO(mgartner): The error should state "procedure definition" too.
statement error pgcode 0A000 unimplemented: CALL usage inside a function definition
CREATE OR REPLACE PROCEDURE p2() LANGUAGE SQL AS $$
CALL 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
CREATE FUNCTION err(i INT) RETURNS VOID LANGUAGE SQL AS 'CALL p()'

statement error pgcode 42809 p\(\) is a procedure\nHINT: To call a procedure, use CALL.
CREATE TABLE err (i INT DEFAULT p())

Expand Down Expand Up @@ -412,20 +403,3 @@ statement error pgcode 42P13 null input attribute not allowed in procedure defin
CREATE PROCEDURE pv() AS 'SELECT 1' STRICT LANGUAGE SQL;

subtest end


subtest udf_calling_udfs

# Validate we can have UDF's both in the select and from clauses.
statement ok
CREATE FUNCTION udfCall(i int) RETURNS INT LANGUAGE SQL AS 'SELECT 100+i';
CREATE FUNCTION udfCallNest(i int, j int) RETURNS INT LANGUAGE SQL AS 'SELECT udfCall(i) + j';
CREATE FUNCTION udfCallNest_2(i int, j int) RETURNS INT LANGUAGE SQL AS 'SELECT udfCall(i) + udfCall(j) + udfCallNest(i, j)';
CREATE FUNCTION udfCallNest_3(i int, j int) RETURNS INT LANGUAGE SQL AS 'SELECT udfCall(j) + udfCallNest(i, j) + udfCallNest_2(i, j) + 1 FROM udfCallNest_2(i, j)';

query I
SELECT * FROM udfCallNest_3(1, 2)
----
512

subtest end
92 changes: 92 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/udf_calling_udf
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# LogicTest: !local-mixed-23.1

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

Expand All @@ -15,10 +17,100 @@ SELECT upper_hello(), nested_udf_for_from(), lower_hello(), concat_hello()
----
HELLO HELLO hello HELLOHELLOhello

# Validate we can have UDF's both in the select and from clauses.
statement ok
CREATE FUNCTION udfCall(i int) RETURNS INT LANGUAGE SQL AS 'SELECT 100+i';
CREATE FUNCTION udfCallNest(i int, j int) RETURNS INT LANGUAGE SQL AS 'SELECT udfCall(i) + j';
CREATE FUNCTION udfCallNest_2(i int, j int) RETURNS INT LANGUAGE SQL AS 'SELECT udfCall(i) + udfCall(j) + udfCallNest(i, j)';
CREATE FUNCTION udfCallNest_3(i int, j int) RETURNS INT LANGUAGE SQL AS 'SELECT udfCall(j) + udfCallNest(i, j) + udfCallNest_2(i, j) + 1 FROM udfCallNest_2(i, j)';

query I
SELECT * FROM udfCallNest_3(1, 2)
----
512

# 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() $$;

# Validate that function renaming is blocked.
statement error pgcode 0A000 cannot rename function \"lower_hello\" because other functions \(\[test.public.upper_hello, test.public.concat_hello\]\) still depend on it
ALTER FUNCTION lower_hello rename to lower_hello_new

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

statement ok
CREATE FUNCTION g() RETURNS INT LANGUAGE SQL AS $$
SELECT f();
$$

# Mutual recursion is not currently allowed.
statement error pgcode 42P13 cannot add dependency from descriptor \d+ to function g \(\d+\) because there will be a dependency cycle
CREATE OR REPLACE FUNCTION f() RETURNS INT LANGUAGE SQL AS $$
SELECT g();
$$

statement ok
DROP FUNCTION g();
DROP FUNCTION f();

statement ok
CREATE TABLE ab (
a INT PRIMARY KEY,
b INT
)

statement ok
CREATE FUNCTION ins_ab(new_a INT, new_b INT) RETURNS INT LANGUAGE SQL AS $$
INSERT INTO ab VALUES (new_a, new_b) RETURNING a;
$$

statement ok
CREATE FUNCTION ins(new_a INT) RETURNS INT LANGUAGE SQL AS $$
SELECT ins_ab(new_a, new_a * 10);
SELECT b FROM ab WHERE a = new_a;
$$

query I rowsort
SELECT ins(i) FROM generate_series(1, 3) g(i)
----
10
20
30

query II
SELECT ins(5), ins(6) FROM (VALUES (1)) v(i) WHERE i < ins(4)
----
50 60

query II rowsort
SELECT * FROM ab
----
1 10
2 20
3 30
4 40
5 50
6 60

statement error pgcode 23505 duplicate key value violates unique constraint \"ab_pkey\"
SELECT ins(4)

skipif config local-legacy-schema-changer
statement error pgcode 2BP01 cannot drop table ab because other objects depend on it
DROP TABLE ab

onlyif config local-legacy-schema-changer
statement error pgcode 2BP01 pq: cannot drop relation \"ab\" because function \"ins\" depends on it\nHINT: consider dropping \"ins\" first.
DROP TABLE ab

statement error pgcode 2BP01 cannot drop function \"ins_ab\" because other objects \(\[test.public.ins\]\) still depend on it
DROP FUNCTION ins_ab

statement ok
DROP FUNCTION ins;
DROP FUNCTION ins_ab;
DROP TABLE ab;
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: 0 additions & 7 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-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.

9 changes: 7 additions & 2 deletions pkg/sql/opt/optbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,11 @@ func (b *Builder) buildStmt(
if !activeVersion.IsActive(clusterversion.V23_2) {
panic(unimplemented.Newf("user-defined functions", "%s usage inside a function definition is not supported until version 23.2", stmt.StatementTag()))
}
case *tree.Call:
activeVersion := b.evalCtx.Settings.Version.ActiveVersion(b.ctx)
if !activeVersion.IsActive(clusterversion.V24_1) {
panic(unimplemented.Newf("stored procedures", "%s usage inside a routine definition is not supported until version 24.1", stmt.StatementTag()))
}
default:
panic(unimplemented.Newf("user-defined functions", "%s usage inside a function definition", stmt.StatementTag()))
}
Expand Down Expand Up @@ -494,7 +499,7 @@ func (b *Builder) trackReferencedColumnForViews(col *scopeColumn) {

func (b *Builder) maybeTrackRegclassDependenciesForViews(texpr tree.TypedExpr) {
if b.trackSchemaDeps {
if texpr.ResolvedType().Identical(types.RegClass) {
if texpr != nil && texpr.ResolvedType().Identical(types.RegClass) {
// We do not add a dependency if the RegClass Expr contains variables,
// we cannot resolve the variables in this context. This matches Postgres
// behavior.
Expand Down Expand Up @@ -528,7 +533,7 @@ func (b *Builder) maybeTrackRegclassDependenciesForViews(texpr tree.TypedExpr) {

func (b *Builder) maybeTrackUserDefinedTypeDepsForViews(texpr tree.TypedExpr) {
if b.trackSchemaDeps {
if texpr.ResolvedType().UserDefined() {
if texpr != nil && texpr.ResolvedType().UserDefined() {
typedesc.GetTypeDescriptorClosure(texpr.ResolvedType()).ForEach(func(id descpb.ID) {
b.schemaTypeDeps.Add(int(id))
})
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/opt/optbuilder/routine.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ func (b *Builder) buildRoutine(
}
// Add a VALUES (NULL) statement if the return type of the function is
// VOID. We cannot simply project NULL from the last statement because
// all column would be pruned and the contents of last statement would
// all columns would be pruned and the contents of last statement would
// not be executed.
// TODO(mgartner): This will add some planning overhead for every
// invocation of the function. Is there a more efficient way to do this?
Expand Down