Skip to content

Commit 4a03f79

Browse files
committed
sql: support referencing a routine from a view query
This commit adds support for invoking a UDF from a view (materialized or otherwise). This requires resolving routine references while planning the `CREATE VIEW` statement and passing them to the schema changer. Fixes #146123 Release note (sql change): Added support for invoking a UDF from a view query. Renaming or setting the schema on the routine is currently not allowed if it is referenced by a view.
1 parent d62f6ca commit 4a03f79

File tree

18 files changed

+412
-56
lines changed

18 files changed

+412
-56
lines changed

pkg/sql/distsql_spec_exec_factory.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,7 @@ func (e *distSQLSpecExecFactory) ConstructCreateView(
15801580
columns colinfo.ResultColumns,
15811581
deps opt.SchemaDeps,
15821582
typeDeps opt.SchemaTypeDeps,
1583+
funcDeps opt.SchemaFunctionDeps,
15831584
) (exec.Node, error) {
15841585
return nil, unimplemented.NewWithIssue(47473, "experimental opt-driven distsql planning: create view")
15851586
}

pkg/sql/logictest/testdata/logic_test/drop_function

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ CREATE SCHEMA altSchema;
301301
CREATE FUNCTION altSchema.f_called_by_b() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;
302302
CREATE TABLE t1_with_b_2_ref(j int default altSchema.f_called_by_b() CHECK (altSchema.f_called_by_b() > 0));
303303

304-
statement error pgcode 0A000 cannot set schema for function \"f_called_by_b\" because other functions \(\[test.public.f_called_by_b2\]\) still depend on it
304+
statement error pgcode 0A000 cannot set schema for function \"f_called_by_b\" because other functions or views \(\[test.public.f_called_by_b2\]\) still depend on it
305305
ALTER FUNCTION f_called_by_b SET SCHEMA altSchema;
306306

307307
skipif config local-legacy-schema-changer

pkg/sql/logictest/testdata/logic_test/udf

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,3 +1046,148 @@ select * from pg_catalog.pg_attrdef;
10461046
3466299041 175 2 unique_rowid() unique_rowid()
10471047

