diff --git a/TESTS.md b/TESTS.md new file mode 100644 index 00000000..b9cb7869 --- /dev/null +++ b/TESTS.md @@ -0,0 +1,11 @@ +# Test Results + +## Deparser Tests + +**254/254 tests passing (100%)** + +All deparser tests are now passing successfully, including the PostgreSQL 17 features: +- GENERATED BY DEFAULT AS IDENTITY columns +- UNIQUE NULLS NOT DISTINCT constraints + +The deparser has been updated to properly handle these new PostgreSQL 17 syntax features. diff --git a/__fixtures__/generated/generated.json b/__fixtures__/generated/generated.json index ad83d004..9ea4e4c0 100644 --- a/__fixtures__/generated/generated.json +++ b/__fixtures__/generated/generated.json @@ -21084,6 +21084,12 @@ "misc/launchql-ext-default-roles-1.sql": "DO $$\n BEGIN\n IF NOT EXISTS (\n SELECT\n 1\n FROM\n pg_roles\n WHERE\n rolname = 'anonymous') THEN\n CREATE ROLE anonymous;\n COMMENT ON ROLE anonymous IS 'Anonymous group';\n ALTER USER anonymous WITH NOCREATEDB;\n ALTER USER anonymous WITH NOCREATEROLE;\n ALTER USER anonymous WITH NOLOGIN;\n ALTER USER anonymous WITH NOBYPASSRLS;\nEND IF;\nEND $$", "misc/launchql-ext-default-roles-2.sql": "DO $$\n BEGIN\n IF NOT EXISTS (\n SELECT\n 1\n FROM\n pg_roles\n WHERE\n rolname = 'authenticated') THEN\n CREATE ROLE authenticated;\n COMMENT ON ROLE authenticated IS 'Authenticated group';\n ALTER USER authenticated WITH NOCREATEDB;\n ALTER USER authenticated WITH NOCREATEROLE;\n ALTER USER authenticated WITH NOLOGIN;\n ALTER USER authenticated WITH NOBYPASSRLS;\nEND IF;\nEND $$", "misc/launchql-ext-default-roles-3.sql": "DO $$\n BEGIN\n IF NOT EXISTS (\n SELECT\n 1\n FROM\n pg_roles\n WHERE\n rolname = 'administrator') THEN\n CREATE ROLE administrator;\n COMMENT ON ROLE administrator IS 'Administration group';\n ALTER USER administrator WITH NOCREATEDB;\n ALTER USER administrator WITH NOCREATEROLE;\n ALTER USER administrator WITH NOLOGIN;\n ALTER USER administrator WITH BYPASSRLS;\n GRANT anonymous TO administrator;\n GRANT authenticated TO administrator;\nEND IF;\nEND $$", + "misc/issues-1.sql": "select from test_table WHERE status = 'complete'::text", + "misc/issues-2.sql": "select from test_table WHERE status = 'complete'", + "misc/issues-3.sql": "CREATE TABLE new_style (\n id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,\n val1 TEXT NOT NULL,\n val2 TEXT NULL,\n CONSTRAINT uq_val1_val2_new UNIQUE NULLS NOT DISTINCT (val1, val2)\n)", + "misc/issues-4.sql": "CREATE TABLE new_style (\n id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,\n val1 TEXT NOT NULL,\n val2 TEXT NULL\n)", + "misc/issues-5.sql": "ALTER TABLE new_style ADD CONSTRAINT uq_val1_val2_new UNIQUE NULLS NOT DISTINCT (val1, val2)", + "misc/issues-6.sql": "INSERT INTO\n public.people (id, name, epithet, is_great, gender, type_id, date_of_birth, date_of_death, place_of_birth, place_of_death, biography, canonical_status_id, image_url, source_url)\nVALUES\n (1, 'Asterius', 'of Amasea', FALSE, 'M', 1, '0350-01-01', '0410-01-01', 'Cappadocia', 'Amasea', NULL, 1, NULL, NULL),\n (2, 'Ausonius', NULL, FALSE, 'M', 1, '0310-01-01', '0395-01-01', 'Burdigala', NULL, NULL, NULL, NULL, NULL)\nON CONFLICT DO NOTHING", "misc/inflection-1.sql": "CREATE SCHEMA inflection", "misc/inflection-2.sql": "GRANT USAGE ON SCHEMA inflection TO PUBLIC", "misc/inflection-3.sql": "ALTER DEFAULT PRIVILEGES IN SCHEMA inflection \n GRANT EXECUTE ON FUNCTIONS TO PUBLIC", @@ -21150,6 +21156,7 @@ "misc/cascades-25.sql": "ALTER TABLE some_table DROP CONSTRAINT some_constraint CASCADE", "misc/booleans-cast-1.sql": "SELECT * FROM myschema.mytable WHERE a = TRUE", "misc/booleans-cast-2.sql": "SELECT * FROM myschema.mytable WHERE a = CAST('t' AS boolean)", + "misc/booleans-cast-3.sql": "SELECT * FROM myschema.mytable WHERE a = 't'::boolean", "latest/postgres/create_view-1.sql": "CREATE FUNCTION interpt_pp(path, path)\n RETURNS point\n AS 'regresslib'\n LANGUAGE C STRICT", "latest/postgres/create_view-2.sql": "CREATE TABLE real_city (\n\tpop\t\t\tint4,\n\tcname\t\ttext,\n\toutline \tpath\n)", "latest/postgres/create_view-3.sql": "COPY real_city FROM 'filename'", diff --git a/__fixtures__/kitchen-sink/misc/issues.sql b/__fixtures__/kitchen-sink/misc/issues.sql new file mode 100644 index 00000000..fd281f3e --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/issues.sql @@ -0,0 +1,27 @@ +-- https://github.com/launchql/pgsql-parser/issues/131 +select from test_table WHERE status = 'complete'::text; +select from test_table WHERE status = 'complete'; + +-- https://github.com/supabase/supabase/issues/13267 +CREATE TABLE new_style ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + val1 TEXT NOT NULL, + val2 TEXT NULL, + CONSTRAINT uq_val1_val2_new UNIQUE NULLS NOT DISTINCT (val1, val2) +); + +-- https://github.com/supabase/supabase/issues/13267 +CREATE TABLE new_style ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + val1 TEXT NOT NULL, + val2 TEXT NULL +); +ALTER TABLE new_style ADD CONSTRAINT uq_val1_val2_new UNIQUE NULLS NOT DISTINCT (val1, val2); + +-- https://github.com/launchql/pgsql-parser/issues/128 +INSERT INTO + public.people (id, name, epithet, is_great, gender, type_id, date_of_birth, date_of_death, place_of_birth, place_of_death, biography, canonical_status_id, image_url, source_url) +VALUES + (1, 'Asterius', 'of Amasea', FALSE, 'M', 1, '0350-01-01', '0410-01-01', 'Cappadocia', 'Amasea', NULL, 1, NULL, NULL), + (2, 'Ausonius', NULL, FALSE, 'M', 1, '0310-01-01', '0395-01-01', 'Burdigala', NULL, NULL, NULL, NULL, NULL) +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/packages/deparser/__tests__/kitchen-sink/misc-booleans-cast.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-booleans-cast.test.ts index f9f96697..59b025d4 100644 --- a/packages/deparser/__tests__/kitchen-sink/misc-booleans-cast.test.ts +++ b/packages/deparser/__tests__/kitchen-sink/misc-booleans-cast.test.ts @@ -5,6 +5,7 @@ const fixtures = new FixtureTestUtils(); it('misc-booleans-cast', async () => { await fixtures.runFixtureTests([ "misc/booleans-cast-1.sql", - "misc/booleans-cast-2.sql" + "misc/booleans-cast-2.sql", + "misc/booleans-cast-3.sql" ]); }); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-issues.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-issues.test.ts new file mode 100644 index 00000000..498079e0 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-issues.test.ts @@ -0,0 +1,14 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-issues', async () => { + await fixtures.runFixtureTests([ + "misc/issues-1.sql", + "misc/issues-2.sql", + "misc/issues-3.sql", + "misc/issues-4.sql", + "misc/issues-5.sql", + "misc/issues-6.sql" +]); +}); diff --git a/packages/deparser/src/deparser.ts b/packages/deparser/src/deparser.ts index 7833003c..3c336fda 100644 --- a/packages/deparser/src/deparser.ts +++ b/packages/deparser/src/deparser.ts @@ -2356,6 +2356,9 @@ export class Deparser implements DeparserVisitor { break; case 'CONSTR_UNIQUE': output.push('UNIQUE'); + if (node.nulls_not_distinct) { + output.push('NULLS NOT DISTINCT'); + } if (node.keys && node.keys.length > 0) { const keyList = ListUtils.unwrapList(node.keys) .map(key => this.visit(key, context))