Skip to content

Commit ebbbe8f

Browse files
committed
sql: support * in udf bodies
This change allows `*` usage in UDF bodies. In order to facilitate schema changes after a UDF is created but should not affect the UDF output, we rewrite UDF ASTs in place to reference tables and columns by ID instead of by name. Informs: #90080 Epic: CRDB-19496 Release note (sql change): Allow `*` expressions in UDFs.
1 parent 51005e4 commit ebbbe8f

File tree

19 files changed

+268
-61
lines changed

19 files changed

+268
-61
lines changed

pkg/ccl/backupccl/backup_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10528,7 +10528,7 @@ $$;
1052810528
require.Equal(t, 111, int(fnDesc.GetID()))
1052910529
require.Equal(t, 104, int(fnDesc.GetParentID()))
1053010530
require.Equal(t, 106, int(fnDesc.GetParentSchemaID()))
10531-
require.Equal(t, "SELECT a FROM db1.sc1.tbl1;\nSELECT nextval(110:::REGCLASS);", fnDesc.GetFunctionBody())
10531+
require.Equal(t, "SELECT a FROM [107(1) AS tbl1];\nSELECT nextval(110:::REGCLASS);", fnDesc.GetFunctionBody())
1053210532
require.Equal(t, 100108, int(fnDesc.GetParams()[0].Type.Oid()))
1053310533
require.Equal(t, []descpb.ID{107, 110}, fnDesc.GetDependsOn())
1053410534
require.Equal(t, []descpb.ID{108, 109}, fnDesc.GetDependsOnTypes())
@@ -10581,7 +10581,7 @@ $$;
1058110581
require.Equal(t, 112, int(fnDesc.GetParentID()))
1058210582
require.Equal(t, 114, int(fnDesc.GetParentSchemaID()))
1058310583
// Make sure db name and IDs are rewritten in function body.
10584-
require.Equal(t, "SELECT a FROM db1_new.sc1.tbl1;\nSELECT nextval(118:::REGCLASS);", fnDesc.GetFunctionBody())
10584+
require.Equal(t, "SELECT a FROM [107(1) AS tbl1];\nSELECT nextval(118:::REGCLASS);", fnDesc.GetFunctionBody())
1058510585
require.Equal(t, 100116, int(fnDesc.GetParams()[0].Type.Oid()))
1058610586
require.Equal(t, []descpb.ID{115, 118}, fnDesc.GetDependsOn())
1058710587
require.Equal(t, []descpb.ID{116, 117}, fnDesc.GetDependsOnTypes())
@@ -10667,7 +10667,7 @@ $$;
1066710667
require.Equal(t, 111, int(fnDesc.GetID()))
1066810668
require.Equal(t, 104, int(fnDesc.GetParentID()))
1066910669
require.Equal(t, 106, int(fnDesc.GetParentSchemaID()))
10670-
require.Equal(t, "SELECT a FROM db1.sc1.tbl1;\nSELECT nextval(110:::REGCLASS);", fnDesc.GetFunctionBody())
10670+
require.Equal(t, "SELECT a FROM [107(1) AS tbl1];\nSELECT nextval(110:::REGCLASS);", fnDesc.GetFunctionBody())
1067110671
require.Equal(t, 100108, int(fnDesc.GetParams()[0].Type.Oid()))
1067210672
require.Equal(t, []descpb.ID{107, 110}, fnDesc.GetDependsOn())
1067310673
require.Equal(t, []descpb.ID{108, 109}, fnDesc.GetDependsOnTypes())
@@ -10722,7 +10722,7 @@ $$;
1072210722
require.Equal(t, 107, int(fnDesc.GetParentID()))
1072310723
require.Equal(t, 125, int(fnDesc.GetParentSchemaID()))
1072410724
// Make sure db name and IDs are rewritten in function body.
10725-
require.Equal(t, "SELECT a FROM db1.sc1.tbl1;\nSELECT nextval(129:::REGCLASS);", fnDesc.GetFunctionBody())
10725+
require.Equal(t, "SELECT a FROM [107(1) AS tbl1];\nSELECT nextval(129:::REGCLASS);", fnDesc.GetFunctionBody())
1072610726
require.Equal(t, 100127, int(fnDesc.GetParams()[0].Type.Oid()))
1072710727
require.Equal(t, []descpb.ID{126, 129}, fnDesc.GetDependsOn())
1072810728
require.Equal(t, []descpb.ID{127, 128}, fnDesc.GetDependsOnTypes())

