diff --git a/__fixtures__/generated/generated.json b/__fixtures__/generated/generated.json index cd156ac1f..90419a2bc 100644 --- a/__fixtures__/generated/generated.json +++ b/__fixtures__/generated/generated.json @@ -21311,6 +21311,221 @@ "misc/pg18-23.sql": "ALTER TABLE nn ADD CONSTRAINT b_not_null NOT NULL a", "misc/pg18-24.sql": "ALTER TABLE nn ADD NOT NULL a NOT VALID", "misc/pg18-25.sql": "CREATE TABLE nn2 (a int, NOT NULL a NO INHERIT)", + "misc/pg18-window-frames-1.sql": "SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) FROM t", + "misc/pg18-window-frames-2.sql": "SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM t", + "misc/pg18-window-frames-3.sql": "SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) FROM t", + "misc/pg18-window-frames-4.sql": "SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM t", + "misc/pg18-window-frames-5.sql": "SELECT sum(x) OVER (ORDER BY x GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t", + "misc/pg18-window-frames-6.sql": "SELECT sum(x) OVER (ORDER BY x GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE GROUP) FROM t", + "misc/pg18-window-frames-7.sql": "SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE TIES) FROM t", + "misc/pg18-window-frames-8.sql": "SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE CURRENT ROW) FROM t", + "misc/pg18-window-frames-9.sql": "SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE NO OTHERS) FROM t", + "misc/pg18-window-frames-10.sql": "SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t", + "misc/pg18-window-frames-11.sql": "SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN INTERVAL '1 day' PRECEDING AND INTERVAL '1 day' FOLLOWING) FROM t", + "misc/pg18-window-frames-12.sql": "SELECT sum(x) OVER (ORDER BY x ROWS UNBOUNDED PRECEDING) FROM t", + "misc/pg18-window-frames-13.sql": "SELECT sum(x) OVER (ORDER BY x ROWS 1 PRECEDING) FROM t", + "misc/pg18-window-frames-14.sql": "SELECT sum(x) OVER (ORDER BY x ROWS CURRENT ROW) FROM t", + "misc/pg18-window-frames-15.sql": "SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 1 FOLLOWING AND 2 FOLLOWING) FROM t", + "misc/pg18-window-frames-16.sql": "SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) FROM t", + "misc/pg18-window-frames-17.sql": "SELECT sum(x) OVER w FROM t WINDOW w AS (ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)", + "misc/pg18-window-frames-18.sql": "SELECT sum(x) OVER (w ORDER BY y) FROM t WINDOW w AS (PARTITION BY z)", + "misc/pg18-window-frames-19.sql": "SELECT sum(x) OVER (w ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t WINDOW w AS (PARTITION BY z ORDER BY x)", + "misc/pg18-window-frames-20.sql": "SELECT sum(x) OVER (PARTITION BY z) FROM t", + "misc/pg18-window-frames-21.sql": "SELECT sum(x) OVER () FROM t", + "misc/pg18-sqljson-1.sql": "SELECT JSON_OBJECT()", + "misc/pg18-sqljson-2.sql": "SELECT JSON_OBJECT(RETURNING json)", + "misc/pg18-sqljson-3.sql": "SELECT JSON_OBJECT(RETURNING jsonb)", + "misc/pg18-sqljson-4.sql": "SELECT JSON_OBJECT(RETURNING text FORMAT JSON)", + "misc/pg18-sqljson-5.sql": "SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF8)", + "misc/pg18-sqljson-6.sql": "SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON)", + "misc/pg18-sqljson-7.sql": "SELECT JSON_OBJECT('foo': 1, 'bar': 'baz')", + "misc/pg18-sqljson-8.sql": "SELECT JSON_OBJECT('foo' VALUE 1, 'bar' VALUE 'baz')", + "misc/pg18-sqljson-9.sql": "SELECT JSON_OBJECT('a': 1 ABSENT ON NULL)", + "misc/pg18-sqljson-10.sql": "SELECT JSON_OBJECT('a': 1 NULL ON NULL)", + "misc/pg18-sqljson-11.sql": "SELECT JSON_OBJECT('a': 1, 'a': 2 WITH UNIQUE KEYS)", + "misc/pg18-sqljson-12.sql": "SELECT JSON_OBJECT('a': 1, 'a': 2 WITHOUT UNIQUE KEYS)", + "misc/pg18-sqljson-13.sql": "SELECT JSON_OBJECT('a': 1, 'b': 2 ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)", + "misc/pg18-sqljson-14.sql": "SELECT JSON_OBJECT('a': '{\"b\": 1}' FORMAT JSON)", + "misc/pg18-sqljson-15.sql": "SELECT JSON_OBJECT('a': '{\"b\": 1}' FORMAT JSON RETURNING jsonb)", + "misc/pg18-sqljson-16.sql": "SELECT JSON_ARRAY()", + "misc/pg18-sqljson-17.sql": "SELECT JSON_ARRAY(RETURNING jsonb)", + "misc/pg18-sqljson-18.sql": "SELECT JSON_ARRAY(1, 2, 'a')", + "misc/pg18-sqljson-19.sql": "SELECT JSON_ARRAY(1, NULL, 2 NULL ON NULL)", + "misc/pg18-sqljson-20.sql": "SELECT JSON_ARRAY(1, NULL, 2 ABSENT ON NULL)", + "misc/pg18-sqljson-21.sql": "SELECT JSON_ARRAY('{\"a\": 1}' FORMAT JSON, 2 RETURNING jsonb)", + "misc/pg18-sqljson-22.sql": "SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i))", + "misc/pg18-sqljson-23.sql": "SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2)) foo(i) RETURNING jsonb)", + "misc/pg18-sqljson-24.sql": "SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1)) foo(i) ORDER BY i)", + "misc/pg18-sqljson-25.sql": "SELECT JSON_ARRAY(SELECT i FROM (VALUES ('[1]')) foo(i) FORMAT JSON RETURNING text)", + "misc/pg18-sqljson-26.sql": "SELECT JSON('{\"a\": 1}')", + "misc/pg18-sqljson-27.sql": "SELECT JSON('{\"a\": 1}' WITH UNIQUE KEYS)", + "misc/pg18-sqljson-28.sql": "SELECT JSON('{\"a\": 1}' WITHOUT UNIQUE KEYS)", + "misc/pg18-sqljson-29.sql": "SELECT JSON('{\"a\": 1}'::bytea FORMAT JSON ENCODING UTF8)", + "misc/pg18-sqljson-30.sql": "SELECT JSON_SCALAR(1)", + "misc/pg18-sqljson-31.sql": "SELECT JSON_SCALAR('text')", + "misc/pg18-sqljson-32.sql": "SELECT JSON_SCALAR(NULL::int)", + "misc/pg18-sqljson-33.sql": "SELECT JSON_SERIALIZE('{\"a\": 1}')", + "misc/pg18-sqljson-34.sql": "SELECT JSON_SERIALIZE('{\"a\": 1}' FORMAT JSON)", + "misc/pg18-sqljson-35.sql": "SELECT JSON_SERIALIZE('{\"a\": 1}'::jsonb RETURNING bytea)", + "misc/pg18-sqljson-36.sql": "SELECT JSON_SERIALIZE('{\"a\": 1}' RETURNING bytea FORMAT JSON ENCODING UTF8)", + "misc/pg18-sqljson-37.sql": "SELECT JSON_OBJECTAGG(k: v) FROM t", + "misc/pg18-sqljson-38.sql": "SELECT JSON_OBJECTAGG(k VALUE v) FROM t", + "misc/pg18-sqljson-39.sql": "SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL) FROM t", + "misc/pg18-sqljson-40.sql": "SELECT JSON_OBJECTAGG(k: v NULL ON NULL WITH UNIQUE KEYS RETURNING jsonb) FROM t", + "misc/pg18-sqljson-41.sql": "SELECT JSON_OBJECTAGG(k: v FORMAT JSON) FROM t", + "misc/pg18-sqljson-42.sql": "SELECT JSON_OBJECTAGG(k: v) FILTER (WHERE k IS NOT NULL) FROM t", + "misc/pg18-sqljson-43.sql": "SELECT JSON_OBJECTAGG(k: v) OVER (PARTITION BY g) FROM t", + "misc/pg18-sqljson-44.sql": "SELECT JSON_OBJECTAGG(k: v) FILTER (WHERE k IS NOT NULL) OVER (ORDER BY k ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM t", + "misc/pg18-sqljson-45.sql": "SELECT JSON_ARRAYAGG(v) FROM t", + "misc/pg18-sqljson-46.sql": "SELECT JSON_ARRAYAGG(v ORDER BY v DESC) FROM t", + "misc/pg18-sqljson-47.sql": "SELECT JSON_ARRAYAGG(v NULL ON NULL) FROM t", + "misc/pg18-sqljson-48.sql": "SELECT JSON_ARRAYAGG(v ABSENT ON NULL RETURNING jsonb) FROM t", + "misc/pg18-sqljson-49.sql": "SELECT JSON_ARRAYAGG(v FORMAT JSON ORDER BY v ABSENT ON NULL RETURNING text) FROM t", + "misc/pg18-sqljson-50.sql": "SELECT JSON_ARRAYAGG(v) FILTER (WHERE v > 0) OVER w FROM t WINDOW w AS (PARTITION BY g)", + "misc/pg18-sqljson-51.sql": "SELECT js IS JSON FROM t", + "misc/pg18-sqljson-52.sql": "SELECT js IS NOT JSON FROM t", + "misc/pg18-sqljson-53.sql": "SELECT js IS JSON VALUE FROM t", + "misc/pg18-sqljson-54.sql": "SELECT js IS JSON OBJECT FROM t", + "misc/pg18-sqljson-55.sql": "SELECT js IS JSON ARRAY FROM t", + "misc/pg18-sqljson-56.sql": "SELECT js IS JSON SCALAR FROM t", + "misc/pg18-sqljson-57.sql": "SELECT js IS JSON WITH UNIQUE KEYS FROM t", + "misc/pg18-sqljson-58.sql": "SELECT js IS JSON OBJECT WITHOUT UNIQUE KEYS FROM t", + "misc/pg18-sqljson-59.sql": "SELECT js IS NOT JSON ARRAY WITH UNIQUE FROM t", + "misc/pg18-sqljson-60.sql": "SELECT CASE WHEN js IS JSON OBJECT THEN 'o' WHEN js IS JSON ARRAY THEN 'a' END FROM t", + "misc/pg18-publication-subscription-1.sql": "CREATE PUBLICATION p FOR ALL TABLES", + "misc/pg18-publication-subscription-2.sql": "CREATE PUBLICATION p FOR TABLE t1, t2 (a, b) WHERE (a > 5)", + "misc/pg18-publication-subscription-3.sql": "CREATE PUBLICATION p FOR TABLES IN SCHEMA s1, s2", + "misc/pg18-publication-subscription-4.sql": "CREATE PUBLICATION p FOR TABLE t1, TABLES IN SCHEMA s1 WITH (publish = 'insert, update', publish_via_partition_root = true)", + "misc/pg18-publication-subscription-5.sql": "CREATE PUBLICATION p WITH (publish_via_partition_root = 1)", + "misc/pg18-publication-subscription-6.sql": "ALTER PUBLICATION p ADD TABLE t3", + "misc/pg18-publication-subscription-7.sql": "ALTER PUBLICATION p ADD TABLE t3 (a) WHERE (a IS NOT NULL)", + "misc/pg18-publication-subscription-8.sql": "ALTER PUBLICATION p DROP TABLE t3", + "misc/pg18-publication-subscription-9.sql": "ALTER PUBLICATION p SET TABLE t1, t2", + "misc/pg18-publication-subscription-10.sql": "ALTER PUBLICATION p SET TABLES IN SCHEMA s1", + "misc/pg18-publication-subscription-11.sql": "ALTER PUBLICATION p ADD TABLES IN SCHEMA s2", + "misc/pg18-publication-subscription-12.sql": "ALTER PUBLICATION p DROP TABLES IN SCHEMA s2", + "misc/pg18-publication-subscription-13.sql": "ALTER PUBLICATION p SET (publish = 'insert')", + "misc/pg18-publication-subscription-14.sql": "ALTER PUBLICATION p SET (publish_via_partition_root = 0)", + "misc/pg18-publication-subscription-15.sql": "ALTER PUBLICATION p SET (publish_via_partition_root = false)", + "misc/pg18-publication-subscription-16.sql": "ALTER PUBLICATION p OWNER TO regress_user", + "misc/pg18-publication-subscription-17.sql": "ALTER PUBLICATION p RENAME TO p2", + "misc/pg18-publication-subscription-18.sql": "CREATE SUBSCRIPTION s CONNECTION 'dbname=doesnotexist' PUBLICATION p WITH (connect = false, slot_name = NONE)", + "misc/pg18-publication-subscription-19.sql": "ALTER SUBSCRIPTION s SET (slot_name = 'newname')", + "misc/pg18-publication-subscription-20.sql": "ALTER SUBSCRIPTION s SET (streaming = parallel)", + "misc/pg18-publication-subscription-21.sql": "ALTER SUBSCRIPTION s CONNECTION 'dbname=other'", + "misc/pg18-publication-subscription-22.sql": "ALTER SUBSCRIPTION s SET PUBLICATION p1, p2 WITH (refresh = false)", + "misc/pg18-publication-subscription-23.sql": "ALTER SUBSCRIPTION s ADD PUBLICATION p3 WITH (refresh = false)", + "misc/pg18-publication-subscription-24.sql": "ALTER SUBSCRIPTION s DROP PUBLICATION p3 WITH (refresh = false)", + "misc/pg18-publication-subscription-25.sql": "ALTER SUBSCRIPTION s REFRESH PUBLICATION WITH (copy_data = false)", + "misc/pg18-publication-subscription-26.sql": "ALTER SUBSCRIPTION s ENABLE", + "misc/pg18-publication-subscription-27.sql": "ALTER SUBSCRIPTION s DISABLE", + "misc/pg18-publication-subscription-28.sql": "ALTER SUBSCRIPTION s SKIP (lsn = '0/12345')", + "misc/pg18-publication-subscription-29.sql": "ALTER SUBSCRIPTION s SKIP (lsn = NONE)", + "misc/pg18-publication-subscription-30.sql": "ALTER SUBSCRIPTION s OWNER TO regress_user", + "misc/pg18-publication-subscription-31.sql": "ALTER SUBSCRIPTION s RENAME TO s2", + "misc/pg18-partition-ctas-1.sql": "CREATE TABLE p1 PARTITION OF parent (f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY) FOR VALUES FROM ('2016-11-01') TO ('2016-12-01')", + "misc/pg18-partition-ctas-2.sql": "CREATE TABLE p1 PARTITION OF parent (a NOT NULL, b DEFAULT 1, CONSTRAINT p1_chk CHECK (a > 0)) FOR VALUES IN (1, 2)", + "misc/pg18-partition-ctas-3.sql": "CREATE TABLE p1 PARTITION OF parent FOR VALUES WITH (MODULUS 2, REMAINDER 0)", + "misc/pg18-partition-ctas-4.sql": "CREATE TABLE p1 PARTITION OF parent FOR VALUES WITH (MODULUS 4, REMAINDER 3)", + "misc/pg18-partition-ctas-5.sql": "CREATE TABLE p1 PARTITION OF parent (a WITH OPTIONS NOT NULL) FOR VALUES WITH (MODULUS 2, REMAINDER 1)", + "misc/pg18-partition-ctas-6.sql": "CREATE TABLE p1 PARTITION OF parent DEFAULT", + "misc/pg18-partition-ctas-7.sql": "CREATE TABLE p1 PARTITION OF parent (a NOT NULL) DEFAULT", + "misc/pg18-partition-ctas-8.sql": "CREATE TABLE p1 PARTITION OF parent FOR VALUES FROM (MINVALUE) TO (MAXVALUE) PARTITION BY HASH (b)", + "misc/pg18-partition-ctas-9.sql": "ALTER TABLE parent ATTACH PARTITION p1 FOR VALUES WITH (MODULUS 2, REMAINDER 0)", + "misc/pg18-partition-ctas-10.sql": "ALTER TABLE parent ATTACH PARTITION p1 DEFAULT", + "misc/pg18-partition-ctas-11.sql": "CREATE TABLE tas_case WITH (\"Fillfactor\" = 10) AS SELECT 1 a", + "misc/pg18-partition-ctas-12.sql": "CREATE TABLE tas WITH (fillfactor = 10, autovacuum_enabled = false) AS SELECT 1 a", + "misc/pg18-partition-ctas-13.sql": "CREATE TEMP TABLE tas ON COMMIT DROP AS SELECT 1 a", + "misc/pg18-partition-ctas-14.sql": "CREATE TABLE tas (x, y) WITH (fillfactor = 50) TABLESPACE pg_default AS SELECT 1, 2 WITH NO DATA", + "misc/pg18-partition-ctas-15.sql": "CREATE TABLE tas USING heap WITH (fillfactor = 50) AS SELECT 1 a", + "misc/pg18-partition-ctas-16.sql": "CREATE MATERIALIZED VIEW mv WITH (fillfactor = 50) AS SELECT 1 a WITH NO DATA", + "misc/pg18-partition-ctas-17.sql": "CREATE TABLE t (a int) WITH (\"Fillfactor\" = 10, toast.autovacuum_enabled = false)", + "misc/pg18-misc-ddl-1.sql": "COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS 'yes, another comment'", + "misc/pg18-misc-ddl-2.sql": "COMMENT ON CONSTRAINT the_constraint ON DOMAIN s.constraint_comments_dom IS NULL", + "misc/pg18-misc-ddl-3.sql": "COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS 'table constraint'", + "misc/pg18-misc-ddl-4.sql": "CREATE TABLE error_tbl (b1 bool DEFAULT (1 IN (1, 2)))", + "misc/pg18-misc-ddl-5.sql": "CREATE TABLE error_tbl (b1 bool DEFAULT (true AND false))", + "misc/pg18-misc-ddl-6.sql": "CREATE TABLE error_tbl (b1 bool DEFAULT ('a' LIKE 'a%'))", + "misc/pg18-misc-ddl-7.sql": "CREATE TABLE error_tbl (b1 bool DEFAULT (1 BETWEEN 0 AND 2))", + "misc/pg18-misc-ddl-8.sql": "CREATE TABLE error_tbl (b1 bool DEFAULT (1 = ANY (ARRAY[1, 2])))", + "misc/pg18-misc-ddl-9.sql": "CREATE TABLE error_tbl (b1 int DEFAULT 1 + 2)", + "misc/pg18-misc-ddl-10.sql": "CREATE TABLE error_tbl (b1 bool DEFAULT 1 IS DISTINCT FROM 2)", + "misc/pg18-misc-ddl-11.sql": "ALTER TABLE error_tbl ALTER COLUMN b1 SET DEFAULT (1 IN (1, 2))", + "misc/pg18-misc-ddl-12.sql": "CREATE TABLE deferred_excl (f1 int, f2 int, CONSTRAINT deferred_excl_con EXCLUDE (f1 WITH =) INITIALLY DEFERRED)", + "misc/pg18-misc-ddl-13.sql": "CREATE TABLE deferred_excl (f1 int, CONSTRAINT deferred_excl_con EXCLUDE (f1 WITH =) DEFERRABLE INITIALLY DEFERRED)", + "misc/pg18-misc-ddl-14.sql": "CREATE TABLE deferred_excl (f1 int, CONSTRAINT deferred_excl_con EXCLUDE USING gist (f1 WITH =) NOT DEFERRABLE)", + "misc/pg18-misc-ddl-15.sql": "ALTER TABLE deferred_excl ADD CONSTRAINT deferred_excl_con2 EXCLUDE (f1 WITH =) DEFERRABLE INITIALLY IMMEDIATE", + "misc/pg18-identity-storage-1.sql": "ALTER TABLE itest ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY", + "misc/pg18-identity-storage-2.sql": "ALTER TABLE itest ALTER COLUMN a ADD GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 2)", + "misc/pg18-identity-storage-3.sql": "ALTER TABLE itest ALTER COLUMN a SET GENERATED ALWAYS", + "misc/pg18-identity-storage-4.sql": "ALTER TABLE itest ALTER COLUMN a SET GENERATED BY DEFAULT", + "misc/pg18-identity-storage-5.sql": "ALTER TABLE itest ALTER COLUMN a SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 100 RESTART", + "misc/pg18-identity-storage-6.sql": "ALTER TABLE itest ALTER COLUMN a RESTART", + "misc/pg18-identity-storage-7.sql": "ALTER TABLE itest ALTER COLUMN a RESTART WITH 1000", + "misc/pg18-identity-storage-8.sql": "ALTER TABLE itest ALTER COLUMN a SET INCREMENT BY 5 SET MINVALUE 1 SET MAXVALUE 100 SET CACHE 10 SET CYCLE", + "misc/pg18-identity-storage-9.sql": "ALTER TABLE itest ALTER COLUMN a SET NO MINVALUE SET NO MAXVALUE SET NO CYCLE", + "misc/pg18-identity-storage-10.sql": "ALTER TABLE itest ALTER COLUMN a DROP IDENTITY", + "misc/pg18-identity-storage-11.sql": "ALTER TABLE itest ALTER COLUMN a DROP IDENTITY IF EXISTS", + "misc/pg18-identity-storage-12.sql": "ALTER SEQUENCE itest_a_seq OWNED BY NONE", + "misc/pg18-identity-storage-13.sql": "ALTER SEQUENCE itest_a_seq OWNED BY itest.a", + "misc/pg18-identity-storage-14.sql": "CREATE TABLE test_storage (a text, c text STORAGE plain)", + "misc/pg18-identity-storage-15.sql": "CREATE TABLE test_storage (a text, b int STORAGE extended, c text STORAGE external, d text STORAGE main)", + "misc/pg18-identity-storage-16.sql": "CREATE TABLE test_compression (a text COMPRESSION lz4, b text COMPRESSION pglz)", + "misc/pg18-identity-storage-17.sql": "CREATE TABLE test_compression (a text STORAGE external COMPRESSION lz4 NOT NULL)", + "misc/pg18-identity-storage-18.sql": "ALTER TABLE t ALTER b TYPE char(2) COLLATE \"POSIX\"", + "misc/pg18-identity-storage-19.sql": "ALTER TABLE t ALTER COLUMN b TYPE text COLLATE \"C\" USING b::text", + "misc/pg18-grant-role-1.sql": "GRANT regress_role1 TO regress_role2", + "misc/pg18-grant-role-2.sql": "GRANT regress_role1 TO regress_role2 WITH ADMIN OPTION", + "misc/pg18-grant-role-3.sql": "GRANT regress_role1 TO regress_role2 WITH ADMIN OPTION GRANTED BY regress_role3", + "misc/pg18-grant-role-4.sql": "GRANT regress_role1 TO regress_role2 GRANTED BY CURRENT_ROLE", + "misc/pg18-grant-role-5.sql": "GRANT regress_role1 TO regress_role2 GRANTED BY CURRENT_USER", + "misc/pg18-grant-role-6.sql": "GRANT regress_role1 TO regress_role2 WITH INHERIT FALSE, ADMIN TRUE", + "misc/pg18-grant-role-7.sql": "GRANT regress_role1 TO regress_role2 WITH INHERIT OPTION, SET FALSE", + "misc/pg18-grant-role-8.sql": "GRANT regress_role1 TO regress_role2 WITH SET TRUE GRANTED BY regress_role3", + "misc/pg18-grant-role-9.sql": "GRANT \"Mixed_Case_Role\" TO regress_role2", + "misc/pg18-grant-role-10.sql": "REVOKE regress_role1 FROM regress_role2", + "misc/pg18-grant-role-11.sql": "REVOKE regress_role1 FROM regress_role2 CASCADE", + "misc/pg18-grant-role-12.sql": "REVOKE ADMIN OPTION FOR regress_role1 FROM regress_role2", + "misc/pg18-grant-role-13.sql": "REVOKE ADMIN OPTION FOR regress_role1 FROM regress_role2 GRANTED BY regress_role3 CASCADE", + "misc/pg18-grant-role-14.sql": "REVOKE INHERIT OPTION FOR regress_role1 FROM regress_role2", + "misc/pg18-grant-role-15.sql": "REVOKE SET OPTION FOR regress_role1 FROM regress_role2", + "misc/pg18-grant-role-16.sql": "REVOKE regress_role1 FROM regress_role2 GRANTED BY CURRENT_ROLE", + "misc/pg18-grant-role-17.sql": "GRANT INSERT ON t TO regress_role2 GRANTED BY CURRENT_USER", + "misc/pg18-grant-role-18.sql": "GRANT TRUNCATE ON t TO regress_role2 GRANTED BY regress_role3", + "misc/pg18-grant-role-19.sql": "REVOKE SELECT ON t FROM regress_role2 GRANTED BY CURRENT_ROLE", + "misc/pg18-cte-insert-1.sql": "WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) SEARCH DEPTH FIRST BY f, t SET seq SELECT * FROM graph", + "misc/pg18-cte-insert-2.sql": "WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) SEARCH BREADTH FIRST BY f, t SET seq SELECT * FROM graph", + "misc/pg18-cte-insert-3.sql": "WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) CYCLE f, t SET is_cycle USING path SELECT * FROM graph", + "misc/pg18-cte-insert-4.sql": "WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) CYCLE f, t SET is_cycle TO 'Y' DEFAULT 'N' USING path SELECT * FROM graph", + "misc/pg18-cte-insert-5.sql": "WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) CYCLE f, t SET is_cycle TO point '(1,1)' DEFAULT point '(0,0)' USING path SELECT * FROM graph", + "misc/pg18-cte-insert-6.sql": "WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) SEARCH DEPTH FIRST BY f, t SET seq CYCLE f, t SET is_cycle USING path SELECT * FROM graph", + "misc/pg18-cte-insert-7.sql": "INSERT INTO itest1 OVERRIDING USER VALUE VALUES (10, 'xyz')", + "misc/pg18-cte-insert-8.sql": "INSERT INTO itest1 (a, b) OVERRIDING SYSTEM VALUE VALUES (10, 'xyz')", + "misc/pg18-cte-insert-9.sql": "INSERT INTO itest1 OVERRIDING SYSTEM VALUE SELECT * FROM itest2", + "misc/pg18-cte-insert-10.sql": "MERGE INTO tgt t USING src s ON t.a = s.a WHEN NOT MATCHED THEN INSERT (a) OVERRIDING SYSTEM VALUE VALUES (s.a)", + "misc/pg18-cte-insert-11.sql": "MERGE INTO tgt t USING src s ON t.a = s.a WHEN MATCHED THEN UPDATE SET (b, c) = (SELECT s.b, s.c)", + "misc/pg18-cte-insert-12.sql": "MERGE INTO tgt t USING src s ON t.a = s.a WHEN MATCHED THEN UPDATE SET (b, c) = (s.b, s.c), d = 1", + "misc/pg18-cte-insert-13.sql": "UPDATE tgt SET (b, c) = (SELECT 1, 2), d = 3", + "misc/pg18-copy-1.sql": "COPY t TO STDOUT", + "misc/pg18-copy-2.sql": "COPY t FROM STDIN", + "misc/pg18-copy-3.sql": "COPY (SELECT * FROM t ORDER BY a) TO STDOUT WITH DELIMITER ','", + "misc/pg18-copy-4.sql": "COPY t TO STDOUT WITH (FORMAT csv, QUOTE '''', DELIMITER '|')", + "misc/pg18-copy-5.sql": "COPY t TO STDOUT WITH (FORMAT csv, FORCE_QUOTE (a, b), ESCAPE E'\\\\')", + "misc/pg18-copy-6.sql": "COPY t TO STDOUT WITH (FORMAT csv, FORCE_QUOTE *)", + "misc/pg18-copy-7.sql": "COPY t FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL (a), FORCE_NULL (b, c))", + "misc/pg18-copy-8.sql": "COPY t FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NULL *)", + "misc/pg18-copy-9.sql": "COPY t FROM STDIN WITH (FORMAT text, DELIMITER ';', NULL '\\N', DEFAULT '\\D', ENCODING 'sql_ascii')", + "misc/pg18-copy-10.sql": "COPY t FROM STDIN WITH (FORMAT binary)", + "misc/pg18-copy-11.sql": "COPY t FROM STDIN WITH (HEADER match)", + "misc/pg18-copy-12.sql": "COPY t FROM STDIN WITH (HEADER true, ON_ERROR ignore, LOG_VERBOSITY verbose)", + "misc/pg18-copy-13.sql": "COPY t FROM STDIN WITH (FREEZE)", + "misc/pg18-copy-14.sql": "COPY t FROM STDIN WITH (REJECT_LIMIT 5)", + "misc/pg18-copy-15.sql": "COPY t (a, b) TO 'file.csv' WITH (FORMAT csv, HEADER)", + "misc/pg18-copy-16.sql": "COPY t FROM PROGRAM 'gunzip < file.gz' WITH (FORMAT text)", + "misc/pg18-copy-17.sql": "COPY t TO PROGRAM 'gzip > file.gz'", + "misc/pg18-copy-18.sql": "COPY t FROM 'it''s.csv'", + "misc/pg18-copy-19.sql": "COPY t FROM STDIN WHERE a > 0", + "misc/pg18-copy-20.sql": "COPY t FROM STDIN WITH NULL AS 'I''m null'", "misc/missing-types-1.sql": "ALTER PUBLICATION \"my_publication\" OWNER TO \"postgres\"", "misc/missing-types-2.sql": "ALTER OPERATOR FAMILY my_opfamily USING btree OWNER TO new_owner", "misc/missing-types-3.sql": "ALTER OPERATOR CLASS my_opclass USING btree OWNER TO new_owner", diff --git a/__fixtures__/kitchen-sink/misc/pg18-copy.sql b/__fixtures__/kitchen-sink/misc/pg18-copy.sql new file mode 100644 index 000000000..24523d186 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-copy.sql @@ -0,0 +1,21 @@ +-- COPY direction, targets and option quoting +COPY t TO STDOUT; +COPY t FROM STDIN; +COPY (SELECT * FROM t ORDER BY a) TO STDOUT WITH DELIMITER ','; +COPY t TO STDOUT WITH (FORMAT csv, QUOTE '''', DELIMITER '|'); +COPY t TO STDOUT WITH (FORMAT csv, FORCE_QUOTE (a, b), ESCAPE E'\\'); +COPY t TO STDOUT WITH (FORMAT csv, FORCE_QUOTE *); +COPY t FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL (a), FORCE_NULL (b, c)); +COPY t FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NULL *); +COPY t FROM STDIN WITH (FORMAT text, DELIMITER ';', NULL '\N', DEFAULT '\D', ENCODING 'sql_ascii'); +COPY t FROM STDIN WITH (FORMAT binary); +COPY t FROM STDIN WITH (HEADER match); +COPY t FROM STDIN WITH (HEADER true, ON_ERROR ignore, LOG_VERBOSITY verbose); +COPY t FROM STDIN WITH (FREEZE); +COPY t FROM STDIN WITH (REJECT_LIMIT 5); +COPY t (a, b) TO 'file.csv' WITH (FORMAT csv, HEADER); +COPY t FROM PROGRAM 'gunzip < file.gz' WITH (FORMAT text); +COPY t TO PROGRAM 'gzip > file.gz'; +COPY t FROM 'it''s.csv'; +COPY t FROM STDIN WHERE a > 0; +COPY t FROM STDIN WITH NULL AS 'I''m null'; diff --git a/__fixtures__/kitchen-sink/misc/pg18-cte-insert.sql b/__fixtures__/kitchen-sink/misc/pg18-cte-insert.sql new file mode 100644 index 000000000..0fd29c1b8 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-cte-insert.sql @@ -0,0 +1,16 @@ +-- CTE SEARCH / CYCLE clauses +WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) SEARCH DEPTH FIRST BY f, t SET seq SELECT * FROM graph; +WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) SEARCH BREADTH FIRST BY f, t SET seq SELECT * FROM graph; +WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) CYCLE f, t SET is_cycle USING path SELECT * FROM graph; +WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) CYCLE f, t SET is_cycle TO 'Y' DEFAULT 'N' USING path SELECT * FROM graph; +WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) CYCLE f, t SET is_cycle TO point '(1,1)' DEFAULT point '(0,0)' USING path SELECT * FROM graph; +WITH RECURSIVE graph(f, t, label) AS (SELECT 1, 2, 'a' UNION ALL SELECT g.f, g.t, g.label FROM graph g) SEARCH DEPTH FIRST BY f, t SET seq CYCLE f, t SET is_cycle USING path SELECT * FROM graph; +-- INSERT OVERRIDING +INSERT INTO itest1 OVERRIDING USER VALUE VALUES (10, 'xyz'); +INSERT INTO itest1 (a, b) OVERRIDING SYSTEM VALUE VALUES (10, 'xyz'); +INSERT INTO itest1 OVERRIDING SYSTEM VALUE SELECT * FROM itest2; +MERGE INTO tgt t USING src s ON t.a = s.a WHEN NOT MATCHED THEN INSERT (a) OVERRIDING SYSTEM VALUE VALUES (s.a); +-- multi-column assignment in MERGE +MERGE INTO tgt t USING src s ON t.a = s.a WHEN MATCHED THEN UPDATE SET (b, c) = (SELECT s.b, s.c); +MERGE INTO tgt t USING src s ON t.a = s.a WHEN MATCHED THEN UPDATE SET (b, c) = (s.b, s.c), d = 1; +UPDATE tgt SET (b, c) = (SELECT 1, 2), d = 3; diff --git a/__fixtures__/kitchen-sink/misc/pg18-grant-role.sql b/__fixtures__/kitchen-sink/misc/pg18-grant-role.sql new file mode 100644 index 000000000..af1337585 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-grant-role.sql @@ -0,0 +1,21 @@ +-- GRANT/REVOKE role membership options, GRANTED BY, CASCADE +GRANT regress_role1 TO regress_role2; +GRANT regress_role1 TO regress_role2 WITH ADMIN OPTION; +GRANT regress_role1 TO regress_role2 WITH ADMIN OPTION GRANTED BY regress_role3; +GRANT regress_role1 TO regress_role2 GRANTED BY CURRENT_ROLE; +GRANT regress_role1 TO regress_role2 GRANTED BY CURRENT_USER; +GRANT regress_role1 TO regress_role2 WITH INHERIT FALSE, ADMIN TRUE; +GRANT regress_role1 TO regress_role2 WITH INHERIT OPTION, SET FALSE; +GRANT regress_role1 TO regress_role2 WITH SET TRUE GRANTED BY regress_role3; +GRANT "Mixed_Case_Role" TO regress_role2; +REVOKE regress_role1 FROM regress_role2; +REVOKE regress_role1 FROM regress_role2 CASCADE; +REVOKE ADMIN OPTION FOR regress_role1 FROM regress_role2; +REVOKE ADMIN OPTION FOR regress_role1 FROM regress_role2 GRANTED BY regress_role3 CASCADE; +REVOKE INHERIT OPTION FOR regress_role1 FROM regress_role2; +REVOKE SET OPTION FOR regress_role1 FROM regress_role2; +REVOKE regress_role1 FROM regress_role2 GRANTED BY CURRENT_ROLE; +-- GRANTED BY on object privileges +GRANT INSERT ON t TO regress_role2 GRANTED BY CURRENT_USER; +GRANT TRUNCATE ON t TO regress_role2 GRANTED BY regress_role3; +REVOKE SELECT ON t FROM regress_role2 GRANTED BY CURRENT_ROLE; diff --git a/__fixtures__/kitchen-sink/misc/pg18-identity-storage.sql b/__fixtures__/kitchen-sink/misc/pg18-identity-storage.sql new file mode 100644 index 000000000..d1cc34f86 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-identity-storage.sql @@ -0,0 +1,22 @@ +-- Identity column alterations +ALTER TABLE itest ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; +ALTER TABLE itest ALTER COLUMN a ADD GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 2); +ALTER TABLE itest ALTER COLUMN a SET GENERATED ALWAYS; +ALTER TABLE itest ALTER COLUMN a SET GENERATED BY DEFAULT; +ALTER TABLE itest ALTER COLUMN a SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 100 RESTART; +ALTER TABLE itest ALTER COLUMN a RESTART; +ALTER TABLE itest ALTER COLUMN a RESTART WITH 1000; +ALTER TABLE itest ALTER COLUMN a SET INCREMENT BY 5 SET MINVALUE 1 SET MAXVALUE 100 SET CACHE 10 SET CYCLE; +ALTER TABLE itest ALTER COLUMN a SET NO MINVALUE SET NO MAXVALUE SET NO CYCLE; +ALTER TABLE itest ALTER COLUMN a DROP IDENTITY; +ALTER TABLE itest ALTER COLUMN a DROP IDENTITY IF EXISTS; +ALTER SEQUENCE itest_a_seq OWNED BY NONE; +ALTER SEQUENCE itest_a_seq OWNED BY itest.a; +-- column STORAGE / COMPRESSION in CREATE TABLE +CREATE TABLE test_storage (a text, c text STORAGE plain); +CREATE TABLE test_storage (a text, b int STORAGE extended, c text STORAGE external, d text STORAGE main); +CREATE TABLE test_compression (a text COMPRESSION lz4, b text COMPRESSION pglz); +CREATE TABLE test_compression (a text STORAGE external COMPRESSION lz4 NOT NULL); +-- ALTER COLUMN TYPE with COLLATE +ALTER TABLE t ALTER b TYPE char(2) COLLATE "POSIX"; +ALTER TABLE t ALTER COLUMN b TYPE text COLLATE "C" USING b::text; diff --git a/__fixtures__/kitchen-sink/misc/pg18-misc-ddl.sql b/__fixtures__/kitchen-sink/misc/pg18-misc-ddl.sql new file mode 100644 index 000000000..650ef4342 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-misc-ddl.sql @@ -0,0 +1,18 @@ +-- COMMENT ON CONSTRAINT ... ON DOMAIN +COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS 'yes, another comment'; +COMMENT ON CONSTRAINT the_constraint ON DOMAIN s.constraint_comments_dom IS NULL; +COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS 'table constraint'; +-- DEFAULT needs parens around non-b_expr expressions +CREATE TABLE error_tbl (b1 bool DEFAULT (1 IN (1, 2))); +CREATE TABLE error_tbl (b1 bool DEFAULT (true AND false)); +CREATE TABLE error_tbl (b1 bool DEFAULT ('a' LIKE 'a%')); +CREATE TABLE error_tbl (b1 bool DEFAULT (1 BETWEEN 0 AND 2)); +CREATE TABLE error_tbl (b1 bool DEFAULT (1 = ANY (ARRAY[1, 2]))); +CREATE TABLE error_tbl (b1 int DEFAULT 1 + 2); +CREATE TABLE error_tbl (b1 bool DEFAULT 1 IS DISTINCT FROM 2); +ALTER TABLE error_tbl ALTER COLUMN b1 SET DEFAULT (1 IN (1, 2)); +-- EXCLUDE constraint deferrability +CREATE TABLE deferred_excl (f1 int, f2 int, CONSTRAINT deferred_excl_con EXCLUDE (f1 WITH =) INITIALLY DEFERRED); +CREATE TABLE deferred_excl (f1 int, CONSTRAINT deferred_excl_con EXCLUDE (f1 WITH =) DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE deferred_excl (f1 int, CONSTRAINT deferred_excl_con EXCLUDE USING gist (f1 WITH =) NOT DEFERRABLE); +ALTER TABLE deferred_excl ADD CONSTRAINT deferred_excl_con2 EXCLUDE (f1 WITH =) DEFERRABLE INITIALLY IMMEDIATE; diff --git a/__fixtures__/kitchen-sink/misc/pg18-partition-ctas.sql b/__fixtures__/kitchen-sink/misc/pg18-partition-ctas.sql new file mode 100644 index 000000000..c681a60c2 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-partition-ctas.sql @@ -0,0 +1,20 @@ +-- PARTITION OF with column options and hash bounds +CREATE TABLE p1 PARTITION OF parent (f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY) FOR VALUES FROM ('2016-11-01') TO ('2016-12-01'); +CREATE TABLE p1 PARTITION OF parent (a NOT NULL, b DEFAULT 1, CONSTRAINT p1_chk CHECK (a > 0)) FOR VALUES IN (1, 2); +CREATE TABLE p1 PARTITION OF parent FOR VALUES WITH (MODULUS 2, REMAINDER 0); +CREATE TABLE p1 PARTITION OF parent FOR VALUES WITH (MODULUS 4, REMAINDER 3); +CREATE TABLE p1 PARTITION OF parent (a WITH OPTIONS NOT NULL) FOR VALUES WITH (MODULUS 2, REMAINDER 1); +CREATE TABLE p1 PARTITION OF parent DEFAULT; +CREATE TABLE p1 PARTITION OF parent (a NOT NULL) DEFAULT; +CREATE TABLE p1 PARTITION OF parent FOR VALUES FROM (MINVALUE) TO (MAXVALUE) PARTITION BY HASH (b); +ALTER TABLE parent ATTACH PARTITION p1 FOR VALUES WITH (MODULUS 2, REMAINDER 0); +ALTER TABLE parent ATTACH PARTITION p1 DEFAULT; +-- CREATE TABLE AS option placement/quoting +CREATE TABLE tas_case WITH ("Fillfactor" = 10) AS SELECT 1 a; +CREATE TABLE tas WITH (fillfactor = 10, autovacuum_enabled = false) AS SELECT 1 a; +CREATE TEMP TABLE tas ON COMMIT DROP AS SELECT 1 a; +CREATE TABLE tas (x, y) WITH (fillfactor = 50) TABLESPACE pg_default AS SELECT 1, 2 WITH NO DATA; +CREATE TABLE tas USING heap WITH (fillfactor = 50) AS SELECT 1 a; +CREATE MATERIALIZED VIEW mv WITH (fillfactor = 50) AS SELECT 1 a WITH NO DATA; +-- reloption quoting in CREATE TABLE +CREATE TABLE t (a int) WITH ("Fillfactor" = 10, toast.autovacuum_enabled = false); diff --git a/__fixtures__/kitchen-sink/misc/pg18-publication-subscription.sql b/__fixtures__/kitchen-sink/misc/pg18-publication-subscription.sql new file mode 100644 index 000000000..9c5ba4f4e --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-publication-subscription.sql @@ -0,0 +1,32 @@ +-- CREATE/ALTER PUBLICATION and ALTER SUBSCRIPTION forms +CREATE PUBLICATION p FOR ALL TABLES; +CREATE PUBLICATION p FOR TABLE t1, t2 (a, b) WHERE (a > 5); +CREATE PUBLICATION p FOR TABLES IN SCHEMA s1, s2; +CREATE PUBLICATION p FOR TABLE t1, TABLES IN SCHEMA s1 WITH (publish = 'insert, update', publish_via_partition_root = true); +CREATE PUBLICATION p WITH (publish_via_partition_root = 1); +ALTER PUBLICATION p ADD TABLE t3; +ALTER PUBLICATION p ADD TABLE t3 (a) WHERE (a IS NOT NULL); +ALTER PUBLICATION p DROP TABLE t3; +ALTER PUBLICATION p SET TABLE t1, t2; +ALTER PUBLICATION p SET TABLES IN SCHEMA s1; +ALTER PUBLICATION p ADD TABLES IN SCHEMA s2; +ALTER PUBLICATION p DROP TABLES IN SCHEMA s2; +ALTER PUBLICATION p SET (publish = 'insert'); +ALTER PUBLICATION p SET (publish_via_partition_root = 0); +ALTER PUBLICATION p SET (publish_via_partition_root = false); +ALTER PUBLICATION p OWNER TO regress_user; +ALTER PUBLICATION p RENAME TO p2; +CREATE SUBSCRIPTION s CONNECTION 'dbname=doesnotexist' PUBLICATION p WITH (connect = false, slot_name = NONE); +ALTER SUBSCRIPTION s SET (slot_name = 'newname'); +ALTER SUBSCRIPTION s SET (streaming = parallel); +ALTER SUBSCRIPTION s CONNECTION 'dbname=other'; +ALTER SUBSCRIPTION s SET PUBLICATION p1, p2 WITH (refresh = false); +ALTER SUBSCRIPTION s ADD PUBLICATION p3 WITH (refresh = false); +ALTER SUBSCRIPTION s DROP PUBLICATION p3 WITH (refresh = false); +ALTER SUBSCRIPTION s REFRESH PUBLICATION WITH (copy_data = false); +ALTER SUBSCRIPTION s ENABLE; +ALTER SUBSCRIPTION s DISABLE; +ALTER SUBSCRIPTION s SKIP (lsn = '0/12345'); +ALTER SUBSCRIPTION s SKIP (lsn = NONE); +ALTER SUBSCRIPTION s OWNER TO regress_user; +ALTER SUBSCRIPTION s RENAME TO s2; diff --git a/__fixtures__/kitchen-sink/misc/pg18-sqljson.sql b/__fixtures__/kitchen-sink/misc/pg18-sqljson.sql new file mode 100644 index 000000000..2a07d9f89 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-sqljson.sql @@ -0,0 +1,63 @@ +-- SQL/JSON constructors +SELECT JSON_OBJECT(); +SELECT JSON_OBJECT(RETURNING json); +SELECT JSON_OBJECT(RETURNING jsonb); +SELECT JSON_OBJECT(RETURNING text FORMAT JSON); +SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF8); +SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON); +SELECT JSON_OBJECT('foo': 1, 'bar': 'baz'); +SELECT JSON_OBJECT('foo' VALUE 1, 'bar' VALUE 'baz'); +SELECT JSON_OBJECT('a': 1 ABSENT ON NULL); +SELECT JSON_OBJECT('a': 1 NULL ON NULL); +SELECT JSON_OBJECT('a': 1, 'a': 2 WITH UNIQUE KEYS); +SELECT JSON_OBJECT('a': 1, 'a': 2 WITHOUT UNIQUE KEYS); +SELECT JSON_OBJECT('a': 1, 'b': 2 ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb); +SELECT JSON_OBJECT('a': '{"b": 1}' FORMAT JSON); +SELECT JSON_OBJECT('a': '{"b": 1}' FORMAT JSON RETURNING jsonb); +SELECT JSON_ARRAY(); +SELECT JSON_ARRAY(RETURNING jsonb); +SELECT JSON_ARRAY(1, 2, 'a'); +SELECT JSON_ARRAY(1, NULL, 2 NULL ON NULL); +SELECT JSON_ARRAY(1, NULL, 2 ABSENT ON NULL); +SELECT JSON_ARRAY('{"a": 1}' FORMAT JSON, 2 RETURNING jsonb); +SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i)); +SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2)) foo(i) RETURNING jsonb); +SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1)) foo(i) ORDER BY i); +SELECT JSON_ARRAY(SELECT i FROM (VALUES ('[1]')) foo(i) FORMAT JSON RETURNING text); +SELECT JSON('{"a": 1}'); +SELECT JSON('{"a": 1}' WITH UNIQUE KEYS); +SELECT JSON('{"a": 1}' WITHOUT UNIQUE KEYS); +SELECT JSON('{"a": 1}'::bytea FORMAT JSON ENCODING UTF8); +SELECT JSON_SCALAR(1); +SELECT JSON_SCALAR('text'); +SELECT JSON_SCALAR(NULL::int); +SELECT JSON_SERIALIZE('{"a": 1}'); +SELECT JSON_SERIALIZE('{"a": 1}' FORMAT JSON); +SELECT JSON_SERIALIZE('{"a": 1}'::jsonb RETURNING bytea); +SELECT JSON_SERIALIZE('{"a": 1}' RETURNING bytea FORMAT JSON ENCODING UTF8); +-- SQL/JSON aggregates +SELECT JSON_OBJECTAGG(k: v) FROM t; +SELECT JSON_OBJECTAGG(k VALUE v) FROM t; +SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL) FROM t; +SELECT JSON_OBJECTAGG(k: v NULL ON NULL WITH UNIQUE KEYS RETURNING jsonb) FROM t; +SELECT JSON_OBJECTAGG(k: v FORMAT JSON) FROM t; +SELECT JSON_OBJECTAGG(k: v) FILTER (WHERE k IS NOT NULL) FROM t; +SELECT JSON_OBJECTAGG(k: v) OVER (PARTITION BY g) FROM t; +SELECT JSON_OBJECTAGG(k: v) FILTER (WHERE k IS NOT NULL) OVER (ORDER BY k ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM t; +SELECT JSON_ARRAYAGG(v) FROM t; +SELECT JSON_ARRAYAGG(v ORDER BY v DESC) FROM t; +SELECT JSON_ARRAYAGG(v NULL ON NULL) FROM t; +SELECT JSON_ARRAYAGG(v ABSENT ON NULL RETURNING jsonb) FROM t; +SELECT JSON_ARRAYAGG(v FORMAT JSON ORDER BY v ABSENT ON NULL RETURNING text) FROM t; +SELECT JSON_ARRAYAGG(v) FILTER (WHERE v > 0) OVER w FROM t WINDOW w AS (PARTITION BY g); +-- IS JSON predicates +SELECT js IS JSON FROM t; +SELECT js IS NOT JSON FROM t; +SELECT js IS JSON VALUE FROM t; +SELECT js IS JSON OBJECT FROM t; +SELECT js IS JSON ARRAY FROM t; +SELECT js IS JSON SCALAR FROM t; +SELECT js IS JSON WITH UNIQUE KEYS FROM t; +SELECT js IS JSON OBJECT WITHOUT UNIQUE KEYS FROM t; +SELECT js IS NOT JSON ARRAY WITH UNIQUE FROM t; +SELECT CASE WHEN js IS JSON OBJECT THEN 'o' WHEN js IS JSON ARRAY THEN 'a' END FROM t; diff --git a/__fixtures__/kitchen-sink/misc/pg18-window-frames.sql b/__fixtures__/kitchen-sink/misc/pg18-window-frames.sql new file mode 100644 index 000000000..0dbddc2ba --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/pg18-window-frames.sql @@ -0,0 +1,22 @@ +-- WindowDef.frameOptions is a bitmask; decode every bound/exclusion combination +SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM t; +SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) FROM t; +SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM t; +SELECT sum(x) OVER (ORDER BY x GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t; +SELECT sum(x) OVER (ORDER BY x GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE GROUP) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE TIES) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE CURRENT ROW) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE NO OTHERS) FROM t; +SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t; +SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN INTERVAL '1 day' PRECEDING AND INTERVAL '1 day' FOLLOWING) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS UNBOUNDED PRECEDING) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS 1 PRECEDING) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS CURRENT ROW) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN 1 FOLLOWING AND 2 FOLLOWING) FROM t; +SELECT sum(x) OVER (ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) FROM t; +SELECT sum(x) OVER w FROM t WINDOW w AS (ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING); +SELECT sum(x) OVER (w ORDER BY y) FROM t WINDOW w AS (PARTITION BY z); +SELECT sum(x) OVER (w ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t WINDOW w AS (PARTITION BY z ORDER BY x); +SELECT sum(x) OVER (PARTITION BY z) FROM t; +SELECT sum(x) OVER () FROM t; diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-copy.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-copy.test.ts new file mode 100644 index 000000000..fc4872801 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-copy.test.ts @@ -0,0 +1,28 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-copy', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-copy-1.sql", + "misc/pg18-copy-2.sql", + "misc/pg18-copy-3.sql", + "misc/pg18-copy-4.sql", + "misc/pg18-copy-5.sql", + "misc/pg18-copy-6.sql", + "misc/pg18-copy-7.sql", + "misc/pg18-copy-8.sql", + "misc/pg18-copy-9.sql", + "misc/pg18-copy-10.sql", + "misc/pg18-copy-11.sql", + "misc/pg18-copy-12.sql", + "misc/pg18-copy-13.sql", + "misc/pg18-copy-14.sql", + "misc/pg18-copy-15.sql", + "misc/pg18-copy-16.sql", + "misc/pg18-copy-17.sql", + "misc/pg18-copy-18.sql", + "misc/pg18-copy-19.sql", + "misc/pg18-copy-20.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-cte-insert.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-cte-insert.test.ts new file mode 100644 index 000000000..9e0a6f075 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-cte-insert.test.ts @@ -0,0 +1,21 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-cte-insert', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-cte-insert-1.sql", + "misc/pg18-cte-insert-2.sql", + "misc/pg18-cte-insert-3.sql", + "misc/pg18-cte-insert-4.sql", + "misc/pg18-cte-insert-5.sql", + "misc/pg18-cte-insert-6.sql", + "misc/pg18-cte-insert-7.sql", + "misc/pg18-cte-insert-8.sql", + "misc/pg18-cte-insert-9.sql", + "misc/pg18-cte-insert-10.sql", + "misc/pg18-cte-insert-11.sql", + "misc/pg18-cte-insert-12.sql", + "misc/pg18-cte-insert-13.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-grant-role.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-grant-role.test.ts new file mode 100644 index 000000000..d2218ac2a --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-grant-role.test.ts @@ -0,0 +1,27 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-grant-role', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-grant-role-1.sql", + "misc/pg18-grant-role-2.sql", + "misc/pg18-grant-role-3.sql", + "misc/pg18-grant-role-4.sql", + "misc/pg18-grant-role-5.sql", + "misc/pg18-grant-role-6.sql", + "misc/pg18-grant-role-7.sql", + "misc/pg18-grant-role-8.sql", + "misc/pg18-grant-role-9.sql", + "misc/pg18-grant-role-10.sql", + "misc/pg18-grant-role-11.sql", + "misc/pg18-grant-role-12.sql", + "misc/pg18-grant-role-13.sql", + "misc/pg18-grant-role-14.sql", + "misc/pg18-grant-role-15.sql", + "misc/pg18-grant-role-16.sql", + "misc/pg18-grant-role-17.sql", + "misc/pg18-grant-role-18.sql", + "misc/pg18-grant-role-19.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-identity-storage.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-identity-storage.test.ts new file mode 100644 index 000000000..2402be699 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-identity-storage.test.ts @@ -0,0 +1,27 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-identity-storage', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-identity-storage-1.sql", + "misc/pg18-identity-storage-2.sql", + "misc/pg18-identity-storage-3.sql", + "misc/pg18-identity-storage-4.sql", + "misc/pg18-identity-storage-5.sql", + "misc/pg18-identity-storage-6.sql", + "misc/pg18-identity-storage-7.sql", + "misc/pg18-identity-storage-8.sql", + "misc/pg18-identity-storage-9.sql", + "misc/pg18-identity-storage-10.sql", + "misc/pg18-identity-storage-11.sql", + "misc/pg18-identity-storage-12.sql", + "misc/pg18-identity-storage-13.sql", + "misc/pg18-identity-storage-14.sql", + "misc/pg18-identity-storage-15.sql", + "misc/pg18-identity-storage-16.sql", + "misc/pg18-identity-storage-17.sql", + "misc/pg18-identity-storage-18.sql", + "misc/pg18-identity-storage-19.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-misc-ddl.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-misc-ddl.test.ts new file mode 100644 index 000000000..dbf788bd4 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-misc-ddl.test.ts @@ -0,0 +1,23 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-misc-ddl', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-misc-ddl-1.sql", + "misc/pg18-misc-ddl-2.sql", + "misc/pg18-misc-ddl-3.sql", + "misc/pg18-misc-ddl-4.sql", + "misc/pg18-misc-ddl-5.sql", + "misc/pg18-misc-ddl-6.sql", + "misc/pg18-misc-ddl-7.sql", + "misc/pg18-misc-ddl-8.sql", + "misc/pg18-misc-ddl-9.sql", + "misc/pg18-misc-ddl-10.sql", + "misc/pg18-misc-ddl-11.sql", + "misc/pg18-misc-ddl-12.sql", + "misc/pg18-misc-ddl-13.sql", + "misc/pg18-misc-ddl-14.sql", + "misc/pg18-misc-ddl-15.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-partition-ctas.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-partition-ctas.test.ts new file mode 100644 index 000000000..9ee753c18 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-partition-ctas.test.ts @@ -0,0 +1,25 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-partition-ctas', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-partition-ctas-1.sql", + "misc/pg18-partition-ctas-2.sql", + "misc/pg18-partition-ctas-3.sql", + "misc/pg18-partition-ctas-4.sql", + "misc/pg18-partition-ctas-5.sql", + "misc/pg18-partition-ctas-6.sql", + "misc/pg18-partition-ctas-7.sql", + "misc/pg18-partition-ctas-8.sql", + "misc/pg18-partition-ctas-9.sql", + "misc/pg18-partition-ctas-10.sql", + "misc/pg18-partition-ctas-11.sql", + "misc/pg18-partition-ctas-12.sql", + "misc/pg18-partition-ctas-13.sql", + "misc/pg18-partition-ctas-14.sql", + "misc/pg18-partition-ctas-15.sql", + "misc/pg18-partition-ctas-16.sql", + "misc/pg18-partition-ctas-17.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-publication-subscription.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-publication-subscription.test.ts new file mode 100644 index 000000000..8aa8afe06 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-publication-subscription.test.ts @@ -0,0 +1,39 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-publication-subscription', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-publication-subscription-1.sql", + "misc/pg18-publication-subscription-2.sql", + "misc/pg18-publication-subscription-3.sql", + "misc/pg18-publication-subscription-4.sql", + "misc/pg18-publication-subscription-5.sql", + "misc/pg18-publication-subscription-6.sql", + "misc/pg18-publication-subscription-7.sql", + "misc/pg18-publication-subscription-8.sql", + "misc/pg18-publication-subscription-9.sql", + "misc/pg18-publication-subscription-10.sql", + "misc/pg18-publication-subscription-11.sql", + "misc/pg18-publication-subscription-12.sql", + "misc/pg18-publication-subscription-13.sql", + "misc/pg18-publication-subscription-14.sql", + "misc/pg18-publication-subscription-15.sql", + "misc/pg18-publication-subscription-16.sql", + "misc/pg18-publication-subscription-17.sql", + "misc/pg18-publication-subscription-18.sql", + "misc/pg18-publication-subscription-19.sql", + "misc/pg18-publication-subscription-20.sql", + "misc/pg18-publication-subscription-21.sql", + "misc/pg18-publication-subscription-22.sql", + "misc/pg18-publication-subscription-23.sql", + "misc/pg18-publication-subscription-24.sql", + "misc/pg18-publication-subscription-25.sql", + "misc/pg18-publication-subscription-26.sql", + "misc/pg18-publication-subscription-27.sql", + "misc/pg18-publication-subscription-28.sql", + "misc/pg18-publication-subscription-29.sql", + "misc/pg18-publication-subscription-30.sql", + "misc/pg18-publication-subscription-31.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-sqljson.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-sqljson.test.ts new file mode 100644 index 000000000..ec78dcc95 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-sqljson.test.ts @@ -0,0 +1,68 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-sqljson', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-sqljson-1.sql", + "misc/pg18-sqljson-2.sql", + "misc/pg18-sqljson-3.sql", + "misc/pg18-sqljson-4.sql", + "misc/pg18-sqljson-5.sql", + "misc/pg18-sqljson-6.sql", + "misc/pg18-sqljson-7.sql", + "misc/pg18-sqljson-8.sql", + "misc/pg18-sqljson-9.sql", + "misc/pg18-sqljson-10.sql", + "misc/pg18-sqljson-11.sql", + "misc/pg18-sqljson-12.sql", + "misc/pg18-sqljson-13.sql", + "misc/pg18-sqljson-14.sql", + "misc/pg18-sqljson-15.sql", + "misc/pg18-sqljson-16.sql", + "misc/pg18-sqljson-17.sql", + "misc/pg18-sqljson-18.sql", + "misc/pg18-sqljson-19.sql", + "misc/pg18-sqljson-20.sql", + "misc/pg18-sqljson-21.sql", + "misc/pg18-sqljson-22.sql", + "misc/pg18-sqljson-23.sql", + "misc/pg18-sqljson-24.sql", + "misc/pg18-sqljson-25.sql", + "misc/pg18-sqljson-26.sql", + "misc/pg18-sqljson-27.sql", + "misc/pg18-sqljson-28.sql", + "misc/pg18-sqljson-29.sql", + "misc/pg18-sqljson-30.sql", + "misc/pg18-sqljson-31.sql", + "misc/pg18-sqljson-32.sql", + "misc/pg18-sqljson-33.sql", + "misc/pg18-sqljson-34.sql", + "misc/pg18-sqljson-35.sql", + "misc/pg18-sqljson-36.sql", + "misc/pg18-sqljson-37.sql", + "misc/pg18-sqljson-38.sql", + "misc/pg18-sqljson-39.sql", + "misc/pg18-sqljson-40.sql", + "misc/pg18-sqljson-41.sql", + "misc/pg18-sqljson-42.sql", + "misc/pg18-sqljson-43.sql", + "misc/pg18-sqljson-44.sql", + "misc/pg18-sqljson-45.sql", + "misc/pg18-sqljson-46.sql", + "misc/pg18-sqljson-47.sql", + "misc/pg18-sqljson-48.sql", + "misc/pg18-sqljson-49.sql", + "misc/pg18-sqljson-50.sql", + "misc/pg18-sqljson-51.sql", + "misc/pg18-sqljson-52.sql", + "misc/pg18-sqljson-53.sql", + "misc/pg18-sqljson-54.sql", + "misc/pg18-sqljson-55.sql", + "misc/pg18-sqljson-56.sql", + "misc/pg18-sqljson-57.sql", + "misc/pg18-sqljson-58.sql", + "misc/pg18-sqljson-59.sql", + "misc/pg18-sqljson-60.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-pg18-window-frames.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-pg18-window-frames.test.ts new file mode 100644 index 000000000..39b208e8b --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-pg18-window-frames.test.ts @@ -0,0 +1,29 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-pg18-window-frames', async () => { + await fixtures.runFixtureTests([ + "misc/pg18-window-frames-1.sql", + "misc/pg18-window-frames-2.sql", + "misc/pg18-window-frames-3.sql", + "misc/pg18-window-frames-4.sql", + "misc/pg18-window-frames-5.sql", + "misc/pg18-window-frames-6.sql", + "misc/pg18-window-frames-7.sql", + "misc/pg18-window-frames-8.sql", + "misc/pg18-window-frames-9.sql", + "misc/pg18-window-frames-10.sql", + "misc/pg18-window-frames-11.sql", + "misc/pg18-window-frames-12.sql", + "misc/pg18-window-frames-13.sql", + "misc/pg18-window-frames-14.sql", + "misc/pg18-window-frames-15.sql", + "misc/pg18-window-frames-16.sql", + "misc/pg18-window-frames-17.sql", + "misc/pg18-window-frames-18.sql", + "misc/pg18-window-frames-19.sql", + "misc/pg18-window-frames-20.sql", + "misc/pg18-window-frames-21.sql" +]); +}); diff --git a/packages/deparser/src/deparser.ts b/packages/deparser/src/deparser.ts index 9ef493526..b74a721a1 100644 --- a/packages/deparser/src/deparser.ts +++ b/packages/deparser/src/deparser.ts @@ -6,6 +6,26 @@ import { ListUtils } from './utils/list-utils'; import { SqlFormatter } from './utils/sql-formatter'; import { DeparserContext, DeparserVisitor } from './visitors/base'; +// WindowDef.frameOptions bit flags (src/include/nodes/parsenodes.h) +const FRAMEOPTION_NONDEFAULT = 0x00001; +const FRAMEOPTION_RANGE = 0x00002; +const FRAMEOPTION_ROWS = 0x00004; +const FRAMEOPTION_GROUPS = 0x00008; +const FRAMEOPTION_BETWEEN = 0x00010; +const FRAMEOPTION_START_UNBOUNDED_PRECEDING = 0x00020; +const FRAMEOPTION_END_UNBOUNDED_PRECEDING = 0x00040; +const FRAMEOPTION_START_UNBOUNDED_FOLLOWING = 0x00080; +const FRAMEOPTION_END_UNBOUNDED_FOLLOWING = 0x00100; +const FRAMEOPTION_START_CURRENT_ROW = 0x00200; +const FRAMEOPTION_END_CURRENT_ROW = 0x00400; +const FRAMEOPTION_START_OFFSET_PRECEDING = 0x00800; +const FRAMEOPTION_END_OFFSET_PRECEDING = 0x01000; +const FRAMEOPTION_START_OFFSET_FOLLOWING = 0x02000; +const FRAMEOPTION_END_OFFSET_FOLLOWING = 0x04000; +const FRAMEOPTION_EXCLUDE_CURRENT_ROW = 0x08000; +const FRAMEOPTION_EXCLUDE_GROUP = 0x10000; +const FRAMEOPTION_EXCLUDE_TIES = 0x20000; + /** * List of real PostgreSQL built-in types as they appear in pg_catalog.pg_type.typname. * These are stored in lowercase in PostgreSQL system catalogs. @@ -1079,6 +1099,12 @@ export class Deparser implements DeparserVisitor { } } + if (node.override === 'OVERRIDING_USER_VALUE') { + output.push('OVERRIDING USER VALUE'); + } else if (node.override === 'OVERRIDING_SYSTEM_VALUE') { + output.push('OVERRIDING SYSTEM VALUE'); + } + if (node.selectStmt) { output.push(this.visit(node.selectStmt as Node, context)); } else if (!node.cols || (node.cols && ListUtils.unwrapList(node.cols).length === 0)) { @@ -1163,43 +1189,8 @@ export class Deparser implements DeparserVisitor { } output.push('SET'); - const targetList = ListUtils.unwrapList(node.targetList); - if (targetList && targetList.length) { - const firstTarget = targetList[0]; - - const processedTargets = new Set(); - const assignmentParts = []; - - for (let i = 0; i < targetList.length; i++) { - if (processedTargets.has(i)) continue; - - const target = targetList[i]; - const multiAssignRef = target.ResTarget?.val?.MultiAssignRef; - - if (multiAssignRef) { - const relatedTargets = []; - for (let j = i; j < targetList.length; j++) { - const otherTarget = targetList[j]; - const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef; - - if (otherMultiAssignRef && - JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) { - relatedTargets.push(otherTarget); - processedTargets.add(j); - } - } - - const names = relatedTargets.map(t => t.ResTarget.name); - const multiAssignment = `${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`; - assignmentParts.push(multiAssignment); - } else { - // Handle regular single-column assignment - assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true }))); - processedTargets.add(i); - } - } - - output.push(assignmentParts.join(',')); + if (node.targetList && node.targetList.length > 0) { + output.push(this.formatUpdateAssignments(node.targetList, context)); } if (node.fromClause) { @@ -1221,6 +1212,41 @@ export class Deparser implements DeparserVisitor { return output.join(' '); } + formatUpdateAssignments(targets: Node[], context: DeparserContext): string { + const targetList = ListUtils.unwrapList(targets); + const processedTargets = new Set(); + const assignmentParts: string[] = []; + + for (let i = 0; i < targetList.length; i++) { + if (processedTargets.has(i)) continue; + + const target = targetList[i]; + const multiAssignRef = target.ResTarget?.val?.MultiAssignRef; + + if (multiAssignRef) { + const relatedTargets = []; + for (let j = i; j < targetList.length; j++) { + const otherTarget = targetList[j]; + const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef; + + if (otherMultiAssignRef && + JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) { + relatedTargets.push(otherTarget); + processedTargets.add(j); + } + } + + const names = relatedTargets.map(t => QuoteUtils.quoteIdentifier(t.ResTarget.name)); + assignmentParts.push(`${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`); + } else { + assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true }))); + processedTargets.add(i); + } + } + + return assignmentParts.join(', '); + } + DeleteStmt(node: t.DeleteStmt, context: DeparserContext): string { try { const output: string[] = []; @@ -1672,44 +1698,48 @@ export class Deparser implements DeparserVisitor { } if (node.over) { - // Handle named window references first - if (node.over.name) { - result += ` OVER ${node.over.name}`; - } else { - const windowParts: string[] = []; + result += this.formatOverClause(node.over, context); + } - if (node.over.partitionClause) { - const partitions = ListUtils.unwrapList(node.over.partitionClause); - const partitionStrs = partitions.map(p => this.visit(p, context)); - windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`); - } + return result; + } - if (node.over.orderClause) { - const orders = ListUtils.unwrapList(node.over.orderClause); - const orderStrs = orders.map(o => this.visit(o, context)); - windowParts.push(`ORDER BY ${orderStrs.join(', ')}`); - } + formatOverClause(over: t.WindowDef, context: DeparserContext): string { + if (over.name) { + return ` OVER ${over.name}`; + } - // Handle window frame specifications using the dedicated formatWindowFrame method - const frameClause = this.formatWindowFrame(node.over, context.spawn('FuncCall')); - if (frameClause) { - windowParts.push(frameClause); - } + const windowParts: string[] = []; - if (windowParts.length > 0) { - if (context.isPretty() && windowParts.length > 1) { - const formattedParts = windowParts.map(part => context.indent(part)); - result += ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`; - } else { - result += ` OVER (${windowParts.join(' ')})`; - } - } else { - result += ` OVER ()`; - } - } + if (over.refname) { + windowParts.push(over.refname); } - return result; + if (over.partitionClause) { + const partitions = ListUtils.unwrapList(over.partitionClause); + const partitionStrs = partitions.map(p => this.visit(p, context)); + windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`); + } + + if (over.orderClause) { + const orders = ListUtils.unwrapList(over.orderClause); + const orderStrs = orders.map(o => this.visit(o, context)); + windowParts.push(`ORDER BY ${orderStrs.join(', ')}`); + } + + const frameClause = this.formatWindowFrame(over, context.spawn('FuncCall')); + if (frameClause) { + windowParts.push(frameClause); + } + + if (windowParts.length === 0) { + return ' OVER ()'; + } + if (context.isPretty() && windowParts.length > 1) { + const formattedParts = windowParts.map(part => context.indent(part)); + return ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`; + } + return ` OVER (${windowParts.join(' ')})`; } FuncExpr(node: t.FuncExpr, context: DeparserContext): string { @@ -2637,7 +2667,7 @@ export class Deparser implements DeparserVisitor { }); output.push(context.parens(elementStrs.join(', '))); } - } else if (node.tableElts) { + } else if (node.tableElts && !node.partbound) { const elements = ListUtils.unwrapList(node.tableElts); const elementStrs = elements.map(el => { return this.deparse(el, context.spawn('CreateStmt')); @@ -2666,7 +2696,16 @@ export class Deparser implements DeparserVisitor { const inheritStrs = inherits.map(rel => this.visit(rel, context)); output.push(inheritStrs[0]); - if (node.partbound.strategy === 'l' && node.partbound.listdatums) { + if (node.tableElts) { + const elementStrs = ListUtils.unwrapList(node.tableElts).map(el => { + return this.deparse(el, context.spawn('CreateStmt')); + }); + output.push(context.parens(elementStrs.join(', '))); + } + + if (node.partbound.is_default) { + output.push('DEFAULT'); + } else if (node.partbound.strategy === 'l' && node.partbound.listdatums) { output.push('FOR VALUES IN'); const listValues = ListUtils.unwrapList(node.partbound.listdatums) .map(datum => this.visit(datum, context)) @@ -2687,12 +2726,9 @@ export class Deparser implements DeparserVisitor { .join(', '); output.push(`(${upperValues})`); } - } else if (node.partbound.strategy === 'h' && node.partbound.modulus !== undefined) { + } else if (node.partbound.strategy === 'h') { output.push('FOR VALUES WITH'); - const remainder = node.partbound.remainder !== undefined ? node.partbound.remainder : 0; - output.push(`(MODULUS ${node.partbound.modulus}, REMAINDER ${remainder})`); - } else if (node.partbound.is_default) { - output.push('DEFAULT'); + output.push(`(MODULUS ${node.partbound.modulus ?? 0}, REMAINDER ${node.partbound.remainder ?? 0})`); } } else if (node.inhRelations) { output.push('INHERITS'); @@ -2766,6 +2802,14 @@ export class Deparser implements DeparserVisitor { output.push(this.TypeName(node.typeName, context)); } + if (node.storage_name) { + output.push('STORAGE', node.storage_name.toUpperCase()); + } + + if (node.compression) { + output.push('COMPRESSION', QuoteUtils.quoteIdentifier(node.compression)); + } + if (node.fdwoptions && node.fdwoptions.length > 0) { output.push('OPTIONS'); const columnContext = context.spawn('ColumnDef'); @@ -2835,7 +2879,8 @@ export class Deparser implements DeparserVisitor { case 'CONSTR_DEFAULT': output.push('DEFAULT'); if (node.raw_expr) { - output.push(this.visit(node.raw_expr, context)); + const defaultExpr = this.visit(node.raw_expr, context); + output.push(this.needsBExprParens(node.raw_expr) ? `(${defaultExpr})` : defaultExpr); } break; case 'CONSTR_CHECK': @@ -3170,7 +3215,7 @@ export class Deparser implements DeparserVisitor { } // Handle deferrable constraints for all constraint types that support it - if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') { + if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN' || node.contype === 'CONSTR_EXCLUSION') { if (node.deferrable) { if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') { output.push('\n' + context.indent('DEFERRABLE')); @@ -3193,6 +3238,8 @@ export class Deparser implements DeparserVisitor { } else { output.push('NOT DEFERRABLE'); } + } else if (node.initdeferred) { + output.push('INITIALLY DEFERRED'); } } @@ -3269,6 +3316,10 @@ export class Deparser implements DeparserVisitor { const windowParts: string[] = []; + if (node.refname) { + windowParts.push(node.refname); + } + if (node.partitionClause) { const partitions = ListUtils.unwrapList(node.partitionClause); const partitionStrs = partitions.map(p => this.visit(p, context)); @@ -3281,12 +3332,9 @@ export class Deparser implements DeparserVisitor { windowParts.push(`ORDER BY ${orderStrs.join(', ')}`); } - // Only add frame clause if frameOptions indicates non-default framing - if (node.frameOptions && node.frameOptions !== 1058) { - const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef')); - if (frameClause) { - windowParts.push(frameClause); - } + const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef')); + if (frameClause) { + windowParts.push(frameClause); } if (windowParts.length > 0) { @@ -3304,101 +3352,88 @@ export class Deparser implements DeparserVisitor { return output.join(' '); } + formatIdentityColumnOptions(def: Node, context: DeparserContext): string { + const seqContext = context.spawn('AlterSeqStmt'); + return ListUtils.unwrapList(def).map(item => { + const el = item.DefElem; + if (!el) return this.visit(item, context); + if (el.defname === 'generated') { + const ival = el.arg?.Integer?.ival ?? 0; + return ival === 97 ? 'SET GENERATED ALWAYS' : 'SET GENERATED BY DEFAULT'; + } + if (el.defname === 'restart') { + return el.arg ? `RESTART WITH ${this.visit(el.arg, context)}` : 'RESTART'; + } + return `SET ${this.visit(item, seqContext)}`; + }).join(' '); + } + + // b_expr (used by DEFAULT) excludes IN/LIKE/BETWEEN and boolean operators. + needsBExprParens(node: Node): boolean { + const nodeType = this.getNodeType(node); + if (nodeType === 'BoolExpr') return true; + if (nodeType === 'A_Expr') { + const kind = this.getNodeData(node).kind; + return kind !== 'AEXPR_OP' && kind !== 'AEXPR_DISTINCT' && kind !== 'AEXPR_NOT_DISTINCT'; + } + return false; + } + formatWindowFrame(node: any, context: DeparserContext): string | null { - if (!node.frameOptions) return null; + if (!node.frameOptions || !(node.frameOptions & FRAMEOPTION_NONDEFAULT)) return null; - const frameOptions = node.frameOptions; - const EXCLUDE_MASK = 0x8000 | 0x10000 | 0x20000; - const baseFrameOptions = frameOptions & ~EXCLUDE_MASK; + const frameOptions: number = node.frameOptions; const frameParts: string[] = []; - if (frameOptions & 0x01) { // FRAMEOPTION_NONDEFAULT - if (frameOptions & 0x02) { // FRAMEOPTION_RANGE - frameParts.push('RANGE'); - } else if (frameOptions & 0x04) { // FRAMEOPTION_ROWS - frameParts.push('ROWS'); - } else if (frameOptions & 0x08) { // FRAMEOPTION_GROUPS - frameParts.push('GROUPS'); - } + if (frameOptions & FRAMEOPTION_RANGE) { + frameParts.push('RANGE'); + } else if (frameOptions & FRAMEOPTION_ROWS) { + frameParts.push('ROWS'); + } else if (frameOptions & FRAMEOPTION_GROUPS) { + frameParts.push('GROUPS'); } if (frameParts.length === 0) return null; - const boundsParts: string[] = []; - - // Handle specific frameOptions values that have known mappings - if (baseFrameOptions === 789) { - boundsParts.push('CURRENT ROW'); - boundsParts.push('AND UNBOUNDED FOLLOWING'); - } else if (baseFrameOptions === 1077) { - boundsParts.push('UNBOUNDED PRECEDING'); - boundsParts.push('AND CURRENT ROW'); - } else if (baseFrameOptions === 18453) { - if (node.startOffset && node.endOffset) { - boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`); - boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`); - } - } else if (baseFrameOptions === 1557) { - boundsParts.push('CURRENT ROW'); - boundsParts.push('AND CURRENT ROW'); - } else if (baseFrameOptions === 16917) { - boundsParts.push('CURRENT ROW'); - if (node.endOffset) { - boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`); - } - } else if (baseFrameOptions === 1058) { - return null; - } else { - // Handle start bound - prioritize explicit offset values over bit flags - if (node.startOffset) { - if (frameOptions & 0x400) { // FRAMEOPTION_START_VALUE_PRECEDING - boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`); - } else if (frameOptions & 0x800) { // FRAMEOPTION_START_VALUE_FOLLOWING - boundsParts.push(`${this.visit(node.startOffset, context)} FOLLOWING`); - } else { - boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`); - } - } else if (frameOptions & 0x10) { // FRAMEOPTION_START_UNBOUNDED_PRECEDING - boundsParts.push('UNBOUNDED PRECEDING'); - } else if (frameOptions & 0x20) { // FRAMEOPTION_START_CURRENT_ROW - boundsParts.push('CURRENT ROW'); - } - - // Handle end bound - prioritize explicit offset values over bit flags - if (node.endOffset) { - if (boundsParts.length > 0) { - if (frameOptions & 0x1000) { // FRAMEOPTION_END_VALUE_PRECEDING - boundsParts.push(`AND ${this.visit(node.endOffset, context)} PRECEDING`); - } else if (frameOptions & 0x2000) { // FRAMEOPTION_END_VALUE_FOLLOWING - boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`); - } else { - boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`); - } - } - } else if (frameOptions & 0x80) { // FRAMEOPTION_END_UNBOUNDED_FOLLOWING - if (boundsParts.length > 0) { - boundsParts.push('AND UNBOUNDED FOLLOWING'); - } - } else if (frameOptions & 0x100) { // FRAMEOPTION_END_CURRENT_ROW - if (boundsParts.length > 0) { - boundsParts.push('AND CURRENT ROW'); - } - } else if (boundsParts.length > 0) { - boundsParts.push('AND CURRENT ROW'); + let start: string | null = null; + if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) { + start = 'UNBOUNDED PRECEDING'; + } else if (frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) { + start = 'UNBOUNDED FOLLOWING'; + } else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW) { + start = 'CURRENT ROW'; + } else if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING && node.startOffset) { + start = `${this.visit(node.startOffset, context)} PRECEDING`; + } else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING && node.startOffset) { + start = `${this.visit(node.startOffset, context)} FOLLOWING`; + } + + let end: string | null = null; + if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING) { + end = 'UNBOUNDED PRECEDING'; + } else if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) { + end = 'UNBOUNDED FOLLOWING'; + } else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW) { + end = 'CURRENT ROW'; + } else if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING && node.endOffset) { + end = `${this.visit(node.endOffset, context)} PRECEDING`; + } else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING && node.endOffset) { + end = `${this.visit(node.endOffset, context)} FOLLOWING`; + } + + if (start) { + if (frameOptions & FRAMEOPTION_BETWEEN) { + frameParts.push(`BETWEEN ${start} AND ${end ?? 'CURRENT ROW'}`); + } else { + frameParts.push(start); } } - if (boundsParts.length > 0) { - frameParts.push('BETWEEN'); - frameParts.push(boundsParts.join(' ')); - } - - // EXCLUDE clause - if (frameOptions & 0x8000) { // FRAMEOPTION_EXCLUDE_CURRENT_ROW + if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) { frameParts.push('EXCLUDE CURRENT ROW'); - } else if (frameOptions & 0x10000) { // FRAMEOPTION_EXCLUDE_GROUP + } else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP) { frameParts.push('EXCLUDE GROUP'); - } else if (frameOptions & 0x20000) { // FRAMEOPTION_EXCLUDE_TIES + } else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES) { frameParts.push('EXCLUDE TIES'); } @@ -3487,9 +3522,39 @@ export class Deparser implements DeparserVisitor { output.push(context.parens(this.visit(node.ctequery, context))); } + if (node.search_clause) { + const sc = node.search_clause; + const cols = ListUtils.unwrapList(sc.search_col_list).map(c => this.visit(c, context)); + output.push('SEARCH', sc.search_breadth_first ? 'BREADTH' : 'DEPTH', 'FIRST BY'); + output.push(cols.join(', ')); + output.push('SET', QuoteUtils.quoteIdentifier(sc.search_seq_column)); + } + + if (node.cycle_clause) { + const cc = node.cycle_clause; + const cols = ListUtils.unwrapList(cc.cycle_col_list).map(c => this.visit(c, context)); + output.push('CYCLE', cols.join(', ')); + output.push('SET', QuoteUtils.quoteIdentifier(cc.cycle_mark_column)); + if (cc.cycle_mark_value && cc.cycle_mark_default) { + output.push('TO', this.formatAexprConst(cc.cycle_mark_value, context)); + output.push('DEFAULT', this.formatAexprConst(cc.cycle_mark_default, context)); + } + output.push('USING', QuoteUtils.quoteIdentifier(cc.cycle_path_column)); + } + return output.join(' '); } + // Renders a constant in the restricted AexprConst grammar (e.g. `point '(1,1)'` + // rather than `CAST('(1,1)' AS point)`). + formatAexprConst(node: Node, context: DeparserContext): string { + const cast = (node as any).TypeCast; + if (cast?.arg?.A_Const?.sval && cast.typeName) { + return `${this.TypeName(cast.typeName, context)} ${this.visit(cast.arg, context)}`; + } + return this.visit(node, context); + } + ParamRef(node: t.ParamRef, context: DeparserContext): string { return `$${node.number}`; } @@ -3956,9 +4021,9 @@ export class Deparser implements DeparserVisitor { .join(', '); output.push(`(${upperValues})`); } - } else if (node.bound.strategy === 'h' && node.bound.modulus !== undefined) { + } else if (node.bound.strategy === 'h') { output.push('FOR VALUES WITH'); - output.push(`(MODULUS ${node.bound.modulus}, REMAINDER ${node.bound.remainder ?? 0})`); + output.push(`(MODULUS ${node.bound.modulus ?? 0}, REMAINDER ${node.bound.remainder ?? 0})`); } else if (node.bound.is_default) { output.push('DEFAULT'); } @@ -4859,9 +4924,9 @@ export class Deparser implements DeparserVisitor { } if (node.filename) { - output.push(`'${node.filename}'`); + output.push(QuoteUtils.escape(node.filename)); } else { - output.push('STDIN'); + output.push(node.is_from ? 'STDIN' : 'STDOUT'); } if (node.options && node.options.length > 0) { @@ -5112,6 +5177,9 @@ export class Deparser implements DeparserVisitor { const nodeData = this.getNodeData(node.def); if (nodeData && nodeData.typeName) { output.push(this.TypeName(nodeData.typeName, context)); + if (nodeData.collClause) { + output.push(this.CollateClause(nodeData.collClause, context)); + } // Handle USING clause (stored in raw_default for ALTER COLUMN TYPE) if (nodeData.raw_default) { output.push('USING'); @@ -5578,9 +5646,8 @@ export class Deparser implements DeparserVisitor { if (node.name) { output.push(QuoteUtils.quoteIdentifier(node.name)); } - output.push('SET'); if (node.def) { - output.push(this.visit(node.def, context)); + output.push(this.formatIdentityColumnOptions(node.def, context)); } break; case 'AT_DropIdentity': @@ -5589,10 +5656,8 @@ export class Deparser implements DeparserVisitor { output.push(QuoteUtils.quoteIdentifier(node.name)); } output.push('DROP IDENTITY'); - if (node.behavior === 'DROP_CASCADE') { - output.push('CASCADE'); - } else if (node.behavior === 'DROP_RESTRICT') { - output.push('RESTRICT'); + if (node.missing_ok) { + output.push('IF EXISTS'); } break; case 'AT_ReAddStatistics': @@ -6073,6 +6138,9 @@ export class Deparser implements DeparserVisitor { if (node.arg && this.getNodeType(node.arg) === 'List') { const listData = this.getNodeData(node.arg); const listItems = ListUtils.unwrapList(listData.items); + if (listItems.length === 1 && listItems[0].String?.sval === 'none') { + return 'OWNED BY NONE'; + } const parts = listItems.map(item => { const itemData = this.getNodeData(item); if (this.getNodeType(item) === 'String') { @@ -6263,12 +6331,15 @@ export class Deparser implements DeparserVisitor { // Handle CreateStmt table options - no quotes, compact formatting if (context.parentNodeTypes.includes('CreateStmt')) { + const relOptName = node.defnamespace + ? `${QuoteUtils.quoteIdentifier(node.defnamespace)}.${QuoteUtils.quoteIdentifier(node.defname)}` + : QuoteUtils.quoteIdentifier(node.defname); // For numeric values, use the raw value without quotes if (node.arg && this.getNodeType(node.arg) === 'Integer') { const integerData = this.getNodeData(node.arg); - return `${node.defname}=${integerData.ival}`; + return `${relOptName}=${integerData.ival}`; } - return `${node.defname}=${argValue}`; + return `${relOptName}=${argValue}`; } // Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments @@ -6311,15 +6382,27 @@ export class Deparser implements DeparserVisitor { // Handle CopyStmt WITH clause options - uppercase format without quotes if (context.parentNodeTypes.includes('CopyStmt')) { - if (node.defname === 'format' && node.arg && this.getNodeType(node.arg) === 'String') { - const stringData = this.getNodeData(node.arg); - return `FORMAT ${stringData.sval.toUpperCase()}`; + const optName = node.defname.toUpperCase(); + if (!node.arg) { + return optName; + } + const copyArgType = this.getNodeType(node.arg); + if (copyArgType === 'Boolean' && this.getNodeData(node.arg).boolval !== false) { + return optName; + } + if (copyArgType === 'List') { + const items = ListUtils.unwrapList(this.getNodeData(node.arg).items) + .map(item => this.visit(item, context)); + return `${optName} (${items.join(', ')})`; } - // Handle other COPY options with uppercase defname - if (node.arg) { - return `${node.defname.toUpperCase()} ${argValue}`; + if (copyArgType === 'String') { + const sval: string = this.getNodeData(node.arg).sval; + if (node.defname === 'format') { + return `${optName} ${sval}`; + } + return `${optName} ${QuoteUtils.escape(sval)}`; } - return node.defname.toUpperCase(); + return `${optName} ${argValue}`; } // Handle CREATE OPERATOR and CREATE TYPE context @@ -6407,6 +6490,10 @@ export class Deparser implements DeparserVisitor { } } + const argType = this.getNodeType(node.arg); + if (argType === 'TypeName' || argType === 'Integer' || argType === 'Float' || argType === 'Boolean') { + return `${node.defname} = ${argValue}`; + } const quotedValue = typeof argValue === 'string' ? QuoteUtils.escape(argValue) : argValue; @@ -6915,6 +7002,9 @@ export class Deparser implements DeparserVisitor { case 'OBJECT_TABCONSTRAINT': output.push('CONSTRAINT'); break; + case 'OBJECT_DOMCONSTRAINT': + output.push('CONSTRAINT'); + break; case 'OBJECT_TRIGGER': output.push('TRIGGER'); break; @@ -6990,6 +7080,13 @@ export class Deparser implements DeparserVisitor { } else { output.push(objectParts.join('.')); } + } else if (node.objtype === 'OBJECT_DOMCONSTRAINT') { + if (objectParts.length === 2) { + const [domain, constraint] = objectParts; + output.push(constraint, 'ON DOMAIN', domain); + } else { + output.push(objectParts.join('.')); + } } else if (node.objtype === 'OBJECT_TRIGGER') { if (objectParts.length === 2) { const [table, trigger] = objectParts; @@ -7346,7 +7443,7 @@ export class Deparser implements DeparserVisitor { } if (node.pubobjects && node.pubobjects.length > 0) { - output.push('FOR', 'TABLE'); + output.push('FOR'); const tables = ListUtils.unwrapList(node.pubobjects).map(table => this.visit(table, context)); output.push(tables.join(', ')); } else if (node.for_all_tables) { @@ -7398,8 +7495,10 @@ export class Deparser implements DeparserVisitor { output.push(QuoteUtils.quoteIdentifier(node.pubname)); } - if (node.action) { - switch (node.action) { + const hasObjects = node.pubobjects && node.pubobjects.length > 0; + + if (hasObjects) { + switch (node.action || 'AP_AddObjects') { case 'AP_AddObjects': output.push('ADD'); break; @@ -7412,18 +7511,10 @@ export class Deparser implements DeparserVisitor { default: throw new Error(`Unsupported AlterPublicationStmt action: ${node.action}`); } - } - - if (node.for_all_tables) { - output.push('FOR ALL TABLES'); - } else if (node.pubobjects && node.pubobjects.length > 0) { - output.push('FOR TABLE'); const objects = ListUtils.unwrapList(node.pubobjects).map(obj => this.visit(obj, context)); output.push(objects.join(', ')); - } - - if (node.options && node.options.length > 0) { - output.push('WITH'); + } else if (node.options && node.options.length > 0) { + output.push('SET'); const options = ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context)); output.push(`(${options.join(', ')})`); } @@ -7438,44 +7529,60 @@ export class Deparser implements DeparserVisitor { output.push(QuoteUtils.quoteIdentifier(node.subname)); } - if (node.kind) { - switch (node.kind) { + const options = node.options && node.options.length > 0 + ? `(${ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context)).join(', ')})` + : null; + const publications = node.publication && node.publication.length > 0 + ? ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context)).join(', ') + : null; + + { + switch (node.kind || 'ALTER_SUBSCRIPTION_OPTIONS') { case 'ALTER_SUBSCRIPTION_OPTIONS': output.push('SET'); + if (options) output.push(options); break; case 'ALTER_SUBSCRIPTION_CONNECTION': output.push('CONNECTION'); if (node.conninfo) { - output.push(`'${node.conninfo}'`); + output.push(QuoteUtils.escape(node.conninfo)); } break; case 'ALTER_SUBSCRIPTION_SET_PUBLICATION': output.push('SET PUBLICATION'); - if (node.publication && node.publication.length > 0) { - const publications = ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context)); - output.push(publications.join(', ')); - } + if (publications) output.push(publications); + if (options) output.push('WITH', options); + break; + case 'ALTER_SUBSCRIPTION_ADD_PUBLICATION': + output.push('ADD PUBLICATION'); + if (publications) output.push(publications); + if (options) output.push('WITH', options); + break; + case 'ALTER_SUBSCRIPTION_DROP_PUBLICATION': + output.push('DROP PUBLICATION'); + if (publications) output.push(publications); + if (options) output.push('WITH', options); break; case 'ALTER_SUBSCRIPTION_REFRESH': output.push('REFRESH PUBLICATION'); + if (options) output.push('WITH', options); break; - case 'ALTER_SUBSCRIPTION_ENABLED': - output.push('ENABLE'); + case 'ALTER_SUBSCRIPTION_ENABLED': { + const enabled = ListUtils.unwrapList(node.options).find(opt => opt.DefElem?.defname === 'enabled'); + const arg = enabled?.DefElem?.arg; + const isEnabled = arg?.Boolean ? arg.Boolean.boolval !== false : true; + output.push(isEnabled ? 'ENABLE' : 'DISABLE'); break; + } case 'ALTER_SUBSCRIPTION_SKIP': output.push('SKIP'); + if (options) output.push(options); break; default: throw new Error(`Unsupported AlterSubscriptionStmt kind: ${node.kind}`); } } - if (node.options && node.options.length > 0) { - output.push('WITH'); - const options = ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context)); - output.push(`(${options.join(', ')})`); - } - return output.join(' '); } @@ -8664,6 +8771,10 @@ export class Deparser implements DeparserVisitor { output.push('WITH GRANT OPTION'); } + if (node.grantor) { + output.push('GRANTED BY', this.RoleSpec(node.grantor, context)); + } + // Only add behavior clauses for REVOKE statements, not for GRANT statements in ALTER DEFAULT PRIVILEGES if (!node.is_grant) { if (node.behavior === 'DROP_CASCADE') { @@ -8679,71 +8790,33 @@ export class Deparser implements DeparserVisitor { GrantRoleStmt(node: t.GrantRoleStmt, context: DeparserContext): string { const output: string[] = []; - // Check for inherit, admin, and set options first to place them correctly - let hasInheritOption = false; - let hasAdminOption = false; - let hasSetOption = false; - let inheritValue: boolean | undefined; - let adminValue: boolean | undefined; - let setValue: boolean | undefined; - - if (node.opt && node.opt.length > 0) { - const options = ListUtils.unwrapList(node.opt); + const options = ListUtils.unwrapList(node.opt) + .map(opt => opt.DefElem) + .filter((opt): opt is t.DefElem => !!opt && !!opt.defname); - const inheritOption = options.find(opt => - opt.DefElem && opt.DefElem.defname === 'inherit' - ); - - const adminOption = options.find(opt => - (opt.String && opt.String.sval === 'admin') || - (opt.DefElem && opt.DefElem.defname === 'admin') - ); - - const setOption = options.find(opt => - opt.DefElem && opt.DefElem.defname === 'set' - ); - - if (inheritOption && inheritOption.DefElem) { - hasInheritOption = true; - inheritValue = inheritOption.DefElem.arg?.Boolean?.boolval; - } - - if (adminOption) { - hasAdminOption = true; - if (adminOption.DefElem && adminOption.DefElem.arg) { - adminValue = adminOption.DefElem.arg.Boolean?.boolval; - } - } + const optionValue = (opt: t.DefElem): boolean => { + if (!opt.arg) return true; + const argType = this.getNodeType(opt.arg); + const argData = this.getNodeData(opt.arg); + if (argType === 'Boolean') return argData.boolval !== false; + if (argType === 'String') return argData.sval !== 'false'; + return true; + }; - if (setOption && setOption.DefElem) { - hasSetOption = true; - setValue = setOption.DefElem.arg?.Boolean?.boolval; - } - } + const roles = ListUtils.unwrapList(node.granted_roles) + .map(role => role.AccessPriv?.priv_name + ? QuoteUtils.quoteIdentifier(role.AccessPriv.priv_name) + : this.visit(role, context)) + .join(', '); if (node.is_grant) { - output.push('GRANT'); + output.push('GRANT', roles, 'TO'); } else { output.push('REVOKE'); - - if (hasInheritOption) { - output.push('INHERIT OPTION FOR'); - } else if (hasAdminOption) { - output.push('ADMIN OPTION FOR'); + if (options.length > 0) { + output.push(`${options[0].defname.toUpperCase()} OPTION FOR`); } - } - - if (node.granted_roles && node.granted_roles.length > 0) { - const roles = ListUtils.unwrapList(node.granted_roles) - .map(role => this.visit(role, context)) - .join(', '); - output.push(roles); - } - - if (node.is_grant) { - output.push('TO'); - } else { - output.push('FROM'); + output.push(roles, 'FROM'); } if (node.grantee_roles && node.grantee_roles.length > 0) { @@ -8753,38 +8826,19 @@ export class Deparser implements DeparserVisitor { output.push(grantees); } - if (node.is_grant) { - const withOptions: string[] = []; - - if (hasAdminOption) { - if (adminValue === true) { - withOptions.push('ADMIN OPTION'); - } else if (adminValue === false) { - withOptions.push('ADMIN FALSE'); - } else { - withOptions.push('ADMIN OPTION'); - } - } - - if (hasInheritOption) { - if (inheritValue === true) { - withOptions.push('INHERIT OPTION'); - } else if (inheritValue === false) { - withOptions.push('INHERIT FALSE'); - } - } + if (node.is_grant && options.length > 0) { + const withOptions = options.map(opt => + `${opt.defname.toUpperCase()} ${optionValue(opt) ? 'TRUE' : 'FALSE'}` + ); + output.push('WITH', withOptions.join(', ')); + } - if (hasSetOption) { - if (setValue === true) { - withOptions.push('SET TRUE'); - } else if (setValue === false) { - withOptions.push('SET FALSE'); - } - } + if (node.grantor) { + output.push('GRANTED BY', this.RoleSpec(node.grantor, context)); + } - if (withOptions.length > 0) { - output.push('WITH', withOptions.join(', ')); - } + if (!node.is_grant && node.behavior === 'DROP_CASCADE') { + output.push('CASCADE'); } return output.join(' '); @@ -9468,10 +9522,9 @@ export class Deparser implements DeparserVisitor { switch (node.commandType) { case 'CMD_UPDATE': { output.push('UPDATE SET'); - const assignments = ListUtils.unwrapList(node.targetList) - .map(target => this.visit(target, context.spawn('UpdateStmt', { update: true }))) - .join(', '); - output.push(assignments); + if (node.targetList && node.targetList.length > 0) { + output.push(this.formatUpdateAssignments(node.targetList, context)); + } break; } case 'CMD_INSERT': { @@ -9932,6 +9985,14 @@ export class Deparser implements DeparserVisitor { output.push(node.into.accessMethod); } + if (node.into && node.into.options && node.into.options.length > 0) { + output.push('WITH'); + const options = ListUtils.unwrapList(node.into.options) + .map(option => this.visit(option, context.spawn('CreateStmt'))) + .join(', '); + output.push(`(${options})`); + } + if (node.into && node.into.onCommit && node.into.onCommit !== 'ONCOMMIT_NOOP') { output.push('ON COMMIT'); switch (node.into.onCommit) { @@ -9947,20 +10008,16 @@ export class Deparser implements DeparserVisitor { } } + if (node.into && node.into.tableSpaceName) { + output.push('TABLESPACE', QuoteUtils.quoteIdentifier(node.into.tableSpaceName)); + } + output.push('AS'); if (node.query) { output.push(this.visit(node.query as any, context)); } - if (node.into && node.into.options && node.into.options.length > 0) { - output.push('WITH'); - const options = ListUtils.unwrapList(node.into.options) - .map(option => this.visit(option, context)) - .join(', '); - output.push(`(${options})`); - } - if (node.into && node.into.skipData) { output.push('WITH NO DATA'); } @@ -10503,7 +10560,7 @@ export class Deparser implements DeparserVisitor { if (node.whereClause) { output.push('WHERE'); - output.push(this.visit(node.whereClause, context)); + output.push(`(${this.visit(node.whereClause, context)})`); } return output.join(' '); @@ -10793,28 +10850,200 @@ export class Deparser implements DeparserVisitor { return output.join(' '); } + formatJsonFormat(format: t.JsonFormat | undefined): string | null { + if (!format || !format.format_type || format.format_type === 'JS_FORMAT_DEFAULT') { + return null; + } + const parts = ['FORMAT JSON']; + switch (format.encoding) { + case 'JS_ENC_UTF8': + parts.push('ENCODING UTF8'); + break; + case 'JS_ENC_UTF16': + parts.push('ENCODING UTF16'); + break; + case 'JS_ENC_UTF32': + parts.push('ENCODING UTF32'); + break; + } + return parts.join(' '); + } + + formatJsonOutput(output: t.JsonOutput | undefined, context: DeparserContext): string | null { + if (!output || !output.typeName) return null; + const parts = ['RETURNING', this.TypeName(output.typeName, context)]; + const format = this.formatJsonFormat(output.returning?.format); + if (format) parts.push(format); + return parts.join(' '); + } + + formatJsonNullClause(absentOnNull: boolean | undefined): string { + return absentOnNull ? 'ABSENT ON NULL' : 'NULL ON NULL'; + } + + formatJsonUniqueClause(unique: boolean | undefined): string | null { + return unique ? 'WITH UNIQUE KEYS' : null; + } + + JsonValueExpr(node: t.JsonValueExpr, context: DeparserContext): string { + const parts: string[] = []; + if (node.raw_expr) { + parts.push(this.visit(node.raw_expr, context)); + } + const format = this.formatJsonFormat(node.format); + if (format) parts.push(format); + return parts.join(' '); + } + + JsonKeyValue(node: t.JsonKeyValue, context: DeparserContext): string { + const key = node.key ? this.visit(node.key, context) : ''; + const value = node.value ? this.JsonValueExpr(node.value, context) : ''; + return `${key} : ${value}`; + } + + JsonObjectConstructor(node: t.JsonObjectConstructor, context: DeparserContext): string { + const args: string[] = []; + + if (node.exprs && node.exprs.length > 0) { + args.push(ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', ')); + args.push(this.formatJsonNullClause(node.absent_on_null)); + const unique = this.formatJsonUniqueClause(node.unique); + if (unique) args.push(unique); + } + + const output = this.formatJsonOutput(node.output, context); + if (output) args.push(output); + + return `JSON_OBJECT(${args.join(' ')})`; + } + + JsonArrayConstructor(node: t.JsonArrayConstructor, context: DeparserContext): string { + const args: string[] = []; + + if (node.exprs && node.exprs.length > 0) { + args.push(ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', ')); + args.push(this.formatJsonNullClause(node.absent_on_null)); + } + + const output = this.formatJsonOutput(node.output, context); + if (output) args.push(output); + + return `JSON_ARRAY(${args.join(' ')})`; + } + JsonArrayQueryConstructor(node: t.JsonArrayQueryConstructor, context: DeparserContext): string { - const output: string[] = ['JSON_ARRAYAGG']; + const args: string[] = []; if (node.query) { - output.push(`(${this.visit(node.query, context)})`); + args.push(this.visit(node.query, context)); + } + + const format = this.formatJsonFormat(node.format); + if (format) args.push(format); + + const output = this.formatJsonOutput(node.output, context); + if (output) args.push(output); + + return `JSON_ARRAY(${args.join(' ')})`; + } + + formatJsonAggSuffix(ctor: t.JsonAggConstructor | undefined, context: DeparserContext): string { + if (!ctor) return ''; + let result = ''; + if (ctor.agg_filter) { + result += ` FILTER (WHERE ${this.visit(ctor.agg_filter, context)})`; + } + if (ctor.over) { + result += this.formatOverClause(ctor.over, context); } + return result; + } - if (node.format) { - output.push('FORMAT JSON'); + JsonObjectAgg(node: t.JsonObjectAgg, context: DeparserContext): string { + const args: string[] = []; + + if (node.arg) { + args.push(this.JsonKeyValue(node.arg, context)); } + args.push(this.formatJsonNullClause(node.absent_on_null)); + const unique = this.formatJsonUniqueClause(node.unique); + if (unique) args.push(unique); - if (node.output) { - output.push('RETURNING TEXT'); + const output = this.formatJsonOutput(node.constructor?.output, context); + if (output) args.push(output); + + return `JSON_OBJECTAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`; + } + + JsonArrayAgg(node: t.JsonArrayAgg, context: DeparserContext): string { + const args: string[] = []; + + if (node.arg) { + args.push(this.JsonValueExpr(node.arg, context)); } - if (node.absent_on_null) { - output.push('ABSENT ON NULL'); - } else { - output.push('NULL ON NULL'); + const aggOrder = node.constructor?.agg_order; + if (aggOrder && aggOrder.length > 0) { + const orderItems = ListUtils.unwrapList(aggOrder).map(item => this.visit(item, context)); + args.push(`ORDER BY ${orderItems.join(', ')}`); } - return output.join(' '); + args.push(this.formatJsonNullClause(node.absent_on_null)); + + const output = this.formatJsonOutput(node.constructor?.output, context); + if (output) args.push(output); + + return `JSON_ARRAYAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`; + } + + JsonParseExpr(node: t.JsonParseExpr, context: DeparserContext): string { + const args: string[] = []; + if (node.expr) { + args.push(this.JsonValueExpr(node.expr, context)); + } + const unique = this.formatJsonUniqueClause(node.unique_keys); + if (unique) args.push(unique); + return `JSON(${args.join(' ')})`; + } + + JsonScalarExpr(node: t.JsonScalarExpr, context: DeparserContext): string { + const expr = node.expr ? this.visit(node.expr, context) : ''; + return `JSON_SCALAR(${expr})`; + } + + JsonSerializeExpr(node: t.JsonSerializeExpr, context: DeparserContext): string { + const args: string[] = []; + if (node.expr) { + args.push(this.JsonValueExpr(node.expr, context)); + } + const output = this.formatJsonOutput(node.output, context); + if (output) args.push(output); + return `JSON_SERIALIZE(${args.join(' ')})`; + } + + JsonIsPredicate(node: t.JsonIsPredicate, context: DeparserContext): string { + const parts: string[] = []; + if (node.expr) { + parts.push(this.visit(node.expr, context)); + } + const format = this.formatJsonFormat(node.format); + if (format) parts.push(format); + parts.push('IS JSON'); + switch (node.item_type) { + case 'JS_TYPE_OBJECT': + parts.push('OBJECT'); + break; + case 'JS_TYPE_ARRAY': + parts.push('ARRAY'); + break; + case 'JS_TYPE_SCALAR': + parts.push('SCALAR'); + break; + } + if (node.unique_keys) { + parts.push('WITH UNIQUE KEYS'); + } + return parts.join(' '); } RangeFunction(node: t.RangeFunction, context: DeparserContext): string { diff --git a/packages/pgsql-types/src/field-metadata.json b/packages/pgsql-types/src/field-metadata.json index 2ab51a94c..c62e9d381 100644 --- a/packages/pgsql-types/src/field-metadata.json +++ b/packages/pgsql-types/src/field-metadata.json @@ -487,6 +487,7 @@ "args": { "nullable": false, "tags": [ + "A_Const", "A_Expr", "BoolExpr", "BooleanTest", @@ -585,6 +586,7 @@ "BooleanTest", "ColumnRef", "FuncCall", + "JsonIsPredicate", "NullTest", "SubLink", "TypeCast" diff --git a/packages/pgsql-types/src/types.ts b/packages/pgsql-types/src/types.ts index d85ef519f..85ebeabb4 100644 --- a/packages/pgsql-types/src/types.ts +++ b/packages/pgsql-types/src/types.ts @@ -60,12 +60,12 @@ type AlterTableStmt_cmds = { AlterTableCmd: AlterTableCmd }; type AlterTypeStmt_options = { DefElem: DefElem }; type AlterTypeStmt_typeName = { String: String }; type AlterUserMappingStmt_options = { DefElem: DefElem }; -type BoolExpr_args = { A_Expr: A_Expr } | { BoolExpr: BoolExpr } | { BooleanTest: BooleanTest } | { CaseExpr: CaseExpr } | { ColumnRef: ColumnRef } | { FuncCall: FuncCall } | { JsonIsPredicate: JsonIsPredicate } | { NullTest: NullTest } | { ParamRef: ParamRef } | { SubLink: SubLink } | { TypeCast: TypeCast } | { XmlExpr: XmlExpr }; +type BoolExpr_args = { A_Const: A_Const } | { A_Expr: A_Expr } | { BoolExpr: BoolExpr } | { BooleanTest: BooleanTest } | { CaseExpr: CaseExpr } | { ColumnRef: ColumnRef } | { FuncCall: FuncCall } | { JsonIsPredicate: JsonIsPredicate } | { NullTest: NullTest } | { ParamRef: ParamRef } | { SubLink: SubLink } | { TypeCast: TypeCast } | { XmlExpr: XmlExpr }; type BooleanTest_arg = { A_Expr: A_Expr } | { BoolExpr: BoolExpr } | { ColumnRef: ColumnRef } | { TypeCast: TypeCast }; type CaseExpr_arg = { A_Const: A_Const } | { A_Expr: A_Expr } | { A_Indirection: A_Indirection } | { CaseExpr: CaseExpr } | { ColumnRef: ColumnRef } | { FuncCall: FuncCall } | { GroupingFunc: GroupingFunc } | { MergeSupportFunc: MergeSupportFunc } | { TypeCast: TypeCast }; type CaseExpr_args = { CaseWhen: CaseWhen }; type CaseExpr_defresult = { A_ArrayExpr: A_ArrayExpr } | { A_Const: A_Const } | { A_Expr: A_Expr } | { CaseExpr: CaseExpr } | { ColumnRef: ColumnRef } | { FuncCall: FuncCall } | { SubLink: SubLink } | { TypeCast: TypeCast }; -type CaseWhen_expr = { A_Const: A_Const } | { A_Expr: A_Expr } | { BoolExpr: BoolExpr } | { BooleanTest: BooleanTest } | { ColumnRef: ColumnRef } | { FuncCall: FuncCall } | { NullTest: NullTest } | { SubLink: SubLink } | { TypeCast: TypeCast }; +type CaseWhen_expr = { A_Const: A_Const } | { A_Expr: A_Expr } | { BoolExpr: BoolExpr } | { BooleanTest: BooleanTest } | { ColumnRef: ColumnRef } | { FuncCall: FuncCall } | { JsonIsPredicate: JsonIsPredicate } | { NullTest: NullTest } | { SubLink: SubLink } | { TypeCast: TypeCast }; type CaseWhen_result = { A_ArrayExpr: A_ArrayExpr } | { A_Const: A_Const } | { A_Expr: A_Expr } | { A_Indirection: A_Indirection } | { CaseExpr: CaseExpr } | { CoalesceExpr: CoalesceExpr } | { ColumnRef: ColumnRef } | { FuncCall: FuncCall } | { SubLink: SubLink } | { TypeCast: TypeCast }; type CoalesceExpr_args = { A_Const: A_Const } | { A_Expr: A_Expr } | { ColumnRef: ColumnRef } | { FuncCall: FuncCall } | { RowExpr: RowExpr } | { TypeCast: TypeCast }; type CollateClause_arg = { A_ArrayExpr: A_ArrayExpr } | { A_Const: A_Const } | { A_Expr: A_Expr } | { CaseExpr: CaseExpr } | { ColumnRef: ColumnRef } | { TypeCast: TypeCast }; diff --git a/packages/plpgsql-deparser/__tests__/__snapshots__/schema-rename-mapped.test.ts.snap b/packages/plpgsql-deparser/__tests__/__snapshots__/schema-rename-mapped.test.ts.snap index 41d9b9182..512e8cfec 100644 --- a/packages/plpgsql-deparser/__tests__/__snapshots__/schema-rename-mapped.test.ts.snap +++ b/packages/plpgsql-deparser/__tests__/__snapshots__/schema-rename-mapped.test.ts.snap @@ -220,7 +220,7 @@ CREATE FUNCTION myapp_v2.update_user_status( p_user_id int, p_status text ) RETURNS void LANGUAGE plpgsql AS $$BEGIN - UPDATE myapp_v2.users SET status = p_status,updated_at = now() WHERE id = p_user_id; + UPDATE myapp_v2.users SET status = p_status, updated_at = now() WHERE id = p_user_id; INSERT INTO myapp_v2.status_history ( user_id, status, @@ -401,6 +401,6 @@ BEGIN (item.id, now()); UPDATE myapp_v2.batch_items SET status = 'processed' WHERE id = item.id; END LOOP; - UPDATE myapp_v2.batches SET status = 'completed',completed_at = now() WHERE id = p_batch_id; + UPDATE myapp_v2.batches SET status = 'completed', completed_at = now() WHERE id = p_batch_id; END$$;" `; diff --git a/packages/scripts/__tests__/invert.test.ts b/packages/scripts/__tests__/invert.test.ts index e6ce93972..a74fb2153 100644 --- a/packages/scripts/__tests__/invert.test.ts +++ b/packages/scripts/__tests__/invert.test.ts @@ -118,7 +118,7 @@ describe('revertFor', () => { }); it('inverts GRANT role TO role', () => { - expect(revert('GRANT reader TO alice;').sql).toEqual('REVOKE READER FROM alice;'); + expect(revert('GRANT reader TO alice;').sql).toEqual('REVOKE reader FROM alice;'); }); it('does not invert REVOKE (prior grants unknown)', () => {