From 06ecfd27c95f01445f1f2442efa3dca64d19e4be Mon Sep 17 00:00:00 2001 From: Henry Su Date: Fri, 4 Sep 2026 13:28:31 -0500 Subject: [PATCH] fix: parse column checks with pg_get_expr instead of substring NOT VALID and NO INHERIT suffixes made the old CHECK (...) length heuristic return a corrupted expression into the columns API. --- src/lib/sql/columns.sql.ts | 18 +++++++++++++----- test/lib/columns.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/lib/sql/columns.sql.ts b/src/lib/sql/columns.sql.ts index d4f6b6c72..91325cbee 100644 --- a/src/lib/sql/columns.sql.ts +++ b/src/lib/sql/columns.sql.ts @@ -98,11 +98,19 @@ FROM SELECT DISTINCT ON (table_id, ordinal_position) conrelid AS table_id, conkey[1] AS ordinal_position, - substring( - pg_get_constraintdef(pg_constraint.oid, true), - 8, - length(pg_get_constraintdef(pg_constraint.oid, true)) - 8 - ) AS "definition" + -- Prefer pg_get_expr over stripping CHECK (...) from pg_get_constraintdef: + -- suffixes like NOT VALID / NO INHERIT make the old length-8 heuristic corrupt + -- the expression (e.g. "id > 0) NOT VALI"). Strip one outer paren pair so the + -- API shape stays "c <> 0" rather than "(c <> 0)". + CASE + WHEN pg_get_expr(conbin, conrelid) LIKE '(%)' THEN + substring( + pg_get_expr(conbin, conrelid), + 2, + length(pg_get_expr(conbin, conrelid)) - 2 + ) + ELSE pg_get_expr(conbin, conrelid) + END AS "definition" FROM pg_constraint WHERE contype = 'c' AND cardinality(conkey) = 1 ORDER BY table_id, ordinal_position, oid asc diff --git a/test/lib/columns.ts b/test/lib/columns.ts index 3fcac79fe..92a4be8b1 100644 --- a/test/lib/columns.ts +++ b/test/lib/columns.ts @@ -981,6 +981,41 @@ test('dropping column checks', async () => { await pgMeta.query(`drop table t`) }) +test('column check with NOT VALID suffix', async () => { + await pgMeta.query(` + create table public.t (id int8); + alter table public.t add constraint t_id_check check (id > 0) not valid; + `) + + const res = await pgMeta.columns.retrieve({ + schema: 'public', + table: 't', + name: 'id', + }) + expect(res.error).toBeNull() + // Must be the expression only — not `id > 0) NOT VALI` from the old substring heuristic + expect(res.data?.check).toBe('id > 0') + + await pgMeta.query(`drop table public.t`) +}) + +test('column check with NO INHERIT suffix', async () => { + await pgMeta.query(` + create table public.t (id int8); + alter table public.t add constraint t_id_check check (id > 0) no inherit; + `) + + const res = await pgMeta.columns.retrieve({ + schema: 'public', + table: 't', + name: 'id', + }) + expect(res.error).toBeNull() + expect(res.data?.check).toBe('id > 0') + + await pgMeta.query(`drop table public.t`) +}) + test('column with fully-qualified type', async () => { await pgMeta.query(`create table public.t(); create schema s; create type s.my_type as enum ();`)