From bdf3be927cd2065be8fbf93e7ba516448d99a22a Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Mon, 27 Nov 2023 18:51:29 +0100 Subject: [PATCH 01/15] Identify PostgreSQL data types, add docs --- docs/datatypeCase.md | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/datatypeCase.md diff --git a/docs/datatypeCase.md b/docs/datatypeCase.md new file mode 100644 index 0000000000..fb89b8eaa2 --- /dev/null +++ b/docs/datatypeCase.md @@ -0,0 +1,52 @@ +# datatypeCase (experimental) + +Converts datatypes to upper- or lowercase. + +This option doesn't yet support all types of data types: + +- multi-word data types with non-datatype keywords like `timestamp with time zone` are not fully supported - the `WITH` will be cased as a normal keyword + +## Options + +- `"preserve"` (default) preserves the original case. +- `"upper"` converts to uppercase. +- `"lower"` converts to lowercase. + +### preserve + +```sql +CREATE TABLE + users ( + id InTeGeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + first_name VarChaR(30) NOT NULL, + bio teXT, + is_email_verified BooL NOT NULL DEFAULT FALSE, + created_timestamp timestamPtz NOT NULL DEFAULT NOW() + ) +``` + +### upper + +```sql +CREATE TABLE + users ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + first_name VARCHAR(30) NOT NULL, + bio TEXT, + is_email_verified BOOL NOT NULL DEFAULT FALSE, + created_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW() + ) +``` + +### lower + +```sql +CREATE TABLE + users ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + first_name varchar(30) NOT NULL, + bio text, + is_email_verified bool NOT NULL DEFAULT FALSE, + created_timestamp timestamptz NOT NULL DEFAULT NOW() + ) +``` From c6fb0b64e72ab8c3d209bc6505213bc4493c3528 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Wed, 29 Nov 2023 13:08:57 +0100 Subject: [PATCH 02/15] Add docs for functionCase, rename to dataTypeCase --- docs/{datatypeCase.md => dataTypeCase.md} | 12 +++--- docs/functionCase.md | 48 +++++++++++++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) rename docs/{datatypeCase.md => dataTypeCase.md} (70%) create mode 100644 docs/functionCase.md diff --git a/docs/datatypeCase.md b/docs/dataTypeCase.md similarity index 70% rename from docs/datatypeCase.md rename to docs/dataTypeCase.md index fb89b8eaa2..f61802d20f 100644 --- a/docs/datatypeCase.md +++ b/docs/dataTypeCase.md @@ -1,10 +1,8 @@ -# datatypeCase (experimental) +# dataTypeCase (experimental) -Converts datatypes to upper- or lowercase. +Converts data types to upper- or lowercase. -This option doesn't yet support all types of data types: - -- multi-word data types with non-datatype keywords like `timestamp with time zone` are not fully supported - the `WITH` will be cased as a normal keyword +Note: Casing of function names like `VARCHAR(30)` are not modified - instead rely on the `functionCase` option for this. ## Options @@ -18,7 +16,7 @@ This option doesn't yet support all types of data types: CREATE TABLE users ( id InTeGeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - first_name VarChaR(30) NOT NULL, + first_name VARCHAR(30) NOT NULL, bio teXT, is_email_verified BooL NOT NULL DEFAULT FALSE, created_timestamp timestamPtz NOT NULL DEFAULT NOW() @@ -44,7 +42,7 @@ CREATE TABLE CREATE TABLE users ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - first_name varchar(30) NOT NULL, + first_name VARCHAR(30) NOT NULL, bio text, is_email_verified bool NOT NULL DEFAULT FALSE, created_timestamp timestamptz NOT NULL DEFAULT NOW() diff --git a/docs/functionCase.md b/docs/functionCase.md new file mode 100644 index 0000000000..e1436527a3 --- /dev/null +++ b/docs/functionCase.md @@ -0,0 +1,48 @@ +# functionCase (experimental) + +Converts functions to upper- or lowercase. + +## Options + +- `"preserve"` (default) preserves the original case. +- `"upper"` converts to uppercase. +- `"lower"` converts to lowercase. + +### preserve + +```sql +CREATE TABLE + users ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + first_name VarChaR(30) NOT NULL, + bio TEXT, + is_email_verified BOOL NOT NULL DEFAULT FALSE, + created_timestamp TIMESTAMPTZ NOT NULL DEFAULT NoW() + ) +``` + +### upper + +```sql +CREATE TABLE + users ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + first_name VARCHAR(30) NOT NULL, + bio TEXT, + is_email_verified BOOL NOT NULL DEFAULT FALSE, + created_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW() + ) +``` + +### lower + +```sql +CREATE TABLE + users ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + first_name varchar(30) NOT NULL, + bio TEXT, + is_email_verified BOOL NOT NULL DEFAULT FALSE, + created_timestamp TIMESTAMPTZ NOT NULL DEFAULT now() + ) +``` From 2ba9d0a43c0e3595676e0b56b4e8059901357202 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Wed, 29 Nov 2023 17:59:34 +0100 Subject: [PATCH 03/15] Add caveat about dataTypeCase language support --- docs/dataTypeCase.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/dataTypeCase.md b/docs/dataTypeCase.md index f61802d20f..4bc336e0a4 100644 --- a/docs/dataTypeCase.md +++ b/docs/dataTypeCase.md @@ -2,6 +2,8 @@ Converts data types to upper- or lowercase. +Caveat: Only supported by languages which export `dataTypes` from their `.keywords.ts` file (eg. `bigquery`, `postgresql` and others) + Note: Casing of function names like `VARCHAR(30)` are not modified - instead rely on the `functionCase` option for this. ## Options From 10b4451096814d46d62dc5245a4dcb030d65ed28 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Thu, 30 Nov 2023 17:43:15 +0100 Subject: [PATCH 04/15] Move more data types to separate constant --- src/languages/bigquery/bigquery.formatter.ts | 5 +- src/languages/bigquery/bigquery.keywords.ts | 2 - src/languages/db2i/db2i.formatter.ts | 3 +- src/languages/db2i/db2i.keywords.ts | 47 ++++++++---- src/languages/hive/hive.formatter.ts | 3 +- src/languages/hive/hive.keywords.ts | 42 +++++----- src/languages/mariadb/mariadb.formatter.ts | 3 +- src/languages/mariadb/mariadb.keywords.ts | 76 ++++++++++--------- src/languages/mysql/mysql.formatter.ts | 3 +- src/languages/mysql/mysql.keywords.ts | 74 +++++++++--------- src/languages/n1ql/n1ql.formatter.ts | 3 +- src/languages/n1ql/n1ql.keywords.ts | 16 ++-- src/languages/plsql/plsql.formatter.ts | 3 +- src/languages/plsql/plsql.keywords.ts | 47 +++++++----- .../postgresql/postgresql.formatter.ts | 5 +- .../postgresql/postgresql.keywords.ts | 2 +- src/languages/redshift/redshift.formatter.ts | 5 +- src/languages/redshift/redshift.keywords.ts | 2 +- .../snowflake/snowflake.formatter.ts | 5 +- src/languages/spark/spark.formatter.ts | 3 +- src/languages/spark/spark.keywords.ts | 46 ++++++++--- src/languages/sql/sql.formatter.ts | 3 +- src/languages/sql/sql.keywords.ts | 66 ++++++++-------- src/languages/sqlite/sqlite.formatter.ts | 3 +- src/languages/sqlite/sqlite.keywords.ts | 14 +++- src/languages/trino/trino.formatter.ts | 5 +- src/languages/trino/trino.keywords.ts | 1 - 27 files changed, 284 insertions(+), 203 deletions(-) diff --git a/src/languages/bigquery/bigquery.formatter.ts b/src/languages/bigquery/bigquery.formatter.ts index d162cfbf60..06ef621b1a 100644 --- a/src/languages/bigquery/bigquery.formatter.ts +++ b/src/languages/bigquery/bigquery.formatter.ts @@ -163,9 +163,8 @@ export const bigquery: DialectOptions = { reservedSetOperations, reservedJoins, reservedPhrases, - reservedKeywords: - // Temporary, will be replaced by reservedDataTypes - [...new Set(keywords.concat(dataTypes))], + reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, extraParens: ['[]'], stringTypes: [ diff --git a/src/languages/bigquery/bigquery.keywords.ts b/src/languages/bigquery/bigquery.keywords.ts index a1a852ba3c..6059c4e445 100644 --- a/src/languages/bigquery/bigquery.keywords.ts +++ b/src/languages/bigquery/bigquery.keywords.ts @@ -3,7 +3,6 @@ export const keywords: string[] = [ 'ALL', 'AND', 'ANY', - 'ARRAY', 'AS', 'ASC', 'ASSERT_ROWS_MODIFIED', @@ -80,7 +79,6 @@ export const keywords: string[] = [ 'SELECT', 'SET', 'SOME', - 'STRUCT', 'TABLE', 'TABLESAMPLE', 'THEN', diff --git a/src/languages/db2i/db2i.formatter.ts b/src/languages/db2i/db2i.formatter.ts index 262deeb920..fc94a422a8 100644 --- a/src/languages/db2i/db2i.formatter.ts +++ b/src/languages/db2i/db2i.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { functions } from './db2i.functions.js'; -import { keywords } from './db2i.keywords.js'; +import { dataTypes, keywords } from './db2i.keywords.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT]']); @@ -162,6 +162,7 @@ export const db2i: DialectOptions = { reservedJoins, reservedPhrases, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, nestedBlockComments: true, extraParens: ['[]'], diff --git a/src/languages/db2i/db2i.keywords.ts b/src/languages/db2i/db2i.keywords.ts index ace8fd82b8..9e54732fca 100644 --- a/src/languages/db2i/db2i.keywords.ts +++ b/src/languages/db2i/db2i.keywords.ts @@ -32,10 +32,7 @@ export const keywords: string[] = [ 'BEFORE', 'BEGIN', 'BETWEEN', - 'BINARY', 'BIND', - 'BIT', - 'BOOLEAN', 'BSON', 'BUFFERPOOL', 'BY', @@ -45,9 +42,6 @@ export const keywords: string[] = [ 'CARDINALITY', 'CASE', 'CAST', - 'CCSID', - 'CHAR', - 'CHARACTER', 'CHECK', 'CL', 'CLOSE', @@ -90,11 +84,9 @@ export const keywords: string[] = [ 'CURRENT_USER', 'CURSOR', 'CYCLE', - 'DATA', 'DATABASE', 'DATAPARTITIONNAME', 'DATAPARTITIONNUM', - 'DATE', 'DAY', 'DAYS', 'DB2GENERAL', @@ -127,7 +119,6 @@ export const keywords: string[] = [ 'DISTINCT', 'DO', 'DOCUMENT', - 'DOUBLE', 'DROP', 'DYNAMIC', 'EACH', @@ -177,7 +168,6 @@ export const keywords: string[] = [ 'GO', 'GOTO', 'GRANT', - 'GRAPHIC', 'GROUP', 'HANDLER', 'HASH', @@ -256,7 +246,6 @@ export const keywords: string[] = [ 'LOCKSIZE', 'LOG', 'LOGGED', - 'LONG', 'LOOP', 'MAINTAINED', 'MASK', @@ -436,8 +425,6 @@ export const keywords: string[] = [ 'TAG', 'THEN', 'THREADSAFE', - 'TIME', - 'TIMESTAMP', 'TO', 'TRANSACTION', 'TRANSFER', @@ -508,3 +495,37 @@ export const keywords: string[] = [ 'YES', 'ZONE', ]; + +export const dataTypes: string[] = [ + // https://www.ibm.com/docs/en/i/7.2?topic=iaodsd-odbc-data-types-how-they-correspond-db2-i-database-types + 'BIGINT', + 'BINARY', + 'BIT', + 'BLOB', + 'BOOLEAN', + 'CCSID', + 'CHAR', + 'CHARACTER', + 'CLOB', + 'DATA', + 'DATALINK', + 'DATE', + 'DBCLOB', + 'DECFLOAT', + 'DECIMAL', + 'DOUBLE', + 'FLOAT', + 'GRAPHIC', + 'INTEGER', + 'LONG', + 'NUMERIC', + 'REAL', + 'ROWID', + 'SMALLINT', + 'TIME', + 'TIMESTAMP', + 'VARBINARY', + 'VARCHAR', + 'VARGRAPHIC', + 'XML', +]; diff --git a/src/languages/hive/hive.formatter.ts b/src/languages/hive/hive.formatter.ts index 9a1c7b358e..eabe87a90e 100644 --- a/src/languages/hive/hive.formatter.ts +++ b/src/languages/hive/hive.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { functions } from './hive.functions.js'; -import { keywords } from './hive.keywords.js'; +import { dataTypes, keywords } from './hive.keywords.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT]']); @@ -91,6 +91,7 @@ export const hive: DialectOptions = { reservedJoins, reservedPhrases, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, extraParens: ['[]'], stringTypes: ['""-bs', "''-bs"], diff --git a/src/languages/hive/hive.keywords.ts b/src/languages/hive/hive.keywords.ts index a747ee7641..27c47287cd 100644 --- a/src/languages/hive/hive.keywords.ts +++ b/src/languages/hive/hive.keywords.ts @@ -126,7 +126,6 @@ export const keywords: string[] = [ 'STORED', 'STREAMTABLE', 'STRING', - 'STRUCT', 'TABLES', 'TBLPROPERTIES', 'TEMPORARY', @@ -186,18 +185,13 @@ export const keywords: string[] = [ 'ALL', 'ALTER', 'AND', - 'ARRAY', 'AS', 'AUTHORIZATION', 'BETWEEN', - 'BIGINT', - 'BINARY', - 'BOOLEAN', 'BOTH', 'BY', 'CASE', 'CAST', - 'CHAR', 'COLUMN', 'CONF', 'CREATE', @@ -208,12 +202,9 @@ export const keywords: string[] = [ 'CURRENT_TIMESTAMP', 'CURSOR', 'DATABASE', - 'DATE', - 'DECIMAL', 'DELETE', 'DESCRIBE', 'DISTINCT', - 'DOUBLE', 'DROP', 'ELSE', 'END', @@ -223,7 +214,6 @@ export const keywords: string[] = [ 'EXTERNAL', 'FALSE', 'FETCH', - 'FLOAT', 'FOLLOWING', 'FOR', 'FROM', @@ -238,9 +228,7 @@ export const keywords: string[] = [ 'IN', 'INNER', 'INSERT', - 'INT', 'INTERSECT', - 'INTERVAL', 'INTO', 'IS', 'JOIN', @@ -250,7 +238,6 @@ export const keywords: string[] = [ 'LIKE', 'LOCAL', 'MACRO', - 'MAP', 'MORE', 'NONE', 'NOT', @@ -278,11 +265,9 @@ export const keywords: string[] = [ 'ROWS', 'SELECT', 'SET', - 'SMALLINT', 'TABLE', 'TABLESAMPLE', 'THEN', - 'TIMESTAMP', 'TO', 'TRANSFORM', 'TRIGGER', @@ -296,7 +281,6 @@ export const keywords: string[] = [ 'USING', 'UTC_TMESTAMP', 'VALUES', - 'VARCHAR', 'WHEN', 'WHERE', 'WINDOW', @@ -315,11 +299,8 @@ export const keywords: string[] = [ 'DAYOFWEEK', 'EXTRACT', 'FLOOR', - 'INTEGER', - 'PRECISION', 'VIEWS', 'TIME', - 'NUMERIC', 'SYNC', // fileTypes @@ -335,3 +316,26 @@ export const keywords: string[] = [ 'INPUTFORMAT', 'OUTPUTFORMAT', ]; + +export const dataTypes: string[] = [ + // https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Types + 'ARRAY', + 'BIGINT', + 'BINARY', + 'BOOLEAN', + 'CHAR', + 'DATE', + 'DECIMAL', + 'DOUBLE', + 'FLOAT', + 'INT', + 'INTEGER', + 'INTERVAL', + 'MAP', + 'NUMERIC', + 'PRECISION', + 'SMALLINT', + 'STRUCT', + 'TIMESTAMP', + 'VARCHAR', +]; diff --git a/src/languages/mariadb/mariadb.formatter.ts b/src/languages/mariadb/mariadb.formatter.ts index 6007a2279f..4e0a412cfa 100644 --- a/src/languages/mariadb/mariadb.formatter.ts +++ b/src/languages/mariadb/mariadb.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { postProcess } from './likeMariaDb.js'; -import { keywords } from './mariadb.keywords.js'; +import { dataTypes, keywords } from './mariadb.keywords.js'; import { functions } from './mariadb.functions.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT | DISTINCTROW]']); @@ -274,6 +274,7 @@ export const mariadb: DialectOptions = { reservedPhrases, supportsXor: true, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, // TODO: support _ char set prefixes such as _utf8, _latin1, _binary, _utf8mb4, etc. stringTypes: [ diff --git a/src/languages/mariadb/mariadb.keywords.ts b/src/languages/mariadb/mariadb.keywords.ts index b8fdfa6846..d9fea8da11 100644 --- a/src/languages/mariadb/mariadb.keywords.ts +++ b/src/languages/mariadb/mariadb.keywords.ts @@ -11,17 +11,12 @@ export const keywords: string[] = [ 'ASENSITIVE', 'BEFORE', 'BETWEEN', - 'BIGINT', - 'BINARY', - 'BLOB', 'BOTH', 'BY', 'CALL', 'CASCADE', 'CASE', 'CHANGE', - 'CHAR', - 'CHARACTER', 'CHECK', 'COLLATE', 'COLUMN', @@ -43,8 +38,6 @@ export const keywords: string[] = [ 'DAY_MICROSECOND', 'DAY_MINUTE', 'DAY_SECOND', - 'DEC', - 'DECIMAL', 'DECLARE', 'DEFAULT', 'DELAYED', @@ -57,7 +50,6 @@ export const keywords: string[] = [ 'DISTINCTROW', 'DIV', 'DO_DOMAIN_IDS', - 'DOUBLE', 'DROP', 'DUAL', 'EACH', @@ -71,9 +63,6 @@ export const keywords: string[] = [ 'EXPLAIN', 'FALSE', 'FETCH', - 'FLOAT', - 'FLOAT4', - 'FLOAT8', 'FOR', 'FORCE', 'FOREIGN', @@ -98,13 +87,6 @@ export const keywords: string[] = [ 'INOUT', 'INSENSITIVE', 'INSERT', - 'INT', - 'INT1', - 'INT2', - 'INT3', - 'INT4', - 'INT8', - 'INTEGER', 'INTERSECT', 'INTERVAL', 'INTO', @@ -125,19 +107,12 @@ export const keywords: string[] = [ 'LOCALTIME', 'LOCALTIMESTAMP', 'LOCK', - 'LONG', - 'LONGBLOB', - 'LONGTEXT', 'LOOP', 'LOW_PRIORITY', 'MASTER_HEARTBEAT_PERIOD', 'MASTER_SSL_VERIFY_SERVER_CERT', 'MATCH', 'MAXVALUE', - 'MEDIUMBLOB', - 'MEDIUMINT', - 'MEDIUMTEXT', - 'MIDDLEINT', 'MINUTE_MICROSECOND', 'MINUTE_SECOND', 'MOD', @@ -146,7 +121,6 @@ export const keywords: string[] = [ 'NOT', 'NO_WRITE_TO_BINLOG', 'NULL', - 'NUMERIC', 'OFFSET', 'ON', 'OPTIMIZE', @@ -162,7 +136,6 @@ export const keywords: string[] = [ 'PARSE_VCOL_EXPR', 'PARTITION', 'POSITION', - 'PRECISION', 'PRIMARY', 'PROCEDURE', 'PURGE', @@ -170,7 +143,6 @@ export const keywords: string[] = [ 'READ', 'READS', 'READ_WRITE', - 'REAL', 'RECURSIVE', 'REF_SYSTEM_ID', 'REFERENCES', @@ -199,7 +171,6 @@ export const keywords: string[] = [ 'SHOW', 'SIGNAL', 'SLOW', - 'SMALLINT', 'SPATIAL', 'SPECIFIC', 'SQL', @@ -218,9 +189,6 @@ export const keywords: string[] = [ 'TABLE', 'TERMINATED', 'THEN', - 'TINYBLOB', - 'TINYINT', - 'TINYTEXT', 'TO', 'TRAILING', 'TRIGGER', @@ -238,10 +206,6 @@ export const keywords: string[] = [ 'UTC_TIME', 'UTC_TIMESTAMP', 'VALUES', - 'VARBINARY', - 'VARCHAR', - 'VARCHARACTER', - 'VARYING', 'WHEN', 'WHERE', 'WHILE', @@ -252,3 +216,43 @@ export const keywords: string[] = [ 'YEAR_MONTH', 'ZEROFILL', ]; + +export const dataTypes: string[] = [ + // https://mariadb.com/kb/en/data-types/ + 'BIGINT', + 'BINARY', + 'BLOB', + 'CHAR', + 'CHARACTER', + 'DEC', + 'DECIMAL', + 'DOUBLE', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'NUMERIC', + 'PRECISION', + 'REAL', + 'SMALLINT', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', +]; diff --git a/src/languages/mysql/mysql.formatter.ts b/src/languages/mysql/mysql.formatter.ts index 0e40291c03..3b993c08ba 100644 --- a/src/languages/mysql/mysql.formatter.ts +++ b/src/languages/mysql/mysql.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { postProcess } from '../mariadb/likeMariaDb.js'; -import { keywords } from './mysql.keywords.js'; +import { dataTypes, keywords } from './mysql.keywords.js'; import { functions } from './mysql.functions.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT | DISTINCTROW]']); @@ -241,6 +241,7 @@ export const mysql: DialectOptions = { reservedPhrases, supportsXor: true, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, // TODO: support _ char set prefixes such as _utf8, _latin1, _binary, _utf8mb4, etc. stringTypes: [ diff --git a/src/languages/mysql/mysql.keywords.ts b/src/languages/mysql/mysql.keywords.ts index 0bb84f0e05..bbbf5be7f9 100644 --- a/src/languages/mysql/mysql.keywords.ts +++ b/src/languages/mysql/mysql.keywords.ts @@ -11,17 +11,12 @@ export const keywords: string[] = [ 'ASENSITIVE', // (R) 'BEFORE', // (R) 'BETWEEN', // (R) - 'BIGINT', // (R) - 'BINARY', // (R) - 'BLOB', // (R) 'BOTH', // (R) 'BY', // (R) 'CALL', // (R) 'CASCADE', // (R) 'CASE', // (R) 'CHANGE', // (R) - 'CHAR', // (R) - 'CHARACTER', // (R) 'CHECK', // (R) 'COLLATE', // (R) 'COLUMN', // (R) @@ -44,8 +39,6 @@ export const keywords: string[] = [ 'DAY_MICROSECOND', // (R) 'DAY_MINUTE', // (R) 'DAY_SECOND', // (R) - 'DEC', // (R) - 'DECIMAL', // (R) 'DECLARE', // (R) 'DEFAULT', // (R) 'DELAYED', // (R) @@ -57,7 +50,6 @@ export const keywords: string[] = [ 'DISTINCT', // (R) 'DISTINCTROW', // (R) 'DIV', // (R) - 'DOUBLE', // (R) 'DROP', // (R) 'DUAL', // (R) 'EACH', // (R) @@ -73,9 +65,6 @@ export const keywords: string[] = [ 'FALSE', // (R) 'FETCH', // (R) 'FIRST_VALUE', // (R) - 'FLOAT', // (R) - 'FLOAT4', // (R) - 'FLOAT8', // (R) 'FOR', // (R) 'FORCE', // (R) 'FOREIGN', // (R) @@ -103,13 +92,6 @@ export const keywords: string[] = [ 'INSENSITIVE', // (R) 'INSERT', // (R) 'IN', // <-- moved over from functions - 'INT', // (R) - 'INT1', // (R) - 'INT2', // (R) - 'INT3', // (R) - 'INT4', // (R) - 'INT8', // (R) - 'INTEGER', // (R) 'INTERSECT', // (R) 'INTERVAL', // (R) 'INTO', // (R) @@ -138,18 +120,12 @@ export const keywords: string[] = [ 'LOCALTIMESTAMP', // (R) 'LOCK', // (R) 'LONG', // (R) - 'LONGBLOB', // (R) - 'LONGTEXT', // (R) 'LOOP', // (R) 'LOW_PRIORITY', // (R) 'MASTER_BIND', // (R) 'MASTER_SSL_VERIFY_SERVER_CERT', // (R) 'MATCH', // (R) 'MAXVALUE', // (R) - 'MEDIUMBLOB', // (R) - 'MEDIUMINT', // (R) - 'MEDIUMTEXT', // (R) - 'MIDDLEINT', // (R) 'MINUTE_MICROSECOND', // (R) 'MINUTE_SECOND', // (R) 'MOD', // (R) @@ -160,7 +136,6 @@ export const keywords: string[] = [ 'NTH_VALUE', // (R) 'NTILE', // (R) 'NULL', // (R) - 'NUMERIC', // (R) 'OF', // (R) 'ON', // (R) 'OPTIMIZE', // (R) @@ -175,7 +150,6 @@ export const keywords: string[] = [ 'OVER', // (R) 'PARTITION', // (R) 'PERCENT_RANK', // (R) - 'PRECISION', // (R) 'PRIMARY', // (R) 'PROCEDURE', // (R) 'PURGE', // (R) @@ -184,7 +158,6 @@ export const keywords: string[] = [ 'READ', // (R) 'READS', // (R) 'READ_WRITE', // (R) - 'REAL', // (R) 'RECURSIVE', // (R) 'REFERENCES', // (R) 'REGEXP', // (R) @@ -211,7 +184,6 @@ export const keywords: string[] = [ 'SET', // (R) 'SHOW', // (R) 'SIGNAL', // (R) - 'SMALLINT', // (R) 'SPATIAL', // (R) 'SPECIFIC', // (R) 'SQL', // (R) @@ -229,9 +201,6 @@ export const keywords: string[] = [ 'TABLE', // (R) 'TERMINATED', // (R) 'THEN', // (R) - 'TINYBLOB', // (R) - 'TINYINT', // (R) - 'TINYTEXT', // (R) 'TO', // (R) 'TRAILING', // (R) 'TRIGGER', // (R) @@ -249,10 +218,6 @@ export const keywords: string[] = [ 'UTC_TIME', // (R) 'UTC_TIMESTAMP', // (R) 'VALUES', // (R) - 'VARBINARY', // (R) - 'VARCHAR', // (R) - 'VARCHARACTER', // (R) - 'VARYING', // (R) 'VIRTUAL', // (R) 'WHEN', // (R) 'WHERE', // (R) @@ -264,3 +229,42 @@ export const keywords: string[] = [ 'YEAR_MONTH', // (R) 'ZEROFILL', // (R) ]; + +export const dataTypes: string[] = [ + // https://dev.mysql.com/doc/refman/8.0/en/data-types.html + 'BIGINT', // (R) + 'BINARY', // (R) + 'BLOB', // (R) + 'CHAR', // (R) + 'CHARACTER', // (R) + 'DEC', // (R) + 'DECIMAL', // (R) + 'DOUBLE', // (R) + 'FLOAT', // (R) + 'FLOAT4', // (R) + 'FLOAT8', // (R) + 'INT', // (R) + 'INT1', // (R) + 'INT2', // (R) + 'INT3', // (R) + 'INT4', // (R) + 'INT8', // (R) + 'INTEGER', // (R) + 'LONGBLOB', // (R) + 'LONGTEXT', // (R) + 'MEDIUMBLOB', // (R) + 'MEDIUMINT', // (R) + 'MEDIUMTEXT', // (R) + 'MIDDLEINT', // (R) + 'NUMERIC', // (R) + 'PRECISION', // (R) + 'REAL', // (R) + 'SMALLINT', // (R) + 'TINYBLOB', // (R) + 'TINYINT', // (R) + 'TINYTEXT', // (R) + 'VARBINARY', // (R) + 'VARCHAR', // (R) + 'VARCHARACTER', // (R) + 'VARYING', // (R) +]; diff --git a/src/languages/n1ql/n1ql.formatter.ts b/src/languages/n1ql/n1ql.formatter.ts index 7d71195a90..1a101f71ec 100644 --- a/src/languages/n1ql/n1ql.formatter.ts +++ b/src/languages/n1ql/n1ql.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { functions } from './n1ql.functions.js'; -import { keywords } from './n1ql.keywords.js'; +import { dataTypes, keywords } from './n1ql.keywords.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT]']); @@ -93,6 +93,7 @@ export const n1ql: DialectOptions = { reservedPhrases, supportsXor: true, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, // NOTE: single quotes are actually not supported in N1QL, // but we support them anyway as all other SQL dialects do, diff --git a/src/languages/n1ql/n1ql.keywords.ts b/src/languages/n1ql/n1ql.keywords.ts index 825739a7a4..43f083ee44 100644 --- a/src/languages/n1ql/n1ql.keywords.ts +++ b/src/languages/n1ql/n1ql.keywords.ts @@ -6,14 +6,11 @@ export const keywords: string[] = [ 'ANALYZE', 'AND', 'ANY', - 'ARRAY', 'AS', 'ASC', 'AT', 'BEGIN', 'BETWEEN', - 'BINARY', - 'BOOLEAN', 'BREAK', 'BUCKET', 'BUILD', @@ -118,8 +115,6 @@ export const keywords: string[] = [ 'NTH_VALUE', 'NULL', 'NULLS', - 'NUMBER', - 'OBJECT', 'OFFSET', 'ON', 'OPTION', @@ -168,7 +163,6 @@ export const keywords: string[] = [ 'SOME', 'START', 'STATISTICS', - 'STRING', 'SYSTEM', 'THEN', 'TIES', @@ -205,3 +199,13 @@ export const keywords: string[] = [ 'WORK', 'XOR', ]; + +export const dataTypes: string[] = [ + // https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/datatypes.html + 'ARRAY', + 'BINARY', + 'BOOLEAN', + 'NUMBER', + 'OBJECT', + 'STRING', +]; diff --git a/src/languages/plsql/plsql.formatter.ts b/src/languages/plsql/plsql.formatter.ts index bf94819286..7b9ebd65bb 100644 --- a/src/languages/plsql/plsql.formatter.ts +++ b/src/languages/plsql/plsql.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { EOF_TOKEN, isReserved, isToken, Token, TokenType } from '../../lexer/token.js'; -import { keywords } from './plsql.keywords.js'; +import { dataTypes, keywords } from './plsql.keywords.js'; import { functions } from './plsql.functions.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT | UNIQUE]']); @@ -91,6 +91,7 @@ export const plsql: DialectOptions = { reservedPhrases, supportsXor: true, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, stringTypes: [ { quote: "''-qq", prefixes: ['N'] }, diff --git a/src/languages/plsql/plsql.keywords.ts b/src/languages/plsql/plsql.keywords.ts index 88dec949ea..c4d92afb7b 100644 --- a/src/languages/plsql/plsql.keywords.ts +++ b/src/languages/plsql/plsql.keywords.ts @@ -8,7 +8,6 @@ export const keywords: string[] = [ 'ALTER', 'AND', 'ANY', - 'ARRAY', 'ARROW', 'AS', 'ASC', @@ -18,9 +17,6 @@ export const keywords: string[] = [ 'AVG', 'BEGIN', 'BETWEEN', - 'BFILE_BASE', - 'BINARY', - 'BLOB_BASE', 'BLOCK', 'BODY', 'BOTH', @@ -33,14 +29,10 @@ export const keywords: string[] = [ 'CALLING', 'CASCADE', 'CASE', - 'CHAR', - 'CHAR_BASE', - 'CHARACTER', 'CHARSET', 'CHARSETFORM', 'CHARSETID', 'CHECK', - 'CLOB_BASE', 'CLOSE', 'CLUSTER', 'CLUSTERS', @@ -65,10 +57,7 @@ export const keywords: string[] = [ 'CUSTOMDATUM', 'DANGLING', 'DATA', - 'DATE', - 'DATE_BASE', 'DAY', - 'DECIMAL', 'DECLARE', 'DEFAULT', 'DEFINE', @@ -76,7 +65,6 @@ export const keywords: string[] = [ 'DESC', 'DETERMINISTIC', 'DISTINCT', - 'DOUBLE', 'DROP', 'DURATION', 'ELEMENT', @@ -96,7 +84,6 @@ export const keywords: string[] = [ 'FETCH', 'FINAL', 'FIXED', - 'FLOAT', 'FOR', 'FORALL', 'FORCE', @@ -124,7 +111,6 @@ export const keywords: string[] = [ 'INFINITE', 'INSERT', 'INSTANTIABLE', - 'INT', 'INTERFACE', 'INTERSECT', 'INTERVAL', @@ -147,7 +133,6 @@ export const keywords: string[] = [ 'LIMITED', 'LOCAL', 'LOCK', - 'LONG', 'LOOP', 'MAP', 'MAX', @@ -166,14 +151,12 @@ export const keywords: string[] = [ 'NAN', 'NATIONAL', 'NATIVE', - 'NCHAR', 'NEW', 'NOCOMPRESS', 'NOCOPY', 'NOT', 'NOWAIT', 'NULL', - 'NUMBER_BASE', 'OBJECT', 'OCICOLL', 'OCIDATE', @@ -215,14 +198,12 @@ export const keywords: string[] = [ 'PIPE', 'PIPELINED', 'PRAGMA', - 'PRECISION', 'PRIOR', 'PRIVATE', 'PROCEDURE', 'PUBLIC', 'RAISE', 'RANGE', - 'RAW', 'READ', 'RECORD', 'REF', @@ -283,7 +264,6 @@ export const keywords: string[] = [ 'THE', 'THEN', 'TIME', - 'TIMESTAMP', 'TIMEZONE_ABBR', 'TIMEZONE_HOUR', 'TIMEZONE_MINUTE', @@ -311,7 +291,6 @@ export const keywords: string[] = [ 'VARIABLE', 'VARIANCE', 'VARRAY', - 'VARYING', 'VIEW', 'VIEWS', 'VOID', @@ -325,3 +304,29 @@ export const keywords: string[] = [ 'YEAR', 'ZONE', ]; + +export const dataTypes: string[] = [ + // https://www.ibm.com/docs/en/db2/10.5?topic=plsql-data-types + 'ARRAY', + 'BFILE_BASE', + 'BINARY', + 'BLOB_BASE', + 'CHAR', + 'CHAR_BASE', + 'CHARACTER', + 'CLOB_BASE', + 'DATE', + 'DATE_BASE', + 'DECIMAL', + 'DOUBLE', + 'FLOAT', + 'INT', + 'LONG', + 'NCHAR', + 'NUMBER_BASE', + 'PRECISION', + 'RAW', + 'TIMESTAMP', + 'VARCHAR', + 'VARYING', +]; diff --git a/src/languages/postgresql/postgresql.formatter.ts b/src/languages/postgresql/postgresql.formatter.ts index 6c6315d752..cb64c5a4e8 100644 --- a/src/languages/postgresql/postgresql.formatter.ts +++ b/src/languages/postgresql/postgresql.formatter.ts @@ -260,9 +260,8 @@ export const postgresql: DialectOptions = { reservedSetOperations, reservedJoins, reservedPhrases, - reservedKeywords: - // Temporary, will be replaced by reservedDataTypes - [...new Set(keywords.concat(dataTypes))], + reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, nestedBlockComments: true, extraParens: ['[]'], diff --git a/src/languages/postgresql/postgresql.keywords.ts b/src/languages/postgresql/postgresql.keywords.ts index aaffb7d078..dcee480006 100644 --- a/src/languages/postgresql/postgresql.keywords.ts +++ b/src/languages/postgresql/postgresql.keywords.ts @@ -16,7 +16,6 @@ export const keywords = [ 'ANALYZE', // reserved 'AND', // reserved 'ANY', // reserved - 'ARRAY', // reserved, requires AS 'AS', // reserved, requires AS 'ASC', // reserved 'ASENSITIVE', @@ -439,6 +438,7 @@ export const keywords = [ export const dataTypes: string[] = [ // https://www.postgresql.org/docs/current/datatype.html + 'ARRAY', // reserved, requires AS 'BIGINT', // (cannot be function or type) 'BIT', // (cannot be function or type) 'BOOL', // (cannot be function or type) diff --git a/src/languages/redshift/redshift.formatter.ts b/src/languages/redshift/redshift.formatter.ts index 8c9dfb96fd..164c9b33d3 100644 --- a/src/languages/redshift/redshift.formatter.ts +++ b/src/languages/redshift/redshift.formatter.ts @@ -147,9 +147,8 @@ export const redshift: DialectOptions = { reservedSetOperations, reservedJoins, reservedPhrases, - reservedKeywords: - // Temporary, will be replaced by reservedDataTypes - [...new Set(keywords.concat(dataTypes))], + reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, stringTypes: ["''-qq"], identTypes: [`""-qq`], diff --git a/src/languages/redshift/redshift.keywords.ts b/src/languages/redshift/redshift.keywords.ts index 73b6441464..dbf1afe492 100644 --- a/src/languages/redshift/redshift.keywords.ts +++ b/src/languages/redshift/redshift.keywords.ts @@ -5,7 +5,6 @@ export const keywords: string[] = [ 'ALL', 'ALLOWOVERWRITE', 'ANY', - 'ARRAY', 'AS', 'ASC', 'AUTHORIZATION', @@ -201,6 +200,7 @@ export const keywords: string[] = [ export const dataTypes: string[] = [ // https://docs.aws.amazon.com/redshift/latest/dg/r_Character_types.html#r_Character_types-text-and-bpchar-types + 'ARRAY', 'BPCHAR', 'TEXT', ]; diff --git a/src/languages/snowflake/snowflake.formatter.ts b/src/languages/snowflake/snowflake.formatter.ts index c583a0b5ad..df1e2e715a 100644 --- a/src/languages/snowflake/snowflake.formatter.ts +++ b/src/languages/snowflake/snowflake.formatter.ts @@ -303,9 +303,8 @@ export const snowflake: DialectOptions = { reservedSetOperations, reservedJoins, reservedPhrases, - reservedKeywords: - // Temporary, will be replaced by reservedDataTypes - [...new Set(keywords.concat(dataTypes))], + reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, stringTypes: ['$$', `''-qq-bs`], identTypes: ['""-qq'], diff --git a/src/languages/spark/spark.formatter.ts b/src/languages/spark/spark.formatter.ts index 05672bac57..fc393f8563 100644 --- a/src/languages/spark/spark.formatter.ts +++ b/src/languages/spark/spark.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { EOF_TOKEN, isToken, Token, TokenType } from '../../lexer/token.js'; -import { keywords } from './spark.keywords.js'; +import { dataTypes, keywords } from './spark.keywords.js'; import { functions } from './spark.functions.js'; // http://spark.apache.org/docs/latest/sql-ref-syntax.html @@ -128,6 +128,7 @@ export const spark: DialectOptions = { reservedPhrases, supportsXor: true, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, extraParens: ['[]'], stringTypes: [ diff --git a/src/languages/spark/spark.keywords.ts b/src/languages/spark/spark.keywords.ts index fdc6f37bf0..0513c3f711 100644 --- a/src/languages/spark/spark.keywords.ts +++ b/src/languages/spark/spark.keywords.ts @@ -9,7 +9,6 @@ export const keywords: string[] = [ 'ANTI', 'ANY', 'ARCHIVE', - 'ARRAY', 'AS', 'ASC', 'AT', @@ -51,7 +50,6 @@ export const keywords: string[] = [ 'DATA', 'DATABASE', 'DATABASES', - 'DAY', 'DBPROPERTIES', 'DEFINED', 'DELETE', @@ -93,7 +91,6 @@ export const keywords: string[] = [ 'GRANT', 'GROUP', 'GROUPING', - 'HOUR', 'IF', 'IGNORE', 'IMPORT', @@ -104,7 +101,6 @@ export const keywords: string[] = [ 'INPATH', 'INPUTFORMAT', 'INTERSECT', - 'INTERVAL', 'INTO', 'IS', 'ITEMS', @@ -124,11 +120,8 @@ export const keywords: string[] = [ 'LOCKS', 'LOGICAL', 'MACRO', - 'MAP', 'MATCHED', 'MERGE', - 'MINUTE', - 'MONTH', 'MSCK', 'NAMESPACE', 'NAMESPACES', @@ -184,7 +177,6 @@ export const keywords: string[] = [ 'ROW', 'ROWS', 'SCHEMA', - 'SECOND', 'SELECT', 'SEMI', 'SEPARATED', @@ -201,7 +193,6 @@ export const keywords: string[] = [ 'STATISTICS', 'STORED', 'STRATIFY', - 'STRUCT', 'SUBSTR', 'SUBSTRING', 'TABLE', @@ -230,7 +221,6 @@ export const keywords: string[] = [ 'USING', 'VIEW', 'WINDOW', - 'YEAR', // other 'ANALYSE', 'ARRAY_ZIP', @@ -264,10 +254,44 @@ export const keywords: string[] = [ 'REGEXP', 'SEPARATOR', 'SIZE', - 'STRING', 'TYPE', 'TYPES', 'UNSIGNED', 'VARIABLES', 'YEAR_MONTH', ]; + +export const dataTypes: string[] = [ + // https://spark.apache.org/docs/latest/sql-ref-datatypes.html + 'BOOLEAN', + 'BYTE', + 'TINYINT', + 'SHORT', + 'SMALLINT', + 'INT', + 'INTEGER', + 'LONG', + 'BIGINT', + 'FLOAT', + 'REAL', + 'DOUBLE', + 'DATE', + 'TIMESTAMP', + 'TIMESTAMP_LTZ', + 'TIMESTAMP_NTZ', + 'STRING', + 'BINARY', + 'DECIMAL', + 'DEC', + 'NUMERIC', + 'INTERVAL', + 'YEAR', + 'MONTH', + 'DAY', + 'HOUR', + 'MINUTE', + 'SECOND', + 'ARRAY', + 'STRUCT', + 'MAP', +]; diff --git a/src/languages/sql/sql.formatter.ts b/src/languages/sql/sql.formatter.ts index 8baa4c9014..6be3b6a9cc 100644 --- a/src/languages/sql/sql.formatter.ts +++ b/src/languages/sql/sql.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { functions } from './sql.functions.js'; -import { keywords } from './sql.keywords.js'; +import { dataTypes, keywords } from './sql.keywords.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT]']); @@ -82,6 +82,7 @@ export const sql: DialectOptions = { reservedJoins, reservedPhrases, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, stringTypes: [ { quote: "''-qq-bs", prefixes: ['N', 'U&'] }, diff --git a/src/languages/sql/sql.keywords.ts b/src/languages/sql/sql.keywords.ts index 496dde3015..66e6f7c1e0 100644 --- a/src/languages/sql/sql.keywords.ts +++ b/src/languages/sql/sql.keywords.ts @@ -5,7 +5,6 @@ export const keywords: string[] = [ 'ALTER', 'ANY', // <- moved over from functions 'ARE', - 'ARRAY', 'AS', 'ASENSITIVE', 'ASYMMETRIC', @@ -14,20 +13,13 @@ export const keywords: string[] = [ 'AUTHORIZATION', 'BEGIN', 'BETWEEN', - 'BIGINT', - 'BINARY', - 'BLOB', - 'BOOLEAN', 'BOTH', 'BY', 'CALL', 'CALLED', 'CASCADED', 'CAST', - 'CHAR', - 'CHARACTER', 'CHECK', - 'CLOB', 'CLOSE', 'COALESCE', 'COLLATE', @@ -50,11 +42,7 @@ export const keywords: string[] = [ 'CURRENT_USER', 'CURSOR', 'CYCLE', - 'DATE', - 'DAY', 'DEALLOCATE', - 'DEC', - 'DECIMAL', 'DECLARE', 'DEFAULT', 'DELETE', @@ -63,7 +51,6 @@ export const keywords: string[] = [ 'DETERMINISTIC', 'DISCONNECT', 'DISTINCT', - 'DOUBLE', 'DROP', 'DYNAMIC', 'EACH', @@ -79,7 +66,6 @@ export const keywords: string[] = [ 'FALSE', 'FETCH', 'FILTER', - 'FLOAT', 'FOR', 'FOREIGN', 'FREE', @@ -92,7 +78,6 @@ export const keywords: string[] = [ 'GROUP', 'HAVING', 'HOLD', - 'HOUR', 'IDENTITY', 'IN', 'INDICATOR', @@ -100,10 +85,7 @@ export const keywords: string[] = [ 'INOUT', 'INSENSITIVE', 'INSERT', - 'INT', - 'INTEGER', 'INTERSECT', - 'INTERVAL', 'INTO', 'IS', 'LANGUAGE', @@ -118,22 +100,15 @@ export const keywords: string[] = [ 'MEMBER', 'MERGE', 'METHOD', - 'MINUTE', 'MODIFIES', 'MODULE', - 'MONTH', - 'MULTISET', - 'NATIONAL', 'NATURAL', - 'NCHAR', - 'NCLOB', 'NEW', 'NO', 'NONE', 'NOT', 'NULL', 'NULLIF', - 'NUMERIC', 'OF', 'OLD', 'ON', @@ -177,7 +152,6 @@ export const keywords: string[] = [ 'SESSION_USER', 'SET', 'SIMILAR', - 'SMALLINT', 'SOME', // <- moved over from functions 'SPECIFIC', 'SQL', @@ -193,8 +167,6 @@ export const keywords: string[] = [ 'TABLE', 'TABLESAMPLE', 'THEN', - 'TIME', - 'TIMESTAMP', 'TIMEZONE_HOUR', 'TIMEZONE_MINUTE', 'TO', @@ -213,12 +185,44 @@ export const keywords: string[] = [ 'USING', 'VALUE', 'VALUES', - 'VARBINARY', - 'VARCHAR', - 'VARYING', 'WHENEVER', 'WINDOW', 'WITHIN', 'WITHOUT', +]; + +export const dataTypes: string[] = [ + // https://jakewheat.github.io/sql-overview/sql-2008-foundation-grammar.html#_6_1_data_type + 'ARRAY', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOOLEAN', + 'CHAR', + 'CHARACTER', + 'CLOB', + 'DATE', + 'DAY', + 'DEC', + 'DECIMAL', + 'DOUBLE', + 'FLOAT', + 'HOUR', + 'INT', + 'INTEGER', + 'INTERVAL', + 'MINUTE', + 'MONTH', + 'MULTISET', + 'NATIONAL', + 'NCHAR', + 'NCLOB', + 'NUMERIC', + 'SMALLINT', + 'TIME', + 'TIMESTAMP', + 'VARBINARY', + 'VARCHAR', + 'VARYING', 'YEAR', ]; diff --git a/src/languages/sqlite/sqlite.formatter.ts b/src/languages/sqlite/sqlite.formatter.ts index 3bcf75beb2..df9c1d9412 100644 --- a/src/languages/sqlite/sqlite.formatter.ts +++ b/src/languages/sqlite/sqlite.formatter.ts @@ -1,7 +1,7 @@ import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; import { functions } from './sqlite.functions.js'; -import { keywords } from './sqlite.keywords.js'; +import { dataTypes, keywords } from './sqlite.keywords.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT]']); @@ -73,6 +73,7 @@ export const sqlite: DialectOptions = { reservedJoins, reservedPhrases, reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, stringTypes: [ "''-qq", diff --git a/src/languages/sqlite/sqlite.keywords.ts b/src/languages/sqlite/sqlite.keywords.ts index 2ab649a7b6..f82376b0ab 100644 --- a/src/languages/sqlite/sqlite.keywords.ts +++ b/src/languages/sqlite/sqlite.keywords.ts @@ -7,9 +7,7 @@ export const keywords: string[] = [ 'ALL', 'ALTER', 'AND', - 'ANY', 'ARE', - 'ARRAY', 'ALWAYS', 'ANALYZE', 'AS', @@ -153,3 +151,15 @@ export const keywords: string[] = [ 'WITH', 'WITHOUT', ]; + +export const dataTypes: string[] = [ + // https://www.sqlite.org/stricttables.html + // https://www.sqlite.org/datatype3.html + 'ANY', + 'ARRAY', + 'BLOB', + 'INT', + 'INTEGER', + 'REAL', + 'TEXT', +]; diff --git a/src/languages/trino/trino.formatter.ts b/src/languages/trino/trino.formatter.ts index d13c3e7030..d56fdea8c1 100644 --- a/src/languages/trino/trino.formatter.ts +++ b/src/languages/trino/trino.formatter.ts @@ -132,9 +132,8 @@ export const trino: DialectOptions = { reservedSetOperations, reservedJoins, reservedPhrases, - reservedKeywords: - // Temporary, will be replaced by reservedDataTypes - [...new Set(keywords.concat(dataTypes))], + reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, // Trino also supports {- ... -} parenthesis. // The formatting of these currently works out as a result of { and - diff --git a/src/languages/trino/trino.keywords.ts b/src/languages/trino/trino.keywords.ts index dd6c0cddaf..12066f6794 100644 --- a/src/languages/trino/trino.keywords.ts +++ b/src/languages/trino/trino.keywords.ts @@ -9,7 +9,6 @@ export const keywords: string[] = [ 'ANALYZE', 'AND', 'ANY', - 'ARRAY', 'AS', 'ASC', 'AT', From a55557574115625451b4b63632ca822e4a1c2791 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Thu, 30 Nov 2023 17:46:07 +0100 Subject: [PATCH 05/15] Add TokenType.RESERVED_DATA_TYPE and dataTypeCase --- src/FormatOptions.ts | 3 +++ src/formatter/ExpressionFormatter.ts | 22 +++++++++++++++++++++- src/lexer/Tokenizer.ts | 15 ++++++++++----- src/lexer/TokenizerOptions.ts | 2 ++ src/lexer/disambiguateTokens.ts | 5 +++++ src/lexer/token.ts | 8 ++++++-- src/parser/ast.ts | 10 +++++++++- src/parser/grammar.ne | 21 ++++++++++++++++++++- src/sqlFormatter.ts | 4 ++-- test/snowflake.test.ts | 4 ++-- 10 files changed, 80 insertions(+), 14 deletions(-) diff --git a/src/FormatOptions.ts b/src/FormatOptions.ts index 7cf102759a..8100c3cb38 100644 --- a/src/FormatOptions.ts +++ b/src/FormatOptions.ts @@ -8,6 +8,8 @@ export type KeywordCase = 'preserve' | 'upper' | 'lower'; export type IdentifierCase = 'preserve' | 'upper' | 'lower'; +export type DataTypeCase = 'preserve' | 'upper' | 'lower'; + export type LogicalOperatorNewline = 'before' | 'after'; export interface FormatOptions { @@ -15,6 +17,7 @@ export interface FormatOptions { useTabs: boolean; keywordCase: KeywordCase; identifierCase: IdentifierCase; + dataTypeCase: DataTypeCase; indentStyle: IndentStyle; logicalOperatorNewline: LogicalOperatorNewline; expressionWidth: number; diff --git a/src/formatter/ExpressionFormatter.ts b/src/formatter/ExpressionFormatter.ts index 88a6f2694f..ee32713d6b 100644 --- a/src/formatter/ExpressionFormatter.ts +++ b/src/formatter/ExpressionFormatter.ts @@ -28,6 +28,7 @@ import { CaseExpressionNode, CaseWhenNode, CaseElseNode, + DataTypeNode, } from '../parser/ast.js'; import Layout, { WS } from './Layout.js'; @@ -130,6 +131,8 @@ export default class ExpressionFormatter { return this.formatLineComment(node); case NodeType.block_comment: return this.formatBlockComment(node); + case NodeType.data_type: + return this.formatDataType(node); case NodeType.keyword: return this.formatKeywordNode(node); } @@ -145,7 +148,9 @@ export default class ExpressionFormatter { private formatArraySubscript(node: ArraySubscriptNode) { this.withComments(node.array, () => { this.layout.add( - node.array.type === NodeType.keyword + node.array.type === NodeType.data_type + ? this.showDataType(node.array) + : node.array.type === NodeType.keyword ? this.showKw(node.array) : this.showIdentifier(node.array) ); @@ -489,6 +494,10 @@ export default class ExpressionFormatter { } } + private formatDataType(node: DataTypeNode) { + this.layout.add(this.showDataType(node), WS.SPACE); + } + private showKw(node: KeywordNode): string { if (isTabularToken(node.tokenType)) { return toTabularFormat(this.showNonTabularKw(node), this.cfg.indentStyle); @@ -523,4 +532,15 @@ export default class ExpressionFormatter { } } } + + private showDataType(node: DataTypeNode): string { + switch (this.cfg.dataTypeCase) { + case 'preserve': + return equalizeWhitespace(node.raw); + case 'upper': + return node.text; + case 'lower': + return node.text.toLowerCase(); + } + } } diff --git a/src/lexer/Tokenizer.ts b/src/lexer/Tokenizer.ts index 2df5a6335a..594925f87b 100644 --- a/src/lexer/Tokenizer.ts +++ b/src/lexer/Tokenizer.ts @@ -1,10 +1,10 @@ -import { Token, TokenType } from './token.js'; -import * as regex from './regexFactory.js'; -import { ParamTypes, TokenizerOptions } from './TokenizerOptions.js'; -import TokenizerEngine, { TokenRule } from './TokenizerEngine.js'; -import { escapeRegExp, patternToRegex } from './regexUtil.js'; import { equalizeWhitespace, Optional } from '../utils.js'; import { NestedComment } from './NestedComment.js'; +import * as regex from './regexFactory.js'; +import { escapeRegExp, patternToRegex } from './regexUtil.js'; +import { Token, TokenType } from './token.js'; +import TokenizerEngine, { TokenRule } from './TokenizerEngine.js'; +import { ParamTypes, TokenizerOptions } from './TokenizerOptions.js'; type OptionalTokenRule = Optional; @@ -130,6 +130,11 @@ export default class Tokenizer { regex: regex.reservedWord(cfg.reservedFunctionNames, cfg.identChars), text: toCanonical, }, + { + type: TokenType.RESERVED_DATA_TYPE, + regex: regex.reservedWord(cfg.reservedDataTypes ?? [], cfg.identChars), + text: toCanonical, + }, { type: TokenType.RESERVED_KEYWORD, regex: regex.reservedWord(cfg.reservedKeywords, cfg.identChars), diff --git a/src/lexer/TokenizerOptions.ts b/src/lexer/TokenizerOptions.ts index beca4575e5..24fb8ff66e 100644 --- a/src/lexer/TokenizerOptions.ts +++ b/src/lexer/TokenizerOptions.ts @@ -69,6 +69,8 @@ export interface TokenizerOptions { reservedPhrases?: string[]; // built in function names reservedFunctionNames: string[]; + // data types + reservedDataTypes?: string[]; // all other reserved words (not included to any of the above lists) reservedKeywords: string[]; // Types of quotes to use for strings diff --git a/src/lexer/disambiguateTokens.ts b/src/lexer/disambiguateTokens.ts index 4527f6192f..e08013dfeb 100644 --- a/src/lexer/disambiguateTokens.ts +++ b/src/lexer/disambiguateTokens.ts @@ -57,6 +57,11 @@ const keywordToArrayKeyword = (token: Token, i: number, tokens: Token[]): Token if (nextToken && isOpenBracket(nextToken)) { return { ...token, type: TokenType.ARRAY_KEYWORD }; } + } else if (token.type === TokenType.RESERVED_DATA_TYPE) { + const nextToken = nextNonCommentToken(tokens, i); + if (nextToken && isOpenBracket(nextToken)) { + return { ...token, type: TokenType.ARRAY_DATA_TYPE }; + } } return token; }; diff --git a/src/lexer/token.ts b/src/lexer/token.ts index 4df232bbd8..9bee7a90c1 100644 --- a/src/lexer/token.ts +++ b/src/lexer/token.ts @@ -4,6 +4,7 @@ export enum TokenType { IDENTIFIER = 'IDENTIFIER', STRING = 'STRING', VARIABLE = 'VARIABLE', + RESERVED_DATA_TYPE = 'RESERVED_DATA_TYPE', RESERVED_KEYWORD = 'RESERVED_KEYWORD', RESERVED_FUNCTION_NAME = 'RESERVED_FUNCTION_NAME', RESERVED_PHRASE = 'RESERVED_PHRASE', @@ -12,6 +13,7 @@ export enum TokenType { RESERVED_SELECT = 'RESERVED_SELECT', RESERVED_JOIN = 'RESERVED_JOIN', ARRAY_IDENTIFIER = 'ARRAY_IDENTIFIER', // IDENTIFIER token in front of [ + ARRAY_DATA_TYPE = 'ARRAY_DATA_TYPE', // RESERVED_DATA_TYPE token in front of [ ARRAY_KEYWORD = 'ARRAY_KEYWORD', // RESERVED_KEYWORD token in front of [ CASE = 'CASE', END = 'END', @@ -73,16 +75,17 @@ export const testToken = /** Util object that allows for easy checking of Reserved Keywords */ export const isToken = { - ARRAY: testToken({ text: 'ARRAY', type: TokenType.RESERVED_KEYWORD }), + ARRAY: testToken({ text: 'ARRAY', type: TokenType.RESERVED_DATA_TYPE }), BY: testToken({ text: 'BY', type: TokenType.RESERVED_KEYWORD }), SET: testToken({ text: 'SET', type: TokenType.RESERVED_CLAUSE }), - STRUCT: testToken({ text: 'STRUCT', type: TokenType.RESERVED_KEYWORD }), + STRUCT: testToken({ text: 'STRUCT', type: TokenType.RESERVED_DATA_TYPE }), WINDOW: testToken({ text: 'WINDOW', type: TokenType.RESERVED_CLAUSE }), VALUES: testToken({ text: 'VALUES', type: TokenType.RESERVED_CLAUSE }), }; /** Checks if token is any Reserved Keyword or Clause */ export const isReserved = (type: TokenType): boolean => + type === TokenType.RESERVED_DATA_TYPE || type === TokenType.RESERVED_KEYWORD || type === TokenType.RESERVED_FUNCTION_NAME || type === TokenType.RESERVED_PHRASE || @@ -90,6 +93,7 @@ export const isReserved = (type: TokenType): boolean => type === TokenType.RESERVED_SELECT || type === TokenType.RESERVED_SET_OPERATION || type === TokenType.RESERVED_JOIN || + type === TokenType.ARRAY_DATA_TYPE || type === TokenType.ARRAY_KEYWORD || type === TokenType.CASE || type === TokenType.END || diff --git a/src/parser/ast.ts b/src/parser/ast.ts index 2a5a7e8424..81e72776fc 100644 --- a/src/parser/ast.ts +++ b/src/parser/ast.ts @@ -17,6 +17,7 @@ export enum NodeType { literal = 'literal', identifier = 'identifier', keyword = 'keyword', + data_type = 'data_type', parameter = 'parameter', operator = 'operator', comma = 'comma', @@ -56,7 +57,7 @@ export interface FunctionCallNode extends BaseNode { // [] export interface ArraySubscriptNode extends BaseNode { type: NodeType.array_subscript; - array: IdentifierNode | KeywordNode; + array: IdentifierNode | KeywordNode | DataTypeNode; parenthesis: ParenthesisNode; } @@ -129,6 +130,12 @@ export interface IdentifierNode extends BaseNode { text: string; } +export interface DataTypeNode extends BaseNode { + type: NodeType.data_type; + text: string; + raw: string; +} + export interface KeywordNode extends BaseNode { type: NodeType.keyword; tokenType: TokenType; @@ -180,6 +187,7 @@ export type AstNode = | AllColumnsAsteriskNode | LiteralNode | IdentifierNode + | DataTypeNode | KeywordNode | ParameterNode | OperatorNode diff --git a/src/parser/grammar.ne b/src/parser/grammar.ne index f380c6bff7..901c17bd1b 100644 --- a/src/parser/grammar.ne +++ b/src/parser/grammar.ne @@ -1,7 +1,7 @@ @preprocessor typescript @{% import LexerAdapter from './LexerAdapter.js'; -import { NodeType, AstNode, CommentNode, KeywordNode, IdentifierNode } from './ast.js'; +import { NodeType, AstNode, CommentNode, KeywordNode, IdentifierNode, DataTypeNode } from './ast.js'; import { Token, TokenType } from '../lexer/token.js'; // The lexer here is only to provide the has() method, @@ -23,6 +23,12 @@ const toKeywordNode = (token: Token): KeywordNode => ({ raw: token.raw, }); +const toDataTypeNode = (token: Token): DataTypeNode => ({ + type: NodeType.data_type, + text: token.text, + raw: token.raw, +}); + interface CommentAttachments { leading?: CommentNode[]; trailing?: CommentNode[]; @@ -197,6 +203,7 @@ atomic_expression -> | identifier | parameter | literal + | data_type | keyword ) {% unwrap %} array_subscript -> %ARRAY_IDENTIFIER _ square_brackets {% @@ -206,6 +213,13 @@ array_subscript -> %ARRAY_IDENTIFIER _ square_brackets {% parenthesis: brackets, }) %} +array_subscript -> %ARRAY_DATA_TYPE _ square_brackets {% + ([arrayToken, _, brackets]) => ({ + type: NodeType.array_subscript, + array: addComments(toDataTypeNode(arrayToken), { trailing: _ }), + parenthesis: brackets, + }) +%} array_subscript -> %ARRAY_KEYWORD _ square_brackets {% ([arrayToken, _, brackets]) => ({ type: NodeType.array_subscript, @@ -329,6 +343,11 @@ keyword -> ([[token]]) => toKeywordNode(token) %} +data_type -> + ( %RESERVED_DATA_TYPE ) {% + ([[token]]) => toDataTypeNode(token) +%} + logic_operator -> ( %AND | %OR diff --git a/src/sqlFormatter.ts b/src/sqlFormatter.ts index 620038ce2f..26513f822a 100644 --- a/src/sqlFormatter.ts +++ b/src/sqlFormatter.ts @@ -1,7 +1,6 @@ import * as allDialects from './allDialects.js'; - -import { FormatOptions } from './FormatOptions.js'; import { createDialect, DialectOptions } from './dialect.js'; +import { FormatOptions } from './FormatOptions.js'; import Formatter from './formatter/Formatter.js'; import { ConfigError, validateConfig } from './validateConfig.js'; @@ -42,6 +41,7 @@ const defaultOptions: FormatOptions = { useTabs: false, keywordCase: 'preserve', identifierCase: 'preserve', + dataTypeCase: 'preserve', indentStyle: 'standard', logicalOperatorNewline: 'before', expressionWidth: 50, diff --git a/test/snowflake.test.ts b/test/snowflake.test.ts index 561c91b406..9182b1f371 100644 --- a/test/snowflake.test.ts +++ b/test/snowflake.test.ts @@ -167,12 +167,12 @@ describe('SnowflakeFormatter', () => { `); }); - it('detects data types as keywords', () => { + it('detects data types as data types', () => { expect( format( `CREATE TABLE tbl (first_column double Precision, second_column numBer (38, 0), third String);`, { - keywordCase: 'upper', + dataTypeCase: 'upper', } ) ).toBe(dedent` From 4ae6c487f63e9a9f6d1b7c7af15067f8ee880cd1 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Thu, 30 Nov 2023 18:14:13 +0100 Subject: [PATCH 06/15] Add functionCase option, make keywordCase default --- docs/dataTypeCase.md | 4 +++- docs/functionCase.md | 4 +++- src/FormatOptions.ts | 3 +++ src/formatter/ExpressionFormatter.ts | 22 ++++++++++++++++++- src/lexer/Tokenizer.ts | 10 ++++----- src/sqlFormatter.ts | 32 +++++++++++++++------------- test/features/case.ts | 5 ++++- test/snowflake.test.ts | 2 +- 8 files changed, 57 insertions(+), 25 deletions(-) diff --git a/docs/dataTypeCase.md b/docs/dataTypeCase.md index 4bc336e0a4..2c017b8178 100644 --- a/docs/dataTypeCase.md +++ b/docs/dataTypeCase.md @@ -8,10 +8,12 @@ Note: Casing of function names like `VARCHAR(30)` are not modified - instead rel ## Options -- `"preserve"` (default) preserves the original case. +- `"preserve"` preserves the original case. - `"upper"` converts to uppercase. - `"lower"` converts to lowercase. +The default is either `options.keywordCase` (if you have set it) or `"preserve"`. + ### preserve ```sql diff --git a/docs/functionCase.md b/docs/functionCase.md index e1436527a3..6c798a620d 100644 --- a/docs/functionCase.md +++ b/docs/functionCase.md @@ -4,10 +4,12 @@ Converts functions to upper- or lowercase. ## Options -- `"preserve"` (default) preserves the original case. +- `"preserve"` preserves the original case. - `"upper"` converts to uppercase. - `"lower"` converts to lowercase. +The default is either `options.keywordCase` (if you have set it) or `"preserve"`. + ### preserve ```sql diff --git a/src/FormatOptions.ts b/src/FormatOptions.ts index 8100c3cb38..937a4ac858 100644 --- a/src/FormatOptions.ts +++ b/src/FormatOptions.ts @@ -10,6 +10,8 @@ export type IdentifierCase = 'preserve' | 'upper' | 'lower'; export type DataTypeCase = 'preserve' | 'upper' | 'lower'; +export type FunctionCase = 'preserve' | 'upper' | 'lower'; + export type LogicalOperatorNewline = 'before' | 'after'; export interface FormatOptions { @@ -18,6 +20,7 @@ export interface FormatOptions { keywordCase: KeywordCase; identifierCase: IdentifierCase; dataTypeCase: DataTypeCase; + functionCase: FunctionCase; indentStyle: IndentStyle; logicalOperatorNewline: LogicalOperatorNewline; expressionWidth: number; diff --git a/src/formatter/ExpressionFormatter.ts b/src/formatter/ExpressionFormatter.ts index ee32713d6b..cb7ecc3c28 100644 --- a/src/formatter/ExpressionFormatter.ts +++ b/src/formatter/ExpressionFormatter.ts @@ -140,7 +140,7 @@ export default class ExpressionFormatter { private formatFunctionCall(node: FunctionCallNode) { this.withComments(node.nameKw, () => { - this.layout.add(this.showKw(node.nameKw)); + this.layout.add(this.showFunctionKw(node.nameKw)); }); this.formatNode(node.parenthesis); } @@ -518,6 +518,26 @@ export default class ExpressionFormatter { } } + private showFunctionKw(node: KeywordNode): string { + if (isTabularToken(node.tokenType)) { + return toTabularFormat(this.showNonTabularFunctionKw(node), this.cfg.indentStyle); + } else { + return this.showNonTabularFunctionKw(node); + } + } + + // Like showFunctionKw(), but skips tabular formatting + private showNonTabularFunctionKw(node: KeywordNode): string { + switch (this.cfg.functionCase) { + case 'preserve': + return equalizeWhitespace(node.raw); + case 'upper': + return node.text; + case 'lower': + return node.text.toLowerCase(); + } + } + private showIdentifier(node: IdentifierNode): string { if (node.quoted) { return node.text; diff --git a/src/lexer/Tokenizer.ts b/src/lexer/Tokenizer.ts index 594925f87b..438455e627 100644 --- a/src/lexer/Tokenizer.ts +++ b/src/lexer/Tokenizer.ts @@ -1,10 +1,10 @@ -import { equalizeWhitespace, Optional } from '../utils.js'; -import { NestedComment } from './NestedComment.js'; -import * as regex from './regexFactory.js'; -import { escapeRegExp, patternToRegex } from './regexUtil.js'; import { Token, TokenType } from './token.js'; -import TokenizerEngine, { TokenRule } from './TokenizerEngine.js'; +import * as regex from './regexFactory.js'; import { ParamTypes, TokenizerOptions } from './TokenizerOptions.js'; +import TokenizerEngine, { TokenRule } from './TokenizerEngine.js'; +import { escapeRegExp, patternToRegex } from './regexUtil.js'; +import { equalizeWhitespace, Optional } from '../utils.js'; +import { NestedComment } from './NestedComment.js'; type OptionalTokenRule = Optional; diff --git a/src/sqlFormatter.ts b/src/sqlFormatter.ts index 26513f822a..e7915f244b 100644 --- a/src/sqlFormatter.ts +++ b/src/sqlFormatter.ts @@ -1,6 +1,7 @@ import * as allDialects from './allDialects.js'; -import { createDialect, DialectOptions } from './dialect.js'; + import { FormatOptions } from './FormatOptions.js'; +import { createDialect, DialectOptions } from './dialect.js'; import Formatter from './formatter/Formatter.js'; import { ConfigError, validateConfig } from './validateConfig.js'; @@ -36,20 +37,6 @@ export type FormatOptionsWithDialect = Partial & { dialect: DialectOptions; }; -const defaultOptions: FormatOptions = { - tabWidth: 2, - useTabs: false, - keywordCase: 'preserve', - identifierCase: 'preserve', - dataTypeCase: 'preserve', - indentStyle: 'standard', - logicalOperatorNewline: 'before', - expressionWidth: 50, - linesBetweenQueries: 1, - denseOperators: false, - newlineBeforeSemicolon: false, -}; - /** * Format whitespace in a query to make it easier to read. * @@ -86,6 +73,21 @@ export const formatDialect = ( throw new Error('Invalid query argument. Expected string, instead got ' + typeof query); } + const defaultOptions: FormatOptions = { + tabWidth: 2, + useTabs: false, + keywordCase: 'preserve', + identifierCase: 'preserve', + dataTypeCase: cfg.keywordCase || 'preserve', + functionCase: cfg.keywordCase || 'preserve', + indentStyle: 'standard', + logicalOperatorNewline: 'before', + expressionWidth: 50, + linesBetweenQueries: 1, + denseOperators: false, + newlineBeforeSemicolon: false, + }; + const options = validateConfig({ ...defaultOptions, ...cfg, diff --git a/test/features/case.ts b/test/features/case.ts index defb1b4bcf..b44febc035 100644 --- a/test/features/case.ts +++ b/test/features/case.ts @@ -79,7 +79,10 @@ export default function supportsCase(format: FormatFn) { it('properly converts to uppercase in case statements', () => { const result = format( "case trim(sqrt(my_field)) when 'one' then 1 when 'two' then 2 when 'three' then 3 else 4 end;", - { keywordCase: 'upper' } + { + keywordCase: 'upper', + functionCase: 'upper', + } ); expect(result).toBe(dedent` CASE TRIM(SQRT(my_field)) diff --git a/test/snowflake.test.ts b/test/snowflake.test.ts index 9182b1f371..42e3edcb35 100644 --- a/test/snowflake.test.ts +++ b/test/snowflake.test.ts @@ -167,7 +167,7 @@ describe('SnowflakeFormatter', () => { `); }); - it('detects data types as data types', () => { + it('detects data types', () => { expect( format( `CREATE TABLE tbl (first_column double Precision, second_column numBer (38, 0), third String);`, From f8df2f60e9139472c69eda91bd24304eb8aa923d Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Thu, 30 Nov 2023 18:16:27 +0100 Subject: [PATCH 07/15] Improve docs --- docs/functionCase.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/functionCase.md b/docs/functionCase.md index 6c798a620d..e51ddcad20 100644 --- a/docs/functionCase.md +++ b/docs/functionCase.md @@ -13,9 +13,9 @@ The default is either `options.keywordCase` (if you have set it) or `"preserve"` ### preserve ```sql -CREATE TABLE +CREATE tabLE users ( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + id iNtegeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, first_name VarChaR(30) NOT NULL, bio TEXT, is_email_verified BOOL NOT NULL DEFAULT FALSE, @@ -26,9 +26,9 @@ CREATE TABLE ### upper ```sql -CREATE TABLE +CREATE tabLE users ( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + id iNtegeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, first_name VARCHAR(30) NOT NULL, bio TEXT, is_email_verified BOOL NOT NULL DEFAULT FALSE, @@ -39,9 +39,9 @@ CREATE TABLE ### lower ```sql -CREATE TABLE +CREATE tabLE users ( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + id iNtegeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, first_name varchar(30) NOT NULL, bio TEXT, is_email_verified BOOL NOT NULL DEFAULT FALSE, From 9d4c929ba5fa11de8751ad6a08c617ed1919cff8 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Fri, 1 Dec 2023 14:48:41 +0100 Subject: [PATCH 08/15] Switch to reservedDataTypes --- src/languages/db2/db2.formatter.ts | 5 ++--- src/languages/singlestoredb/singlestoredb.formatter.ts | 5 ++--- src/languages/transactsql/transactsql.formatter.ts | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/languages/db2/db2.formatter.ts b/src/languages/db2/db2.formatter.ts index a9021831ad..c0cc4ed8c8 100644 --- a/src/languages/db2/db2.formatter.ts +++ b/src/languages/db2/db2.formatter.ts @@ -265,9 +265,8 @@ export const db2: DialectOptions = { reservedSetOperations, reservedJoins, reservedPhrases, - reservedKeywords: - // Temporary, will be replaced by reservedDataTypes - [...new Set(keywords.concat(dataTypes))], + reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, extraParens: ['[]'], stringTypes: [ diff --git a/src/languages/singlestoredb/singlestoredb.formatter.ts b/src/languages/singlestoredb/singlestoredb.formatter.ts index a1f80a9fd6..25274b10ae 100644 --- a/src/languages/singlestoredb/singlestoredb.formatter.ts +++ b/src/languages/singlestoredb/singlestoredb.formatter.ts @@ -240,9 +240,8 @@ export const singlestoredb: DialectOptions = { reservedSetOperations, reservedJoins, reservedPhrases, - reservedKeywords: - // Temporary, will be replaced by reservedDataTypes - [...new Set(keywords.concat(dataTypes))], + reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, // TODO: support _binary"some string" prefix stringTypes: [ diff --git a/src/languages/transactsql/transactsql.formatter.ts b/src/languages/transactsql/transactsql.formatter.ts index 8f4091c8b0..b092fb8072 100644 --- a/src/languages/transactsql/transactsql.formatter.ts +++ b/src/languages/transactsql/transactsql.formatter.ts @@ -229,9 +229,8 @@ export const transactsql: DialectOptions = { reservedSetOperations, reservedJoins, reservedPhrases, - reservedKeywords: - // Temporary, will be replaced by reservedDataTypes - [...new Set(keywords.concat(dataTypes))], + reservedKeywords: keywords, + reservedDataTypes: dataTypes, reservedFunctionNames: functions, nestedBlockComments: true, stringTypes: [{ quote: "''-qq", prefixes: ['N'] }], From 4f4684538686d4cdc791eb3edb2cb81470905cf8 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Fri, 1 Dec 2023 14:50:52 +0100 Subject: [PATCH 09/15] Revert to independent defaults --- docs/dataTypeCase.md | 4 +--- docs/functionCase.md | 4 +--- src/sqlFormatter.ts | 30 +++++++++++++++--------------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/docs/dataTypeCase.md b/docs/dataTypeCase.md index 2c017b8178..4bc336e0a4 100644 --- a/docs/dataTypeCase.md +++ b/docs/dataTypeCase.md @@ -8,12 +8,10 @@ Note: Casing of function names like `VARCHAR(30)` are not modified - instead rel ## Options -- `"preserve"` preserves the original case. +- `"preserve"` (default) preserves the original case. - `"upper"` converts to uppercase. - `"lower"` converts to lowercase. -The default is either `options.keywordCase` (if you have set it) or `"preserve"`. - ### preserve ```sql diff --git a/docs/functionCase.md b/docs/functionCase.md index e51ddcad20..9132c974e4 100644 --- a/docs/functionCase.md +++ b/docs/functionCase.md @@ -4,12 +4,10 @@ Converts functions to upper- or lowercase. ## Options -- `"preserve"` preserves the original case. +- `"preserve"` (default) preserves the original case. - `"upper"` converts to uppercase. - `"lower"` converts to lowercase. -The default is either `options.keywordCase` (if you have set it) or `"preserve"`. - ### preserve ```sql diff --git a/src/sqlFormatter.ts b/src/sqlFormatter.ts index e7915f244b..50cb2cbed9 100644 --- a/src/sqlFormatter.ts +++ b/src/sqlFormatter.ts @@ -37,6 +37,21 @@ export type FormatOptionsWithDialect = Partial & { dialect: DialectOptions; }; +const defaultOptions: FormatOptions = { + tabWidth: 2, + useTabs: false, + keywordCase: 'preserve', + identifierCase: 'preserve', + dataTypeCase: 'preserve', + functionCase: 'preserve', + indentStyle: 'standard', + logicalOperatorNewline: 'before', + expressionWidth: 50, + linesBetweenQueries: 1, + denseOperators: false, + newlineBeforeSemicolon: false, +}; + /** * Format whitespace in a query to make it easier to read. * @@ -73,21 +88,6 @@ export const formatDialect = ( throw new Error('Invalid query argument. Expected string, instead got ' + typeof query); } - const defaultOptions: FormatOptions = { - tabWidth: 2, - useTabs: false, - keywordCase: 'preserve', - identifierCase: 'preserve', - dataTypeCase: cfg.keywordCase || 'preserve', - functionCase: cfg.keywordCase || 'preserve', - indentStyle: 'standard', - logicalOperatorNewline: 'before', - expressionWidth: 50, - linesBetweenQueries: 1, - denseOperators: false, - newlineBeforeSemicolon: false, - }; - const options = validateConfig({ ...defaultOptions, ...cfg, From 9815a46da1710babb53a67fbd79dc5427aac7d8d Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Fri, 1 Dec 2023 17:42:33 +0100 Subject: [PATCH 10/15] Address ESLint no-nested-ternary rule error --- src/formatter/ExpressionFormatter.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/formatter/ExpressionFormatter.ts b/src/formatter/ExpressionFormatter.ts index cb7ecc3c28..489889da4a 100644 --- a/src/formatter/ExpressionFormatter.ts +++ b/src/formatter/ExpressionFormatter.ts @@ -146,15 +146,24 @@ export default class ExpressionFormatter { } private formatArraySubscript(node: ArraySubscriptNode) { + let formattedArray: string; + + switch (node.array.type) { + case NodeType.data_type: + formattedArray = this.showDataType(node.array); + break; + case NodeType.keyword: + formattedArray = this.showKw(node.array); + break; + default: + formattedArray = this.showIdentifier(node.array); + break; + } + this.withComments(node.array, () => { - this.layout.add( - node.array.type === NodeType.data_type - ? this.showDataType(node.array) - : node.array.type === NodeType.keyword - ? this.showKw(node.array) - : this.showIdentifier(node.array) - ); + this.layout.add(formattedArray); }); + this.formatNode(node.parenthesis); } From fb6092838bd2086d7369bc73ddf8118917c544ba Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Fri, 1 Dec 2023 18:09:41 +0100 Subject: [PATCH 11/15] Improve grammar and wording --- test/features/arrayLiterals.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/features/arrayLiterals.ts b/test/features/arrayLiterals.ts index beaa24e659..cc17e43694 100644 --- a/test/features/arrayLiterals.ts +++ b/test/features/arrayLiterals.ts @@ -12,20 +12,20 @@ export default function supportsArrayLiterals(format: FormatFn, cfg: ArrayLitera it('supports ARRAY[] literals', () => { expect( format( - `SELECT ARRAY[1, 2, 3] FROM ARRAY['cammon', 'seriously', 'this', 'is', 'one', 'hello-of-a', 'damn', 'long', 'array'];` + `SELECT ARRAY[1, 2, 3] FROM ARRAY['come-on', 'seriously', 'this', 'is', 'a', 'very', 'very', 'long', 'array'];` ) ).toBe(dedent` SELECT ARRAY[1, 2, 3] FROM ARRAY[ - 'cammon', + 'come-on', 'seriously', 'this', 'is', - 'one', - 'hello-of-a', - 'damn', + 'a', + 'very', + 'very', 'long', 'array' ]; @@ -37,20 +37,20 @@ export default function supportsArrayLiterals(format: FormatFn, cfg: ArrayLitera it('supports array literals', () => { expect( format( - `SELECT [1, 2, 3] FROM ['cammon', 'seriously', 'this', 'is', 'one', 'hello-of-a', 'damn', 'long', 'array'];` + `SELECT [1, 2, 3] FROM ['come-on', 'seriously', 'this', 'is', 'a', 'very', 'very', 'long', 'array'];` ) ).toBe(dedent` SELECT [1, 2, 3] FROM [ - 'cammon', + 'come-on', 'seriously', 'this', 'is', - 'one', - 'hello-of-a', - 'damn', + 'a', + 'very', + 'very', 'long', 'array' ]; From 66d596ebce3d9a54495aa24941023c5f11653be4 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Fri, 1 Dec 2023 18:22:26 +0100 Subject: [PATCH 12/15] Add tests for casing ARRAY data types --- src/languages/db2/db2.keywords.ts | 1 + src/languages/db2i/db2i.keywords.ts | 1 + test/features/arrayLiterals.ts | 81 +++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/src/languages/db2/db2.keywords.ts b/src/languages/db2/db2.keywords.ts index d012308080..8762576390 100644 --- a/src/languages/db2/db2.keywords.ts +++ b/src/languages/db2/db2.keywords.ts @@ -403,6 +403,7 @@ export const keywords: string[] = [ export const dataTypes: string[] = [ // https://www.ibm.com/docs/en/db2-for-zos/12?topic=columns-data-types + 'ARRAY', 'CCSID', 'CHAR', 'CHARACTER', diff --git a/src/languages/db2i/db2i.keywords.ts b/src/languages/db2i/db2i.keywords.ts index 9e54732fca..3c71319bca 100644 --- a/src/languages/db2i/db2i.keywords.ts +++ b/src/languages/db2i/db2i.keywords.ts @@ -498,6 +498,7 @@ export const keywords: string[] = [ export const dataTypes: string[] = [ // https://www.ibm.com/docs/en/i/7.2?topic=iaodsd-odbc-data-types-how-they-correspond-db2-i-database-types + 'ARRAY', 'BIGINT', 'BINARY', 'BIT', diff --git a/test/features/arrayLiterals.ts b/test/features/arrayLiterals.ts index cc17e43694..f7761b4bb3 100644 --- a/test/features/arrayLiterals.ts +++ b/test/features/arrayLiterals.ts @@ -31,6 +31,87 @@ export default function supportsArrayLiterals(format: FormatFn, cfg: ArrayLitera ]; `); }); + + it('supports preserving ARRAY[] literals keywords casing', () => { + expect( + format( + `SELECT ArrAy[1, 2] FROM aRRAY['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff', 'ggg', 'hhh', 'iii', 'jjj'];`, + { + dataTypeCase: 'preserve', + } + ) + ).toBe(dedent` + SELECT + ArrAy[1, 2] + FROM + aRRAY[ + 'aaa', + 'bbb', + 'ccc', + 'ddd', + 'eee', + 'fff', + 'ggg', + 'hhh', + 'iii', + 'jjj' + ]; + `); + }); + + it('supports converting ARRAY[] literals keywords to uppercase', () => { + expect( + format( + `SELECT ArrAy[1, 2] FROM aRRAY['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff', 'ggg', 'hhh', 'iii', 'jjj'];`, + { + dataTypeCase: 'upper', + } + ) + ).toBe(dedent` + SELECT + ARRAY[1, 2] + FROM + ARRAY[ + 'aaa', + 'bbb', + 'ccc', + 'ddd', + 'eee', + 'fff', + 'ggg', + 'hhh', + 'iii', + 'jjj' + ]; + `); + }); + + it('supports converting ARRAY[] literals keywords to lowercase', () => { + expect( + format( + `SELECT ArrAy[1, 2] FROM aRRAY['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff', 'ggg', 'hhh', 'iii', 'jjj'];`, + { + dataTypeCase: 'lower', + } + ) + ).toBe(dedent` + SELECT + array[1, 2] + FROM + array[ + 'aaa', + 'bbb', + 'ccc', + 'ddd', + 'eee', + 'fff', + 'ggg', + 'hhh', + 'iii', + 'jjj' + ]; + `); + }); } if (cfg.withoutArrayPrefix) { From 8d8124404f04e41789ea8650080e658966f9ec81 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Sat, 2 Dec 2023 15:52:52 +0100 Subject: [PATCH 13/15] Add tests for dataTypeCase and functionCase --- src/languages/db2/db2.keywords.ts | 4 ++ src/languages/db2i/db2i.keywords.ts | 1 + src/languages/mysql/mysql.keywords.ts | 5 ++ src/languages/redshift/redshift.keywords.ts | 7 +++ src/lexer/disambiguateTokens.ts | 34 ++++++++++- test/behavesLikeMariaDbFormatter.ts | 5 +- test/behavesLikeSqlFormatter.ts | 2 + test/bigquery.test.ts | 8 ++- test/db2.test.ts | 2 + test/db2i.test.ts | 2 + test/features/createTable.ts | 27 ++++++--- test/hive.test.ts | 2 + test/mariadb.test.ts | 2 + test/mysql.test.ts | 2 + test/options/dataTypeCase.ts | 67 +++++++++++++++++++++ test/options/functionCase.ts | 39 ++++++++++++ test/plsql.test.ts | 2 + test/postgresql.test.ts | 2 + test/redshift.test.ts | 2 + test/singlestoredb.test.ts | 2 + test/snowflake.test.ts | 2 + test/spark.test.ts | 2 + test/sql.test.ts | 2 + test/sqlite.test.ts | 2 + test/transactsql.test.ts | 2 + test/trino.test.ts | 2 + 26 files changed, 218 insertions(+), 11 deletions(-) create mode 100644 test/options/dataTypeCase.ts create mode 100644 test/options/functionCase.ts diff --git a/src/languages/db2/db2.keywords.ts b/src/languages/db2/db2.keywords.ts index 8762576390..6196cdbc2a 100644 --- a/src/languages/db2/db2.keywords.ts +++ b/src/languages/db2/db2.keywords.ts @@ -404,12 +404,16 @@ export const keywords: string[] = [ export const dataTypes: string[] = [ // https://www.ibm.com/docs/en/db2-for-zos/12?topic=columns-data-types 'ARRAY', + 'BIGINT', 'CCSID', 'CHAR', 'CHARACTER', 'DATE', 'DOUBLE', + 'INT', + 'INTEGER', 'LONG', + 'SMALLINT', 'TIME', 'TIMESTAMP', ]; diff --git a/src/languages/db2i/db2i.keywords.ts b/src/languages/db2i/db2i.keywords.ts index 3c71319bca..d2664ea4a5 100644 --- a/src/languages/db2i/db2i.keywords.ts +++ b/src/languages/db2i/db2i.keywords.ts @@ -517,6 +517,7 @@ export const dataTypes: string[] = [ 'DOUBLE', 'FLOAT', 'GRAPHIC', + 'INT', 'INTEGER', 'LONG', 'NUMERIC', diff --git a/src/languages/mysql/mysql.keywords.ts b/src/languages/mysql/mysql.keywords.ts index bbbf5be7f9..6769724642 100644 --- a/src/languages/mysql/mysql.keywords.ts +++ b/src/languages/mysql/mysql.keywords.ts @@ -235,8 +235,12 @@ export const dataTypes: string[] = [ 'BIGINT', // (R) 'BINARY', // (R) 'BLOB', // (R) + 'BOOL', // (R) + 'BOOLEAN', // (R) 'CHAR', // (R) 'CHARACTER', // (R) + 'DATE', // (R) + 'DATETIME', // (R) 'DEC', // (R) 'DECIMAL', // (R) 'DOUBLE', // (R) @@ -260,6 +264,7 @@ export const dataTypes: string[] = [ 'PRECISION', // (R) 'REAL', // (R) 'SMALLINT', // (R) + 'TIMESTAMP', // (R) 'TINYBLOB', // (R) 'TINYINT', // (R) 'TINYTEXT', // (R) diff --git a/src/languages/redshift/redshift.keywords.ts b/src/languages/redshift/redshift.keywords.ts index dbf1afe492..6b548e6422 100644 --- a/src/languages/redshift/redshift.keywords.ts +++ b/src/languages/redshift/redshift.keywords.ts @@ -201,6 +201,13 @@ export const keywords: string[] = [ export const dataTypes: string[] = [ // https://docs.aws.amazon.com/redshift/latest/dg/r_Character_types.html#r_Character_types-text-and-bpchar-types 'ARRAY', + 'BIGINT', 'BPCHAR', + 'INT', + 'INT2', + 'INT4', + 'INT8', + 'INTEGER', + 'SMALLINT', 'TEXT', ]; diff --git a/src/lexer/disambiguateTokens.ts b/src/lexer/disambiguateTokens.ts index e08013dfeb..5586d032d3 100644 --- a/src/lexer/disambiguateTokens.ts +++ b/src/lexer/disambiguateTokens.ts @@ -35,7 +35,39 @@ const funcNameToKeyword = (token: Token, i: number, tokens: Token[]): Token => { if (token.type === TokenType.RESERVED_FUNCTION_NAME) { const nextToken = nextNonCommentToken(tokens, i); if (!nextToken || !isOpenParen(nextToken)) { - return { ...token, type: TokenType.RESERVED_KEYWORD }; + return { + ...token, + type: + // Function names which are also data types + [ + 'BIGINT', + 'BINARY', + 'BIT', + 'BLOB', + 'BOOLEAN', + 'CHAR', + 'DATE', + 'DECIMAL', + 'DOUBLE', + 'FLOAT', + 'INT', + 'INTEGER', + 'JSON', + 'NCHAR', + 'NUMBER', + 'NVARCHAR', + 'REAL', + 'SMALLINT', + 'TEXT', + 'TIME', + 'TIMESTAMP', + 'TINYINT', + 'VARCHAR', + 'XML', + ].includes(token.text) + ? TokenType.RESERVED_DATA_TYPE + : TokenType.RESERVED_KEYWORD, + }; } } return token; diff --git a/test/behavesLikeMariaDbFormatter.ts b/test/behavesLikeMariaDbFormatter.ts index 9fe47ddd0e..aa32f58c23 100644 --- a/test/behavesLikeMariaDbFormatter.ts +++ b/test/behavesLikeMariaDbFormatter.ts @@ -163,7 +163,10 @@ export default function behavesLikeMariaDbFormatter(format: FormatFn) { `create table account (id int comment 'the most important column'); select * from mysql.user; insert into user (id, name) values (1, 'Blah');`, - { keywordCase: 'upper' } + { + keywordCase: 'upper', + dataTypeCase: 'upper', + } ) ).toBe(dedent` CREATE TABLE diff --git a/test/behavesLikeSqlFormatter.ts b/test/behavesLikeSqlFormatter.ts index 25f990c6ea..bcd8da6537 100644 --- a/test/behavesLikeSqlFormatter.ts +++ b/test/behavesLikeSqlFormatter.ts @@ -16,6 +16,7 @@ import supportsNewlineBeforeSemicolon from './options/newlineBeforeSemicolon.js' import supportsLogicalOperatorNewline from './options/logicalOperatorNewline.js'; import supportsParamTypes from './options/paramTypes.js'; import supportsWindowFunctions from './features/windowFunctions.js'; +import supportsFunctionCase from './options/functionCase.js'; /** * Core tests for all SQL formatters @@ -29,6 +30,7 @@ export default function behavesLikeSqlFormatter(format: FormatFn) { supportsUseTabs(format); supportsKeywordCase(format); supportsIdentifierCase(format); + supportsFunctionCase(format); supportsIndentStyle(format); supportsLinesBetweenQueries(format); supportsExpressionWidth(format); diff --git a/test/bigquery.test.ts b/test/bigquery.test.ts index 2456953bcf..11a1a9f9b9 100644 --- a/test/bigquery.test.ts +++ b/test/bigquery.test.ts @@ -24,6 +24,7 @@ import supportsMergeInto from './features/mergeInto.js'; import supportsCreateView from './features/createView.js'; import supportsAlterTable from './features/alterTable.js'; import supportsIsDistinctFrom from './features/isDistinctFrom.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('BigQueryFormatter', () => { const language = 'bigquery'; @@ -32,7 +33,11 @@ describe('BigQueryFormatter', () => { behavesLikeSqlFormatter(format); supportsComments(format, { hashComments: true }); supportsCreateView(format, { orReplace: true, materialized: true, ifNotExists: true }); - supportsCreateTable(format, { orReplace: true, ifNotExists: true }); + supportsCreateTable(format, { + orReplace: true, + ifNotExists: true, + dialectDoesntHaveVarchar: true, + }); supportsDropTable(format, { ifExists: true }); supportsAlterTable(format, { addColumn: true, @@ -60,6 +65,7 @@ describe('BigQueryFormatter', () => { supportsParams(format, { positional: true, named: ['@'], quoted: ['@``'] }); supportsWindow(format); supportsLimiting(format, { limit: true, offset: true }); + supportsDataTypeCase(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/db2.test.ts b/test/db2.test.ts index 8dc642a3fc..9cde32e856 100644 --- a/test/db2.test.ts +++ b/test/db2.test.ts @@ -11,6 +11,7 @@ import supportsStrings from './features/strings.js'; import supportsComments from './features/comments.js'; import supportsOperators from './features/operators.js'; import supportsLimiting from './features/limiting.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('Db2Formatter', () => { const language = 'db2'; @@ -49,6 +50,7 @@ describe('Db2Formatter', () => { ]); // Additional U& string type in addition to others shared by all DB2 implementations supportsStrings(format, ["U&''"]); + supportsDataTypeCase(format); it('supports non-standard FOR clause', () => { expect(format('SELECT * FROM tbl FOR UPDATE OF other_tbl FOR RS USE AND KEEP EXCLUSIVE LOCKS')) diff --git a/test/db2i.test.ts b/test/db2i.test.ts index 36b3c23885..bc56529e1d 100644 --- a/test/db2i.test.ts +++ b/test/db2i.test.ts @@ -8,6 +8,7 @@ import supportsDropTable from './features/dropTable.js'; import supportsJoin from './features/join.js'; import supportsOperators from './features/operators.js'; import supportsLimiting from './features/limiting.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('Db2iFormatter', () => { const language = 'db2i'; @@ -28,4 +29,5 @@ describe('Db2iFormatter', () => { additionally: ['EXCEPTION JOIN', 'LEFT EXCEPTION JOIN', 'RIGHT EXCEPTION JOIN'], }); supportsOperators(format, ['**', '¬=', '¬>', '¬<', '!>', '!<', '||', '=>']); + supportsDataTypeCase(format); }); diff --git a/test/features/createTable.ts b/test/features/createTable.ts index 508d9c1a86..223a8c981d 100644 --- a/test/features/createTable.ts +++ b/test/features/createTable.ts @@ -7,12 +7,10 @@ interface CreateTableConfig { ifNotExists?: boolean; columnComment?: boolean; tableComment?: boolean; + dialectDoesntHaveVarchar?: boolean; } -export default function supportsCreateTable( - format: FormatFn, - { orReplace, ifNotExists, columnComment, tableComment }: CreateTableConfig = {} -) { +export default function supportsCreateTable(format: FormatFn, cfg: CreateTableConfig = {}) { it('formats short CREATE TABLE', () => { expect(format('CREATE TABLE tbl (a INT PRIMARY KEY, b TEXT);')).toBe(dedent` CREATE TABLE @@ -20,6 +18,19 @@ export default function supportsCreateTable( `); }); + if (!cfg.dialectDoesntHaveVarchar) { + it('formats short CREATE TABLE with lowercase data types', () => { + expect( + format('CREATE TABLE tbl (a INT PRIMARY KEY, b VARCHAR);', { + dataTypeCase: 'lower', + }) + ).toBe(dedent` + CREATE TABLE + tbl (a int PRIMARY KEY, b varchar); + `); + }); + } + // The decision to place it to multiple lines is made based on the length of text inside braces // ignoring the whitespace. (Which is not quite right :P) it('formats long CREATE TABLE', () => { @@ -36,7 +47,7 @@ export default function supportsCreateTable( `); }); - if (orReplace) { + if (cfg.orReplace) { it('formats short CREATE OR REPLACE TABLE', () => { expect(format('CREATE OR REPLACE TABLE tbl (a INT PRIMARY KEY, b TEXT);')).toBe(dedent` CREATE OR REPLACE TABLE @@ -45,7 +56,7 @@ export default function supportsCreateTable( }); } - if (ifNotExists) { + if (cfg.ifNotExists) { it('formats short CREATE TABLE IF NOT EXISTS', () => { expect(format('CREATE TABLE IF NOT EXISTS tbl (a INT PRIMARY KEY, b TEXT);')).toBe(dedent` CREATE TABLE IF NOT EXISTS @@ -54,7 +65,7 @@ export default function supportsCreateTable( }); } - if (columnComment) { + if (cfg.columnComment) { it('formats short CREATE TABLE with column comments', () => { expect( format(`CREATE TABLE tbl (a INT COMMENT 'Hello world!', b TEXT COMMENT 'Here we are!');`) @@ -68,7 +79,7 @@ export default function supportsCreateTable( }); } - if (tableComment) { + if (cfg.tableComment) { it('formats short CREATE TABLE with comment', () => { expect(format(`CREATE TABLE tbl (a INT, b TEXT) COMMENT = 'Hello, world!';`)).toBe(dedent` CREATE TABLE diff --git a/test/hive.test.ts b/test/hive.test.ts index 8327fe7321..eb4554fb36 100644 --- a/test/hive.test.ts +++ b/test/hive.test.ts @@ -22,6 +22,7 @@ import supportsDeleteFrom from './features/deleteFrom.js'; import supportsTruncateTable from './features/truncateTable.js'; import supportsMergeInto from './features/mergeInto.js'; import supportsCreateView from './features/createView.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('HiveFormatter', () => { const language = 'hive'; @@ -50,6 +51,7 @@ describe('HiveFormatter', () => { supportsArrayAndMapAccessors(format); supportsWindow(format); supportsLimiting(format, { limit: true }); + supportsDataTypeCase(format); // eslint-disable-next-line no-template-curly-in-string it('recognizes ${hivevar:name} substitution variables', () => { diff --git a/test/mariadb.test.ts b/test/mariadb.test.ts index d76dfdbcf2..cfb081273c 100644 --- a/test/mariadb.test.ts +++ b/test/mariadb.test.ts @@ -14,6 +14,7 @@ import supportsCreateView from './features/createView.js'; import supportsAlterTable from './features/alterTable.js'; import supportsStrings from './features/strings.js'; import supportsConstraints from './features/constraints.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('MariaDbFormatter', () => { const language = 'mariadb'; @@ -52,6 +53,7 @@ describe('MariaDbFormatter', () => { renameTo: true, renameColumn: true, }); + supportsDataTypeCase(format); it(`supports @"name" variables`, () => { expect(format(`SELECT @"foo fo", @"foo\\"x", @"foo""y" FROM tbl;`)).toBe(dedent` diff --git a/test/mysql.test.ts b/test/mysql.test.ts index 405476f83c..3bad166e4c 100644 --- a/test/mysql.test.ts +++ b/test/mysql.test.ts @@ -14,6 +14,7 @@ import supportsCreateView from './features/createView.js'; import supportsAlterTable from './features/alterTable.js'; import supportsStrings from './features/strings.js'; import supportsConstraints from './features/constraints.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('MySqlFormatter', () => { const language = 'mysql'; @@ -54,6 +55,7 @@ describe('MySqlFormatter', () => { renameTo: true, renameColumn: true, }); + supportsDataTypeCase(format); it(`supports @"name" variables`, () => { expect(format(`SELECT @"foo fo", @"foo\\"x", @"foo""y" FROM tbl;`)).toBe(dedent` diff --git a/test/options/dataTypeCase.ts b/test/options/dataTypeCase.ts new file mode 100644 index 0000000000..1e3e799938 --- /dev/null +++ b/test/options/dataTypeCase.ts @@ -0,0 +1,67 @@ +import dedent from 'dedent-js'; + +import { FormatFn } from '../../src/sqlFormatter.js'; + +export default function supportsDataTypeCase(format: FormatFn) { + it('preserves data type keyword case by default', () => { + const result = format('CREATE TABLE users ( id iNt PRIMARY KEY )'); + expect(result).toBe(dedent` + CREATE TABLE + users (id iNt PRIMARY KEY) + `); + }); + + it('converts data type keyword case to uppercase', () => { + const result = format('CREATE TABLE users ( id iNt PRIMARY KEY )', { + dataTypeCase: 'upper', + }); + expect(result).toBe(dedent` + CREATE TABLE + users (id INT PRIMARY KEY) + `); + }); + + it('converts data type keyword case to lowercase', () => { + const result = format('CREATE TABLE users ( id iNt PRIMARY KEY )', { + dataTypeCase: 'lower', + }); + expect(result).toBe(dedent` + CREATE TABLE + users (id int PRIMARY KEY) + `); + }); + + it('preserves data type keyword case in cast by default', () => { + const result = format('SELECT CAST(quantity AS InT) FROM orders'); + expect(result).toBe(dedent` + SELECT + CAST(quantity AS InT) + FROM + orders + `); + }); + + it('converts data type keyword case in cast to uppercase', () => { + const result = format('SELECT CAST(quantity AS InT) FROM orders', { + dataTypeCase: 'upper', + }); + expect(result).toBe(dedent` + SELECT + CAST(quantity AS INT) + FROM + orders + `); + }); + + it('converts data type keyword case in cast to lowercase', () => { + const result = format('SELECT CAST(quantity AS InT) FROM orders', { + dataTypeCase: 'lower', + }); + expect(result).toBe(dedent` + SELECT + CAST(quantity AS int) + FROM + orders + `); + }); +} diff --git a/test/options/functionCase.ts b/test/options/functionCase.ts new file mode 100644 index 0000000000..437951fc5b --- /dev/null +++ b/test/options/functionCase.ts @@ -0,0 +1,39 @@ +import dedent from 'dedent-js'; + +import { FormatFn } from '../../src/sqlFormatter.js'; + +export default function supportsFunctionCase(format: FormatFn) { + it('preserves function name case by default', () => { + const result = format('SELECT MiN(price) AS min_price FROM products'); + expect(result).toBe(dedent` + SELECT + MiN(price) AS min_price + FROM + products + `); + }); + + it('converts function names to uppercase', () => { + const result = format('SELECT MiN(price) AS min_price FROM products', { + functionCase: 'upper', + }); + expect(result).toBe(dedent` + SELECT + MIN(price) AS min_price + FROM + products + `); + }); + + it('converts function names to lowercase', () => { + const result = format('SELECT MiN(price) AS min_price FROM products', { + functionCase: 'lower', + }); + expect(result).toBe(dedent` + SELECT + min(price) AS min_price + FROM + products + `); + }); +} diff --git a/test/plsql.test.ts b/test/plsql.test.ts index d1fb8924f9..2d233c9575 100644 --- a/test/plsql.test.ts +++ b/test/plsql.test.ts @@ -25,6 +25,7 @@ import supportsUpdate from './features/update.js'; import supportsTruncateTable from './features/truncateTable.js'; import supportsMergeInto from './features/mergeInto.js'; import supportsCreateView from './features/createView.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('PlSqlFormatter', () => { const language = 'plsql'; @@ -64,6 +65,7 @@ describe('PlSqlFormatter', () => { supportsReturning(format); supportsParams(format, { numbered: [':'], named: [':'] }); supportsLimiting(format, { offset: true, fetchFirst: true, fetchNext: true }); + supportsDataTypeCase(format); it('recognizes _, $, # as part of identifiers', () => { const result = format('SELECT my_col$1#, col.a$, type#, procedure$, user# FROM tbl;'); diff --git a/test/postgresql.test.ts b/test/postgresql.test.ts index 607f376a97..24f54a72d7 100644 --- a/test/postgresql.test.ts +++ b/test/postgresql.test.ts @@ -29,6 +29,7 @@ import supportsCreateView from './features/createView.js'; import supportsOnConflict from './features/onConflict.js'; import supportsIsDistinctFrom from './features/isDistinctFrom.js'; import supportsArrayLiterals from './features/arrayLiterals.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('PostgreSqlFormatter', () => { const language = 'postgresql'; @@ -150,6 +151,7 @@ describe('PostgreSqlFormatter', () => { supportsParams(format, { numbered: ['$'] }); supportsWindow(format); supportsLimiting(format, { limit: true, offset: true, fetchFirst: true, fetchNext: true }); + supportsDataTypeCase(format); it('allows $ character as part of identifiers', () => { expect(format('SELECT foo$, some$$ident')).toBe(dedent` diff --git a/test/redshift.test.ts b/test/redshift.test.ts index c44a1d0859..d7b6e6766e 100644 --- a/test/redshift.test.ts +++ b/test/redshift.test.ts @@ -20,6 +20,7 @@ import supportsInsertInto from './features/insertInto.js'; import supportsUpdate from './features/update.js'; import supportsTruncateTable from './features/truncateTable.js'; import supportsCreateView from './features/createView.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('RedshiftFormatter', () => { const language = 'redshift'; @@ -49,6 +50,7 @@ describe('RedshiftFormatter', () => { supportsSetOperations(format, ['UNION', 'UNION ALL', 'EXCEPT', 'INTERSECT', 'MINUS']); supportsParams(format, { numbered: ['$'] }); supportsLimiting(format, { limit: true, offset: true }); + supportsDataTypeCase(format); it('formats type-cast operator without spaces', () => { expect(format('SELECT 2 :: numeric AS foo;')).toBe(dedent` diff --git a/test/singlestoredb.test.ts b/test/singlestoredb.test.ts index deac08e45a..8ae5a75d73 100644 --- a/test/singlestoredb.test.ts +++ b/test/singlestoredb.test.ts @@ -10,6 +10,7 @@ import supportsCreateTable from './features/createTable.js'; import supportsCreateView from './features/createView.js'; import supportsAlterTable from './features/alterTable.js'; import supportsStrings from './features/strings.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('SingleStoreDbFormatter', () => { const language = 'singlestoredb'; @@ -46,6 +47,7 @@ describe('SingleStoreDbFormatter', () => { modify: true, renameTo: true, }); + supportsDataTypeCase(format); describe(`formats traversal of semi structured data`, () => { it(`formats '::' path-operator without spaces`, () => { diff --git a/test/snowflake.test.ts b/test/snowflake.test.ts index 42e3edcb35..23cbc10dd8 100644 --- a/test/snowflake.test.ts +++ b/test/snowflake.test.ts @@ -21,6 +21,7 @@ import supportsUpdate from './features/update.js'; import supportsTruncateTable from './features/truncateTable.js'; import supportsCreateView from './features/createView.js'; import supportsConstraints from './features/constraints.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('SnowflakeFormatter', () => { const language = 'snowflake'; @@ -57,6 +58,7 @@ describe('SnowflakeFormatter', () => { supportsJoin(format, { without: ['NATURAL INNER JOIN'] }); supportsSetOperations(format, ['UNION', 'UNION ALL', 'MINUS', 'EXCEPT', 'INTERSECT']); supportsLimiting(format, { limit: true, offset: true, fetchFirst: true, fetchNext: true }); + supportsDataTypeCase(format); it('allows $ character as part of unquoted identifiers', () => { expect(format('SELECT foo$')).toBe(dedent` diff --git a/test/spark.test.ts b/test/spark.test.ts index 7dd1cffaa4..f406e1ce2c 100644 --- a/test/spark.test.ts +++ b/test/spark.test.ts @@ -18,6 +18,7 @@ import supportsLimiting from './features/limiting.js'; import supportsInsertInto from './features/insertInto.js'; import supportsTruncateTable from './features/truncateTable.js'; import supportsCreateView from './features/createView.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('SparkFormatter', () => { const language = 'spark'; @@ -60,6 +61,7 @@ describe('SparkFormatter', () => { }); supportsSetOperations(format); supportsLimiting(format, { limit: true }); + supportsDataTypeCase(format); it('formats basic WINDOW clause', () => { const result = format(`SELECT * FROM tbl WINDOW win1, WINDOW win2, WINDOW win3;`); diff --git a/test/sql.test.ts b/test/sql.test.ts index 9a8bf58093..02405852c2 100644 --- a/test/sql.test.ts +++ b/test/sql.test.ts @@ -23,6 +23,7 @@ import supportsInsertInto from './features/insertInto.js'; import supportsUpdate from './features/update.js'; import supportsTruncateTable from './features/truncateTable.js'; import supportsCreateView from './features/createView.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('SqlFormatter', () => { const language = 'sql'; @@ -54,6 +55,7 @@ describe('SqlFormatter', () => { supportsParams(format, { positional: true }); supportsWindow(format); supportsLimiting(format, { limit: true, offset: true, fetchFirst: true, fetchNext: true }); + supportsDataTypeCase(format); it('throws error when encountering characters or operators it does not recognize', () => { expect(() => format('SELECT @name, :bar FROM foo;')).toThrowError( diff --git a/test/sqlite.test.ts b/test/sqlite.test.ts index 78ec8e7438..7d12dea399 100644 --- a/test/sqlite.test.ts +++ b/test/sqlite.test.ts @@ -22,6 +22,7 @@ import supportsInsertInto from './features/insertInto.js'; import supportsUpdate from './features/update.js'; import supportsCreateView from './features/createView.js'; import supportsOnConflict from './features/onConflict.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('SqliteFormatter', () => { const language = 'sqlite'; @@ -53,6 +54,7 @@ describe('SqliteFormatter', () => { supportsParams(format, { positional: true, numbered: ['?'], named: [':', '$', '@'] }); supportsWindow(format); supportsLimiting(format, { limit: true, offset: true }); + supportsDataTypeCase(format); it('supports REPLACE INTO syntax', () => { expect(format(`REPLACE INTO tbl VALUES (1,'Leopard'),(2,'Dog');`)).toBe(dedent` diff --git a/test/transactsql.test.ts b/test/transactsql.test.ts index 0a9f0fefbb..96704ac58b 100644 --- a/test/transactsql.test.ts +++ b/test/transactsql.test.ts @@ -23,6 +23,7 @@ import supportsUpdate from './features/update.js'; import supportsTruncateTable from './features/truncateTable.js'; import supportsMergeInto from './features/mergeInto.js'; import supportsCreateView from './features/createView.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('TransactSqlFormatter', () => { const language = 'transactsql'; @@ -68,6 +69,7 @@ describe('TransactSqlFormatter', () => { supportsParams(format, { named: ['@'], quoted: ['@""', '@[]'] }); supportsWindow(format); supportsLimiting(format, { offset: true, fetchFirst: true, fetchNext: true }); + supportsDataTypeCase(format); it('supports language:tsql alias', () => { const result = originalFormat('SELECT [my column] FROM [my table];', { language: 'tsql' }); diff --git a/test/trino.test.ts b/test/trino.test.ts index a300ee4e92..216ed70708 100644 --- a/test/trino.test.ts +++ b/test/trino.test.ts @@ -25,6 +25,7 @@ import supportsTruncateTable from './features/truncateTable.js'; import supportsCreateView from './features/createView.js'; import supportsAlterTable from './features/alterTable.js'; import supportsIsDistinctFrom from './features/isDistinctFrom.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; describe('TrinoFormatter', () => { const language = 'trino'; @@ -59,6 +60,7 @@ describe('TrinoFormatter', () => { supportsParams(format, { positional: true }); supportsWindow(format); supportsLimiting(format, { limit: true, offset: true, fetchFirst: true, fetchNext: true }); + supportsDataTypeCase(format); it('formats SET SESSION', () => { const result = format('SET SESSION foo = 444;'); From a4e40704ae27bc0fef78db30f62d79cff82824b6 Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Sat, 2 Dec 2023 16:02:42 +0100 Subject: [PATCH 14/15] Improve docs --- README.md | 2 ++ docs/dataTypeCase.md | 6 +++--- docs/functionCase.md | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b41da467ba..a6899b2002 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,8 @@ All fields are optional and all fields that are not specified will be filled wit - [**`useTabs`**](docs/useTabs.md) to use tabs for indentation. - [**`keywordCase`**](docs/keywordCase.md) uppercases or lowercases keywords. - [**`identifierCase`**](docs/identifierCase.md) uppercases or lowercases identifiers. (**experimental!**) +- [**`dataTypeCase`**](docs/dataTypeCase.md) uppercases or lowercases data types. (**experimental!**) +- [**`functionCase`**](docs/functionCase.md) uppercases or lowercases function names. (**experimental!**) - [**`indentStyle`**](docs/indentStyle.md) defines overall indentation style. - [**`logicalOperatorNewline`**](docs/logicalOperatorNewline.md) newline before or after boolean operator (AND, OR, XOR). - [**`expressionWidth`**](docs/expressionWidth.md) maximum number of characters in parenthesized expressions to be kept on single line. diff --git a/docs/dataTypeCase.md b/docs/dataTypeCase.md index 4bc336e0a4..97b4deb970 100644 --- a/docs/dataTypeCase.md +++ b/docs/dataTypeCase.md @@ -18,7 +18,7 @@ Note: Casing of function names like `VARCHAR(30)` are not modified - instead rel CREATE TABLE users ( id InTeGeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - first_name VARCHAR(30) NOT NULL, + first_name VarChaR(30) NOT NULL, bio teXT, is_email_verified BooL NOT NULL DEFAULT FALSE, created_timestamp timestamPtz NOT NULL DEFAULT NOW() @@ -31,7 +31,7 @@ CREATE TABLE CREATE TABLE users ( id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - first_name VARCHAR(30) NOT NULL, + first_name VarChaR(30) NOT NULL, bio TEXT, is_email_verified BOOL NOT NULL DEFAULT FALSE, created_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW() @@ -44,7 +44,7 @@ CREATE TABLE CREATE TABLE users ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - first_name VARCHAR(30) NOT NULL, + first_name VarChaR(30) NOT NULL, bio text, is_email_verified bool NOT NULL DEFAULT FALSE, created_timestamp timestamptz NOT NULL DEFAULT NOW() diff --git a/docs/functionCase.md b/docs/functionCase.md index 9132c974e4..cb740d62fb 100644 --- a/docs/functionCase.md +++ b/docs/functionCase.md @@ -1,6 +1,6 @@ # functionCase (experimental) -Converts functions to upper- or lowercase. +Converts function names to upper- or lowercase. ## Options @@ -11,7 +11,7 @@ Converts functions to upper- or lowercase. ### preserve ```sql -CREATE tabLE +CREATE TABLE users ( id iNtegeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, first_name VarChaR(30) NOT NULL, @@ -24,7 +24,7 @@ CREATE tabLE ### upper ```sql -CREATE tabLE +CREATE TABLE users ( id iNtegeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, first_name VARCHAR(30) NOT NULL, @@ -37,7 +37,7 @@ CREATE tabLE ### lower ```sql -CREATE tabLE +CREATE TABLE users ( id iNtegeR PRIMARY KEY GENERATED ALWAYS AS IDENTITY, first_name varchar(30) NOT NULL, From 4ee49653a391fee887c55f6d1be9fcd613fbb25a Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Sun, 3 Dec 2023 13:56:34 +0100 Subject: [PATCH 15/15] Add NUMERIC --- src/lexer/disambiguateTokens.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lexer/disambiguateTokens.ts b/src/lexer/disambiguateTokens.ts index 5586d032d3..ad32cff675 100644 --- a/src/lexer/disambiguateTokens.ts +++ b/src/lexer/disambiguateTokens.ts @@ -55,6 +55,7 @@ const funcNameToKeyword = (token: Token, i: number, tokens: Token[]): Token => { 'JSON', 'NCHAR', 'NUMBER', + 'NUMERIC', 'NVARCHAR', 'REAL', 'SMALLINT',