10481048
subtest end
1049+
1050+
subtest view
1051+
1052+
statement ok
1053+
CREATE TABLE xy (x INT, y INT);
1054+
INSERT INTO xy VALUES (1, 2), (3, 4), (5, 6);
1055+
1056+
statement ok
1057+
CREATE FUNCTION f_scalar() RETURNS INT LANGUAGE SQL AS $$ SELECT count(*) FROM xy $$;
1058+
1059+
statement ok
1060+
CREATE FUNCTION f_setof() RETURNS SETOF xy LANGUAGE SQL AS $$ SELECT * FROM xy $$;
1061+
1062+
statement ok
1063+
CREATE VIEW v AS SELECT x, y, f_scalar() FROM f_setof();
1064+
1065+
statement ok
1066+
CREATE MATERIALIZED VIEW mv AS SELECT x, y, f_scalar() FROM f_setof();
1067+
1068+
query III
1069+
SELECT * FROM v ORDER BY x, y;
1070+
----
1071+
1 2 3
1072+
3 4 3
1073+
5 6 3
1074+
1075+
query III
1076+
SELECT * FROM mv ORDER BY x, y;
1077+
----
1078+
1 2 3
1079+
3 4 3
1080+
5 6 3
1081+
1082+
statement ok
1083+
REFRESH MATERIALIZED VIEW mv;
1084+
1085+
query III
1086+
SELECT * FROM mv ORDER BY x, y;
1087+
----
1088+
1 2 3
1089+
3 4 3
1090+
5 6 3
1091+
1092+
statement ok
1093+
INSERT INTO xy VALUES (7, 8);
1094+
1095+
query III
1096+
SELECT * FROM v ORDER BY x, y;
1097+
----
1098+
1 2 4
1099+
3 4 4
1100+
5 6 4
1101+
7 8 4
1102+
1103+
query III
1104+
SELECT * FROM mv ORDER BY x, y;
1105+
----
1106+
1 2 3
1107+
3 4 3
1108+
5 6 3
1109+
1110+
statement ok
1111+
REFRESH MATERIALIZED VIEW mv;
1112+
1113+
query III
1114+
SELECT * FROM mv ORDER BY x, y;
1115+
----
1116+
1 2 4
1117+
3 4 4
1118+
5 6 4
1119+
7 8 4
1120+
1121+
statement ok
1122+
CREATE ROLE bob;
1123+
1124+
statement ok
1125+
GRANT ALL ON SCHEMA public TO bob;
1126+
1127+
statement ok
1128+
GRANT ALL ON v TO bob;
1129+
1130+
statement ok
1131+
GRANT ALL ON mv TO bob;
1132+
1133+
statement ok
1134+
REVOKE EXECUTE ON FUNCTION f_scalar() FROM PUBLIC;
1135+
1136+
statement ok
1137+
REVOKE EXECUTE ON FUNCTION f_scalar() FROM bob;
1138+
1139+
statement ok
1140+
SET ROLE bob;
1141+
1142+
statement error pgcode 42501 pq: user bob does not have EXECUTE privilege on function f_scalar
1143+
SELECT f_scalar();
1144+
1145+
statement error pgcode 42501 pq: user bob does not have EXECUTE privilege on function f_scalar
1146+
SELECT * FROM v;
1147+
1148+
statement ok
1149+
SELECT * FROM mv;
1150+
1151+
statement ok
1152+
SET ROLE root;
1153+
1154+
statement error pgcode 0A000 cannot rename function "f_scalar" because other functions or views \(\[test.public.v, test.public.mv\]\) still depend on it
1155+
ALTER FUNCTION f_scalar RENAME TO f_scalar_renamed;
1156+
1157+
statement error pgcode 0A000 cannot rename function "f_setof" because other functions or views \(\[test.public.v, test.public.mv\]\) still depend on it
1158+
ALTER FUNCTION f_setof RENAME TO f_setof_renamed;
1159+
1160+
statement error pgcode 2BP01 pq: cannot drop function "f_scalar" because other objects \(\[test.public.v, test.public.mv\]\) still depend on it
1161+
DROP FUNCTION f_scalar;
1162+
1163+
statement error pgcode 2BP01 pq: cannot drop function "f_setof" because other objects \(\[test.public.v, test.public.mv\]\) still depend on it
1164+
DROP FUNCTION f_setof;
1165+
1166+
statement ok
1167+
DROP VIEW v;
1168+
1169+
statement ok
1170+
DROP MATERIALIZED VIEW mv;
1171+
1172+
statement ok
1173+
DROP FUNCTION f_scalar;
1174+
1175+
statement ok
1176+
DROP FUNCTION f_setof;
1177+
1178+
# Test a view referencing a builtin function.
1179+
statement ok
1180+
CREATE VIEW v_builtin AS SELECT * FROM generate_series(1, 4);
1181+
1182+
query I
1183+
SELECT * FROM v_builtin ORDER BY 1;
1184+
----
1185+
1
1186+
2
1187+
3
1188+
4
1189+
1190+
statement ok
1191+
DROP VIEW v_builtin;
1192+
1193+
subtest end

pkg/sql/logictest/testdata/logic_test/udf_calling_udf

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,16 @@ statement error pgcode 42883 unknown function: recursion_check\(\)
5555
CREATE FUNCTION recursion_check() RETURNS STRING LANGUAGE SQL AS $$ SELECT recursion_check() $$;
5656

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

6161
statement ok
6262
CREATE SCHEMA sc2;
6363

6464
# Validate that function schema changes are blocked.
65-
statement error pgcode 0A000 cannot set schema for function \"lower_hello\" because other functions \(\[test.public.upper_hello, test.public.concat_hello\]\) still depend on it
65+
statement error pgcode 0A000 cannot set schema for function \"lower_hello\" because other functions or views \(\[test.public.upper_hello, test.public.concat_hello\]\) still depend on it
6666
ALTER FUNCTION lower_hello SET SCHEMA sc2;
6767

68-
6968
statement ok
7069
CREATE FUNCTION f() RETURNS INT LANGUAGE SQL AS $$
7170
SELECT 1;

pkg/sql/logictest/testdata/logic_test/udf_unsupported

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -69,35 +69,6 @@ CREATE INDEX idx_b ON test_tbl_t (test_tbl_f());
6969
subtest end
7070

7171

72-
subtest disallow_udf_in_views
73-
74-
statement ok
75-
CREATE FUNCTION test_vf_f() RETURNS STRING LANGUAGE SQL AS $$ SELECT lower('hello') $$;
76-
77-
78-
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.
79-
CREATE VIEW v AS SELECT test_vf_f();
80-
81-
statement ok
82-
CREATE VIEW v AS SELECT lower('hello');
83-
84-
query T
85-
SELECT create_statement FROM [SHOW CREATE FUNCTION test_vf_f];
86-
----
87-
CREATE FUNCTION public.test_vf_f()
88-
RETURNS STRING
89-
VOLATILE
90-
NOT LEAKPROOF
91-
CALLED ON NULL INPUT
92-
LANGUAGE SQL
93-
SECURITY INVOKER
94-
AS $$
95-
SELECT lower('hello':::STRING);
96-
$$
97-
98-
subtest end
99-
100-
10172
subtest cross_db
10273

