Skip to content

Commit b2f57d9

Browse files
committed
fix(deparser): emit CREATE INDEX trailing clauses in grammar order
NULLS NOT DISTINCT now follows the column/INCLUDE list and WHERE moves after WITH/TABLESPACE, so partial unique indexes survive round-trip.
1 parent a437944 commit b2f57d9

4 files changed

Lines changed: 62 additions & 7 deletions

File tree

‎__fixtures__/generated/generated.json‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21413,6 +21413,18 @@
2141321413
"misc/indexes-11.sql": "DROP INDEX CONCURRENTLY IF EXISTS my_index CASCADE",
2141421414
"misc/indexes-12.sql": "CREATE UNIQUE INDEX new_unique_idx ON new_example(a, b) INCLUDE (c)",
2141521415
"misc/indexes-13.sql": "CREATE INDEX CONCURRENTLY idx_with_operator ON boom.merkle_tree USING GIN ( name gin_trgm_ops ( param1 = 32, param2 = true) )",
21416+
"misc/index-clause-ordering-1.sql": "CREATE UNIQUE INDEX u1 ON t (a) NULLS NOT DISTINCT",
21417+
"misc/index-clause-ordering-2.sql": "CREATE UNIQUE INDEX u2 ON t (a, b) NULLS NOT DISTINCT WHERE c IS NULL",
21418+
"misc/index-clause-ordering-3.sql": "CREATE UNIQUE INDEX u3 ON t (a) INCLUDE (b) NULLS NOT DISTINCT",
21419+
"misc/index-clause-ordering-4.sql": "CREATE UNIQUE INDEX u4 ON t (a) INCLUDE (b) NULLS NOT DISTINCT WITH (fillfactor = 70) WHERE c IS NULL",
21420+
"misc/index-clause-ordering-5.sql": "CREATE UNIQUE INDEX u5 ON t (a) NULLS NOT DISTINCT WITH (fillfactor = 70) TABLESPACE pg_default WHERE c IS NULL",
21421+
"misc/index-clause-ordering-6.sql": "CREATE UNIQUE INDEX u6 ON s.t (a, b) WITH (fillfactor = 70) WHERE c IS NULL",
21422+
"misc/index-clause-ordering-7.sql": "CREATE UNIQUE INDEX u7 ON t (a) WHERE c IS NULL",
21423+
"misc/index-clause-ordering-8.sql": "CREATE INDEX u8 ON t USING btree (a) WITH (fillfactor = 70) TABLESPACE pg_default WHERE c IS NULL",
21424+
"misc/index-clause-ordering-9.sql": "CREATE UNIQUE INDEX platform_secrets_namespace_id_name_realm_idx\n ON \"constructive-store-private\".platform_secrets ( namespace_id, name, realm )\n NULLS NOT DISTINCT WHERE retired_at IS NULL",
21425+
"misc/index-clause-ordering-10.sql": "CREATE UNIQUE INDEX secrets_database_id_namespace_id_name_realm_idx\n ON \"constructive-store-private\".secrets ( database_id, namespace_id, name, realm )\n NULLS NOT DISTINCT WHERE retired_at IS NULL",
21426+
"misc/index-clause-ordering-11.sql": "ALTER TABLE t ADD CONSTRAINT c UNIQUE NULLS NOT DISTINCT (a, b)",
21427+
"misc/index-clause-ordering-12.sql": "CREATE TABLE nnd (a int, b int, UNIQUE NULLS NOT DISTINCT (a, b))",
2141621428
"misc/generated-columns-1.sql": "CREATE TABLE generated_misc_test (\n a INT,\n b INT,\n c INT GENERATED ALWAYS AS (a + b) STORED\n)",
2141721429
"misc/generated-columns-2.sql": "CREATE TABLE generated_func_test (\n name TEXT,\n name_upper TEXT GENERATED ALWAYS AS (upper(name)) STORED\n)",
2141821430
"misc/generated-columns-3.sql": "CREATE TABLE generated_numeric_test (\n quantity INT,\n unit_price NUMERIC(10, 2),\n total_price NUMERIC(10, 2) GENERATED ALWAYS AS (quantity * unit_price) STORED NOT NULL UNIQUE\n)",
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-- CREATE INDEX trailing-clause ordering: the grammar requires
2+
-- ( params ) INCLUDE ... NULLS NOT DISTINCT WITH ... TABLESPACE ... WHERE ...
3+
-- Ref: constructive-io/constructive-planning#1382
4+
CREATE UNIQUE INDEX u1 ON t (a) NULLS NOT DISTINCT;
5+
CREATE UNIQUE INDEX u2 ON t (a, b) NULLS NOT DISTINCT WHERE c IS NULL;
6+
CREATE UNIQUE INDEX u3 ON t (a) INCLUDE (b) NULLS NOT DISTINCT;
7+
CREATE UNIQUE INDEX u4 ON t (a) INCLUDE (b) NULLS NOT DISTINCT WITH (fillfactor = 70) WHERE c IS NULL;
8+
CREATE UNIQUE INDEX u5 ON t (a) NULLS NOT DISTINCT WITH (fillfactor = 70) TABLESPACE pg_default WHERE c IS NULL;
9+
CREATE UNIQUE INDEX u6 ON s.t (a, b) WITH (fillfactor = 70) WHERE c IS NULL;
10+
CREATE UNIQUE INDEX u7 ON t (a) WHERE c IS NULL;
11+
CREATE INDEX u8 ON t USING btree (a) WITH (fillfactor = 70) TABLESPACE pg_default WHERE c IS NULL;
12+
13+
CREATE UNIQUE INDEX platform_secrets_namespace_id_name_realm_idx
14+
ON "constructive-store-private".platform_secrets ( namespace_id, name, realm )
15+
NULLS NOT DISTINCT WHERE retired_at IS NULL;
16+
17+
CREATE UNIQUE INDEX secrets_database_id_namespace_id_name_realm_idx
18+
ON "constructive-store-private".secrets ( database_id, namespace_id, name, realm )
19+
NULLS NOT DISTINCT WHERE retired_at IS NULL;
20+
21+
-- table-constraint path, which already emitted NULLS NOT DISTINCT before the key list
22+
ALTER TABLE t ADD CONSTRAINT c UNIQUE NULLS NOT DISTINCT (a, b);
23+
CREATE TABLE nnd (a int, b int, UNIQUE NULLS NOT DISTINCT (a, b));
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
import { FixtureTestUtils } from '../../test-utils';
3+
const fixtures = new FixtureTestUtils();
4+
5+
it('misc-index-clause-ordering', async () => {
6+
await fixtures.runFixtureTests([
7+
"misc/index-clause-ordering-1.sql",
8+
"misc/index-clause-ordering-2.sql",
9+
"misc/index-clause-ordering-3.sql",
10+
"misc/index-clause-ordering-4.sql",
11+
"misc/index-clause-ordering-5.sql",
12+
"misc/index-clause-ordering-6.sql",
13+
"misc/index-clause-ordering-7.sql",
14+
"misc/index-clause-ordering-8.sql",
15+
"misc/index-clause-ordering-9.sql",
16+
"misc/index-clause-ordering-10.sql",
17+
"misc/index-clause-ordering-11.sql",
18+
"misc/index-clause-ordering-12.sql"
19+
]);
20+
});

