/
udf_in_constraints
466 lines (405 loc) · 9.97 KB
/
udf_in_constraints
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# LogicTest: !local-mixed-22.2-23.1
statement ok
CREATE FUNCTION f1(a INT) RETURNS INT LANGUAGE SQL AS $$ SELECT a + 1 $$;
statement ok
CREATE VIEW v_checks AS
SELECT
id,
jsonb_pretty(
crdb_internal.pb_to_json(
'cockroach.sql.sqlbase.Descriptor',
descriptor,
false
)->'table'->'checks'
) as checks
FROM system.descriptor
statement ok
CREATE FUNCTION get_checks(table_id INT) RETURNS STRING
LANGUAGE SQL
AS $$
SELECT checks
FROM v_checks
WHERE id = table_id
$$;
statement ok
CREATE VIEW v_fn_depended_on_by AS
SELECT
id,
jsonb_pretty(
crdb_internal.pb_to_json(
'cockroach.sql.sqlbase.Descriptor',
descriptor,
false
)->'function'->'dependedOnBy'
) as depended_on_by
FROM system.descriptor
statement ok
CREATE FUNCTION get_fn_depended_on_by(function_id INT) RETURNS STRING
LANGUAGE SQL
AS $$
SELECT depended_on_by
FROM v_fn_depended_on_by
WHERE id = function_id
$$;
# Make sure that check constraint expression is properly serialized and
# deserialized.
statement ok
CREATE TABLE t1(
a INT PRIMARY KEY,
b INT CHECK (f1(b) > 1),
FAMILY fam_0 (a, b)
);
let $tbl_id
SELECT id FROM system.namespace WHERE name = 't1';
query T
SELECT get_checks($tbl_id);
----
[
{
"columnIds": [
2
],
"constraintId": 2,
"expr": "[FUNCTION 100106](b) \u003e 1:::INT8",
"name": "check_b"
}
]
query T
SELECT create_statement FROM [SHOW CREATE TABLE t1];
----
CREATE TABLE public.t1 (
a INT8 NOT NULL,
b INT8 NULL,
CONSTRAINT t1_pkey PRIMARY KEY (a ASC),
FAMILY fam_0 (a, b),
CONSTRAINT check_b CHECK (public.f1(b) > 1:::INT8)
)
# Make sure back references are tracked properly.
let $fn_id
SELECT oid::int - 100000 FROM pg_catalog.pg_proc WHERE proname = 'f1';
query T
SELECT get_fn_depended_on_by($fn_id)
----
[
{
"constraintIds": [
2
],
"id": 111
}
]
# Make sure ADD CONSTRAINT works as expected.
statement ok
ALTER TABLE t1 ADD CONSTRAINT cka CHECK (f1(a) > 1);
query T
SELECT get_checks($tbl_id);
----
[
{
"columnIds": [
2
],
"constraintId": 2,
"expr": "[FUNCTION 100106](b) \u003e 1:::INT8",
"name": "check_b"
},
{
"columnIds": [
1
],
"constraintId": 3,
"expr": "[FUNCTION 100106](a) \u003e 1:::INT8",
"name": "cka"
}
]
query T
SELECT get_fn_depended_on_by($fn_id)
----
[
{
"constraintIds": [
2,
3
],
"id": 111
}
]
# Make sure references from different tables are tracked properly.
statement ok
CREATE TABLE t2(
a INT PRIMARY KEY,
b INT CHECK (f1(b) > 1),
CONSTRAINT cka CHECK (f1(a) > 1)
);
query T
SELECT get_fn_depended_on_by($fn_id)
----
[
{
"constraintIds": [
2,
3
],
"id": 111
},
{
"constraintIds": [
2,
3
],
"id": 112
}
]
# Make sure DROP CONSTRAINT remove references properly.
statement ok
ALTER TABLE t2 DROP CONSTRAINT check_b;
query T
SELECT get_fn_depended_on_by($fn_id)
----
[
{
"constraintIds": [
2,
3
],
"id": 111
},
{
"constraintIds": [
2
],
"id": 112
}
]
statement ok
ALTER TABLE t2 DROP CONSTRAINT cka;
query T
SELECT get_fn_depended_on_by($fn_id)
----
[
{
"constraintIds": [
2,
3
],
"id": 111
}
]
# Make sure that DROP TABLE remove references properly.
statement ok
DROP TABLE t1;
DROP TABLE t2;
query T
SELECT get_fn_depended_on_by($fn_id)
----
NULL
# Make sure function cannot be dropped if used in constraints
statement ok
CREATE TABLE t1(
a INT PRIMARY KEY,
b INT CHECK (f1(b) > 1),
FAMILY fam_0 (a, b)
);
CREATE TABLE t2(
a INT PRIMARY KEY,
b INT CHECK (f1(b) > 1),
FAMILY fam_0 (a, b)
);
statement error pgcode 2BP01 cannot drop function "f1" because other objects \(\[test.public.t1, test.public.t2\]\) still depend on it
DROP FUNCTION f1;
statement ok
ALTER TABLE t1 DROP CONSTRAINT check_b;
ALTER TABLE t2 DROP CONSTRAINT check_b;
statement ok
DROP FUNCTION f1;
statement ok
DROP TABLE t1;
DROP TABLE t2;
# Make sure that CREATE FUNCTION and CREATE TABLE works in one txn.
statement ok
BEGIN;
CREATE FUNCTION f1(a INT) RETURNS INT LANGUAGE SQL AS $$ SELECT a + 1 $$;
CREATE TABLE t1(
a INT PRIMARY KEY,
b INT CHECK (f1(b) > 1),
FAMILY fam_0 (a, b)
);
END;
let $tbl_id
SELECT id FROM system.namespace WHERE name = 't1';
let $fn_id
SELECT oid::int - 100000 FROM pg_catalog.pg_proc WHERE proname = 'f1';
query T
SELECT get_checks($tbl_id);
----
[
{
"columnIds": [
2
],
"constraintId": 2,
"expr": "[FUNCTION 100115](b) \u003e 1:::INT8",
"name": "check_b"
}
]
query T
SELECT get_fn_depended_on_by($fn_id);
----
[
{
"constraintIds": [
2
],
"id": 116
}
]
statement ok
BEGIN;
DROP TABLE t1;
DROP FUNCTION f1;
END;
# Make sure that CREATE FUNCTION and ADD CONSTRAINT works in one txn.
statement ok
CREATE TABLE t1 (
a INT PRIMARY KEY,
b INT,
FAMILY fam_0 (a, b)
);
statement ok
BEGIN;
CREATE FUNCTION f1(a INT) RETURNS INT LANGUAGE SQL AS $$ SELECT a + 1 $$;
ALTER TABLE t1 ADD CONSTRAINT check_b CHECK (f1(b) > 1);
END;
let $tbl_id
SELECT id FROM system.namespace WHERE name = 't1';
let $fn_id
SELECT oid::int - 100000 FROM pg_catalog.pg_proc WHERE proname = 'f1';
query T
SELECT get_checks($tbl_id);
----
[
{
"columnIds": [
2
],
"constraintId": 2,
"expr": "[FUNCTION 100118](b) \u003e 1:::INT8",
"name": "check_b"
}
]
query T
SELECT get_fn_depended_on_by($fn_id);
----
[
{
"constraintIds": [
2
],
"id": 117
}
]
skipif config local-legacy-schema-changer
statement ok
SET use_declarative_schema_changer = 'unsafe_always';
# In legacy schema changer, constraints are formally dropped in jobs.
# So by the point we do DROP FUNCTION, constraints are still there.
skipif config local-legacy-schema-changer
statement ok
BEGIN;
ALTER TABLE t1 DROP CONSTRAINT check_b;
DROP FUNCTION f1;
END;
statement ok
DROP TABLE t1;
skipif config local-legacy-schema-changer
statement ok
SET use_declarative_schema_changer = 'on';
# Make sure check constraint actually validates.
statement ok
CREATE OR REPLACE FUNCTION f1(a INT) RETURNS INT LANGUAGE SQL AS $$ SELECT a + 1 $$;
CREATE TABLE t1 (
a INT PRIMARY KEY,
b INT CHECK (f1(b) > 1),
FAMILY fam_0 (a, b)
);
statement error pgcode 23514 failed to satisfy CHECK constraint \(public\.f1\(b\) > 1:::INT8\)
INSERT INTO t1 VALUES (1,0);
statement ok
INSERT INTO t1 VALUES (1,1);
statement error pgcode 23514 validation of CHECK "public\.f1\(a\) > 10:::INT8" failed on row: a=1, b=1
ALTER TABLE t1 ADD CONSTRAINT cka CHECK (f1(a) > 10);
# Make sure that constraint still works after a function is renamed.
statement ok
ALTER TABLE t1 ADD CONSTRAINT cka CHECK (f1(a) > 1);
statement error pgcode 23514 pq: failed to satisfy CHECK constraint \(public\.f1\(b\) > 1:::INT8\)
INSERT INTO t1 VALUES (2, -1);
statement ok
ALTER FUNCTION f1 RENAME to f2;
statement error pgcode 23514 pq: failed to satisfy CHECK constraint \(public\.f2\(b\) > 1:::INT8\)
INSERT INTO t1 VALUES (2, -1);
query T
SELECT create_statement FROM [SHOW CREATE TABLE t1]
----
CREATE TABLE public.t1 (
a INT8 NOT NULL,
b INT8 NULL,
CONSTRAINT t1_pkey PRIMARY KEY (a ASC),
FAMILY fam_0 (a, b),
CONSTRAINT check_b CHECK (public.f2(b) > 1:::INT8),
CONSTRAINT cka CHECK (public.f2(a) > 1:::INT8)
)
# Make sure that schema prefix is preserved through serialization and
# deserialization.
statement ok
CREATE DATABASE db1;
USE db1;
CREATE SCHEMA sc1;
CREATE FUNCTION sc1.f1(a INT) RETURNS INT LANGUAGE SQL AS $$ SELECT a + 1 $$;
CREATE FUNCTION sc1.f1() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;
CREATE TABLE t(
a INT PRIMARY KEY,
b INT CHECK (sc1.f1(b) > 1),
FAMILY fam_0_b_a (b, a)
);
query T
SELECT create_statement FROM [SHOW CREATE TABLE t]
----
CREATE TABLE public.t (
a INT8 NOT NULL,
b INT8 NULL,
CONSTRAINT t_pkey PRIMARY KEY (a ASC),
FAMILY fam_0_b_a (b, a),
CONSTRAINT check_b CHECK (sc1.f1(b) > 1:::INT8)
)
# Make sure dependency circle is not allowed.
statement ok
CREATE TABLE t_circle(a INT PRIMARY KEY, b INT);
CREATE FUNCTION f_circle() RETURNS INT LANGUAGE SQL AS $$ SELECT a FROM t_circle $$;
# TODO(107369): This does not appear to error in postgres.
statement error .*cannot add dependency from descriptor \d+ to function f_circle \(\d+\) because there will be a dependency cycle
ALTER TABLE t_circle ADD CONSTRAINT ckb CHECK (b + f_circle() > 1);
# Reproduction/regression test for https://github.com/cockroachdb/cockroach/issues/109414
# Adding a check constraint with alter table doesn't appropriately update back references.
subtest issue-109414-minimal
statement ok
CREATE TABLE accounts (id UUID NOT NULL);
CREATE FUNCTION has_account(account_id UUID) RETURNS BOOL LANGUAGE SQL AS $$ SELECT EXISTS(SELECT * FROM accounts WHERE id = account_id) $$;
CREATE TABLE account_ref ( account_id UUID NOT NULL );
ALTER TABLE account_ref ADD CONSTRAINT diy_fk CHECK (has_account(account_id));
# This is the original Reproduction of #109414. It's quite interesting in and
# of itself, so it's been included as it may catch other regressions that the
# minimal case wouldn't.
subtest issue-109414-full
statement ok
CREATE TABLE accounts_a (id UUID NOT NULL);
CREATE TABLE accounts_b (id UUID NOT NULL);
CREATE FUNCTION is_a_or_b(account_id UUID, account_type TEXT) RETURNS BOOL LANGUAGE SQL AS $$ SELECT (CASE
WHEN account_type = 'type_a' THEN (SELECT EXISTS(SELECT * FROM accounts_a WHERE id = account_id))
WHEN account_type = 'type_b' THEN (SELECT EXISTS(SELECT * FROM accounts_b WHERE id = account_id))
ELSE false
END) $$;
CREATE TABLE a (
account_id UUID NOT NULL,
account_type TEXT NOT NULL
);
ALTER TABLE a ADD CONSTRAINT is_a_or_b CHECK (is_a_or_b(account_id, account_type));