diff --git a/src/formatter/ExpressionFormatter.ts b/src/formatter/ExpressionFormatter.ts index 485eed9c3..4a917db03 100644 --- a/src/formatter/ExpressionFormatter.ts +++ b/src/formatter/ExpressionFormatter.ts @@ -339,6 +339,11 @@ export default class ExpressionFormatter { // would otherwise re-parse as a single dashed identifier. if (text === '-' && this.dialectCfg.identifierDashes) { this.layout.add(text, WS.SPACE); + } else if (/^OPERATOR\s*\(/iu.test(text)) { + // PostgreSQL's "OPERATOR(schema.+)" is a keyword-like operator. Densing it + // would glue it to its operands ("aOPERATOR(...)b") and re-parse as invalid + // SQL, so keep its surrounding spaces even with denseOperators. + this.layout.add(text, WS.SPACE); } else if (this.cfg.denseOperators || this.dialectCfg.alwaysDenseOperators.includes(text)) { this.layout.add(WS.NO_SPACE, text); } else if (text === ':') { diff --git a/test/postgresql.test.ts b/test/postgresql.test.ts index 7870962f6..4542d364a 100644 --- a/test/postgresql.test.ts +++ b/test/postgresql.test.ts @@ -221,6 +221,19 @@ describe('PostgreSqlFormatter', () => { `); }); + it('keeps spaces around OPERATOR() syntax with denseOperators', () => { + // Densing "foo OPERATOR(public.===) bar" into "fooOPERATOR(public.===)bar" + // glues the operands onto the keyword and re-parses as invalid SQL. + expect(format(`SELECT foo OPERATOR(public.===) bar;`, { denseOperators: true })).toBe(dedent` + SELECT + foo OPERATOR(public.===) bar; + `); + expect(format(`SELECT a OPERATOR(+) b;`, { denseOperators: true })).toBe(dedent` + SELECT + a OPERATOR(+) b; + `); + }); + // Issue #813 it('supports OR REPLACE in CREATE FUNCTION', () => { expect(format(`CREATE OR REPLACE FUNCTION foo ();`)).toBe(dedent`