Skip to content

Commit 754cd4d

Browse files
authored
DuckDB support (#857)
Created a brand new DuckDB configuration to replace the old #738 pull request. This should now support the most important bits of DuckDB. There are some caveats though: - No support for the percentage syntax, like `LIMIT 10%` (conflicts with modulo operator). - No support for named parameters like `$foo` (conflicts with $$-quoted strings). - No support for array-slice operator (conflicts with `:` in struct literals and prefix aliases). There's definitely quite a bit more than the above three little things. But I think at least for start we have some DuckDB support that should work for most users.
2 parents f5e283e + 6622304 commit 754cd4d

17 files changed

+1504
-196
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Erik Forsström <[email protected]>
2121
Gajus Kuizinas <[email protected]>
2222
George Leslie-Waksman <[email protected]>
2323
Grant Forsythe <[email protected]>
24+
Hugh Cameron <[email protected]>
2425
htaketani <[email protected]>
2526
Ian Campbell <[email protected]>
2627
ivan baktsheev

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
It started as a port of a [PHP Library][], but has since considerably diverged.
88

99
It supports various SQL dialects:
10-
GCP BigQuery, IBM DB2, Apache Hive, MariaDB, MySQL, TiDB, Couchbase N1QL, Oracle PL/SQL, PostgreSQL, Amazon Redshift, SingleStoreDB, Snowflake, Spark, SQL Server Transact-SQL, Trino (and Presto).
10+
GCP BigQuery, IBM DB2, DuckDB, Apache Hive, MariaDB, MySQL, TiDB, Couchbase N1QL, Oracle PL/SQL, PostgreSQL, Amazon Redshift, SingleStoreDB, Snowflake, Spark, SQL Server Transact-SQL, Trino (and Presto).
1111
See [language option docs](docs/language.md) for more details.
1212

1313
It does not support:

docs/dialect.md

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ The following dialects can be imported from `"sql-formatter"` module:
2424
- `bigquery` - [GCP BigQuery][]
2525
- `db2` - [IBM DB2][]
2626
- `db2i` - [IBM DB2i][] (experimental)
27+
- `duckdb` - [DuckDB][]
2728
- `hive` - [Apache Hive][]
2829
- `mariadb` - [MariaDB][]
2930
- `mysql` - [MySQL][]
@@ -73,6 +74,7 @@ You likely only want to use this if your other alternative is to fork SQL Format
7374
[gcp bigquery]: https://cloud.google.com/bigquery
7475
[ibm db2]: https://www.ibm.com/analytics/us/en/technology/db2/
7576
[ibm db2i]: https://www.ibm.com/docs/en/i/7.5?topic=overview-db2-i
77+
[duckdb]: https://duckdb.org/
7678
[apache hive]: https://hive.apache.org/
7779
[mariadb]: https://mariadb.com/
7880
[mysql]: https://www.mysql.com/

docs/language.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const result = format('SELECT * FROM tbl', { language: 'sqlite' });
1616
- `"bigquery"` - [GCP BigQuery][]
1717
- `"db2"` - [IBM DB2][]
1818
- `"db2i"` - [IBM DB2i][] (experimental)
19+
- `"duckdb"` - [DuckDB][]
1920
- `"hive"` - [Apache Hive][]
2021
- `"mariadb"` - [MariaDB][]
2122
- `"mysql"` - [MySQL][]
@@ -50,6 +51,7 @@ See docs for [dialect][] option.
5051
[gcp bigquery]: https://cloud.google.com/bigquery
5152
[ibm db2]: https://www.ibm.com/analytics/us/en/technology/db2/
5253
[ibm db2i]: https://www.ibm.com/docs/en/i/7.5?topic=overview-db2-i
54+
[duckdb]: https://duckdb.org/
5355
[apache hive]: https://hive.apache.org/
5456
[mariadb]: https://mariadb.com/
5557
[mysql]: https://www.mysql.com/

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
"trino",
4040
"presto",
4141
"prestosql",
42-
"snowflake"
42+
"snowflake",
43+
"duckdb"
4344
],
4445
"files": [
4546
"dist",

src/allDialects.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export { bigquery } from './languages/bigquery/bigquery.formatter.js';
22
export { db2 } from './languages/db2/db2.formatter.js';
33
export { db2i } from './languages/db2i/db2i.formatter.js';
4+
export { duckdb } from './languages/duckdb/duckdb.formatter.js';
45
export { hive } from './languages/hive/hive.formatter.js';
56
export { mariadb } from './languages/mariadb/mariadb.formatter.js';
67
export { mysql } from './languages/mysql/mysql.formatter.js';

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export { ConfigError } from './validateConfig.js';
66
export { bigquery } from './languages/bigquery/bigquery.formatter.js';
77
export { db2 } from './languages/db2/db2.formatter.js';
88
export { db2i } from './languages/db2i/db2i.formatter.js';
9+
export { duckdb } from './languages/duckdb/duckdb.formatter.js';
910
export { hive } from './languages/hive/hive.formatter.js';
1011
export { mariadb } from './languages/mariadb/mariadb.formatter.js';
1112
export { mysql } from './languages/mysql/mysql.formatter.js';
+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import { DialectOptions } from '../../dialect.js';
2+
import { expandPhrases } from '../../expandPhrases.js';
3+
import { functions } from './duckdb.functions.js';
4+
import { dataTypes, keywords } from './duckdb.keywords.js';
5+
6+
const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT]']);
7+
8+
const reservedClauses = expandPhrases([
9+
// queries
10+
'WITH [RECURSIVE]',
11+
'FROM',
12+
'WHERE',
13+
'GROUP BY [ALL]',
14+
'HAVING',
15+
'WINDOW',
16+
'PARTITION BY',
17+
'ORDER BY [ALL]',
18+
'LIMIT',
19+
'OFFSET',
20+
// 'USING' (conflicts with 'USING' in JOIN)
21+
'USING SAMPLE',
22+
'QUALIFY',
23+
// Data manipulation
24+
// - insert:
25+
'INSERT [OR REPLACE] INTO',
26+
'VALUES',
27+
'DEFAULT VALUES',
28+
// - update:
29+
'SET',
30+
// other:
31+
'RETURNING',
32+
]);
33+
34+
const standardOnelineClauses = expandPhrases([
35+
'CREATE [OR REPLACE] [TEMPORARY | TEMP] TABLE [IF NOT EXISTS]',
36+
]);
37+
38+
const tabularOnelineClauses = expandPhrases([
39+
// TABLE
40+
// - update:
41+
'UPDATE',
42+
// - insert:
43+
'ON CONFLICT',
44+
// - delete:
45+
'DELETE FROM',
46+
// - drop table:
47+
'DROP TABLE [IF EXISTS]',
48+
// - truncate
49+
'TRUNCATE',
50+
// - alter table:
51+
'ALTER TABLE',
52+
'ADD [COLUMN] [IF NOT EXISTS]',
53+
'ADD PRIMARY KEY',
54+
'DROP [COLUMN] [IF EXISTS]',
55+
'ALTER [COLUMN]',
56+
'RENAME [COLUMN]',
57+
'RENAME TO',
58+
'SET [DATA] TYPE', // for alter column
59+
'{SET | DROP} DEFAULT', // for alter column
60+
'{SET | DROP} NOT NULL', // for alter column
61+
62+
// MACRO / FUNCTION
63+
'CREATE [OR REPLACE] [TEMPORARY | TEMP] {MACRO | FUNCTION}',
64+
'DROP MACRO [TABLE] [IF EXISTS]',
65+
'DROP FUNCTION [IF EXISTS]',
66+
// INDEX
67+
'CREATE [UNIQUE] INDEX [IF NOT EXISTS]',
68+
'DROP INDEX [IF EXISTS]',
69+
// SCHEMA
70+
'CREATE [OR REPLACE] SCHEMA [IF NOT EXISTS]',
71+
'DROP SCHEMA [IF EXISTS]',
72+
// SECRET
73+
'CREATE [OR REPLACE] [PERSISTENT | TEMPORARY] SECRET [IF NOT EXISTS]',
74+
'DROP [PERSISTENT | TEMPORARY] SECRET [IF EXISTS]',
75+
// SEQUENCE
76+
'CREATE [OR REPLACE] [TEMPORARY | TEMP] SEQUENCE',
77+
'DROP SEQUENCE [IF EXISTS]',
78+
// VIEW
79+
'CREATE [OR REPLACE] [TEMPORARY | TEMP] VIEW [IF NOT EXISTS]',
80+
'DROP VIEW [IF EXISTS]',
81+
'ALTER VIEW',
82+
// TYPE
83+
'CREATE TYPE',
84+
'DROP TYPE [IF EXISTS]',
85+
86+
// other
87+
'ANALYZE',
88+
'ATTACH [DATABASE] [IF NOT EXISTS]',
89+
'DETACH [DATABASE] [IF EXISTS]',
90+
'CALL',
91+
'[FORCE] CHECKPOINT',
92+
'COMMENT ON [TABLE | COLUMN | VIEW | INDEX | SEQUENCE | TYPE | MACRO | MACRO TABLE]',
93+
'COPY [FROM DATABASE]',
94+
'DESCRIBE',
95+
'EXPORT DATABASE',
96+
'IMPORT DATABASE',
97+
'INSTALL',
98+
'LOAD',
99+
'PIVOT',
100+
'PIVOT_WIDER',
101+
'UNPIVOT',
102+
'EXPLAIN [ANALYZE]',
103+
// plain SET conflicts with SET clause in UPDATE
104+
'SET {LOCAL | SESSION | GLOBAL}',
105+
'RESET [LOCAL | SESSION | GLOBAL]',
106+
'{SET | RESET} VARIABLE',
107+
'SUMMARIZE',
108+
'BEGIN TRANSACTION',
109+
'ROLLBACK',
110+
'COMMIT',
111+
'ABORT',
112+
'USE',
113+
'VACUUM [ANALYZE]',
114+
// prepared statements
115+
'PREPARE',
116+
'EXECUTE',
117+
'DEALLOCATE [PREPARE]',
118+
]);
119+
120+
const reservedSetOperations = expandPhrases([
121+
'UNION [ALL | BY NAME]',
122+
'EXCEPT [ALL]',
123+
'INTERSECT [ALL]',
124+
]);
125+
126+
const reservedJoins = expandPhrases([
127+
'JOIN',
128+
'{LEFT | RIGHT | FULL} [OUTER] JOIN',
129+
'{INNER | CROSS} JOIN',
130+
'{NATURAL | ASOF} [INNER] JOIN',
131+
'{NATURAL | ASOF} {LEFT | RIGHT | FULL} [OUTER] JOIN',
132+
'POSITIONAL JOIN',
133+
'ANTI JOIN',
134+
'SEMI JOIN',
135+
]);
136+
137+
const reservedPhrases = expandPhrases([
138+
'{ROWS | RANGE | GROUPS} BETWEEN',
139+
'SIMILAR TO',
140+
'IS [NOT] DISTINCT FROM',
141+
'TIMESTAMP WITH TIME ZONE',
142+
]);
143+
144+
export const duckdb: DialectOptions = {
145+
name: 'duckdb',
146+
tokenizerOptions: {
147+
reservedSelect,
148+
reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses],
149+
reservedSetOperations,
150+
reservedJoins,
151+
reservedPhrases,
152+
supportsXor: true,
153+
reservedKeywords: keywords,
154+
reservedDataTypes: dataTypes,
155+
reservedFunctionNames: functions,
156+
nestedBlockComments: true,
157+
extraParens: ['[]', '{}'],
158+
stringTypes: [
159+
'$$',
160+
"''-qq",
161+
{ quote: "''-qq-bs", prefixes: ['E'], requirePrefix: true },
162+
{ quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true },
163+
],
164+
identTypes: [`""-qq`],
165+
identChars: { rest: '$' },
166+
// TODO: named params $foo currently conflict with $$-quoted strings
167+
paramTypes: { positional: true, numbered: ['$'], quoted: ['$'] },
168+
operators: [
169+
// Arithmetic:
170+
'//',
171+
'%',
172+
'**',
173+
'^',
174+
'!',
175+
// Bitwise:
176+
'&',
177+
'|',
178+
'~',
179+
'<<',
180+
'>>',
181+
// Cast:
182+
'::',
183+
// Comparison:
184+
'==',
185+
// Lambda:
186+
'->',
187+
// key-value separator:
188+
':',
189+
// Named function params:
190+
':=',
191+
'=>',
192+
// Pattern matching:
193+
'~~',
194+
'!~~',
195+
'~~*',
196+
'!~~*',
197+
'~~~',
198+
// Regular expressions:
199+
'~',
200+
'!~',
201+
'~*',
202+
'!~*',
203+
// String:
204+
'^@',
205+
'||',
206+
// INET extension:
207+
'>>=',
208+
'<<=',
209+
],
210+
},
211+
formatOptions: {
212+
alwaysDenseOperators: ['::'],
213+
onelineClauses: [...standardOnelineClauses, ...tabularOnelineClauses],
214+
tabularOnelineClauses,
215+
},
216+
};

0 commit comments

Comments
 (0)