diff --git a/__fixtures__/generated/generated.json b/__fixtures__/generated/generated.json index a9ad7570..b2030dbf 100644 --- a/__fixtures__/generated/generated.json +++ b/__fixtures__/generated/generated.json @@ -21220,6 +21220,43 @@ "original/alter/alter-table-column-7.sql": "ALTER TABLE public.books\nADD COLUMN tags TEXT[] DEFAULT '{}'", "original/alter/alter-table-column-8.sql": "CREATE TYPE mood AS ENUM ('happy', 'sad', 'neutral')", "original/alter/alter-table-column-9.sql": "ALTER TABLE public.profiles\nADD COLUMN current_mood mood DEFAULT 'neutral'", + "misc/xmlserialize-indent-1.sql": "SELECT xmlserialize(DOCUMENT '42' AS text INDENT)", + "misc/xmlserialize-indent-2.sql": "SELECT xmlserialize(CONTENT '42' AS text INDENT)", + "misc/xmlserialize-indent-3.sql": "SELECT xmlserialize(DOCUMENT '42' AS text NO INDENT)", + "misc/xmlserialize-indent-4.sql": "SELECT xmlserialize(CONTENT doc AS varchar INDENT) FROM xmltest", + "misc/transaction-chain-1.sql": "START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE", + "misc/transaction-chain-2.sql": "COMMIT AND CHAIN", + "misc/transaction-chain-3.sql": "ROLLBACK AND CHAIN", + "misc/transaction-chain-4.sql": "COMMIT AND NO CHAIN", + "misc/transaction-chain-5.sql": "ROLLBACK AND NO CHAIN", + "misc/transaction-chain-6.sql": "END AND CHAIN", + "misc/transaction-chain-7.sql": "ABORT AND CHAIN", + "misc/tablespace-clauses-1.sql": "CREATE TABLE testschema.foo (i int) TABLESPACE regress_tblspace", + "misc/tablespace-clauses-2.sql": "CREATE TABLE t (a int) ON COMMIT DROP TABLESPACE ts", + "misc/tablespace-clauses-3.sql": "CREATE TABLE t (a int) USING heap TABLESPACE ts", + "misc/tablespace-clauses-4.sql": "CREATE TABLE testschema.dflt (a int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace) PARTITION BY LIST (a) TABLESPACE pg_default", + "misc/tablespace-clauses-5.sql": "CREATE TABLE testschema.part_1 PARTITION OF testschema.part FOR VALUES IN (1) TABLESPACE \"My Space\"", + "misc/tablespace-clauses-6.sql": "CREATE TABLE t (a int, b int, PRIMARY KEY (a) INCLUDE (b) WITH (fillfactor=10) USING INDEX TABLESPACE ts DEFERRABLE)", + "misc/tablespace-clauses-7.sql": "CREATE TABLE t (a int UNIQUE NULLS NOT DISTINCT USING INDEX TABLESPACE \"My Space\")", + "misc/tablespace-clauses-8.sql": "CREATE TABLE t (a int, CONSTRAINT u UNIQUE (a) INCLUDE (a) WITH (fillfactor=50))", + "misc/tablespace-clauses-9.sql": "CREATE TABLE t (a int, EXCLUDE USING gist (a WITH =) INCLUDE (a) WITH (fillfactor=10) USING INDEX TABLESPACE ts WHERE (a > 0))", + "misc/tablespace-clauses-10.sql": "ALTER TABLE testschema.atable ADD CONSTRAINT anindex_t UNIQUE (column1) USING INDEX TABLESPACE regress_tblspace", + "misc/tablespace-clauses-11.sql": "ALTER TABLE t ADD CONSTRAINT pk PRIMARY KEY USING INDEX \"My Index\"", + "misc/tablespace-clauses-12.sql": "ALTER TABLE t ADD UNIQUE USING INDEX idx DEFERRABLE INITIALLY DEFERRED", + "misc/tablespace-clauses-13.sql": "CREATE TABLESPACE \"My Space\" OWNER regress_user LOCATION '/tmp' WITH (random_page_cost = 3, seq_page_cost = '1.5')", + "misc/tablespace-clauses-14.sql": "ALTER TABLESPACE \"My Space\" SET (random_page_cost = 1.5, effective_io_concurrency = 2)", + "misc/tablespace-clauses-15.sql": "ALTER TABLESPACE \"My Space\" RESET (random_page_cost)", + "misc/tablespace-clauses-16.sql": "DROP TABLESPACE IF EXISTS \"My Space\"", + "misc/show-quoting-1.sql": "SHOW \"TimeZone\"", + "misc/show-quoting-2.sql": "SHOW \"Foo\".\"Bar\"", + "misc/show-quoting-3.sql": "SHOW \"my.ext\".setting", + "misc/show-quoting-4.sql": "SHOW \"ALL\"", + "misc/show-quoting-5.sql": "SHOW ALL", + "misc/show-quoting-6.sql": "SHOW TIME ZONE", + "misc/show-quoting-7.sql": "SHOW TRANSACTION ISOLATION LEVEL", + "misc/show-quoting-8.sql": "SHOW SESSION AUTHORIZATION", + "misc/show-quoting-9.sql": "SHOW search_path", + "misc/show-quoting-10.sql": "SHOW plpgsql.extra_warnings", "misc/rename-qualified-and-sequence-privs-1.sql": "ALTER TYPE app.t2 RENAME TO t", "misc/rename-qualified-and-sequence-privs-2.sql": "ALTER DOMAIN app.d2 RENAME TO d", "misc/rename-qualified-and-sequence-privs-3.sql": "GRANT ALL ON SEQUENCE app.seq TO bob", @@ -21719,6 +21756,14 @@ "misc/generated-columns-6.sql": "ALTER TABLE generated_misc_test\nALTER COLUMN c SET EXPRESSION AS (a - b)", "misc/generated-columns-7.sql": "ALTER TABLE generated_misc_test\nALTER COLUMN c DROP EXPRESSION", "misc/generated-columns-8.sql": "ALTER TABLE generated_misc_test\nALTER COLUMN c DROP EXPRESSION IF EXISTS", + "misc/fetch-with-ties-1.sql": "SELECT thousand FROM onek WHERE thousand < 5 ORDER BY thousand FETCH FIRST 2 ROW WITH TIES", + "misc/fetch-with-ties-2.sql": "SELECT thousand FROM onek WHERE thousand < 5 ORDER BY thousand FETCH FIRST ROWS WITH TIES", + "misc/fetch-with-ties-3.sql": "SELECT thousand FROM onek WHERE thousand < 5 ORDER BY thousand OFFSET 2 ROWS FETCH NEXT (1 + 1) ROWS WITH TIES", + "misc/fetch-with-ties-4.sql": "CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ORDER BY thousand FETCH FIRST 5 ROWS WITH TIES OFFSET 10", + "misc/fetch-with-ties-5.sql": "CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ORDER BY thousand FETCH FIRST (NULL+1) ROWS WITH TIES", + "misc/fetch-with-ties-6.sql": "CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ORDER BY thousand FETCH FIRST NULL ROWS ONLY", + "misc/fetch-with-ties-7.sql": "SELECT * FROM t ORDER BY a FETCH FIRST $1 ROWS WITH TIES", + "misc/fetch-with-ties-8.sql": "SELECT * FROM t ORDER BY a FETCH FIRST -1 ROWS WITH TIES", "misc/extract-1.sql": "SELECT EXTRACT(EPOCH FROM now())", "misc/extract-2.sql": "SELECT EXTRACT(EPOCH FROM DATE '1970-01-01')", "misc/extract-3.sql": "SELECT EXTRACT(EPOCH FROM TIMESTAMP '1970-01-01')", @@ -21737,6 +21782,10 @@ "misc/extract-16.sql": "SELECT EXTRACT(TIMEZONE FROM CURRENT_TIMESTAMP)", "misc/extract-17.sql": "SELECT EXTRACT(TIMEZONE_HOUR FROM CURRENT_TIMESTAMP)", "misc/extract-18.sql": "SELECT EXTRACT(TIMEZONE_MINUTE FROM CURRENT_TIMESTAMP)", + "misc/copy-option-values-1.sql": "COPY t FROM STDIN (HEADER false)", + "misc/copy-option-values-2.sql": "COPY t FROM STDIN (HEADER true, FREEZE false)", + "misc/copy-option-values-3.sql": "COPY t FROM STDIN (FORMAT \"CSV\")", + "misc/copy-option-values-4.sql": "COPY t FROM STDIN (FORMAT 'csv', HEADER off)", "misc/cascades-1.sql": "DROP TABLE IF EXISTS some_table CASCADE", "misc/cascades-2.sql": "DROP VIEW IF EXISTS some_view CASCADE", "misc/cascades-3.sql": "DROP MATERIALIZED VIEW IF EXISTS some_mat_view CASCADE", @@ -21765,6 +21814,9 @@ "misc/booleans-cast-1.sql": "SELECT * FROM myschema.mytable WHERE a = TRUE", "misc/booleans-cast-2.sql": "SELECT * FROM myschema.mytable WHERE a = CAST('t' AS boolean)", "misc/booleans-cast-3.sql": "SELECT * FROM myschema.mytable WHERE a = 't'::boolean", + "misc/alter-trigger-rename-1.sql": "ALTER TRIGGER p ON grandp RENAME TO q", + "misc/alter-trigger-rename-2.sql": "ALTER TRIGGER \"Mixed Trig\" ON \"My Schema\".\"My Table\" RENAME TO \"New Trig\"", + "misc/alter-trigger-rename-3.sql": "ALTER TRIGGER modified_a ON main_table RENAME TO modified_modified_a", "latest/postgres/create_view-1.sql": "CREATE FUNCTION interpt_pp(path, path)\n RETURNS point\n AS 'regresslib'\n LANGUAGE C STRICT", "latest/postgres/create_view-2.sql": "CREATE TABLE real_city (\n\tpop\t\t\tint4,\n\tcname\t\ttext,\n\toutline \tpath\n)", "latest/postgres/create_view-3.sql": "COPY real_city FROM 'filename'", diff --git a/__fixtures__/kitchen-sink/misc/alter-trigger-rename.sql b/__fixtures__/kitchen-sink/misc/alter-trigger-rename.sql new file mode 100644 index 00000000..dc37c35e --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/alter-trigger-rename.sql @@ -0,0 +1,5 @@ +-- ALTER TRIGGER name ON table RENAME TO (RenameStmt OBJECT_TRIGGER keeps subname) +-- Ref: PostgreSQL regress triggers.sql +ALTER TRIGGER p ON grandp RENAME TO q; +ALTER TRIGGER "Mixed Trig" ON "My Schema"."My Table" RENAME TO "New Trig"; +ALTER TRIGGER modified_a ON main_table RENAME TO modified_modified_a; diff --git a/__fixtures__/kitchen-sink/misc/copy-option-values.sql b/__fixtures__/kitchen-sink/misc/copy-option-values.sql new file mode 100644 index 00000000..674955a7 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/copy-option-values.sql @@ -0,0 +1,5 @@ +-- COPY option values keep their meaning +COPY t FROM STDIN (HEADER false); +COPY t FROM STDIN (HEADER true, FREEZE false); +COPY t FROM STDIN (FORMAT "CSV"); +COPY t FROM STDIN (FORMAT 'csv', HEADER off); diff --git a/__fixtures__/kitchen-sink/misc/fetch-with-ties.sql b/__fixtures__/kitchen-sink/misc/fetch-with-ties.sql new file mode 100644 index 00000000..d19b3470 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/fetch-with-ties.sql @@ -0,0 +1,10 @@ +-- FETCH FIRST n ROWS WITH TIES (SelectStmt.limitOption) +-- Ref: PostgreSQL regress limit.sql +SELECT thousand FROM onek WHERE thousand < 5 ORDER BY thousand FETCH FIRST 2 ROW WITH TIES; +SELECT thousand FROM onek WHERE thousand < 5 ORDER BY thousand FETCH FIRST ROWS WITH TIES; +SELECT thousand FROM onek WHERE thousand < 5 ORDER BY thousand OFFSET 2 ROWS FETCH NEXT (1 + 1) ROWS WITH TIES; +CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ORDER BY thousand FETCH FIRST 5 ROWS WITH TIES OFFSET 10; +CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ORDER BY thousand FETCH FIRST (NULL+1) ROWS WITH TIES; +CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ORDER BY thousand FETCH FIRST NULL ROWS ONLY; +SELECT * FROM t ORDER BY a FETCH FIRST $1 ROWS WITH TIES; +SELECT * FROM t ORDER BY a FETCH FIRST -1 ROWS WITH TIES; diff --git a/__fixtures__/kitchen-sink/misc/show-quoting.sql b/__fixtures__/kitchen-sink/misc/show-quoting.sql new file mode 100644 index 00000000..f4101d16 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/show-quoting.sql @@ -0,0 +1,11 @@ +-- SHOW keeps identifier quoting (VariableShowStmt.name) +SHOW "TimeZone"; +SHOW "Foo"."Bar"; +SHOW "my.ext".setting; +SHOW "ALL"; +SHOW ALL; +SHOW TIME ZONE; +SHOW TRANSACTION ISOLATION LEVEL; +SHOW SESSION AUTHORIZATION; +SHOW search_path; +SHOW plpgsql.extra_warnings; diff --git a/__fixtures__/kitchen-sink/misc/tablespace-clauses.sql b/__fixtures__/kitchen-sink/misc/tablespace-clauses.sql new file mode 100644 index 00000000..1eb0fab0 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/tablespace-clauses.sql @@ -0,0 +1,18 @@ +-- TABLESPACE / USING INDEX TABLESPACE / INCLUDE / WITH on table and constraint definitions +-- Ref: PostgreSQL regress tablespace.sql, create_table.sql, constraints.sql +CREATE TABLE testschema.foo (i int) TABLESPACE regress_tblspace; +CREATE TABLE t (a int) ON COMMIT DROP TABLESPACE ts; +CREATE TABLE t (a int) USING heap TABLESPACE ts; +CREATE TABLE testschema.dflt (a int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace) PARTITION BY LIST (a) TABLESPACE pg_default; +CREATE TABLE testschema.part_1 PARTITION OF testschema.part FOR VALUES IN (1) TABLESPACE "My Space"; +CREATE TABLE t (a int, b int, PRIMARY KEY (a) INCLUDE (b) WITH (fillfactor=10) USING INDEX TABLESPACE ts DEFERRABLE); +CREATE TABLE t (a int UNIQUE NULLS NOT DISTINCT USING INDEX TABLESPACE "My Space"); +CREATE TABLE t (a int, CONSTRAINT u UNIQUE (a) INCLUDE (a) WITH (fillfactor=50)); +CREATE TABLE t (a int, EXCLUDE USING gist (a WITH =) INCLUDE (a) WITH (fillfactor=10) USING INDEX TABLESPACE ts WHERE (a > 0)); +ALTER TABLE testschema.atable ADD CONSTRAINT anindex_t UNIQUE (column1) USING INDEX TABLESPACE regress_tblspace; +ALTER TABLE t ADD CONSTRAINT pk PRIMARY KEY USING INDEX "My Index"; +ALTER TABLE t ADD UNIQUE USING INDEX idx DEFERRABLE INITIALLY DEFERRED; +CREATE TABLESPACE "My Space" OWNER regress_user LOCATION '/tmp' WITH (random_page_cost = 3, seq_page_cost = '1.5'); +ALTER TABLESPACE "My Space" SET (random_page_cost = 1.5, effective_io_concurrency = 2); +ALTER TABLESPACE "My Space" RESET (random_page_cost); +DROP TABLESPACE IF EXISTS "My Space"; diff --git a/__fixtures__/kitchen-sink/misc/transaction-chain.sql b/__fixtures__/kitchen-sink/misc/transaction-chain.sql new file mode 100644 index 00000000..8205f519 --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/transaction-chain.sql @@ -0,0 +1,9 @@ +-- COMMIT/ROLLBACK AND CHAIN (TransactionStmt.chain) +-- Ref: PostgreSQL regress transactions.sql +START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE; +COMMIT AND CHAIN; +ROLLBACK AND CHAIN; +COMMIT AND NO CHAIN; +ROLLBACK AND NO CHAIN; +END AND CHAIN; +ABORT AND CHAIN; diff --git a/__fixtures__/kitchen-sink/misc/xmlserialize-indent.sql b/__fixtures__/kitchen-sink/misc/xmlserialize-indent.sql new file mode 100644 index 00000000..025ce37f --- /dev/null +++ b/__fixtures__/kitchen-sink/misc/xmlserialize-indent.sql @@ -0,0 +1,6 @@ +-- XMLSERIALIZE ... INDENT (XmlSerialize.indent) +-- Ref: PostgreSQL regress xml.sql +SELECT xmlserialize(DOCUMENT '42' AS text INDENT); +SELECT xmlserialize(CONTENT '42' AS text INDENT); +SELECT xmlserialize(DOCUMENT '42' AS text NO INDENT); +SELECT xmlserialize(CONTENT doc AS varchar INDENT) FROM xmltest; diff --git a/packages/deparser/__tests__/kitchen-sink/misc-alter-trigger-rename.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-alter-trigger-rename.test.ts new file mode 100644 index 00000000..aa8b9f69 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-alter-trigger-rename.test.ts @@ -0,0 +1,11 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-alter-trigger-rename', async () => { + await fixtures.runFixtureTests([ + "misc/alter-trigger-rename-1.sql", + "misc/alter-trigger-rename-2.sql", + "misc/alter-trigger-rename-3.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-copy-option-values.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-copy-option-values.test.ts new file mode 100644 index 00000000..5442ff20 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-copy-option-values.test.ts @@ -0,0 +1,12 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-copy-option-values', async () => { + await fixtures.runFixtureTests([ + "misc/copy-option-values-1.sql", + "misc/copy-option-values-2.sql", + "misc/copy-option-values-3.sql", + "misc/copy-option-values-4.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-fetch-with-ties.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-fetch-with-ties.test.ts new file mode 100644 index 00000000..acd1d65b --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-fetch-with-ties.test.ts @@ -0,0 +1,16 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-fetch-with-ties', async () => { + await fixtures.runFixtureTests([ + "misc/fetch-with-ties-1.sql", + "misc/fetch-with-ties-2.sql", + "misc/fetch-with-ties-3.sql", + "misc/fetch-with-ties-4.sql", + "misc/fetch-with-ties-5.sql", + "misc/fetch-with-ties-6.sql", + "misc/fetch-with-ties-7.sql", + "misc/fetch-with-ties-8.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-show-quoting.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-show-quoting.test.ts new file mode 100644 index 00000000..b4def202 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-show-quoting.test.ts @@ -0,0 +1,18 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-show-quoting', async () => { + await fixtures.runFixtureTests([ + "misc/show-quoting-1.sql", + "misc/show-quoting-2.sql", + "misc/show-quoting-3.sql", + "misc/show-quoting-4.sql", + "misc/show-quoting-5.sql", + "misc/show-quoting-6.sql", + "misc/show-quoting-7.sql", + "misc/show-quoting-8.sql", + "misc/show-quoting-9.sql", + "misc/show-quoting-10.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-tablespace-clauses.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-tablespace-clauses.test.ts new file mode 100644 index 00000000..295fae50 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-tablespace-clauses.test.ts @@ -0,0 +1,24 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-tablespace-clauses', async () => { + await fixtures.runFixtureTests([ + "misc/tablespace-clauses-1.sql", + "misc/tablespace-clauses-2.sql", + "misc/tablespace-clauses-3.sql", + "misc/tablespace-clauses-4.sql", + "misc/tablespace-clauses-5.sql", + "misc/tablespace-clauses-6.sql", + "misc/tablespace-clauses-7.sql", + "misc/tablespace-clauses-8.sql", + "misc/tablespace-clauses-9.sql", + "misc/tablespace-clauses-10.sql", + "misc/tablespace-clauses-11.sql", + "misc/tablespace-clauses-12.sql", + "misc/tablespace-clauses-13.sql", + "misc/tablespace-clauses-14.sql", + "misc/tablespace-clauses-15.sql", + "misc/tablespace-clauses-16.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-transaction-chain.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-transaction-chain.test.ts new file mode 100644 index 00000000..30e58df2 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-transaction-chain.test.ts @@ -0,0 +1,15 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-transaction-chain', async () => { + await fixtures.runFixtureTests([ + "misc/transaction-chain-1.sql", + "misc/transaction-chain-2.sql", + "misc/transaction-chain-3.sql", + "misc/transaction-chain-4.sql", + "misc/transaction-chain-5.sql", + "misc/transaction-chain-6.sql", + "misc/transaction-chain-7.sql" +]); +}); diff --git a/packages/deparser/__tests__/kitchen-sink/misc-xmlserialize-indent.test.ts b/packages/deparser/__tests__/kitchen-sink/misc-xmlserialize-indent.test.ts new file mode 100644 index 00000000..3a188442 --- /dev/null +++ b/packages/deparser/__tests__/kitchen-sink/misc-xmlserialize-indent.test.ts @@ -0,0 +1,12 @@ + +import { FixtureTestUtils } from '../../test-utils'; +const fixtures = new FixtureTestUtils(); + +it('misc-xmlserialize-indent', async () => { + await fixtures.runFixtureTests([ + "misc/xmlserialize-indent-1.sql", + "misc/xmlserialize-indent-2.sql", + "misc/xmlserialize-indent-3.sql", + "misc/xmlserialize-indent-4.sql" +]); +}); diff --git a/packages/deparser/__tests__/misc/copy-boolean-options.test.ts b/packages/deparser/__tests__/misc/copy-boolean-options.test.ts new file mode 100644 index 00000000..e6af5ad0 --- /dev/null +++ b/packages/deparser/__tests__/misc/copy-boolean-options.test.ts @@ -0,0 +1,21 @@ +import { Deparser } from '../../src'; + +// The parser only emits Boolean(true) COPY option args (e.g. `CSV HEADER`), and the +// JSON AST omits `boolval` when it is false, so a false Boolean must be hand-built. +const copyWith = (defname: string, arg: any) => ({ + CopyStmt: { + relation: { relname: 't', inh: true, relpersistence: 'p' }, + is_from: true, + options: [{ DefElem: { defname, arg, defaction: 'DEFELEM_UNSPEC' } }] + } +}); + +describe('COPY Boolean option args', () => { + it.each([ + ['false (boolval omitted)', { Boolean: {} }, 'COPY t FROM STDIN WITH (HEADER false)'], + ['false', { Boolean: { boolval: false } }, 'COPY t FROM STDIN WITH (HEADER false)'], + ['true', { Boolean: { boolval: true } }, 'COPY t FROM STDIN WITH (HEADER)'] + ])('keeps a %s Boolean value', (_label, arg, expected) => { + expect(Deparser.deparse(copyWith('header', arg) as any)).toBe(expected); + }); +}); diff --git a/packages/deparser/src/deparser.ts b/packages/deparser/src/deparser.ts index 94c7f625..619a6941 100644 --- a/packages/deparser/src/deparser.ts +++ b/packages/deparser/src/deparser.ts @@ -717,12 +717,28 @@ export class Deparser implements DeparserVisitor { } } - if (node.limitCount) { - output.push('LIMIT ' + this.visit(node.limitCount as Node, context)); - } + if (node.limitOption === 'LIMIT_OPTION_WITH_TIES') { + if (node.limitOffset) { + output.push('OFFSET ' + this.visit(node.limitOffset as Node, context) + ' ROWS'); + } + const count = node.limitCount as Node | undefined; + if (count) { + let countStr = this.visit(count, context); + if (!('A_Const' in count) || countStr.startsWith('-')) { + countStr = `(${countStr})`; + } + output.push(`FETCH FIRST ${countStr} ROWS WITH TIES`); + } else { + output.push('FETCH FIRST ROWS WITH TIES'); + } + } else { + if (node.limitCount) { + output.push('LIMIT ' + this.visit(node.limitCount as Node, context)); + } - if (node.limitOffset) { - output.push('OFFSET ' + this.visit(node.limitOffset as Node, context)); + if (node.limitOffset) { + output.push('OFFSET ' + this.visit(node.limitOffset as Node, context)); + } } if (node.lockingClause) { @@ -2808,6 +2824,10 @@ export class Deparser implements DeparserVisitor { output.push(this.quoteIfNeeded(node.accessMethod)); } + if (node.tablespacename) { + output.push('TABLESPACE', QuoteUtils.quoteIdentifier(node.tablespacename)); + } + return output.join(' '); } @@ -3018,10 +3038,7 @@ export class Deparser implements DeparserVisitor { } output.push(`(${keyItems.join(', ')})`); } - if (node.indexname) { - output.push('USING INDEX'); - output.push(node.indexname); - } + this.pushIndexConstraintOptions(node, output, context); break; case 'CONSTR_UNIQUE': if (context.isPretty() && !context.isColumnConstraint) { @@ -3040,10 +3057,7 @@ export class Deparser implements DeparserVisitor { } output.push(`(${keyItems.join(', ')})`); } - if (node.indexname) { - output.push('USING INDEX'); - output.push(node.indexname); - } + this.pushIndexConstraintOptions(node, output, context); break; case 'CONSTR_FOREIGN': // Only add "FOREIGN KEY" for table-level constraints, not column-level constraints @@ -3227,6 +3241,7 @@ export class Deparser implements DeparserVisitor { }); output.push(`(${exclusionElements.join(', ')})`); } + this.pushIndexConstraintOptions(node, output, context); if (node.where_clause) { output.push('WHERE'); output.push(context.parens(this.visit(node.where_clause as any, context))); @@ -3280,6 +3295,25 @@ export class Deparser implements DeparserVisitor { return output.join(' '); } + private pushIndexConstraintOptions(node: t.Constraint, output: string[], context: DeparserContext): void { + if (node.indexname) { + output.push('USING INDEX', QuoteUtils.quoteIdentifier(node.indexname)); + return; + } + if (node.including && node.including.length > 0) { + const cols = ListUtils.unwrapList(node.including).map(col => this.visit(col, context)); + output.push(`INCLUDE (${cols.join(', ')})`); + } + if (node.options && node.options.length > 0) { + const optionContext = context.spawn('IndexStmt'); + const opts = ListUtils.unwrapList(node.options).map(opt => this.visit(opt, optionContext)); + output.push(`WITH (${opts.join(', ')})`); + } + if (node.indexspace) { + output.push('USING INDEX TABLESPACE', QuoteUtils.quoteIdentifier(node.indexspace)); + } + } + SubLink(node: t.SubLink, context: DeparserContext): string { const subselect = context.parens(this.visit(node.subselect, context)); @@ -4347,6 +4381,10 @@ export class Deparser implements DeparserVisitor { } } + if (node.chain) { + output.push('AND CHAIN'); + } + return output.join(' '); } @@ -4466,10 +4504,10 @@ export class Deparser implements DeparserVisitor { } VariableShowStmt(node: t.VariableShowStmt, context: DeparserContext): string { - if (node.name === 'ALL') { + if (node.name === 'all') { return 'SHOW ALL'; } - return `SHOW ${node.name}`; + return `SHOW ${QuoteUtils.quoteDottedName(node.name.split('.'))}`; } CreateSchemaStmt(node: t.CreateSchemaStmt, context: DeparserContext): string { @@ -6187,7 +6225,7 @@ export class Deparser implements DeparserVisitor { } if (context.parentNodeTypes.includes('CreateTableSpaceStmt') || context.parentNodeTypes.includes('AlterTableSpaceOptionsStmt')) { - return `${node.defname.toUpperCase()} ${argValue}`; + return `${node.defname} = ${this.formatOptionValue(node.arg, argValue)}`; } if (context.parentNodeTypes.includes('ExplainStmt')) { @@ -6397,8 +6435,8 @@ export class Deparser implements DeparserVisitor { return optName; } const copyArgType = this.getNodeType(node.arg); - if (copyArgType === 'Boolean' && this.getNodeData(node.arg).boolval !== false) { - return optName; + if (copyArgType === 'Boolean') { + return this.getNodeData(node.arg).boolval ? optName : `${optName} false`; } if (copyArgType === 'List') { const items = ListUtils.unwrapList(this.getNodeData(node.arg).items) @@ -6407,7 +6445,7 @@ export class Deparser implements DeparserVisitor { } if (copyArgType === 'String') { const sval: string = this.getNodeData(node.arg).sval; - if (node.defname === 'format') { + if (node.defname === 'format' && QuoteUtils.quoteIdentifier(sval) === sval) { return `${optName} ${sval}`; } return `${optName} ${QuoteUtils.escape(sval)}`; @@ -6525,7 +6563,7 @@ export class Deparser implements DeparserVisitor { const output: string[] = ['CREATE', 'TABLESPACE']; if (node.tablespacename) { - output.push(node.tablespacename); + output.push(QuoteUtils.quoteIdentifier(node.tablespacename)); } if (node.owner) { @@ -6558,7 +6596,7 @@ export class Deparser implements DeparserVisitor { } if (node.tablespacename) { - output.push(node.tablespacename); + output.push(QuoteUtils.quoteIdentifier(node.tablespacename)); } return output.join(' '); @@ -6568,7 +6606,7 @@ export class Deparser implements DeparserVisitor { const output: string[] = ['ALTER', 'TABLESPACE']; if (node.tablespacename) { - output.push(node.tablespacename); + output.push(QuoteUtils.quoteIdentifier(node.tablespacename)); } if (node.isReset) { @@ -8485,6 +8523,9 @@ export class Deparser implements DeparserVisitor { break; case 'OBJECT_TRIGGER': output.push('TRIGGER'); + if (node.subname) { + output.push(QuoteUtils.quoteIdentifier(node.subname)); + } break; case 'OBJECT_TSCONFIGURATION': output.push('TEXT SEARCH CONFIGURATION'); @@ -8530,8 +8571,8 @@ export class Deparser implements DeparserVisitor { } else if (node.relation) { const rangeVarContext = context.spawn('RenameStmt', { objtype: node.relationType }); - // Add ON keyword for policy operations - if (node.renameType === 'OBJECT_POLICY') { + // Add ON keyword for policy and trigger operations + if (node.renameType === 'OBJECT_POLICY' || node.renameType === 'OBJECT_TRIGGER') { output.push('ON'); } @@ -11417,6 +11458,10 @@ export class Deparser implements DeparserVisitor { output.push(this.visit(node.expr as any, context)); output.push('AS'); output.push(this.TypeName(node.typeName, context)); + + if (node.indent) { + output.push('INDENT'); + } } output.push(')');