diff --git a/src/languages/bigquery.formatter.ts b/src/languages/bigquery.formatter.ts index b7cb9bc103..193043392b 100644 --- a/src/languages/bigquery.formatter.ts +++ b/src/languages/bigquery.formatter.ts @@ -696,6 +696,7 @@ const reservedCommands = [ 'ORDER BY', 'QUALIFY', 'WINDOW', + 'PARTITION BY', 'LIMIT', 'OFFSET', 'WITH', diff --git a/src/languages/hive.formatter.ts b/src/languages/hive.formatter.ts index 306fc0fe2c..7d0e4473d8 100644 --- a/src/languages/hive.formatter.ts +++ b/src/languages/hive.formatter.ts @@ -572,6 +572,8 @@ const reservedCommands = [ 'VALUES', 'WHERE', 'WITH', + 'WINDOW', + 'PARTITION BY', // newline keywords 'STORED AS', diff --git a/src/languages/mysql.formatter.ts b/src/languages/mysql.formatter.ts index 3c4ac3a895..a23bfdf189 100644 --- a/src/languages/mysql.formatter.ts +++ b/src/languages/mysql.formatter.ts @@ -1274,6 +1274,8 @@ const reservedCommands = [ 'OFFSET', 'ORDER BY', 'WHERE', + 'WINDOW', + 'PARTITION BY', ]; const reservedBinaryCommands = [ diff --git a/src/languages/n1ql.formatter.ts b/src/languages/n1ql.formatter.ts index 6e433d5c1d..58ee0bc931 100644 --- a/src/languages/n1ql.formatter.ts +++ b/src/languages/n1ql.formatter.ts @@ -472,6 +472,8 @@ const reservedCommands = [ 'VALUES', 'WHERE', 'WITH', + 'WINDOW', + 'PARTITION BY', ]; const reservedBinaryCommands = [ diff --git a/src/languages/postgresql.formatter.ts b/src/languages/postgresql.formatter.ts index 7d4a6222e7..1dd49b3405 100644 --- a/src/languages/postgresql.formatter.ts +++ b/src/languages/postgresql.formatter.ts @@ -1580,6 +1580,8 @@ const reservedCommands = [ 'ORDER BY', 'WHERE', 'WITH', + 'WINDOW', + 'PARTITION BY', ]; const reservedBinaryCommands = [ diff --git a/src/languages/spark.formatter.ts b/src/languages/spark.formatter.ts index 3943e081fe..b82c9b5736 100644 --- a/src/languages/spark.formatter.ts +++ b/src/languages/spark.formatter.ts @@ -665,7 +665,6 @@ const reservedCommands = [ 'WITH', 'CLUSTER BY', 'DISTRIBUTE BY', - 'PARTITION BY', // verify 'GROUP BY', 'HAVING', 'VALUES', @@ -711,7 +710,7 @@ const reservedCommands = [ 'INSERT', 'LATERAL VIEW', 'UPDATE', - 'WINDOW', // verify + 'WINDOW', ]; const reservedBinaryCommands = [ diff --git a/src/languages/sql.formatter.ts b/src/languages/sql.formatter.ts index 90cfb94864..c7ea0c128c 100644 --- a/src/languages/sql.formatter.ts +++ b/src/languages/sql.formatter.ts @@ -347,6 +347,8 @@ const reservedCommands = [ 'VALUES', 'WHERE', 'WITH', + 'WINDOW', + 'PARTITION BY', ]; const reservedBinaryCommands = [ diff --git a/src/languages/sqlite.formatter.ts b/src/languages/sqlite.formatter.ts index badc00257f..545fe38784 100644 --- a/src/languages/sqlite.formatter.ts +++ b/src/languages/sqlite.formatter.ts @@ -239,6 +239,8 @@ const reservedCommands = [ 'VALUES', 'WHERE', 'WITH', + 'WINDOW', + 'PARTITION BY', ]; const reservedBinaryCommands = [ diff --git a/src/languages/tsql.formatter.ts b/src/languages/tsql.formatter.ts index 85f03e56b4..08230a8c8b 100644 --- a/src/languages/tsql.formatter.ts +++ b/src/languages/tsql.formatter.ts @@ -1189,6 +1189,8 @@ const reservedCommands = [ 'VALUES', 'WHERE', 'WITH', + 'WINDOW', + 'PARTITION BY', ]; const reservedBinaryCommands = [ diff --git a/test/bigquery.test.ts b/test/bigquery.test.ts index 8f0161b48b..e8e75c28be 100644 --- a/test/bigquery.test.ts +++ b/test/bigquery.test.ts @@ -15,6 +15,7 @@ import supportsDeleteFrom from './features/deleteFrom'; import supportsComments from './features/comments'; import supportsIdentifiers from './features/identifiers'; import supportsParams from './options/param'; +import supportsWindow from './features/window'; describe('BigQueryFormatter', () => { const language = 'bigquery'; @@ -32,6 +33,7 @@ describe('BigQueryFormatter', () => { supportsJoin(format, { without: ['NATURAL JOIN'] }); supportsOperators(format, BigQueryFormatter.operators); supportsParams(format, { positional: true, named: ['@'], quoted: ['@``'] }); + supportsWindow(format); // Note: BigQuery supports single dashes inside identifiers, so my-ident would be // detected as identifier, while other SQL dialects would detect it as diff --git a/test/features/window.ts b/test/features/window.ts new file mode 100644 index 0000000000..020d993d43 --- /dev/null +++ b/test/features/window.ts @@ -0,0 +1,47 @@ +import dedent from 'dedent-js'; + +import { FormatFn } from 'src/sqlFormatter'; + +export default function supportsWindow(format: FormatFn) { + it('formats WINDOW clause at top level', () => { + const result = format( + 'SELECT *, LAG(value) OVER wnd AS next_value FROM tbl WINDOW wnd AS (PARTITION BY id ORDER BY time);' + ); + expect(result).toBe(dedent` + SELECT + *, + LAG(value) OVER wnd AS next_value + FROM + tbl + WINDOW + wnd AS ( + PARTITION BY + id + ORDER BY + time + ); + `); + }); + + it('formats multiple WINDOW specifications', () => { + const result = format( + 'SELECT * FROM table1 WINDOW w1 AS (PARTITION BY col1), w2 AS (PARTITION BY col1, col2);' + ); + expect(result).toBe(dedent` + SELECT + * + FROM + table1 + WINDOW + w1 AS ( + PARTITION BY + col1 + ), + w2 AS ( + PARTITION BY + col1, + col2 + ); + `); + }); +} diff --git a/test/hive.test.ts b/test/hive.test.ts index 3df989c2c1..99c5da8b7a 100644 --- a/test/hive.test.ts +++ b/test/hive.test.ts @@ -15,6 +15,7 @@ import supportsOperators from './features/operators'; import supportsArrayAndMapAccessors from './features/arrayAndMapAccessors'; import supportsComments from './features/comments'; import supportsIdentifiers from './features/identifiers'; +import supportsWindow from './features/window'; describe('HiveFormatter', () => { const language = 'hive'; @@ -31,6 +32,7 @@ describe('HiveFormatter', () => { supportsJoin(format, { without: ['NATURAL JOIN'] }); supportsOperators(format, HiveFormatter.operators); supportsArrayAndMapAccessors(format); + supportsWindow(format); it('throws error when params option used', () => { expect(() => format('SELECT *', { params: ['1', '2', '3'] })).toThrow( diff --git a/test/mysql.test.ts b/test/mysql.test.ts index a57243cdc2..746347a1c8 100644 --- a/test/mysql.test.ts +++ b/test/mysql.test.ts @@ -5,6 +5,7 @@ import MySqlFormatter from 'src/languages/mysql.formatter'; import behavesLikeMariaDbFormatter from './behavesLikeMariaDbFormatter'; import supportsOperators from './features/operators'; +import supportsWindow from './features/window'; describe('MySqlFormatter', () => { const language = 'mysql'; @@ -13,6 +14,7 @@ describe('MySqlFormatter', () => { behavesLikeMariaDbFormatter(format); supportsOperators(format, MySqlFormatter.operators, ['AND', 'OR', 'XOR']); + supportsWindow(format); // TODO: disabled for now it.skip('supports @@ system variables', () => { diff --git a/test/n1ql.test.ts b/test/n1ql.test.ts index b2adb6c4ed..be5e152025 100644 --- a/test/n1ql.test.ts +++ b/test/n1ql.test.ts @@ -16,6 +16,7 @@ import supportsArrayLiterals from './features/arrayLiterals'; import supportsComments from './features/comments'; import supportsIdentifiers from './features/identifiers'; import supportsParams from './options/param'; +import supportsWindow from './features/window'; describe('N1qlFormatter', () => { const language = 'n1ql'; @@ -34,6 +35,7 @@ describe('N1qlFormatter', () => { supportsJoin(format, { without: ['FULL', 'CROSS', 'NATURAL'] }); supportsReturning(format); supportsParams(format, { positional: true, numbered: ['$'], named: ['$'] }); + supportsWindow(format); it('formats INSERT with {} object literal', () => { const result = format( diff --git a/test/postgresql.test.ts b/test/postgresql.test.ts index 90a0de2b6f..5c3c95d7ed 100644 --- a/test/postgresql.test.ts +++ b/test/postgresql.test.ts @@ -18,6 +18,7 @@ import supportsComments from './features/comments'; import supportsIdentifiers from './features/identifiers'; import supportsParams from './options/param'; import supportsArrayAndMapAccessors from './features/arrayAndMapAccessors'; +import supportsWindow from './features/window'; describe('PostgreSqlFormatter', () => { const language = 'postgresql'; @@ -41,6 +42,7 @@ describe('PostgreSqlFormatter', () => { supportsJoin(format); supportsReturning(format); supportsParams(format, { numbered: ['$'] }); + supportsWindow(format); it('allows $ character as part of identifiers', () => { expect(format('SELECT foo$, some$$ident')).toBe(dedent` diff --git a/test/spark.test.ts b/test/spark.test.ts index c5734c1763..70dd8245cb 100644 --- a/test/spark.test.ts +++ b/test/spark.test.ts @@ -50,23 +50,19 @@ describe('SparkFormatter', () => { ], }); - it('formats WINDOW specification as top level', () => { - const result = format( - 'SELECT *, LAG(value) OVER wnd AS next_value FROM tbl WINDOW wnd as (PARTITION BY id ORDER BY time);' - ); + it('formats basic WINDOW clause', () => { + const result = format(`SELECT * FROM tbl WINDOW win1, WINDOW win2, WINDOW win3;`); expect(result).toBe(dedent` SELECT - *, - LAG(value) OVER wnd AS next_value + * FROM tbl WINDOW - wnd as ( - PARTITION BY - id - ORDER BY - time - ); + win1, + WINDOW + win2, + WINDOW + win3; `); }); diff --git a/test/sql.test.ts b/test/sql.test.ts index ab591e07e8..3650088da1 100644 --- a/test/sql.test.ts +++ b/test/sql.test.ts @@ -16,6 +16,7 @@ import supportsDeleteFrom from './features/deleteFrom'; import supportsComments from './features/comments'; import supportsIdentifiers from './features/identifiers'; import supportsParams from './options/param'; +import supportsWindow from './features/window'; describe('SqlFormatter', () => { const language = 'sql'; @@ -34,6 +35,7 @@ describe('SqlFormatter', () => { supportsJoin(format); supportsOperators(format, SqlFormatter.operators); supportsParams(format, { positional: true }); + supportsWindow(format); it('formats FETCH FIRST like LIMIT', () => { const result = format('SELECT * FETCH FIRST 2 ROWS ONLY;'); diff --git a/test/sqlite.test.ts b/test/sqlite.test.ts index f8dceea7df..77098011fc 100644 --- a/test/sqlite.test.ts +++ b/test/sqlite.test.ts @@ -16,6 +16,7 @@ import supportsDeleteFrom from './features/deleteFrom'; import supportsComments from './features/comments'; import supportsIdentifiers from './features/identifiers'; import supportsParams from './options/param'; +import supportsWindow from './features/window'; describe('SqliteFormatter', () => { const language = 'sqlite'; @@ -37,6 +38,7 @@ describe('SqliteFormatter', () => { }); supportsOperators(format, SqliteFormatter.operators); supportsParams(format, { positional: true, numbered: ['?'], named: [':', '$', '@'] }); + supportsWindow(format); it('formats FETCH FIRST like LIMIT', () => { const result = format('SELECT * FETCH FIRST 2 ROWS ONLY;'); diff --git a/test/tsql.test.ts b/test/tsql.test.ts index fc824fcd6b..2fd7744c91 100644 --- a/test/tsql.test.ts +++ b/test/tsql.test.ts @@ -16,6 +16,7 @@ import supportsDeleteFrom from './features/deleteFrom'; import supportsComments from './features/comments'; import supportsIdentifiers from './features/identifiers'; import supportsParams from './options/param'; +import supportsWindow from './features/window'; describe('TSqlFormatter', () => { const language = 'tsql'; @@ -37,6 +38,7 @@ describe('TSqlFormatter', () => { ); supportsJoin(format, { without: ['NATURAL'] }); supportsParams(format, { named: ['@'], quoted: ['@""', '@[]'] }); + supportsWindow(format); // TODO: The following are duplicated from StandardSQLFormatter test