‎packages/deparser/src/deparser.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3808,9 +3808,8 @@ export class Deparser implements DeparserVisitor {
38083808
output.push(context.parens(includeStrs.join(', ')));
38093809
}
38103810

3811-
if (node.whereClause) {
3812-
output.push('WHERE');
3813-
output.push(this.visit(node.whereClause, context));
3811+
if (node.nulls_not_distinct) {
3812+
output.push('NULLS NOT DISTINCT');
38143813
}
38153814

38163815
if (node.options && node.options.length > 0) {
@@ -3820,15 +3819,16 @@ export class Deparser implements DeparserVisitor {
38203819
output.push(context.parens(optionStrs.join(', ')));
38213820
}
38223821

3823-
if (node.nulls_not_distinct) {
3824-
output.push('NULLS NOT DISTINCT');
3825-
}
3826-
38273822
if (node.tableSpace) {
38283823
output.push('TABLESPACE');
38293824
output.push(QuoteUtils.quoteIdentifier(node.tableSpace));
38303825
}
38313826

3827+
if (node.whereClause) {
3828+
output.push('WHERE');
3829+
output.push(this.visit(node.whereClause, context));
3830+
}
3831+
38323832
return output.join(' ');
38333833
}
38343834

0 commit comments

Comments
 (0)