diff --git a/src/languages/bigquery/bigquery.formatter.ts b/src/languages/bigquery/bigquery.formatter.ts index 5b40bc13d0..398951015a 100644 --- a/src/languages/bigquery/bigquery.formatter.ts +++ b/src/languages/bigquery/bigquery.formatter.ts @@ -145,7 +145,7 @@ const reservedJoins = expandPhrases([ '{INNER | CROSS} JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ // https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#tablesample_operator 'TABLESAMPLE SYSTEM', // From DDL: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language @@ -158,6 +158,8 @@ const reservedPhrases = expandPhrases([ 'IS [NOT] DISTINCT FROM', ]); +const reservedDataTypePhrases = expandPhrases([]); + // https://cloud.google.com/bigquery/docs/reference/#standard-sql-reference export const bigquery: DialectOptions = { name: 'bigquery', @@ -166,7 +168,8 @@ export const bigquery: DialectOptions = { reservedClauses: [...reservedClauses, ...tabularOnelineClauses, ...standardOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/db2/db2.formatter.ts b/src/languages/db2/db2.formatter.ts index dfe5815e0a..1aa18a7a09 100644 --- a/src/languages/db2/db2.formatter.ts +++ b/src/languages/db2/db2.formatter.ts @@ -253,13 +253,15 @@ const reservedJoins = expandPhrases([ '{INNER | CROSS} JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON DELETE', 'ON UPDATE', 'SET NULL', '{ROWS | RANGE} BETWEEN', ]); +const reservedDataTypePhrases = expandPhrases([]); + export const db2: DialectOptions = { name: 'db2', tokenizerOptions: { @@ -267,7 +269,8 @@ export const db2: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/db2i/db2i.formatter.ts b/src/languages/db2i/db2i.formatter.ts index de4900a805..b05c66fb01 100644 --- a/src/languages/db2i/db2i.formatter.ts +++ b/src/languages/db2i/db2i.formatter.ts @@ -146,13 +146,15 @@ const reservedJoins = expandPhrases([ '{INNER | CROSS} JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON DELETE', 'ON UPDATE', 'SET NULL', '{ROWS | RANGE} BETWEEN', ]); +const reservedDataTypePhrases = expandPhrases([]); + // https://www.ibm.com/docs/en/i/7.5?topic=reference-sql export const db2i: DialectOptions = { name: 'db2i', @@ -161,7 +163,8 @@ export const db2i: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/duckdb/duckdb.formatter.ts b/src/languages/duckdb/duckdb.formatter.ts index e9388f2cc8..1d84a498a1 100644 --- a/src/languages/duckdb/duckdb.formatter.ts +++ b/src/languages/duckdb/duckdb.formatter.ts @@ -134,13 +134,14 @@ const reservedJoins = expandPhrases([ 'SEMI JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ '{ROWS | RANGE | GROUPS} BETWEEN', 'SIMILAR TO', 'IS [NOT] DISTINCT FROM', - 'TIMESTAMP WITH TIME ZONE', ]); +const reservedDataTypePhrases = expandPhrases(['TIMESTAMP WITH TIME ZONE']); + export const duckdb: DialectOptions = { name: 'duckdb', tokenizerOptions: { @@ -148,7 +149,8 @@ export const duckdb: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, supportsXor: true, reservedKeywords: keywords, reservedDataTypes: dataTypes, diff --git a/src/languages/hive/hive.formatter.ts b/src/languages/hive/hive.formatter.ts index 4e6a9e1cf6..00677e5323 100644 --- a/src/languages/hive/hive.formatter.ts +++ b/src/languages/hive/hive.formatter.ts @@ -84,6 +84,8 @@ const reservedJoins = expandPhrases([ const reservedPhrases = expandPhrases(['{ROWS | RANGE} BETWEEN']); +const reservedDataTypePhrases = expandPhrases([]); + // https://cwiki.apache.org/confluence/display/Hive/LanguageManual export const hive: DialectOptions = { name: 'hive', @@ -92,7 +94,8 @@ export const hive: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases: reservedPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/mariadb/mariadb.formatter.ts b/src/languages/mariadb/mariadb.formatter.ts index d1384da022..d71877b5fb 100644 --- a/src/languages/mariadb/mariadb.formatter.ts +++ b/src/languages/mariadb/mariadb.formatter.ts @@ -260,13 +260,15 @@ const reservedJoins = expandPhrases([ 'STRAIGHT_JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON {UPDATE | DELETE} [SET NULL | SET DEFAULT]', 'CHARACTER SET', '{ROWS | RANGE} BETWEEN', 'IDENTIFIED BY', ]); +const reservedDataTypePhrases = expandPhrases([]); + // For reference: https://mariadb.com/kb/en/sql-statements-structure/ export const mariadb: DialectOptions = { name: 'mariadb', @@ -275,7 +277,8 @@ export const mariadb: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, supportsXor: true, reservedKeywords: keywords, reservedDataTypes: dataTypes, diff --git a/src/languages/mysql/mysql.formatter.ts b/src/languages/mysql/mysql.formatter.ts index 501f0707d8..c5e2ff840a 100644 --- a/src/languages/mysql/mysql.formatter.ts +++ b/src/languages/mysql/mysql.formatter.ts @@ -225,13 +225,15 @@ const reservedJoins = expandPhrases([ 'STRAIGHT_JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON {UPDATE | DELETE} [SET NULL]', 'CHARACTER SET', '{ROWS | RANGE} BETWEEN', 'IDENTIFIED BY', ]); +const reservedDataTypePhrases = expandPhrases([]); + // https://dev.mysql.com/doc/refman/8.0/en/ export const mysql: DialectOptions = { name: 'mysql', @@ -240,7 +242,8 @@ export const mysql: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, supportsXor: true, reservedKeywords: keywords, reservedDataTypes: dataTypes, diff --git a/src/languages/n1ql/n1ql.formatter.ts b/src/languages/n1ql/n1ql.formatter.ts index 1a101f71ec..f52afdad71 100644 --- a/src/languages/n1ql/n1ql.formatter.ts +++ b/src/languages/n1ql/n1ql.formatter.ts @@ -80,7 +80,9 @@ const reservedSetOperations = expandPhrases(['UNION [ALL]', 'EXCEPT [ALL]', 'INT const reservedJoins = expandPhrases(['JOIN', '{LEFT | RIGHT} [OUTER] JOIN', 'INNER JOIN']); -const reservedPhrases = expandPhrases(['{ROWS | RANGE | GROUPS} BETWEEN']); +const reservedKeywordPhrases = expandPhrases(['{ROWS | RANGE | GROUPS} BETWEEN']); + +const reservedDataTypePhrases = expandPhrases([]); // For reference: http://docs.couchbase.com.s3-website-us-west-1.amazonaws.com/server/6.0/n1ql/n1ql-language-reference/index.html export const n1ql: DialectOptions = { @@ -90,7 +92,8 @@ export const n1ql: DialectOptions = { reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, supportsXor: true, reservedKeywords: keywords, reservedDataTypes: dataTypes, diff --git a/src/languages/plsql/plsql.formatter.ts b/src/languages/plsql/plsql.formatter.ts index d4503f08c2..c0f79d41f8 100644 --- a/src/languages/plsql/plsql.formatter.ts +++ b/src/languages/plsql/plsql.formatter.ts @@ -78,12 +78,14 @@ const reservedJoins = expandPhrases([ '{CROSS | OUTER} APPLY', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON {UPDATE | DELETE} [SET NULL]', 'ON COMMIT', '{ROWS | RANGE} BETWEEN', ]); +const reservedDataTypePhrases = expandPhrases([]); + export const plsql: DialectOptions = { name: 'plsql', tokenizerOptions: { @@ -91,7 +93,8 @@ export const plsql: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, supportsXor: true, reservedKeywords: keywords, reservedDataTypes: dataTypes, diff --git a/src/languages/postgresql/postgresql.formatter.ts b/src/languages/postgresql/postgresql.formatter.ts index 3886924fe0..a479f0799c 100644 --- a/src/languages/postgresql/postgresql.formatter.ts +++ b/src/languages/postgresql/postgresql.formatter.ts @@ -248,21 +248,24 @@ const reservedJoins = expandPhrases([ 'NATURAL {LEFT | RIGHT | FULL} [OUTER] JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'PRIMARY KEY', 'GENERATED {ALWAYS | BY DEFAULT} AS IDENTITY', 'ON {UPDATE | DELETE} [NO ACTION | RESTRICT | CASCADE | SET NULL | SET DEFAULT]', 'DO {NOTHING | UPDATE}', 'AS MATERIALIZED', '{ROWS | RANGE | GROUPS} BETWEEN', - // https://www.postgresql.org/docs/current/datatype-datetime.html - '[TIMESTAMP | TIME] {WITH | WITHOUT} TIME ZONE', // comparison operator 'IS [NOT] DISTINCT FROM', 'NULLS {FIRST | LAST}', 'WITH ORDINALITY', ]); +const reservedDataTypePhrases = expandPhrases([ + // https://www.postgresql.org/docs/current/datatype-datetime.html + '[TIMESTAMP | TIME] {WITH | WITHOUT} TIME ZONE', +]); + // https://www.postgresql.org/docs/14/index.html export const postgresql: DialectOptions = { name: 'postgresql', @@ -271,7 +274,8 @@ export const postgresql: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/redshift/redshift.formatter.ts b/src/languages/redshift/redshift.formatter.ts index 64f0901d5e..3518ff5cd8 100644 --- a/src/languages/redshift/redshift.formatter.ts +++ b/src/languages/redshift/redshift.formatter.ts @@ -132,7 +132,7 @@ const reservedJoins = expandPhrases([ 'NATURAL {LEFT | RIGHT | FULL} [OUTER] JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ // https://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-data-conversion.html 'NULL AS', // https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_EXTERNAL_SCHEMA.html @@ -142,6 +142,8 @@ const reservedPhrases = expandPhrases([ '{ROWS | RANGE} BETWEEN', ]); +const reservedDataTypePhrases = expandPhrases([]); + // https://docs.aws.amazon.com/redshift/latest/dg/cm_chap_SQLCommandRef.html export const redshift: DialectOptions = { name: 'redshift', @@ -150,7 +152,8 @@ export const redshift: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/singlestoredb/singlestoredb.formatter.ts b/src/languages/singlestoredb/singlestoredb.formatter.ts index 819bccdd1b..5f4d236950 100644 --- a/src/languages/singlestoredb/singlestoredb.formatter.ts +++ b/src/languages/singlestoredb/singlestoredb.formatter.ts @@ -229,7 +229,7 @@ const reservedJoins = expandPhrases([ 'STRAIGHT_JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON DELETE', 'ON UPDATE', 'CHARACTER SET', @@ -237,6 +237,8 @@ const reservedPhrases = expandPhrases([ 'IDENTIFIED BY', ]); +const reservedDataTypePhrases = expandPhrases([]); + export const singlestoredb: DialectOptions = { name: 'singlestoredb', tokenizerOptions: { @@ -244,7 +246,8 @@ export const singlestoredb: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/snowflake/snowflake.formatter.ts b/src/languages/snowflake/snowflake.formatter.ts index 9f97cd9a98..bff8498f4e 100644 --- a/src/languages/snowflake/snowflake.formatter.ts +++ b/src/languages/snowflake/snowflake.formatter.ts @@ -292,11 +292,13 @@ const reservedJoins = expandPhrases([ '{CROSS | NATURAL} JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ '{ROWS | RANGE} BETWEEN', 'ON {UPDATE | DELETE} [SET NULL | SET DEFAULT]', ]); +const reservedDataTypePhrases = expandPhrases([]); + export const snowflake: DialectOptions = { name: 'snowflake', tokenizerOptions: { @@ -304,7 +306,8 @@ export const snowflake: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/spark/spark.formatter.ts b/src/languages/spark/spark.formatter.ts index 2bf1cce4c6..192675eadb 100644 --- a/src/languages/spark/spark.formatter.ts +++ b/src/languages/spark/spark.formatter.ts @@ -111,13 +111,15 @@ const reservedJoins = expandPhrases([ 'NATURAL [LEFT] {ANTI | SEMI} JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON DELETE', 'ON UPDATE', 'CURRENT ROW', '{ROWS | RANGE} BETWEEN', ]); +const reservedDataTypePhrases = expandPhrases([]); + // http://spark.apache.org/docs/latest/sql-programming-guide.html export const spark: DialectOptions = { name: 'spark', @@ -126,7 +128,8 @@ export const spark: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, supportsXor: true, reservedKeywords: keywords, reservedDataTypes: dataTypes, diff --git a/src/languages/sql/sql.formatter.ts b/src/languages/sql/sql.formatter.ts index 765f4c79af..ea9817333a 100644 --- a/src/languages/sql/sql.formatter.ts +++ b/src/languages/sql/sql.formatter.ts @@ -69,11 +69,13 @@ const reservedJoins = expandPhrases([ 'NATURAL {LEFT | RIGHT | FULL} [OUTER] JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON {UPDATE | DELETE} [SET NULL | SET DEFAULT]', '{ROWS | RANGE} BETWEEN', ]); +const reservedDataTypePhrases = expandPhrases([]); + export const sql: DialectOptions = { name: 'sql', tokenizerOptions: { @@ -81,7 +83,8 @@ export const sql: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/sqlite/sqlite.formatter.ts b/src/languages/sqlite/sqlite.formatter.ts index d33af5bef4..b2115ea4b6 100644 --- a/src/languages/sqlite/sqlite.formatter.ts +++ b/src/languages/sqlite/sqlite.formatter.ts @@ -62,12 +62,14 @@ const reservedJoins = expandPhrases([ 'NATURAL {LEFT | RIGHT | FULL} [OUTER] JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON {UPDATE | DELETE} [SET NULL | SET DEFAULT]', '{ROWS | RANGE | GROUPS} BETWEEN', 'DO UPDATE', ]); +const reservedDataTypePhrases = expandPhrases([]); + export const sqlite: DialectOptions = { name: 'sqlite', tokenizerOptions: { @@ -75,7 +77,8 @@ export const sqlite: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/tidb/tidb.formatter.ts b/src/languages/tidb/tidb.formatter.ts index abbb8a0301..395616f1e0 100644 --- a/src/languages/tidb/tidb.formatter.ts +++ b/src/languages/tidb/tidb.formatter.ts @@ -159,13 +159,15 @@ const reservedJoins = expandPhrases([ 'STRAIGHT_JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON {UPDATE | DELETE} [SET NULL]', 'CHARACTER SET', '{ROWS | RANGE} BETWEEN', 'IDENTIFIED BY', ]); +const reservedDataTypePhrases = expandPhrases([]); + // https://docs.pingcap.com/tidb/stable/basic-features export const tidb: DialectOptions = { name: 'tidb', @@ -174,7 +176,8 @@ export const tidb: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, supportsXor: true, reservedKeywords: keywords, reservedDataTypes: dataTypes, diff --git a/src/languages/transactsql/transactsql.formatter.ts b/src/languages/transactsql/transactsql.formatter.ts index e910ea0a6c..5aa3267791 100644 --- a/src/languages/transactsql/transactsql.formatter.ts +++ b/src/languages/transactsql/transactsql.formatter.ts @@ -228,11 +228,13 @@ const reservedJoins = expandPhrases([ '{CROSS | OUTER} APPLY', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ 'ON {UPDATE | DELETE} [SET NULL | SET DEFAULT]', '{ROWS | RANGE} BETWEEN', ]); +const reservedDataTypePhrases = expandPhrases([]); + // https://docs.microsoft.com/en-us/sql/t-sql/language-reference?view=sql-server-ver15 export const transactsql: DialectOptions = { name: 'transactsql', @@ -241,7 +243,8 @@ export const transactsql: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/languages/trino/trino.formatter.ts b/src/languages/trino/trino.formatter.ts index 68ac8009c7..73888f5c65 100644 --- a/src/languages/trino/trino.formatter.ts +++ b/src/languages/trino/trino.formatter.ts @@ -119,12 +119,14 @@ const reservedJoins = expandPhrases([ 'NATURAL {LEFT | RIGHT | FULL} [OUTER] JOIN', ]); -const reservedPhrases = expandPhrases([ +const reservedKeywordPhrases = expandPhrases([ '{ROWS | RANGE | GROUPS} BETWEEN', // comparison operator 'IS [NOT] DISTINCT FROM', ]); +const reservedDataTypePhrases = expandPhrases([]); + export const trino: DialectOptions = { name: 'trino', tokenizerOptions: { @@ -132,7 +134,8 @@ export const trino: DialectOptions = { reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, - reservedPhrases, + reservedKeywordPhrases, + reservedDataTypePhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, diff --git a/src/lexer/Tokenizer.ts b/src/lexer/Tokenizer.ts index 1e5fda99e3..9361ced2a1 100644 --- a/src/lexer/Tokenizer.ts +++ b/src/lexer/Tokenizer.ts @@ -54,11 +54,16 @@ export default class Tokenizer { ? /(?:0x[0-9a-fA-F_]+|0b[01_]+|(?:-\s*)?(?:[0-9_]*\.[0-9_]+|[0-9_]+(?:\.[0-9_]*)?)(?:[eE][-+]?[0-9_]+(?:\.[0-9_]+)?)?)(?![\w\p{Alphabetic}])/uy : /(?:0x[0-9a-fA-F]+|0b[01]+|(?:-\s*)?(?:[0-9]*\.[0-9]+|[0-9]+(?:\.[0-9]*)?)(?:[eE][-+]?[0-9]+(?:\.[0-9]+)?)?)(?![\w\p{Alphabetic}])/uy, }, - // RESERVED_PHRASE is matched before all other keyword tokens + // RESERVED_KEYWORD_PHRASE and RESERVED_DATA_TYPE_PHRASE is matched before all other keyword tokens // to e.g. prioritize matching "TIMESTAMP WITH TIME ZONE" phrase over "WITH" clause. { - type: TokenType.RESERVED_PHRASE, - regex: regex.reservedWord(cfg.reservedPhrases ?? [], cfg.identChars), + type: TokenType.RESERVED_KEYWORD_PHRASE, + regex: regex.reservedWord(cfg.reservedKeywordPhrases ?? [], cfg.identChars), + text: toCanonical, + }, + { + type: TokenType.RESERVED_DATA_TYPE_PHRASE, + regex: regex.reservedWord(cfg.reservedDataTypePhrases ?? [], cfg.identChars), text: toCanonical, }, { diff --git a/src/lexer/TokenizerOptions.ts b/src/lexer/TokenizerOptions.ts index 3d71033f85..51da900f50 100644 --- a/src/lexer/TokenizerOptions.ts +++ b/src/lexer/TokenizerOptions.ts @@ -66,7 +66,10 @@ export interface TokenizerOptions { reservedJoins: string[]; // These are essentially multi-word sequences of keywords, // that we prioritize over all other keywords (RESERVED_* tokens) - reservedPhrases?: string[]; + reservedKeywordPhrases?: string[]; + // These are essentially multi-word sequences of keywords, + // that we prioritize over all other keywords (RESERVED_* tokens) + reservedDataTypePhrases?: string[]; // built in function names reservedFunctionNames: string[]; // data types diff --git a/src/lexer/token.ts b/src/lexer/token.ts index aaef9ee987..345a46e220 100644 --- a/src/lexer/token.ts +++ b/src/lexer/token.ts @@ -8,7 +8,8 @@ export enum TokenType { RESERVED_PARAMETERIZED_DATA_TYPE = 'RESERVED_PARAMETERIZED_DATA_TYPE', RESERVED_KEYWORD = 'RESERVED_KEYWORD', RESERVED_FUNCTION_NAME = 'RESERVED_FUNCTION_NAME', - RESERVED_PHRASE = 'RESERVED_PHRASE', + RESERVED_KEYWORD_PHRASE = 'RESERVED_KEYWORD_PHRASE', + RESERVED_DATA_TYPE_PHRASE = 'RESERVED_DATA_TYPE_PHRASE', RESERVED_SET_OPERATION = 'RESERVED_SET_OPERATION', RESERVED_CLAUSE = 'RESERVED_CLAUSE', RESERVED_SELECT = 'RESERVED_SELECT', @@ -90,7 +91,8 @@ export const isReserved = (type: TokenType): boolean => type === TokenType.RESERVED_DATA_TYPE || type === TokenType.RESERVED_KEYWORD || type === TokenType.RESERVED_FUNCTION_NAME || - type === TokenType.RESERVED_PHRASE || + type === TokenType.RESERVED_KEYWORD_PHRASE || + type === TokenType.RESERVED_DATA_TYPE_PHRASE || type === TokenType.RESERVED_CLAUSE || type === TokenType.RESERVED_SELECT || type === TokenType.RESERVED_SET_OPERATION || diff --git a/src/parser/grammar.ne b/src/parser/grammar.ne index 0b3bd57c78..6b4cef1487 100644 --- a/src/parser/grammar.ne +++ b/src/parser/grammar.ne @@ -332,13 +332,14 @@ literal -> keyword -> ( %RESERVED_KEYWORD - | %RESERVED_PHRASE + | %RESERVED_KEYWORD_PHRASE | %RESERVED_JOIN ) {% ([[token]]) => toKeywordNode(token) %} data_type -> - ( %RESERVED_DATA_TYPE ) {% + ( %RESERVED_DATA_TYPE + | %RESERVED_DATA_TYPE_PHRASE ) {% ([[token]]) => toDataTypeNode(token) %} data_type -> %RESERVED_PARAMETERIZED_DATA_TYPE _ parenthesis {% diff --git a/test/duckdb.test.ts b/test/duckdb.test.ts index b6ea26583f..7b68a68b47 100644 --- a/test/duckdb.test.ts +++ b/test/duckdb.test.ts @@ -195,6 +195,22 @@ describe('DuckDBFormatter', () => { `); }); + it('formats TIMESTAMP WITH TIME ZONE syntax lowercase', () => { + expect( + format( + ` + CREATE TABLE time_table (id INT PRIMARY KEY NOT NULL, + created_at TIMESTAMP WITH TIME ZONE);`, + { dataTypeCase: 'lower' } + ) + ).toBe(dedent` + CREATE TABLE time_table ( + id int PRIMARY KEY NOT NULL, + created_at timestamp with time zone + ); + `); + }); + it('formats JSON data type', () => { expect( format(`CREATE TABLE foo (bar json, baz json);`, { diff --git a/test/postgresql.test.ts b/test/postgresql.test.ts index cbb8317f14..510ef9cb61 100644 --- a/test/postgresql.test.ts +++ b/test/postgresql.test.ts @@ -157,6 +157,27 @@ describe('PostgreSqlFormatter', () => { `); }); + // Format timestamp as datatype + it('formats TIMESTAMP WITH TIME ZONE syntax', () => { + expect( + format( + ` + CREATE TABLE time_table (id INT, + created_at TIMESTAMP WITH TIME ZONE, + deleted_at TIME WITH TIME ZONE, + modified_at TIMESTAMP(0) WITH TIME ZONE);`, + { dataTypeCase: 'lower' } + ) + ).toBe(dedent` + CREATE TABLE time_table ( + id int, + created_at timestamp with time zone, + deleted_at time with time zone, + modified_at timestamp(0) with time zone + ); + `); + }); + // Regression test for issues #391 and #618 it('formats TIMESTAMP WITH TIME ZONE syntax', () => { expect(