-
Notifications
You must be signed in to change notification settings - Fork 32
Description
Bug Description
The deparser silently drops the WHERE clause from EXCLUDE constraints during parse → deparse cycles.
Input SQL
CREATE TABLE test_exclude_where (
id uuid PRIMARY KEY,
database_id uuid NOT NULL,
status text NOT NULL DEFAULT 'pending',
EXCLUDE USING btree (database_id WITH =)
WHERE (status = 'pending')
);Deparsed output
CREATE TABLE test_exclude_where (id uuid PRIMARY KEY, database_id uuid NOT NULL, status text NOT NULL DEFAULT 'pending', EXCLUDE USING btree (database_id WITH =))The WHERE (status = 'pending') predicate is completely lost.
Root Cause
The CONSTR_EXCLUSION handler in packages/deparser/src/deparser.ts (lines ~3038-3070) handles access_method and exclusions but never checks or outputs node.where_clause.
The parser correctly preserves where_clause in the AST (verified — the constraint node has a full where_clause A_Expr), but the deparser never visits it.
Impact
This silently changes constraint semantics:
- With WHERE: "only one pending row per database_id" (partial exclusion)
- Without WHERE: "only one row per database_id ever" (total exclusion)
Discovered in constructive-db's database_transfer table — pgpm package uses the deparser to bundle SQL, and the bundled output was missing the WHERE clause.
Suggested Fix
In the CONSTR_EXCLUSION case, after the exclusions block and before the break:
if (node.where_clause) {
output.push('WHERE');
output.push('(' + this.visit(node.where_clause, context) + ')');
}Failing Test
PR #286 adds a failing test case (misc/issues-18.sql) that reproduces this bug.