Skip to content

Commit 3067dc0

Browse files
authored
Merge pull request #560 from supabase/feat/check-constraints
feat: view and update check constraints
2 parents 7010660 + 3c18e57 commit 3067dc0

File tree

6 files changed

+92
-0
lines changed

6 files changed

+92
-0
lines changed

src/lib/PostgresMetaColumns.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ COMMIT;`
213213
is_nullable,
214214
is_unique,
215215
comment,
216+
check,
216217
}: {
217218
name?: string
218219
type?: string
@@ -224,6 +225,7 @@ COMMIT;`
224225
is_nullable?: boolean
225226
is_unique?: boolean
226227
comment?: string
228+
check?: string
227229
}
228230
): Promise<PostgresMetaResult<PostgresColumn>> {
229231
const { data: old, error } = await this.retrieve({ id })
@@ -328,6 +330,46 @@ $$;
328330
old!.name
329331
)} IS ${literal(comment)};`
330332

333+
const checkSql =
334+
check === undefined
335+
? ''
336+
: `
337+
DO $$
338+
DECLARE
339+
v_conname name;
340+
v_conkey int2[];
341+
BEGIN
342+
SELECT conname into v_conname FROM pg_constraint WHERE
343+
contype = 'c'
344+
AND cardinality(conkey) = 1
345+
AND conrelid = ${literal(old!.table_id)}
346+
AND conkey[1] = ${literal(old!.ordinal_position)}
347+
ORDER BY oid asc
348+
LIMIT 1;
349+
350+
IF v_conname IS NOT NULL THEN
351+
EXECUTE format('ALTER TABLE ${ident(old!.schema)}.${ident(
352+
old!.table
353+
)} DROP CONSTRAINT %s', v_conname);
354+
END IF;
355+
356+
ALTER TABLE ${ident(old!.schema)}.${ident(old!.table)} ADD CONSTRAINT ${ident(
357+
`${old!.table}_${old!.name}_check`
358+
)} CHECK (${check});
359+
360+
SELECT conkey into v_conkey FROM pg_constraint WHERE conname = ${literal(
361+
`${old!.table}_${old!.name}_check`
362+
)};
363+
364+
ASSERT v_conkey IS NOT NULL, 'error creating column constraint: check condition must refer to this column';
365+
ASSERT cardinality(v_conkey) = 1, 'error creating column constraint: check condition cannot refer to multiple columns';
366+
ASSERT v_conkey[1] = ${literal(
367+
old!.ordinal_position
368+
)}, 'error creating column constraint: check condition cannot refer to other columns';
369+
END
370+
$$;
371+
`
372+
331373
// TODO: Can't set default if column is previously identity even if
332374
// is_identity: false. Must do two separate PATCHes (once to drop identity
333375
// and another to set default).
@@ -341,6 +383,7 @@ BEGIN;
341383
${identitySql}
342384
${isUniqueSql}
343385
${commentSql}
386+
${checkSql}
344387
${nameSql}
345388
COMMIT;`
346389
{

src/lib/sql/columns.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ SELECT
4343
OR c.relkind IN ('v', 'f') AND pg_column_is_updatable(c.oid, a.attnum, FALSE)
4444
) AS is_updatable,
4545
uniques.table_id IS NOT NULL AS is_unique,
46+
check_constraints.definition AS "check",
4647
array_to_json(
4748
array(
4849
SELECT
@@ -81,6 +82,19 @@ FROM
8182
FROM pg_catalog.pg_constraint
8283
WHERE contype = 'u' AND cardinality(conkey) = 1
8384
) AS uniques ON uniques.table_id = c.oid AND uniques.ordinal_position = a.attnum
85+
LEFT JOIN (
86+
SELECT
87+
conrelid AS table_id,
88+
conkey[1] AS ordinal_position,
89+
substring(
90+
pg_get_constraintdef(pg_constraint.oid, true),
91+
8,
92+
length(pg_get_constraintdef(pg_constraint.oid, true)) - 8
93+
) AS "definition"
94+
FROM pg_constraint
95+
WHERE contype = 'c' AND cardinality(conkey) = 1
96+
ORDER BY oid asc
97+
) AS check_constraints ON check_constraints.table_id = c.oid AND check_constraints.ordinal_position = a.attnum
8498
WHERE
8599
NOT pg_is_other_temp_schema(nc.oid)
86100
AND a.attnum > 0

test/lib/columns.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ test('list', async () => {
99
},
1010
`
1111
{
12+
"check": null,
1213
"comment": null,
1314
"data_type": "bigint",
1415
"default_value": null,
@@ -54,6 +55,7 @@ test('list from a single table', async () => {
5455
{
5556
"data": [
5657
{
58+
"check": null,
5759
"comment": null,
5860
"data_type": "text",
5961
"default_value": null,
@@ -73,6 +75,7 @@ test('list from a single table', async () => {
7375
"table_id": Any<Number>,
7476
},
7577
{
78+
"check": null,
7679
"comment": null,
7780
"data_type": "text",
7881
"default_value": null,
@@ -150,6 +153,7 @@ test('retrieve, create, update, delete', async () => {
150153
`
151154
{
152155
"data": {
156+
"check": null,
153157
"comment": "foo",
154158
"data_type": "smallint",
155159
"default_value": "'42'::smallint",
@@ -180,6 +184,7 @@ test('retrieve, create, update, delete', async () => {
180184
`
181185
{
182186
"data": {
187+
"check": null,
183188
"comment": "foo",
184189
"data_type": "smallint",
185190
"default_value": "'42'::smallint",
@@ -218,6 +223,7 @@ test('retrieve, create, update, delete', async () => {
218223
`
219224
{
220225
"data": {
226+
"check": null,
221227
"comment": "bar",
222228
"data_type": "integer",
223229
"default_value": null,
@@ -248,6 +254,7 @@ test('retrieve, create, update, delete', async () => {
248254
`
249255
{
250256
"data": {
257+
"check": null,
251258
"comment": "bar",
252259
"data_type": "integer",
253260
"default_value": null,
@@ -292,6 +299,7 @@ test('enum column with quoted name', async () => {
292299
},
293300
`
294301
{
302+
"check": null,
295303
"comment": null,
296304
"data_type": "USER-DEFINED",
297305
"default_value": null,
@@ -399,6 +407,7 @@ test('array column', async () => {
399407
`
400408
{
401409
"data": {
410+
"check": null,
402411
"comment": null,
403412
"data_type": "ARRAY",
404413
"default_value": null,
@@ -445,6 +454,7 @@ test('column with default value', async () => {
445454
`
446455
{
447456
"data": {
457+
"check": null,
448458
"comment": null,
449459
"data_type": "timestamp with time zone",
450460
"default_value": "now()",
@@ -522,6 +532,7 @@ test('update with name unchanged', async () => {
522532
`
523533
{
524534
"data": {
535+
"check": null,
525536
"comment": null,
526537
"data_type": "smallint",
527538
"default_value": null,
@@ -569,6 +580,7 @@ test('update with array types', async () => {
569580
`
570581
{
571582
"data": {
583+
"check": null,
572584
"comment": null,
573585
"data_type": "ARRAY",
574586
"default_value": null,
@@ -616,6 +628,7 @@ test('update with incompatible types', async () => {
616628
`
617629
{
618630
"data": {
631+
"check": null,
619632
"comment": null,
620633
"data_type": "integer",
621634
"default_value": null,
@@ -662,6 +675,7 @@ test('update is_unique', async () => {
662675
`
663676
{
664677
"data": {
678+
"check": null,
665679
"comment": null,
666680
"data_type": "text",
667681
"default_value": null,
@@ -695,6 +709,7 @@ test('update is_unique', async () => {
695709
`
696710
{
697711
"data": {
712+
"check": null,
698713
"comment": null,
699714
"data_type": "text",
700715
"default_value": null,
@@ -743,6 +758,7 @@ test('alter column to type with uppercase', async () => {
743758
`
744759
{
745760
"data": {
761+
"check": null,
746762
"comment": null,
747763
"data_type": "USER-DEFINED",
748764
"default_value": null,
@@ -789,6 +805,7 @@ test('enums are populated in enum array columns', async () => {
789805
`
790806
{
791807
"data": {
808+
"check": null,
792809
"comment": null,
793810
"data_type": "ARRAY",
794811
"default_value": null,
@@ -842,6 +859,7 @@ create table public.t (
842859
`
843860
{
844861
"data": {
862+
"check": null,
845863
"comment": null,
846864
"data_type": "bigint",
847865
"default_value": null,

test/lib/foreign-tables.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ test('list', async () => {
2424
{
2525
"columns": [
2626
{
27+
"check": null,
2728
"comment": null,
2829
"data_type": "bigint",
2930
"default_value": null,
@@ -41,6 +42,7 @@ test('list', async () => {
4142
"table": "foreign_table",
4243
},
4344
{
45+
"check": null,
4446
"comment": null,
4547
"data_type": "text",
4648
"default_value": null,
@@ -58,6 +60,7 @@ test('list', async () => {
5860
"table": "foreign_table",
5961
},
6062
{
63+
"check": null,
6164
"comment": null,
6265
"data_type": "USER-DEFINED",
6366
"default_value": null,
@@ -104,6 +107,7 @@ test('retrieve', async () => {
104107
"data": {
105108
"columns": [
106109
{
110+
"check": null,
107111
"comment": null,
108112
"data_type": "bigint",
109113
"default_value": null,
@@ -121,6 +125,7 @@ test('retrieve', async () => {
121125
"table": "foreign_table",
122126
},
123127
{
128+
"check": null,
124129
"comment": null,
125130
"data_type": "text",
126131
"default_value": null,
@@ -138,6 +143,7 @@ test('retrieve', async () => {
138143
"table": "foreign_table",
139144
},
140145
{
146+
"check": null,
141147
"comment": null,
142148
"data_type": "USER-DEFINED",
143149
"default_value": null,

test/lib/tables.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ test('list', async () => {
4242
"bytes": Any<Number>,
4343
"columns": [
4444
{
45+
"check": null,
4546
"comment": null,
4647
"data_type": "bigint",
4748
"default_value": null,
@@ -59,6 +60,7 @@ test('list', async () => {
5960
"table": "users",
6061
},
6162
{
63+
"check": null,
6264
"comment": null,
6365
"data_type": "text",
6466
"default_value": null,
@@ -76,6 +78,7 @@ test('list', async () => {
7678
"table": "users",
7779
},
7880
{
81+
"check": null,
7982
"comment": null,
8083
"data_type": "USER-DEFINED",
8184
"default_value": "'ACTIVE'::user_status",
@@ -419,6 +422,7 @@ test('primary keys', async () => {
419422
"bytes": Any<Number>,
420423
"columns": [
421424
{
425+
"check": null,
422426
"comment": null,
423427
"data_type": "bigint",
424428
"default_value": null,
@@ -436,6 +440,7 @@ test('primary keys', async () => {
436440
"table": "t",
437441
},
438442
{
443+
"check": null,
439444
"comment": null,
440445
"data_type": "text",
441446
"default_value": null,

test/lib/views.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ test('list', async () => {
88
{
99
"columns": [
1010
{
11+
"check": null,
1112
"comment": null,
1213
"data_type": "bigint",
1314
"default_value": null,
@@ -27,6 +28,7 @@ test('list', async () => {
2728
"table_id": 16420,
2829
},
2930
{
31+
"check": null,
3032
"comment": null,
3133
"data_type": "text",
3234
"default_value": null,
@@ -46,6 +48,7 @@ test('list', async () => {
4648
"table_id": 16420,
4749
},
4850
{
51+
"check": null,
4952
"comment": null,
5053
"data_type": "bigint",
5154
"default_value": null,
@@ -102,6 +105,7 @@ test('retrieve', async () => {
102105
"data": {
103106
"columns": [
104107
{
108+
"check": null,
105109
"comment": null,
106110
"data_type": "bigint",
107111
"default_value": null,
@@ -121,6 +125,7 @@ test('retrieve', async () => {
121125
"table_id": 16420,
122126
},
123127
{
128+
"check": null,
124129
"comment": null,
125130
"data_type": "text",
126131
"default_value": null,
@@ -140,6 +145,7 @@ test('retrieve', async () => {
140145
"table_id": 16420,
141146
},
142147
{
148+
"check": null,
143149
"comment": null,
144150
"data_type": "bigint",
145151
"default_value": null,

0 commit comments

Comments
 (0)