10374
statement ok

pkg/sql/opt/exec/execbuilder/statement.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func (b *Builder) buildCreateView(
6565
cols,
6666
cv.Deps,
6767
cv.TypeDeps,
68+
cv.FuncDeps,
6869
)
6970
return execPlan{root: root}, colOrdMap{}, err
7071
}

pkg/sql/opt/exec/factory.opt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ define CreateView {
692692
Columns colinfo.ResultColumns
693693
deps opt.SchemaDeps
694694
typeDeps opt.SchemaTypeDeps
695+
functionDeps opt.SchemaFunctionDeps
695696
}
696697

697698
# SequenceSelect implements a scan of a sequence as a data source.

pkg/sql/opt/memo/expr_format.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -795,19 +795,19 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) {
795795
f.formatCol(col.Alias, col.ID, opt.ColSet{} /* notNullCols */)
796796
}
797797
tp.Child(f.Buffer.String())
798-
f.formatDependencies(tp, t.Deps, t.TypeDeps)
798+
f.formatDependencies(tp, t.Deps, t.TypeDeps, t.FuncDeps)
799799

800800
case *CreateFunctionExpr:
801801
fmtFlags := tree.FmtSimple
802802
if f.RedactableValues {
803803
fmtFlags = tree.FmtMarkRedactionNode | tree.FmtOmitNameRedaction
804804
}
805805
tp.Child(tree.AsStringWithFlags(t.Syntax, fmtFlags))
806-
f.formatDependencies(tp, t.Deps, t.TypeDeps)
806+
f.formatDependencies(tp, t.Deps, t.TypeDeps, t.FuncDeps)
807807

808808
case *CreateTriggerExpr:
809809
tp.Child(t.Syntax.String())
810-
f.formatDependencies(tp, t.Deps, t.TypeDeps)
810+
f.formatDependencies(tp, t.Deps, t.TypeDeps, t.FuncDeps)
811811

812812
case *CreateStatisticsExpr:
813813
tp.Child(t.Syntax.String())
@@ -1836,9 +1836,12 @@ func (f *ExprFmtCtx) formatLockingWithPrefix(
18361836

18371837
// formatDependencies adds a new treeprinter child for schema dependencies.
18381838
func (f *ExprFmtCtx) formatDependencies(
1839-
tp treeprinter.Node, deps opt.SchemaDeps, typeDeps opt.SchemaTypeDeps,
1839+
tp treeprinter.Node,
1840+
deps opt.SchemaDeps,
1841+
typeDeps opt.SchemaTypeDeps,
1842+
funcDeps opt.SchemaFunctionDeps,
18401843
) {
1841-
if len(deps) == 0 && typeDeps.Empty() {
1844+
if len(deps) == 0 && typeDeps.Empty() && funcDeps.Empty() {
18421845
tp.Child("no dependencies")
18431846
return
18441847
}
@@ -1868,6 +1871,11 @@ func (f *ExprFmtCtx) formatDependencies(
18681871
n.Child(typ.Name())
18691872
}
18701873
}
1874+
funcDeps.ForEach(func(routineID int) {
1875+
// TODO(drewk): format using the routine name.
1876+
ref := tree.FunctionOID{OID: catid.FuncIDToOID(catid.DescID(routineID))}
1877+
n.Child(ref.String())
1878+
})
18711879
}
18721880

18731881
// ScanIsReverseFn is a callback that is used to figure out if a scan needs to

pkg/sql/opt/ops/statement.opt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ define CreateViewPrivate {
5555
# TypeDeps contains the type dependencies of the view.
5656
TypeDeps SchemaTypeDeps
5757

58+
# FuncDeps contains the function dependencies of the function.
59+
FuncDeps SchemaFunctionDeps
60+
5861
# WithData indicates if the materialized view is populated
5962
# with data upon creation.
6063
WithData bool

pkg/sql/opt/optbuilder/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ go_library(
6262
"//pkg/sql/catalog/catpb",
6363
"//pkg/sql/catalog/colinfo",
6464
"//pkg/sql/catalog/descpb",
65+
"//pkg/sql/catalog/funcdesc",
6566
"//pkg/sql/catalog/funcinfo",
6667
"//pkg/sql/catalog/schemaexpr",
6768
"//pkg/sql/catalog/seqexpr",

0 commit comments

Comments
 (0)