pkg/ccl/backupccl/testdata/backup-restore/user-defined-functions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ CREATE FUNCTION sc1.f1(IN a db1.sc1.enum1)
5959
CALLED ON NULL INPUT
6060
LANGUAGE SQL
6161
AS $$
62-
SELECT a FROM db1.sc1.tbl1;
62+
SELECT a FROM [110(1) AS tbl1];
6363
SELECT nextval('sc1.sq1'::REGCLASS);
6464
$$
6565

pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/create_function_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ CREATE SCHEMA test_sc;
7070
require.Equal(t, funcDesc.GetName(), "f")
7171

7272
require.Equal(t,
73-
`SELECT a FROM defaultdb.public.t;
74-
SELECT b FROM defaultdb.public.t@t_idx_b;
75-
SELECT c FROM defaultdb.public.t@t_idx_c;
73+
`SELECT a FROM [104(1, 2, 3) AS t];
74+
SELECT b FROM [104(1, 2, 3) AS t]@t_idx_b;
75+
SELECT c FROM [104(1, 2, 3) AS t]@t_idx_c;
7676
SELECT a FROM defaultdb.public.v;
7777
SELECT nextval(105:::REGCLASS);`,
7878
funcDesc.GetFunctionBody())
@@ -255,7 +255,7 @@ $$;
255255
require.Equal(t, funcDesc.GetName(), "f")
256256

257257
require.Equal(t,
258-
`SELECT b FROM defaultdb.public.t1@t1_idx_b;
258+
`SELECT b FROM [104(1, 2) AS t1]@t1_idx_b;
259259
SELECT a FROM defaultdb.public.v1;
260260
SELECT nextval(106:::REGCLASS);`,
261261
funcDesc.GetFunctionBody())
@@ -298,7 +298,7 @@ $$;
298298
require.Equal(t, funcDesc.GetName(), "f")
299299

300300
require.Equal(t,
301-
`SELECT b FROM defaultdb.public.t2@t2_idx_b;
301+
`SELECT b FROM [105(1, 2) AS t2]@t2_idx_b;
302302
SELECT a FROM defaultdb.public.v2;
303303
SELECT nextval(107:::REGCLASS);`,
304304
funcDesc.GetFunctionBody())

pkg/sql/drop_function_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ CREATE SCHEMA test_sc;
6767
require.Equal(t, funcDesc.GetName(), "f")
6868

6969
require.Equal(t,
70-
`SELECT a FROM defaultdb.public.t;
71-
SELECT b FROM defaultdb.public.t@t_idx_b;
72-
SELECT c FROM defaultdb.public.t@t_idx_c;
70+
`SELECT a FROM [104(1, 2, 3) AS t];
71+
SELECT b FROM [104(1, 2, 3) AS t]@t_idx_b;
72+
SELECT c FROM [104(1, 2, 3) AS t]@t_idx_c;
7373
SELECT a FROM defaultdb.public.v;
7474
SELECT nextval(105:::REGCLASS);`,
7575
funcDesc.GetFunctionBody())

pkg/sql/function_resolver_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ CREATE FUNCTION f(INT) RETURNS INT IMMUTABLE LANGUAGE SQL AS $$ SELECT a FROM t
120120

121121
_, overload, err := funcResolver.ResolveFunctionByOID(ctx, funcDef.Overloads[0].Oid)
122122
require.NoError(t, err)
123-
require.Equal(t, `SELECT a FROM defaultdb.public.t;
124-
SELECT b FROM defaultdb.public.t@t_idx_b;
125-
SELECT c FROM defaultdb.public.t@t_idx_c;
123+
require.Equal(t, `SELECT a FROM [104(1, 2, 3) AS t];
124+
SELECT b FROM [104(1, 2, 3) AS t]@t_idx_b;
125+
SELECT c FROM [104(1, 2, 3) AS t]@t_idx_c;
126126
SELECT a FROM defaultdb.public.v;
127127
SELECT nextval(105:::REGCLASS);`, overload.Body)
128128
require.True(t, overload.IsUDF)
@@ -142,7 +142,7 @@ SELECT nextval(105:::REGCLASS);`, overload.Body)
142142

143143
_, overload, err = funcResolver.ResolveFunctionByOID(ctx, funcDef.Overloads[2].Oid)
144144
require.NoError(t, err)
145-
require.Equal(t, `SELECT a FROM defaultdb.public.t;`, overload.Body)
145+
require.Equal(t, `SELECT a FROM [104(1, 2, 3) AS t];`, overload.Body)
146146
require.True(t, overload.IsUDF)
147147
require.False(t, overload.UDFContainsOnlySignature)
148148
require.Equal(t, 1, len(overload.Types.Types()))

pkg/sql/logictest/testdata/logic_test/udf

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,6 @@ CREATE FUNCTION err(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT j'
5252
statement error pgcode 42703 column \"j\" does not exist
5353
CREATE FUNCTION err(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT a FROM ab WHERE a = j'
5454

55-
statement error pgcode 0A000 functions do not currently support \* expressions
56-
CREATE FUNCTION err(i INT) RETURNS ab LANGUAGE SQL AS 'SELECT * FROM ab'
57-
58-
statement error pgcode 0A000 functions do not currently support \* expressions
59-
CREATE FUNCTION err(i INT) RETURNS ab LANGUAGE SQL AS 'SELECT ab.* FROM ab'
60-
61-
statement error pgcode 0A000 functions do not currently support \* expressions
62-
CREATE FUNCTION err(i INT) RETURNS ab LANGUAGE SQL AS $$
63-
SELECT 1;
64-
SELECT * FROM ab;
65-
$$
66-
67-
statement error pgcode 0A000 functions do not currently support \* expressions
68-
CREATE FUNCTION err(i INT) RETURNS INT LANGUAGE SQL AS $$
69-
SELECT * FROM ab;
70-
SELECT 1;
71-
$$
72-
7355
statement ok
7456
CREATE FUNCTION d(i INT2) RETURNS INT4 LANGUAGE SQL AS 'SELECT i'
7557

@@ -142,9 +124,9 @@ CREATE FUNCTION public.f(IN a test.public.notmyworkday)
142124
CALLED ON NULL INPUT
143125
LANGUAGE SQL
144126
AS $$
145-
SELECT a FROM test.public.t;
146-
SELECT b FROM test.public.t@t_idx_b;
147-
SELECT c FROM test.public.t@t_idx_c;
127+
SELECT a FROM [113(1, 2, 3) AS t];
128+
SELECT b FROM [113(1, 2, 3) AS t]@t_idx_b;
129+
SELECT c FROM [113(1, 2, 3) AS t]@t_idx_c;
148130
SELECT nextval('public.sq1'::REGCLASS);
149131
$$
150132

@@ -2321,7 +2303,7 @@ CREATE FUNCTION public.get_l(IN i INT8)
23212303
CALLED ON NULL INPUT
23222304
LANGUAGE SQL
23232305
AS $$
2324-
SELECT v FROM test.public.kv WHERE k = i;
2306+
SELECT v FROM [225(1, 2) AS kv] WHERE k = i;
23252307
$$
23262308

23272309
query T
@@ -2788,7 +2770,7 @@ SELECT oid, proname, pronamespace, proowner, prolang, proleakproof, proisstrict,
27882770
FROM pg_catalog.pg_proc WHERE proname IN ('f_93314', 'f_93314_alias', 'f_93314_comp', 'f_93314_comp_t')
27892771
ORDER BY oid;
27902772
----
2791-
100257 f_93314 105 1546506610 14 false false false v 0 100256 · {} NULL SELECT i, e FROM test.public.t_93314 ORDER BY i LIMIT 1;
2792-
100259 f_93314_alias 105 1546506610 14 false false false v 0 100258 · {} NULL SELECT i, e FROM test.public.t_93314_alias ORDER BY i LIMIT 1;
2773+
100257 f_93314 105 1546506610 14 false false false v 0 100256 · {} NULL SELECT i, e FROM [256(1, 2) AS t_93314] ORDER BY i LIMIT 1;
2774+
100259 f_93314_alias 105 1546506610 14 false false false v 0 100258 · {} NULL SELECT i, e FROM [258(1, 2) AS t_93314_alias] ORDER BY i LIMIT 1;
27932775
100263 f_93314_comp 105 1546506610 14 false false false v 0 100260 · {} NULL SELECT (1, 2);
2794-
100264 f_93314_comp_t 105 1546506610 14 false false false v 0 100262 · {} NULL SELECT a, c FROM test.public.t_93314_comp LIMIT 1;
2776+
100264 f_93314_comp_t 105 1546506610 14 false false false v 0 100262 · {} NULL SELECT a, c FROM [262(1, 2) AS t_93314_comp] LIMIT 1;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
statement ok
2+
CREATE TABLE t_onecol (a INT);
3+
INSERT INTO t_onecol VALUES (1)
4+
5+
statement ok
6+
CREATE TABLE t_twocol (a INT, b INT);
7+
INSERT INTO t_twocol VALUES (1,2)
8+
9+
statement ok
10+
CREATE FUNCTION f_unqualified_onecol() RETURNS INT AS
11+
$$
12+
SELECT * FROM t_onecol;
13+
$$ LANGUAGE SQL;
14+
15+
statement ok
16+
CREATE FUNCTION f_subquery() RETURNS INT AS
17+
$$
18+
SELECT * FROM (SELECT a FROM (SELECT * FROM t_onecol) AS foo) AS bar;
19+
$$ LANGUAGE SQL;
20+
21+
statement ok
22+
CREATE FUNCTION f_unqualified_twocol() RETURNS t_twocol AS
23+
$$
24+
SELECT * FROM t_twocol;
25+
$$ LANGUAGE SQL;
26+
27+
statement ok
28+
CREATE FUNCTION f_allcolsel() RETURNS t_twocol AS
29+
$$
30+
SELECT t_twocol.* FROM t_twocol;
31+
$$ LANGUAGE SQL;
32+
33+
statement ok
34+
CREATE FUNCTION f_allcolsel_alias() RETURNS t_twocol AS
35+
$$
36+
SELECT t1.* FROM t_twocol AS t1, t_twocol AS t2 WHERE t1.a = t2.a;
37+
$$ LANGUAGE SQL;
38+
39+
statement ok
40+
CREATE FUNCTION f_tuplestar() RETURNS t_twocol AS
41+
$$
42+
SELECT (t_twocol.*).* FROM t_twocol;
43+
$$ LANGUAGE SQL;
44+
45+
query TTT
46+
SELECT oid, proname, prosrc
47+
FROM pg_catalog.pg_proc WHERE proname LIKE 'f\_%' ORDER BY oid;
48+
----
49+
100108 f_unqualified_onecol SELECT * FROM [106(1) AS t_onecol];
50+
100109 f_subquery SELECT * FROM (SELECT a FROM (SELECT * FROM [106(1) AS t_onecol]) AS foo) AS bar;
51+
100110 f_unqualified_twocol SELECT * FROM [107(1, 2) AS t_twocol];
52+
100111 f_allcolsel SELECT t_twocol.* FROM [107(1, 2) AS t_twocol];
53+
100112 f_allcolsel_alias SELECT t1.* FROM [107(1, 2) AS t_twocol] AS t1, [107(1, 2) AS t_twocol] AS t2 WHERE t1.a = t2.a;
54+
100113 f_tuplestar SELECT (t_twocol.*).* FROM [107(1, 2) AS t_twocol];
55+
56+
query I
57+
SELECT f_unqualified_onecol()
58+
----
59+
1
60+
61+
query I
62+
SELECT f_subquery()
63+
----
64+
1
65+
66+
statement ok
67+
ALTER TABLE t_onecol ADD COLUMN b INT DEFAULT 5;
68+
69+
query I
70+
SELECT f_unqualified_onecol()
71+
----
72+
1
73+
74+
query I
75+
SELECT f_subquery()
76+
----
77+
1
78+
79+
# It's ok to drop a column that was not used by the original UDF.
80+
statement ok
81+
ALTER TABLE t_onecol DROP COLUMN b;
82+
83+
query T
84+
SELECT f_unqualified_twocol()
85+
----
86+
(1,2)
87+
88+
query T
89+
SELECT f_allcolsel()
90+
----
91+
(1,2)
92+
93+
query T
94+
SELECT f_allcolsel_alias()
95+
----
96+
(1,2)
97+
98+
statement ok
99+
ALTER TABLE t_twocol ADD COLUMN c INT DEFAULT 5;
100+
101+
# TODO(#95558): With early binding, postgres returns an error after adding a
102+
# column when the table is used as the return type. Note that this behavior is
103+
# ok for late binding.
104+
query T
105+
SELECT f_unqualified_twocol()
106+
----
107+
(1,2)
108+
109+
# Altering a column type is not allowed in postgres or CRDB.
110+
statement error pq: cannot alter type of column "b" because function "f_unqualified_twocol" depends on it
111+
ALTER TABLE t_twocol ALTER b TYPE FLOAT;
112+
113+
# TODO(harding): Postgres allows column renaming when only referenced by UDFs.
114+
statement error pq: cannot rename column "a" because function "f_unqualified_twocol" depends on it
115+
ALTER TABLE t_twocol RENAME COLUMN a TO d;
116+
117+
# TODO(harding): Postgres allows table renaming when only referenced by UDFs.
118+
statement error pq: cannot rename relation "t_twocol" because function "f_unqualified_twocol" depends on it
119+
ALTER TABLE t_twocol RENAME TO t_twocol_prime;
120+
121+
# Dropping a column a UDF depends on is not allowed.
122+
statement error pq: cannot drop column "b" because function "f_unqualified_twocol" depends on it
123+
ALTER TABLE t_twocol DROP COLUMN b;

pkg/sql/logictest/tests/fakedist-disk/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/fakedist/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/local-legacy-schema-changer/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/local-vec-off/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/local/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)