From bdf3be927cd2065be8fbf93e7ba516448d99a22a Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Mon, 27 Nov 2023 18:51:29 +0100 Subject: [PATCH 01/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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', From 96d3ef784603f4c82751b5e160461af3ea0336fa Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 6 Dec 2023 12:31:47 +0200 Subject: [PATCH 16/35] Revert funcNameToKeyword() to its previous form --- src/lexer/disambiguateTokens.ts | 35 +-------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/lexer/disambiguateTokens.ts b/src/lexer/disambiguateTokens.ts index ad32cff675..e08013dfeb 100644 --- a/src/lexer/disambiguateTokens.ts +++ b/src/lexer/disambiguateTokens.ts @@ -35,40 +35,7 @@ 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: - // Function names which are also data types - [ - 'BIGINT', - 'BINARY', - 'BIT', - 'BLOB', - 'BOOLEAN', - 'CHAR', - 'DATE', - 'DECIMAL', - 'DOUBLE', - 'FLOAT', - 'INT', - 'INTEGER', - 'JSON', - 'NCHAR', - 'NUMBER', - 'NUMERIC', - 'NVARCHAR', - 'REAL', - 'SMALLINT', - 'TEXT', - 'TIME', - 'TIMESTAMP', - 'TINYINT', - 'VARCHAR', - 'XML', - ].includes(token.text) - ? TokenType.RESERVED_DATA_TYPE - : TokenType.RESERVED_KEYWORD, - }; + return { ...token, type: TokenType.RESERVED_KEYWORD }; } } return token; From 23848a19bfb009d3cf4ac301ee360d18048f7106 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 6 Dec 2023 12:38:46 +0200 Subject: [PATCH 17/35] Remove data types from function name lists --- src/languages/bigquery/bigquery.functions.ts | 9 ----- src/languages/hive/hive.functions.ts | 9 ----- src/languages/mariadb/mariadb.functions.ts | 32 ----------------- src/languages/mariadb/mariadb.keywords.ts | 13 +++++++ src/languages/mysql/mysql.functions.ts | 31 ---------------- src/languages/mysql/mysql.keywords.ts | 10 ++++++ src/languages/plsql/plsql.functions.ts | 29 --------------- src/languages/plsql/plsql.keywords.ts | 20 +++++++++-- .../postgresql/postgresql.functions.ts | 14 -------- .../postgresql/postgresql.keywords.ts | 4 ++- src/languages/redshift/redshift.functions.ts | 11 ------ src/languages/redshift/redshift.keywords.ts | 9 +++++ .../singlestoredb/singlestoredb.functions.ts | 30 ---------------- .../singlestoredb/singlestoredb.keywords.ts | 12 ++++++- src/languages/spark/spark.functions.ts | 8 ----- src/languages/spark/spark.keywords.ts | 35 ++++++++++--------- src/languages/sql/sql.functions.ts | 30 ---------------- src/languages/sql/sql.keywords.ts | 15 ++++++-- src/languages/sqlite/sqlite.functions.ts | 12 ------- src/languages/sqlite/sqlite.keywords.ts | 11 ++++++ .../transactsql/transactsql.functions.ts | 17 --------- .../transactsql/transactsql.keywords.ts | 7 +++- 22 files changed, 111 insertions(+), 257 deletions(-) diff --git a/src/languages/bigquery/bigquery.functions.ts b/src/languages/bigquery/bigquery.functions.ts index 8a0663421b..4253fa5a92 100644 --- a/src/languages/bigquery/bigquery.functions.ts +++ b/src/languages/bigquery/bigquery.functions.ts @@ -585,13 +585,4 @@ export const functions: string[] = [ // pivot 'PIVOT', 'UNPIVOT', - - // Data types with parameters like VARCHAR(100) - // https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#parameterized_data_types - 'BYTES', - 'NUMERIC', - 'DECIMAL', - 'BIGNUMERIC', - 'BIGDECIMAL', - 'STRING', ]; diff --git a/src/languages/hive/hive.functions.ts b/src/languages/hive/hive.functions.ts index 588e60cd1c..8328f0a095 100644 --- a/src/languages/hive/hive.functions.ts +++ b/src/languages/hive/hive.functions.ts @@ -216,13 +216,4 @@ export const functions: string[] = [ 'CUME_DIST', 'PERCENT_RANK', 'NTILE', - - // Parameterized data types - // https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=82706456 - // Though in reality Hive only supports parameters for DECIMAL(), - // it doesn't hurt to allow others in here as well. - 'DECIMAL', - 'NUMERIC', - 'VARCHAR', - 'CHAR', ]; diff --git a/src/languages/mariadb/mariadb.functions.ts b/src/languages/mariadb/mariadb.functions.ts index e4c6092a34..563883932e 100644 --- a/src/languages/mariadb/mariadb.functions.ts +++ b/src/languages/mariadb/mariadb.functions.ts @@ -236,36 +236,4 @@ export const functions: string[] = [ // CASE expression shorthands 'COALESCE', 'NULLIF', - // Data types with parameters - // https://mariadb.com/kb/en/data-types/ - 'TINYINT', - 'SMALLINT', - 'MEDIUMINT', - 'INT', - 'INTEGER', - 'BIGINT', - 'DECIMAL', - 'DEC', - 'NUMERIC', - 'FIXED', - // 'NUMBER', // ?? In oracle mode only - 'FLOAT', - 'DOUBLE', - 'DOUBLE PRECISION', - 'REAL', - 'BIT', - 'BINARY', - 'BLOB', - 'CHAR', - 'NATIONAL CHAR', - 'CHAR BYTE', - 'ENUM', - 'VARBINARY', - 'VARCHAR', - 'NATIONAL VARCHAR', - // 'SET' // handled as special-case in postProcess - 'TIME', - 'DATETIME', - 'TIMESTAMP', - 'YEAR', ]; diff --git a/src/languages/mariadb/mariadb.keywords.ts b/src/languages/mariadb/mariadb.keywords.ts index d9fea8da11..0e805a65cf 100644 --- a/src/languages/mariadb/mariadb.keywords.ts +++ b/src/languages/mariadb/mariadb.keywords.ts @@ -221,12 +221,18 @@ export const dataTypes: string[] = [ // https://mariadb.com/kb/en/data-types/ 'BIGINT', 'BINARY', + 'BIT', 'BLOB', + 'CHAR BYTE', 'CHAR', 'CHARACTER', + 'DATETIME', 'DEC', 'DECIMAL', + 'DOUBLE PRECISION', 'DOUBLE', + 'ENUM', + 'FIXED', 'FLOAT', 'FLOAT4', 'FLOAT8', @@ -244,10 +250,14 @@ export const dataTypes: string[] = [ 'MEDIUMINT', 'MEDIUMTEXT', 'MIDDLEINT', + 'NATIONAL CHAR', + 'NATIONAL VARCHAR', 'NUMERIC', 'PRECISION', 'REAL', 'SMALLINT', + 'TEXT', + 'TIMESTAMP', 'TINYBLOB', 'TINYINT', 'TINYTEXT', @@ -255,4 +265,7 @@ export const dataTypes: string[] = [ 'VARCHAR', 'VARCHARACTER', 'VARYING', + 'YEAR', + // 'NUMBER', // ?? In oracle mode only + // 'SET' // handled as special-case in postProcess ]; diff --git a/src/languages/mysql/mysql.functions.ts b/src/languages/mysql/mysql.functions.ts index 6464569058..87593e8a42 100644 --- a/src/languages/mysql/mysql.functions.ts +++ b/src/languages/mysql/mysql.functions.ts @@ -421,35 +421,4 @@ export const functions: string[] = [ // 'XOR', 'YEAR', 'YEARWEEK', - // Data types with parameters - // https://dev.mysql.com/doc/refman/8.0/en/data-types.html - 'BIT', - 'TINYINT', - 'SMALLINT', - 'MEDIUMINT', - 'INT', - 'INTEGER', - 'BIGINT', - 'DECIMAL', - 'DEC', - 'NUMERIC', - 'FIXED', - 'FLOAT', - 'DOUBLE', - 'DOUBLE PRECISION', - 'REAL', - 'DATETIME', - 'TIMESTAMP', - 'TIME', - 'YEAR', - 'CHAR', - 'NATIONAL CHAR', - 'VARCHAR', - 'NATIONAL VARCHAR', - 'BINARY', - 'VARBINARY', - 'BLOB', - 'TEXT', - 'ENUM', - // 'SET' // handled as special-case in postProcess ]; diff --git a/src/languages/mysql/mysql.keywords.ts b/src/languages/mysql/mysql.keywords.ts index 6769724642..999112c1d7 100644 --- a/src/languages/mysql/mysql.keywords.ts +++ b/src/languages/mysql/mysql.keywords.ts @@ -234,6 +234,7 @@ export const dataTypes: string[] = [ // https://dev.mysql.com/doc/refman/8.0/en/data-types.html 'BIGINT', // (R) 'BINARY', // (R) + 'BIT', 'BLOB', // (R) 'BOOL', // (R) 'BOOLEAN', // (R) @@ -243,7 +244,10 @@ export const dataTypes: string[] = [ 'DATETIME', // (R) 'DEC', // (R) 'DECIMAL', // (R) + 'DOUBLE PRECISION', 'DOUBLE', // (R) + 'ENUM', + 'FIXED', 'FLOAT', // (R) 'FLOAT4', // (R) 'FLOAT8', // (R) @@ -260,10 +264,14 @@ export const dataTypes: string[] = [ 'MEDIUMINT', // (R) 'MEDIUMTEXT', // (R) 'MIDDLEINT', // (R) + 'NATIONAL CHAR', // (R) + 'NATIONAL VARCHAR', // (R) 'NUMERIC', // (R) 'PRECISION', // (R) 'REAL', // (R) 'SMALLINT', // (R) + 'TEXT', + 'TIME', 'TIMESTAMP', // (R) 'TINYBLOB', // (R) 'TINYINT', // (R) @@ -272,4 +280,6 @@ export const dataTypes: string[] = [ 'VARCHAR', // (R) 'VARCHARACTER', // (R) 'VARYING', // (R) + 'YEAR', + // 'SET' // handled as special-case in postProcess ]; diff --git a/src/languages/plsql/plsql.functions.ts b/src/languages/plsql/plsql.functions.ts index d3858dabcf..cf545e6175 100644 --- a/src/languages/plsql/plsql.functions.ts +++ b/src/languages/plsql/plsql.functions.ts @@ -281,33 +281,4 @@ export const functions: string[] = [ 'PRESENTNNV', 'PRESENTV', 'PREVIOUS', - - // Parameterized data types - // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Data-Types.html - // Oracle builtin data types - 'VARCHAR2', - 'NVARCHAR2', - 'NUMBER', - 'FLOAT', - 'TIMESTAMP', - 'INTERVAL YEAR', - 'INTERVAL DAY', - 'RAW', - 'UROWID', - 'NCHAR', - // ANSI Data Types - 'CHARACTER', - 'CHAR', - 'CHARACTER VARYING', - 'CHAR VARYING', - 'NATIONAL CHARACTER', - 'NATIONAL CHAR', - 'NATIONAL CHARACTER VARYING', - 'NATIONAL CHAR VARYING', - 'NCHAR VARYING', - 'NUMERIC', - 'DECIMAL', - 'FLOAT', - // SQL/DS and DB2 Data Types - 'VARCHAR', ]; diff --git a/src/languages/plsql/plsql.keywords.ts b/src/languages/plsql/plsql.keywords.ts index c4d92afb7b..1f61e3e731 100644 --- a/src/languages/plsql/plsql.keywords.ts +++ b/src/languages/plsql/plsql.keywords.ts @@ -311,22 +311,36 @@ export const dataTypes: string[] = [ 'BFILE_BASE', 'BINARY', 'BLOB_BASE', - 'CHAR', + 'CHAR VARYING', 'CHAR_BASE', + 'CHAR', + 'CHARACTER VARYING', 'CHARACTER', 'CLOB_BASE', - 'DATE', 'DATE_BASE', + 'DATE', 'DECIMAL', 'DOUBLE', 'FLOAT', 'INT', + 'INTERVAL DAY', + 'INTERVAL YEAR', 'LONG', + 'NATIONAL CHAR VARYING', + 'NATIONAL CHAR', + 'NATIONAL CHARACTER VARYING', + 'NATIONAL CHARACTER', + 'NCHAR VARYING', + 'NCHAR', 'NCHAR', 'NUMBER_BASE', + 'NUMBER', + 'NUMBERIC', + 'NVARCHAR', 'PRECISION', 'RAW', 'TIMESTAMP', + 'UROWID', 'VARCHAR', - 'VARYING', + 'VARCHAR2', ]; diff --git a/src/languages/postgresql/postgresql.functions.ts b/src/languages/postgresql/postgresql.functions.ts index b3f256a94e..8d9abc738e 100644 --- a/src/languages/postgresql/postgresql.functions.ts +++ b/src/languages/postgresql/postgresql.functions.ts @@ -708,18 +708,4 @@ export const functions: string[] = [ // cast 'CAST', - - // Parameterized data types - // https://www.postgresql.org/docs/current/datatype.html - 'BIT', - 'BIT VARYING', - 'CHARACTER', - 'CHARACTER VARYING', - 'VARCHAR', - 'CHAR', - 'DECIMAL', - 'NUMERIC', - 'TIME', - 'TIMESTAMP', - 'ENUM', ]; diff --git a/src/languages/postgresql/postgresql.keywords.ts b/src/languages/postgresql/postgresql.keywords.ts index b15a3c3de1..d00cfae0e0 100644 --- a/src/languages/postgresql/postgresql.keywords.ts +++ b/src/languages/postgresql/postgresql.keywords.ts @@ -441,12 +441,15 @@ export const dataTypes: string[] = [ 'ARRAY', // reserved, requires AS 'BIGINT', // (cannot be function or type) 'BIT', // (cannot be function or type) + 'BIT VARYING', 'BOOL', // (cannot be function or type) 'BOOLEAN', // (cannot be function or type) 'CHAR', // (cannot be function or type), requires AS 'CHARACTER', // (cannot be function or type), requires AS + 'CHARACTER VARYING', 'DECIMAL', // (cannot be function or type) 'DOUBLE', + 'ENUM', 'FLOAT', // (cannot be function or type) 'INT', // (cannot be function or type) 'INTEGER', // (cannot be function or type) @@ -460,7 +463,6 @@ export const dataTypes: string[] = [ 'TIMESTAMP', // (cannot be function or type) 'TIMESTAMPTZ', // (cannot be function or type) 'VARCHAR', // (cannot be function or type) - 'VARYING', // requires AS 'XML', 'ZONE', ]; diff --git a/src/languages/redshift/redshift.functions.ts b/src/languages/redshift/redshift.functions.ts index 5c65e1fb7b..e52ad737e7 100644 --- a/src/languages/redshift/redshift.functions.ts +++ b/src/languages/redshift/redshift.functions.ts @@ -360,15 +360,4 @@ export const functions: string[] = [ 'SLICE_NUM', 'USER', 'VERSION', - - // dataTypes - 'DECIMAL', - 'NUMERIC', - 'CHAR', - 'CHARACTER', - 'VARCHAR', - 'CHARACTER VARYING', - 'NCHAR', - 'NVARCHAR', - 'VARBYTE', ]; diff --git a/src/languages/redshift/redshift.keywords.ts b/src/languages/redshift/redshift.keywords.ts index 6b548e6422..46b73e02c1 100644 --- a/src/languages/redshift/redshift.keywords.ts +++ b/src/languages/redshift/redshift.keywords.ts @@ -203,11 +203,20 @@ export const dataTypes: string[] = [ 'ARRAY', 'BIGINT', 'BPCHAR', + 'CHAR', + 'CHARACTER VARYING', + 'CHARACTER', + 'DECIMAL', 'INT', 'INT2', 'INT4', 'INT8', 'INTEGER', + 'NCHAR', + 'NUMERIC', + 'NVARCHAR', 'SMALLINT', 'TEXT', + 'VARBYTE', + 'VARCHAR', ]; diff --git a/src/languages/singlestoredb/singlestoredb.functions.ts b/src/languages/singlestoredb/singlestoredb.functions.ts index 1ecd4e167b..5f30b55007 100644 --- a/src/languages/singlestoredb/singlestoredb.functions.ts +++ b/src/languages/singlestoredb/singlestoredb.functions.ts @@ -278,34 +278,4 @@ export const functions: string[] = [ 'WEEKDAY', 'WEEKOFYEAR', 'YEAR', - // Data types with parameters - // https://docs.singlestore.com/managed-service/en/reference/sql-reference/data-types.html - 'BIT', - 'TINYINT', - 'SMALLINT', - 'MEDIUMINT', - 'INT', - 'INTEGER', - 'BIGINT', - 'DECIMAL', - 'DEC', - 'NUMERIC', - 'FIXED', - 'FLOAT', - 'DOUBLE', - 'DOUBLE PRECISION', - 'REAL', - 'DATETIME', - 'TIMESTAMP', - 'TIME', - 'YEAR', - 'CHAR', - 'NATIONAL CHAR', - 'VARCHAR', - 'NATIONAL VARCHAR', - 'BINARY', - 'VARBINARY', - 'BLOB', - 'TEXT', - 'ENUM', ]; diff --git a/src/languages/singlestoredb/singlestoredb.keywords.ts b/src/languages/singlestoredb/singlestoredb.keywords.ts index 2a384c9ab3..a9f59ada92 100644 --- a/src/languages/singlestoredb/singlestoredb.keywords.ts +++ b/src/languages/singlestoredb/singlestoredb.keywords.ts @@ -217,12 +217,17 @@ export const dataTypes: string[] = [ // https://docs.singlestore.com/cloud/reference/sql-reference/data-types/ 'BIGINT', 'BINARY', + 'BIT', 'BLOB', 'CHAR', 'CHARACTER', + 'DATETIME', 'DEC', 'DECIMAL', + 'DOUBLE PRECISION', 'DOUBLE', + 'ENUM', + 'FIXED', 'FLOAT', 'FLOAT4', 'FLOAT8', @@ -240,10 +245,15 @@ export const dataTypes: string[] = [ 'MEDIUMINT', 'MEDIUMTEXT', 'MIDDLEINT', + 'NATIONAL CHAR', + 'NATIONAL VARCHAR', 'NUMERIC', 'PRECISION', 'REAL', 'SMALLINT', + 'TEXT', + 'TIME', + 'TIMESTAMP', 'TINYBLOB', 'TINYINT', 'TINYTEXT', @@ -251,5 +261,5 @@ export const dataTypes: string[] = [ 'VARBINARY', 'VARCHAR', 'VARCHARACTER', - 'VARYING', + 'YEAR', ]; diff --git a/src/languages/spark/spark.functions.ts b/src/languages/spark/spark.functions.ts index 78e6dbb78f..2dbca67034 100644 --- a/src/languages/spark/spark.functions.ts +++ b/src/languages/spark/spark.functions.ts @@ -340,12 +340,4 @@ export const functions: string[] = [ // Shorthand functions to use in place of CASE expression 'COALESCE', 'NULLIF', - - // Parameterized data types - // https://spark.apache.org/docs/latest/sql-ref-datatypes.html - 'DECIMAL', - 'DEC', - 'NUMERIC', - // No varchar type in Spark, only STRING. Added for the sake of tests - 'VARCHAR', ]; diff --git a/src/languages/spark/spark.keywords.ts b/src/languages/spark/spark.keywords.ts index 1d143f1044..bba4df8073 100644 --- a/src/languages/spark/spark.keywords.ts +++ b/src/languages/spark/spark.keywords.ts @@ -269,29 +269,30 @@ export const keywords: string[] = [ export const dataTypes: string[] = [ // https://spark.apache.org/docs/latest/sql-ref-datatypes.html + 'ARRAY', + 'BIGINT', + 'BINARY', 'BOOLEAN', 'BYTE', - 'TINYINT', - 'SHORT', - 'SMALLINT', + 'DATE', + 'DEC', + 'DECIMAL', + 'DOUBLE', + 'FLOAT', 'INT', 'INTEGER', + 'INTERVAL', 'LONG', - 'BIGINT', - 'FLOAT', + 'MAP', + 'NUMERIC', 'REAL', - 'DOUBLE', - 'DATE', - 'TIMESTAMP', - 'TIMESTAMP_LTZ', - 'TIMESTAMP_NTZ', + 'SHORT', + 'SMALLINT', 'STRING', - 'BINARY', - 'DECIMAL', - 'DEC', - 'NUMERIC', - 'INTERVAL', - 'ARRAY', 'STRUCT', - 'MAP', + 'TIMESTAMP_LTZ', + 'TIMESTAMP_NTZ', + 'TIMESTAMP', + 'TINYINT', + 'VARCHAR', // No varchar type in Spark, only STRING. Added for the sake of tests ]; diff --git a/src/languages/sql/sql.functions.ts b/src/languages/sql/sql.functions.ts index d608f6e5f3..feb03caf9b 100644 --- a/src/languages/sql/sql.functions.ts +++ b/src/languages/sql/sql.functions.ts @@ -104,34 +104,4 @@ export const functions: string[] = [ 'ASIN', 'ACOS', 'ATAN', - - // Data types with parameters like VARCHAR(100) - // https://jakewheat.github.io/sql-overview/sql-2008-foundation-grammar.html#predefined-type - 'CHARACTER', - 'CHAR', - 'CHARACTER VARYING', - 'CHAR VARYING', - 'VARCHAR', - 'CHARACTER LARGE OBJECT', - 'CHAR LARGE OBJECT', - 'CLOB', - 'NATIONAL CHARACTER', - 'NATIONAL CHAR', - 'NCHAR', - 'NATIONAL CHARACTER VARYING', - 'NATIONAL CHAR VARYING', - 'NCHAR VARYING', - 'NATIONAL CHARACTER LARGE OBJECT', - 'NCHAR LARGE OBJECT', - 'NCLOB', - 'BINARY', - 'BINARY VARYING', - 'VARBINARY', - 'BINARY LARGE OBJECT', - 'BLOB', - 'NUMERIC', - 'DECIMAL', - 'DEC', - 'TIME', - 'TIMESTAMP', ]; diff --git a/src/languages/sql/sql.keywords.ts b/src/languages/sql/sql.keywords.ts index 79a1f474a6..3dfe858331 100644 --- a/src/languages/sql/sql.keywords.ts +++ b/src/languages/sql/sql.keywords.ts @@ -200,10 +200,16 @@ export const dataTypes: string[] = [ // https://jakewheat.github.io/sql-overview/sql-2008-foundation-grammar.html#_6_1_data_type 'ARRAY', 'BIGINT', + 'BINARY LARGE OBJECT', + 'BINARY VARYING', 'BINARY', 'BLOB', 'BOOLEAN', + 'CHAR LARGE OBJECT', + 'CHAR VARYING', 'CHAR', + 'CHARACTER LARGE OBJECT', + 'CHARACTER VARYING', 'CHARACTER', 'CLOB', 'DATE', @@ -215,7 +221,13 @@ export const dataTypes: string[] = [ 'INTEGER', 'INTERVAL', 'MULTISET', - 'NATIONAL', + 'NATIONAL CHAR VARYING', + 'NATIONAL CHAR', + 'NATIONAL CHARACTER LARGE OBJECT', + 'NATIONAL CHARACTER VARYING', + 'NATIONAL CHARACTER', + 'NCHAR LARGE OBJECT', + 'NCHAR VARYING', 'NCHAR', 'NCLOB', 'NUMERIC', @@ -224,5 +236,4 @@ export const dataTypes: string[] = [ 'TIMESTAMP', 'VARBINARY', 'VARCHAR', - 'VARYING', ]; diff --git a/src/languages/sqlite/sqlite.functions.ts b/src/languages/sqlite/sqlite.functions.ts index 782ea8e2d5..7878802d10 100644 --- a/src/languages/sqlite/sqlite.functions.ts +++ b/src/languages/sqlite/sqlite.functions.ts @@ -128,16 +128,4 @@ export const functions: string[] = [ // cast 'CAST', - - // SQLite allows parameters for all data types - // Well, in fact it allows any word as a data type, e.g. CREATE TABLE foo (col1 madeupname(123)); - // https://www.sqlite.org/datatype3.html - 'CHARACTER', - 'VARCHAR', - 'VARYING CHARACTER', - 'NCHAR', - 'NATIVE CHARACTER', - 'NVARCHAR', - 'NUMERIC', - 'DECIMAL', ]; diff --git a/src/languages/sqlite/sqlite.keywords.ts b/src/languages/sqlite/sqlite.keywords.ts index f82376b0ab..90f84a618a 100644 --- a/src/languages/sqlite/sqlite.keywords.ts +++ b/src/languages/sqlite/sqlite.keywords.ts @@ -153,13 +153,24 @@ export const keywords: string[] = [ ]; export const dataTypes: string[] = [ + // SQLite allows any word as a data type, e.g. CREATE TABLE foo (col1 madeupname(123)); + // Here we just list some common ones as SQL Formatter + // is only able to detect a predefined list of data types. // https://www.sqlite.org/stricttables.html // https://www.sqlite.org/datatype3.html 'ANY', 'ARRAY', 'BLOB', + 'CHARACTER', + 'DECIMAL', 'INT', 'INTEGER', + 'NATIVE CHARACTER', + 'NCHAR', + 'NUMERIC', + 'NVARCHAR', 'REAL', 'TEXT', + 'VARCHAR', + 'VARYING CHARACTER', ]; diff --git a/src/languages/transactsql/transactsql.functions.ts b/src/languages/transactsql/transactsql.functions.ts index ae055632b2..326a2cb995 100644 --- a/src/languages/transactsql/transactsql.functions.ts +++ b/src/languages/transactsql/transactsql.functions.ts @@ -321,21 +321,4 @@ export const functions: string[] = [ // Shorthand functions to use in place of CASE expression 'COALESCE', 'NULLIF', - - // Parameterized types - // https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql?view=sql-server-ver15 - - 'DECIMAL', - 'NUMERIC', - 'FLOAT', - 'REAL', - 'DATETIME2', - 'DATETIMEOFFSET', - 'TIME', - 'CHAR', - 'VARCHAR', - 'NCHAR', - 'NVARCHAR', - 'BINARY', - 'VARBINARY', ]; diff --git a/src/languages/transactsql/transactsql.keywords.ts b/src/languages/transactsql/transactsql.keywords.ts index 65516829c2..9bedf6cc0e 100644 --- a/src/languages/transactsql/transactsql.keywords.ts +++ b/src/languages/transactsql/transactsql.keywords.ts @@ -293,10 +293,14 @@ export const keywords: string[] = [ export const dataTypes: string[] = [ // https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql?view=sql-server-ver15 + 'BINARY', 'BIT', 'CHAR', + 'CHAR', 'CHARACTER', 'DATE', + 'DATETIME2', + 'DATETIMEOFFSET', 'DEC', 'DECIMAL', 'DOUBLE', @@ -306,11 +310,12 @@ export const dataTypes: string[] = [ 'NATIONAL', 'NCHAR', 'NUMERIC', + 'NVARCHAR', 'PRECISION', 'REAL', 'SMALLINT', 'TIME', 'TIMESTAMP', + 'VARBINARY', 'VARCHAR', - 'VARYING', ]; From 8db91fa6ee580f8d3787d43adc65ef2869c31585 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 6 Dec 2023 20:39:44 +0200 Subject: [PATCH 18/35] Move DB2 datatypes from functions to dataTypes array --- src/languages/db2/db2.functions.ts | 37 ++---------------------------- src/languages/db2/db2.keywords.ts | 29 ++++++++++++++++++++++- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/languages/db2/db2.functions.ts b/src/languages/db2/db2.functions.ts index fc45871741..a2039b83ef 100644 --- a/src/languages/db2/db2.functions.ts +++ b/src/languages/db2/db2.functions.ts @@ -56,25 +56,19 @@ export const functions: string[] = [ 'ATAN', 'ATAN2', 'ATANH', - 'BIGINT', - 'BINARY', 'BITAND', 'BITANDNOT', 'BITOR', 'BITXOR', 'BITNOT', - 'BLOB', - 'BOOLEAN', 'BPCHAR', 'BSON_TO_JSON', 'BTRIM', 'CARDINALITY', 'CEILING', 'CEIL', - 'CHAR', 'CHARACTER_LENGTH', 'CHR', - 'CLOB', 'COALESCE', 'COLLATION_KEY', 'COLLATION_KEY_BIT', @@ -85,8 +79,6 @@ export const functions: string[] = [ 'COT', 'CURSOR_ROWCOUNT', 'DATAPARTITIONNUM', - 'DATE', - 'DATETIME', 'DATE_PART', 'DATE_TRUNC', 'DAY', @@ -98,12 +90,9 @@ export const functions: string[] = [ 'DAYS', 'DAYS_BETWEEN', 'DAYS_TO_END_OF_MONTH', - 'DBCLOB', 'DBPARTITIONNUM', 'DECFLOAT', 'DECFLOAT_FORMAT', - 'DECIMAL', - 'DEC', 'DECODE', 'DECRYPT_BIN', 'DECRYPT_CHAR', @@ -112,7 +101,6 @@ export const functions: string[] = [ 'DIFFERENCE', 'DIGITS', 'DOUBLE_PRECISION', - 'DOUBLE', 'EMPTY_BLOB', 'EMPTY_CLOB', 'EMPTY_DBCLOB', @@ -122,14 +110,10 @@ export const functions: string[] = [ 'EXP', 'EXTRACT', 'FIRST_DAY', - 'FLOAT', - 'FLOAT4', - 'FLOAT8', 'FLOOR', 'FROM_UTC_TIMESTAMP', 'GENERATE_UNIQUE', 'GETHINT', - 'GRAPHIC', 'GREATEST', 'HASH', 'HASH4', @@ -147,12 +131,6 @@ export const functions: string[] = [ 'INSTR2', 'INSTR4', 'INSTRB', - 'INT', - 'INTERVAL', - 'INTEGER', - 'INT2', - 'INT4', - 'INT8', 'INTNAND', 'INTNOR', 'INTNXOR', @@ -193,10 +171,6 @@ export const functions: string[] = [ 'MONTHNAME', 'MONTHS_BETWEEN', 'MULTIPLY_ALT', - 'NCHAR', - 'NCHR', - 'NCLOB', - 'NVARCHAR', 'NEXT_DAY', 'NEXT_MONTH', 'NEXT_QUARTER', @@ -205,7 +179,6 @@ export const functions: string[] = [ 'NORMALIZE_DECFLOAT', 'NOW', 'NULLIF', - 'NUMERIC', 'NVL', 'NVL2', 'OCTET_LENGTH', @@ -224,7 +197,6 @@ export const functions: string[] = [ 'RAND', 'RANDOM', 'RAWTOHEX', - 'REAL', 'REC2XML', 'REGEXP_COUNT', 'REGEXP_EXTRACT', @@ -235,7 +207,8 @@ export const functions: string[] = [ 'REGEXP_SUBSTR', 'REPEAT', 'REPLACE', - 'RID and RID_BIT', + 'RID', + 'RID_BIT', 'RIGHT', 'ROUND', 'ROUND_TIMESTAMP', @@ -249,7 +222,6 @@ export const functions: string[] = [ 'SIGN', 'SIN', 'SINH', - 'SMALLINT', 'SOUNDEX', 'SPACE', 'SQRT', @@ -270,8 +242,6 @@ export const functions: string[] = [ 'THIS_QUARTER', 'THIS_WEEK', 'THIS_YEAR', - 'TIME', - 'TIMESTAMP', 'TIMESTAMP_FORMAT', 'TIMESTAMP_ISO', 'TIMESTAMPDIFF', @@ -301,12 +271,9 @@ export const functions: string[] = [ 'UNICODE_STR', 'UPPER', 'VALUE', - 'VARBINARY', - 'VARCHAR', 'VARCHAR_BIT_FORMAT', 'VARCHAR_FORMAT', 'VARCHAR_FORMAT_BIT', - 'VARGRAPHIC', 'VERIFY_GROUP_FOR_USER', 'VERIFY_ROLE_FOR_USER', 'VERIFY_TRUSTED_CONTEXT_ROLE_FOR_USER', diff --git a/src/languages/db2/db2.keywords.ts b/src/languages/db2/db2.keywords.ts index 6196cdbc2a..f125edef0d 100644 --- a/src/languages/db2/db2.keywords.ts +++ b/src/languages/db2/db2.keywords.ts @@ -405,15 +405,42 @@ export const dataTypes: string[] = [ // https://www.ibm.com/docs/en/db2-for-zos/12?topic=columns-data-types 'ARRAY', 'BIGINT', + 'BINARY', + 'BLOB', + 'BOOLEAN', 'CCSID', 'CHAR', 'CHARACTER', + 'CLOB', 'DATE', + 'DATETIME', + 'DBCLOB', + 'DEC', + 'DECIMAL', 'DOUBLE', + 'DOUBLE PRECISION', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'GRAPHIC', 'INT', + 'INT2', + 'INT4', + 'INT8', 'INTEGER', - 'LONG', + 'INTERVAL', + 'LONG VARCHAR', + 'LONG VARGRAPHIC', + 'NCHAR', + 'NCHR', + 'NCLOB', + 'NVARCHAR', + 'NUMERIC', 'SMALLINT', + 'REAL', 'TIME', 'TIMESTAMP', + 'VARBINARY', + 'VARCHAR', + 'VARGRAPHIC', ]; From cf7a6563a825a40a5b5ba148ff46f97045f33f61 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 6 Dec 2023 20:46:49 +0200 Subject: [PATCH 19/35] Move DB2i datatypes from functions to dataTypes array --- src/languages/db2i/db2i.functions.ts | 26 ++------------------------ src/languages/db2i/db2i.keywords.ts | 2 ++ 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/languages/db2i/db2i.functions.ts b/src/languages/db2i/db2i.functions.ts index a3a42d559d..4eaa0d03d0 100644 --- a/src/languages/db2i/db2i.functions.ts +++ b/src/languages/db2i/db2i.functions.ts @@ -65,25 +65,19 @@ export const functions: string[] = [ 'ATANH', 'BASE64_DECODE', 'BASE64_ENCODE', - 'BIGINT', - 'BINARY', 'BIT_LENGTH', 'BITAND', 'BITANDNOT', 'BITNOT', 'BITOR', 'BITXOR', - 'BLOB', - 'BOOLEAN', 'BSON_TO_JSON', 'CARDINALITY', 'CEIL', 'CEILING', 'CHAR_LENGTH', - 'CHAR', 'CHARACTER_LENGTH', 'CHR', - 'CLOB', 'COALESCE', 'COMPARE_DECFLOAT', 'CONCAT', @@ -96,7 +90,6 @@ export const functions: string[] = [ 'DATABASE', 'DATAPARTITIONNAME', 'DATAPARTITIONNUM', - 'DATE', 'DAY', 'DAYNAME', 'DAYOFMONTH', @@ -104,14 +97,10 @@ export const functions: string[] = [ 'DAYOFWEEK', 'DAYOFYEAR', 'DAYS', - 'DBCLOB', 'DBPARTITIONNAME', 'DBPARTITIONNUM', - 'DEC', 'DECFLOAT_FORMAT', 'DECFLOAT_SORTKEY', - 'DECFLOAT', - 'DECIMAL', 'DECRYPT_BINARY', 'DECRYPT_BIT', 'DECRYPT_CHAR', @@ -137,7 +126,6 @@ export const functions: string[] = [ 'EXP', 'EXTRACT', 'FIRST_DAY', - 'FLOAT', 'FLOOR', 'GENERATE_UNIQUE', 'GET_BLOB_FROM_FILE', @@ -145,7 +133,6 @@ export const functions: string[] = [ 'GET_DBCLOB_FROM_FILE', 'GET_XML_FILE', 'GETHINT', - 'GRAPHIC', 'GREATEST', 'HASH_MD5', 'HASH_ROW', @@ -173,8 +160,6 @@ export const functions: string[] = [ 'IFNULL', 'INSERT', 'INSTR', - 'INT', - 'INTEGER', 'INTERPRET', 'ISFALSE', 'ISNOTFALSE', @@ -234,8 +219,8 @@ export const functions: string[] = [ 'QUARTER', 'RADIANS', 'RAISE_ERROR', - 'RANDOM or RAND', - 'REAL', + 'RANDOM', + 'RAND', 'REGEXP_COUNT', 'REGEXP_INSTR', 'REGEXP_REPLACE', @@ -246,7 +231,6 @@ export const functions: string[] = [ 'RIGHT', 'ROUND_TIMESTAMP', 'ROUND', - 'ROWID', 'RPAD', 'RRN', 'RTRIM', @@ -255,7 +239,6 @@ export const functions: string[] = [ 'SIGN', 'SIN', 'SINH', - 'SMALLINT', 'SOUNDEX', 'SPACE', 'SQRT', @@ -269,10 +252,8 @@ export const functions: string[] = [ 'TABLE_SCHEMA', 'TAN', 'TANH', - 'TIME', 'TIMESTAMP_FORMAT', 'TIMESTAMP_ISO', - 'TIMESTAMP', 'TIMESTAMPDIFF_BIG', 'TIMESTAMPDIFF', 'TO_CHAR', @@ -293,12 +274,9 @@ export const functions: string[] = [ 'URL_ENCODE', 'VALUE', 'VARBINARY_FORMAT', - 'VARBINARY', 'VARCHAR_BIT_FORMAT', 'VARCHAR_FORMAT_BINARY', 'VARCHAR_FORMAT', - 'VARCHAR', - 'VARGRAPHIC', 'VERIFY_GROUP_FOR_USER', 'WEEK_ISO', 'WEEK', diff --git a/src/languages/db2i/db2i.keywords.ts b/src/languages/db2i/db2i.keywords.ts index d2664ea4a5..ab897f01c4 100644 --- a/src/languages/db2i/db2i.keywords.ts +++ b/src/languages/db2i/db2i.keywords.ts @@ -514,7 +514,9 @@ export const dataTypes: string[] = [ 'DBCLOB', 'DECFLOAT', 'DECIMAL', + 'DEC', 'DOUBLE', + 'DOUBLE PRECISION', 'FLOAT', 'GRAPHIC', 'INT', From ea7f390f99b680e03b9a6fb39e86ecc8f4c7cbac Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 6 Dec 2023 20:52:42 +0200 Subject: [PATCH 20/35] Move Spark datatypes from functions to dataTypes array --- src/languages/spark/spark.functions.ts | 13 ------------- src/languages/spark/spark.keywords.ts | 1 + 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/languages/spark/spark.functions.ts b/src/languages/spark/spark.functions.ts index 2dbca67034..7d7a6bedf1 100644 --- a/src/languages/spark/spark.functions.ts +++ b/src/languages/spark/spark.functions.ts @@ -83,7 +83,6 @@ export const functions: string[] = [ // http://spark.apache.org/docs/latest/sql-ref-functions-builtin.html#map-functions 'ELEMENT_AT', 'ELEMENT_AT', - 'MAP', 'MAP_CONCAT', 'MAP_ENTRIES', 'MAP_FROM_ARRAYS', @@ -169,20 +168,16 @@ export const functions: string[] = [ 'ATAN2', 'ATANH', 'BASE64', - 'BIGINT', 'BIN', - 'BINARY', 'BIT_COUNT', 'BIT_GET', 'BIT_LENGTH', - 'BOOLEAN', 'BROUND', 'BTRIM', 'CARDINALITY', 'CBRT', 'CEIL', 'CEILING', - 'CHAR', 'CHAR_LENGTH', 'CHARACTER_LENGTH', 'CHR', @@ -196,17 +191,13 @@ export const functions: string[] = [ 'CURRENT_CATALOG', 'CURRENT_DATABASE', 'CURRENT_USER', - 'DATE', - 'DECIMAL', 'DEGREES', - 'DOUBLE', // 'E', 'ELT', 'EXP', 'EXPM1', 'FACTORIAL', 'FIND_IN_SET', - 'FLOAT', 'FLOOR', 'FORALL', 'FORMAT_NUMBER', @@ -223,7 +214,6 @@ export const functions: string[] = [ 'INPUT_FILE_BLOCK_START', 'INPUT_FILE_NAME', 'INSTR', - 'INT', 'ISNAN', 'ISNOTNULL', 'ISNULL', @@ -293,7 +283,6 @@ export const functions: string[] = [ 'SIGNUM', 'SIN', 'SINH', - 'SMALLINT', 'SOUNDEX', 'SPACE', 'SPARK_PARTITION_ID', @@ -305,8 +294,6 @@ export const functions: string[] = [ 'SUBSTRING_INDEX', 'TAN', 'TANH', - 'TIMESTAMP', - 'TINYINT', 'TO_CSV', 'TRANSFORM_KEYS', 'TRANSFORM_VALUES', diff --git a/src/languages/spark/spark.keywords.ts b/src/languages/spark/spark.keywords.ts index bba4df8073..61a9a387ca 100644 --- a/src/languages/spark/spark.keywords.ts +++ b/src/languages/spark/spark.keywords.ts @@ -274,6 +274,7 @@ export const dataTypes: string[] = [ 'BINARY', 'BOOLEAN', 'BYTE', + 'CHAR', 'DATE', 'DEC', 'DECIMAL', From 2ebd732e62d7d1a1871eb52a499bf7f1ec29fb87 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 6 Dec 2023 21:10:17 +0200 Subject: [PATCH 21/35] Parse parameterized data types to separate type of AST node --- src/formatter/ExpressionFormatter.ts | 10 ++++++++++ src/lexer/disambiguateTokens.ts | 13 +++++++++++++ src/lexer/token.ts | 1 + src/parser/ast.ts | 8 ++++++++ src/parser/grammar.ne | 7 +++++++ test/bigquery.test.ts | 2 +- test/snowflake.test.ts | 2 +- 7 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/formatter/ExpressionFormatter.ts b/src/formatter/ExpressionFormatter.ts index 489889da4a..fe1505c006 100644 --- a/src/formatter/ExpressionFormatter.ts +++ b/src/formatter/ExpressionFormatter.ts @@ -29,6 +29,7 @@ import { CaseWhenNode, CaseElseNode, DataTypeNode, + ParameterizedDataTypeNode, } from '../parser/ast.js'; import Layout, { WS } from './Layout.js'; @@ -95,6 +96,8 @@ export default class ExpressionFormatter { switch (node.type) { case NodeType.function_call: return this.formatFunctionCall(node); + case NodeType.parameterized_data_type: + return this.formatParameterizedDataType(node); case NodeType.array_subscript: return this.formatArraySubscript(node); case NodeType.property_access: @@ -145,6 +148,13 @@ export default class ExpressionFormatter { this.formatNode(node.parenthesis); } + private formatParameterizedDataType(node: ParameterizedDataTypeNode) { + this.withComments(node.dataType, () => { + this.layout.add(this.showDataType(node.dataType)); + }); + this.formatNode(node.parenthesis); + } + private formatArraySubscript(node: ArraySubscriptNode) { let formattedArray: string; diff --git a/src/lexer/disambiguateTokens.ts b/src/lexer/disambiguateTokens.ts index e08013dfeb..8c62da0253 100644 --- a/src/lexer/disambiguateTokens.ts +++ b/src/lexer/disambiguateTokens.ts @@ -6,6 +6,8 @@ import { isReserved, Token, TokenType } from './token.js'; * Ensures that all RESERVED_FUNCTION_NAME tokens are followed by "(". * If they're not, converts the token to RESERVED_KEYWORD. * + * Converts RESERVED_DATA_TYPE tokens followed by "(" to RESERVED_PARAMETERIZED_DATA_TYPE. + * * When IDENTIFIER and RESERVED_KEYWORD token is followed by "[" * converts it to ARRAY_IDENTIFIER or ARRAY_KEYWORD accordingly. * @@ -17,6 +19,7 @@ export function disambiguateTokens(tokens: Token[]): Token[] { return tokens .map(dotKeywordToIdent) .map(funcNameToKeyword) + .map(dataTypeToParameterizedDataType) .map(identToArrayIdent) .map(keywordToArrayKeyword); } @@ -41,6 +44,16 @@ const funcNameToKeyword = (token: Token, i: number, tokens: Token[]): Token => { return token; }; +const dataTypeToParameterizedDataType = (token: Token, i: number, tokens: Token[]): Token => { + if (token.type === TokenType.RESERVED_DATA_TYPE) { + const nextToken = nextNonCommentToken(tokens, i); + if (nextToken && isOpenParen(nextToken)) { + return { ...token, type: TokenType.RESERVED_PARAMETERIZED_DATA_TYPE }; + } + } + return token; +}; + const identToArrayIdent = (token: Token, i: number, tokens: Token[]): Token => { if (token.type === TokenType.IDENTIFIER) { const nextToken = nextNonCommentToken(tokens, i); diff --git a/src/lexer/token.ts b/src/lexer/token.ts index 9bee7a90c1..42490e9197 100644 --- a/src/lexer/token.ts +++ b/src/lexer/token.ts @@ -5,6 +5,7 @@ export enum TokenType { STRING = 'STRING', VARIABLE = 'VARIABLE', RESERVED_DATA_TYPE = 'RESERVED_DATA_TYPE', + RESERVED_PARAMETERIZED_DATA_TYPE = 'RESERVED_PARAMETERIZED_DATA_TYPE', RESERVED_KEYWORD = 'RESERVED_KEYWORD', RESERVED_FUNCTION_NAME = 'RESERVED_FUNCTION_NAME', RESERVED_PHRASE = 'RESERVED_PHRASE', diff --git a/src/parser/ast.ts b/src/parser/ast.ts index 81e72776fc..0a944c0f8c 100644 --- a/src/parser/ast.ts +++ b/src/parser/ast.ts @@ -5,6 +5,7 @@ export enum NodeType { clause = 'clause', set_operation = 'set_operation', function_call = 'function_call', + parameterized_data_type = 'parameterized_data_type', array_subscript = 'array_subscript', property_access = 'property_access', parenthesis = 'parenthesis', @@ -54,6 +55,12 @@ export interface FunctionCallNode extends BaseNode { parenthesis: ParenthesisNode; } +export interface ParameterizedDataTypeNode extends BaseNode { + type: NodeType.parameterized_data_type; + dataType: DataTypeNode; + parenthesis: ParenthesisNode; +} + // [] export interface ArraySubscriptNode extends BaseNode { type: NodeType.array_subscript; @@ -176,6 +183,7 @@ export type AstNode = | ClauseNode | SetOperationNode | FunctionCallNode + | ParameterizedDataTypeNode | ArraySubscriptNode | PropertyAccessNode | ParenthesisNode diff --git a/src/parser/grammar.ne b/src/parser/grammar.ne index 901c17bd1b..5ed25247f6 100644 --- a/src/parser/grammar.ne +++ b/src/parser/grammar.ne @@ -347,6 +347,13 @@ data_type -> ( %RESERVED_DATA_TYPE ) {% ([[token]]) => toDataTypeNode(token) %} +data_type -> %RESERVED_PARAMETERIZED_DATA_TYPE _ parenthesis {% + ([nameToken, _, parens]) => ({ + type: NodeType.parameterized_data_type, + dataType: addComments(toDataTypeNode(nameToken), { trailing: _ }), + parenthesis: parens, + }) +%} logic_operator -> ( %AND diff --git a/test/bigquery.test.ts b/test/bigquery.test.ts index 11a1a9f9b9..864125bc24 100644 --- a/test/bigquery.test.ts +++ b/test/bigquery.test.ts @@ -137,7 +137,7 @@ describe('BigQueryFormatter', () => { const result = format('SELECT STRUCT("Alpha" as name, [23.4, 26.3, 26.4] as splits) FROM beta'); expect(result).toBe(dedent` SELECT - STRUCT ("Alpha" as name, [23.4, 26.3, 26.4] as splits) + STRUCT("Alpha" as name, [23.4, 26.3, 26.4] as splits) FROM beta `); diff --git a/test/snowflake.test.ts b/test/snowflake.test.ts index 23cbc10dd8..670165fa61 100644 --- a/test/snowflake.test.ts +++ b/test/snowflake.test.ts @@ -181,7 +181,7 @@ describe('SnowflakeFormatter', () => { CREATE TABLE tbl ( first_column DOUBLE PRECISION, - second_column NUMBER (38, 0), + second_column NUMBER(38, 0), third STRING );`); }); From fdb8f32c06f66543cf7e3b9079645b42cad2bc4e Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 6 Dec 2023 21:35:48 +0200 Subject: [PATCH 22/35] Remove support for ANY operator in SQLite Other dialects might also not support it, haven't checked. But SQLite is the only one with data type named "ANY". --- test/bigquery.test.ts | 2 +- test/db2.test.ts | 42 +++++---- test/db2i.test.ts | 2 +- test/features/operators.ts | 16 +++- test/hive.test.ts | 2 +- test/mariadb.test.ts | 9 +- test/mysql.test.ts | 5 +- test/n1ql.test.ts | 5 +- test/plsql.test.ts | 5 +- test/postgresql.test.ts | 172 +++++++++++++++++++------------------ test/redshift.test.ts | 4 +- test/singlestoredb.test.ts | 2 +- test/snowflake.test.ts | 2 +- test/spark.test.ts | 9 +- test/sql.test.ts | 2 +- test/transactsql.test.ts | 22 ++--- test/trino.test.ts | 2 +- 17 files changed, 159 insertions(+), 144 deletions(-) diff --git a/test/bigquery.test.ts b/test/bigquery.test.ts index 864125bc24..174b95ee12 100644 --- a/test/bigquery.test.ts +++ b/test/bigquery.test.ts @@ -60,7 +60,7 @@ describe('BigQueryFormatter', () => { 'EXCEPT DISTINCT', 'INTERSECT DISTINCT', ]); - supportsOperators(format, ['&', '|', '^', '~', '>>', '<<', '||', '=>']); + supportsOperators(format, ['&', '|', '^', '~', '>>', '<<', '||', '=>'], { any: true }); supportsIsDistinctFrom(format); supportsParams(format, { positional: true, named: ['@'], quoted: ['@``'] }); supportsWindow(format); diff --git a/test/db2.test.ts b/test/db2.test.ts index 9cde32e856..e12732eb83 100644 --- a/test/db2.test.ts +++ b/test/db2.test.ts @@ -29,25 +29,29 @@ describe('Db2Formatter', () => { }); supportsDropTable(format); supportsJoin(format, { without: ['NATURAL'] }); - supportsOperators(format, [ - '**', - '%', - '&', - '|', - '^', - '~', - '¬=', - '¬>', - '¬<', - '!>', - '!<', - '^=', - '^>', - '^<', - '||', - '->', - '=>', - ]); + supportsOperators( + format, + [ + '**', + '%', + '&', + '|', + '^', + '~', + '¬=', + '¬>', + '¬<', + '!>', + '!<', + '^=', + '^>', + '^<', + '||', + '->', + '=>', + ], + { any: true } + ); // Additional U& string type in addition to others shared by all DB2 implementations supportsStrings(format, ["U&''"]); supportsDataTypeCase(format); diff --git a/test/db2i.test.ts b/test/db2i.test.ts index bc56529e1d..e844a6ea8c 100644 --- a/test/db2i.test.ts +++ b/test/db2i.test.ts @@ -28,6 +28,6 @@ describe('Db2iFormatter', () => { without: ['NATURAL'], additionally: ['EXCEPTION JOIN', 'LEFT EXCEPTION JOIN', 'RIGHT EXCEPTION JOIN'], }); - supportsOperators(format, ['**', '¬=', '¬>', '¬<', '!>', '!<', '||', '=>']); + supportsOperators(format, ['**', '¬=', '¬>', '¬<', '!>', '!<', '||', '=>'], { any: true }); supportsDataTypeCase(format); }); diff --git a/test/features/operators.ts b/test/features/operators.ts index 2bc8714d93..d7a6ffb83e 100644 --- a/test/features/operators.ts +++ b/test/features/operators.ts @@ -2,10 +2,15 @@ import dedent from 'dedent-js'; import { FormatFn } from '../../src/sqlFormatter.js'; +type OperatorsConfig = { + logicalOperators?: string[]; + any?: boolean; +}; + export default function supportsOperators( format: FormatFn, operators: string[], - logicalOperators: string[] = ['AND', 'OR'] + cfg: OperatorsConfig = {} ) { // Always test for standard SQL operators const standardOperators = ['+', '-', '*', '/', '>', '<', '=', '<>', '<=', '>=', '!=']; @@ -26,7 +31,7 @@ export default function supportsOperators( }); }); - logicalOperators.forEach(op => { + (cfg.logicalOperators || ['AND', 'OR']).forEach(op => { it(`supports ${op} operator`, () => { const result = format(`SELECT true ${op} false AS foo;`); expect(result).toBe(dedent` @@ -39,11 +44,16 @@ export default function supportsOperators( it('supports set operators', () => { expect(format('foo ALL bar')).toBe('foo ALL bar'); - expect(format('foo = ANY (1, 2, 3)')).toBe('foo = ANY (1, 2, 3)'); expect(format('EXISTS bar')).toBe('EXISTS bar'); expect(format('foo IN (1, 2, 3)')).toBe('foo IN (1, 2, 3)'); expect(format("foo LIKE 'hello%'")).toBe("foo LIKE 'hello%'"); expect(format('foo IS NULL')).toBe('foo IS NULL'); expect(format('UNIQUE foo')).toBe('UNIQUE foo'); }); + + if (cfg.any) { + it('supports ANY set-operator', () => { + expect(format('foo = ANY (1, 2, 3)')).toBe('foo = ANY (1, 2, 3)'); + }); + } } diff --git a/test/hive.test.ts b/test/hive.test.ts index eb4554fb36..0c8c7982bf 100644 --- a/test/hive.test.ts +++ b/test/hive.test.ts @@ -47,7 +47,7 @@ describe('HiveFormatter', () => { supportsUsing: false, }); supportsSetOperations(format, ['UNION', 'UNION ALL', 'UNION DISTINCT']); - supportsOperators(format, ['%', '~', '^', '|', '&', '<=>', '==', '!', '||']); + supportsOperators(format, ['%', '~', '^', '|', '&', '<=>', '==', '!', '||'], { any: true }); supportsArrayAndMapAccessors(format); supportsWindow(format); supportsLimiting(format, { limit: true }); diff --git a/test/mariadb.test.ts b/test/mariadb.test.ts index cfb081273c..3219e92ba2 100644 --- a/test/mariadb.test.ts +++ b/test/mariadb.test.ts @@ -30,11 +30,10 @@ describe('MariaDbFormatter', () => { additionally: ['STRAIGHT_JOIN'], }); supportsSetOperations(format, [...standardSetOperations, 'MINUS', 'MINUS ALL', 'MINUS DISTINCT']); - supportsOperators( - format, - ['%', ':=', '&', '|', '^', '~', '<<', '>>', '<=>', '&&', '||', '!'], - ['AND', 'OR', 'XOR'] - ); + supportsOperators(format, ['%', ':=', '&', '|', '^', '~', '<<', '>>', '<=>', '&&', '||', '!'], { + logicalOperators: ['AND', 'OR', 'XOR'], + any: true, + }); supportsReturning(format); supportsLimiting(format, { limit: true, offset: true, fetchFirst: true, fetchNext: true }); supportsCreateTable(format, { diff --git a/test/mysql.test.ts b/test/mysql.test.ts index 3bad166e4c..893507553d 100644 --- a/test/mysql.test.ts +++ b/test/mysql.test.ts @@ -33,7 +33,10 @@ describe('MySqlFormatter', () => { supportsOperators( format, ['%', ':=', '&', '|', '^', '~', '<<', '>>', '<=>', '->', '->>', '&&', '||', '!'], - ['AND', 'OR', 'XOR'] + { + logicalOperators: ['AND', 'OR', 'XOR'], + any: true, + } ); supportsWindow(format); supportsLimiting(format, { limit: true, offset: true }); diff --git a/test/n1ql.test.ts b/test/n1ql.test.ts index 6427d8a2e9..eee6b800a6 100644 --- a/test/n1ql.test.ts +++ b/test/n1ql.test.ts @@ -33,7 +33,10 @@ describe('N1qlFormatter', () => { supportsIdentifiers(format, ['``']); supportsBetween(format); supportsSchema(format); - supportsOperators(format, ['%', '==', '||'], ['AND', 'OR', 'XOR']); + supportsOperators(format, ['%', '==', '||'], { + logicalOperators: ['AND', 'OR', 'XOR'], + any: true, + }); supportsArrayAndMapAccessors(format); supportsArrayLiterals(format, { withoutArrayPrefix: true }); supportsJoin(format, { without: ['FULL', 'CROSS', 'NATURAL'], supportsUsing: false }); diff --git a/test/plsql.test.ts b/test/plsql.test.ts index 2d233c9575..ba76750753 100644 --- a/test/plsql.test.ts +++ b/test/plsql.test.ts @@ -58,7 +58,10 @@ describe('PlSqlFormatter', () => { format, // Missing: '..' operator ['**', ':=', '%', '~=', '^=', '>>', '<<', '=>', '||'], - ['AND', 'OR', 'XOR'] + { + logicalOperators: ['AND', 'OR', 'XOR'], + any: true, + } ); supportsJoin(format, { supportsApply: true }); supportsSetOperations(format, ['UNION', 'UNION ALL', 'EXCEPT', 'INTERSECT']); diff --git a/test/postgresql.test.ts b/test/postgresql.test.ts index 24f54a72d7..513586d021 100644 --- a/test/postgresql.test.ts +++ b/test/postgresql.test.ts @@ -60,90 +60,94 @@ describe('PostgreSqlFormatter', () => { supportsBetween(format); supportsSchema(format); // Missing: '::' type cast (tested separately) - supportsOperators(format, [ - // Arithmetic - '%', - '^', - '|/', - '||/', - '@', - // Assignment - ':=', - // Bitwise - '&', - '|', - '#', - '~', - '<<', - '>>', - // Byte comparison - '~>~', - '~<~', - '~>=~', - '~<=~', - // Geometric - '@-@', - '@@', - '##', - '<->', - '&&', - '&<', - '&>', - '<<|', - '&<|', - '|>>', - '|&>', - '<^', - '^>', - '?#', - '?-', - '?|', - '?-|', - '?||', - '@>', - '<@', - '~=', - // JSON - '?', - '@?', - '?&', - '->', - '->>', - '#>', - '#>>', - '#-', - // Named function params - '=>', - // Network address - '>>=', - '<<=', - // Pattern matching - '~~', - '~~*', - '!~~', - '!~~*', - // POSIX RegExp - '~', - '~*', - '!~', - '!~*', - // Range/multirange - '-|-', - // String concatenation - '||', - // Text search - '@@@', - '!!', - // Trigram/trigraph - '<%', - '<<%', - '%>', - '%>>', - '<<->', - '<->>', - '<<<->', - '<->>>', - ]); + supportsOperators( + format, + [ + // Arithmetic + '%', + '^', + '|/', + '||/', + '@', + // Assignment + ':=', + // Bitwise + '&', + '|', + '#', + '~', + '<<', + '>>', + // Byte comparison + '~>~', + '~<~', + '~>=~', + '~<=~', + // Geometric + '@-@', + '@@', + '##', + '<->', + '&&', + '&<', + '&>', + '<<|', + '&<|', + '|>>', + '|&>', + '<^', + '^>', + '?#', + '?-', + '?|', + '?-|', + '?||', + '@>', + '<@', + '~=', + // JSON + '?', + '@?', + '?&', + '->', + '->>', + '#>', + '#>>', + '#-', + // Named function params + '=>', + // Network address + '>>=', + '<<=', + // Pattern matching + '~~', + '~~*', + '!~~', + '!~~*', + // POSIX RegExp + '~', + '~*', + '!~', + '!~*', + // Range/multirange + '-|-', + // String concatenation + '||', + // Text search + '@@@', + '!!', + // Trigram/trigraph + '<%', + '<<%', + '%>', + '%>>', + '<<->', + '<->>', + '<<<->', + '<->>>', + ], + { any: true } + ); supportsIsDistinctFrom(format); supportsJoin(format); supportsSetOperations(format); diff --git a/test/redshift.test.ts b/test/redshift.test.ts index d7b6e6766e..b68b875d85 100644 --- a/test/redshift.test.ts +++ b/test/redshift.test.ts @@ -45,7 +45,9 @@ describe('RedshiftFormatter', () => { supportsStrings(format, ["''-qq"]); supportsIdentifiers(format, [`""-qq`]); // Missing: '#' and '::' operator (tested separately) - supportsOperators(format, ['^', '%', '@', '|/', '||/', '&', '|', '~', '<<', '>>', '||']); + supportsOperators(format, ['^', '%', '@', '|/', '||/', '&', '|', '~', '<<', '>>', '||'], { + any: true, + }); supportsJoin(format); supportsSetOperations(format, ['UNION', 'UNION ALL', 'EXCEPT', 'INTERSECT', 'MINUS']); supportsParams(format, { numbered: ['$'] }); diff --git a/test/singlestoredb.test.ts b/test/singlestoredb.test.ts index 8ae5a75d73..858cf94e7f 100644 --- a/test/singlestoredb.test.ts +++ b/test/singlestoredb.test.ts @@ -36,7 +36,7 @@ describe('SingleStoreDbFormatter', () => { supportsOperators( format, [':=', '&', '|', '^', '~', '<<', '>>', '<=>', '&&', '||', ':>', '!:>'], - ['AND', 'OR'] + { any: true } ); supportsLimiting(format, { limit: true, offset: true }); supportsCreateTable(format, { ifNotExists: true, columnComment: true, tableComment: true }); diff --git a/test/snowflake.test.ts b/test/snowflake.test.ts index 670165fa61..4f2a8b5217 100644 --- a/test/snowflake.test.ts +++ b/test/snowflake.test.ts @@ -54,7 +54,7 @@ describe('SnowflakeFormatter', () => { supportsIdentifiers(format, [`""-qq`]); supportsBetween(format); // ':' and '::' are tested later, since they should always be dense - supportsOperators(format, ['%', '||', '=>']); + supportsOperators(format, ['%', '||', '=>'], { any: true }); supportsJoin(format, { without: ['NATURAL INNER JOIN'] }); supportsSetOperations(format, ['UNION', 'UNION ALL', 'MINUS', 'EXCEPT', 'INTERSECT']); supportsLimiting(format, { limit: true, offset: true, fetchFirst: true, fetchNext: true }); diff --git a/test/spark.test.ts b/test/spark.test.ts index f406e1ce2c..ff0910908f 100644 --- a/test/spark.test.ts +++ b/test/spark.test.ts @@ -39,11 +39,10 @@ describe('SparkFormatter', () => { supportsStrings(format, ["''-bs", '""-bs', "X''", 'X""', "R''", 'R""']); supportsIdentifiers(format, ['``']); supportsBetween(format); - supportsOperators( - format, - ['%', '~', '^', '|', '&', '<=>', '==', '!', '||', '->'], - ['AND', 'OR', 'XOR'] - ); + supportsOperators(format, ['%', '~', '^', '|', '&', '<=>', '==', '!', '||', '->'], { + logicalOperators: ['AND', 'OR', 'XOR'], + any: true, + }); supportsArrayAndMapAccessors(format); supportsJoin(format, { additionally: [ diff --git a/test/sql.test.ts b/test/sql.test.ts index 02405852c2..56d37c6db2 100644 --- a/test/sql.test.ts +++ b/test/sql.test.ts @@ -51,7 +51,7 @@ describe('SqlFormatter', () => { supportsSchema(format); supportsJoin(format); supportsSetOperations(format); - supportsOperators(format, ['||']); + supportsOperators(format, ['||'], { any: true }); supportsParams(format, { positional: true }); supportsWindow(format); supportsLimiting(format, { limit: true, offset: true, fetchFirst: true, fetchNext: true }); diff --git a/test/transactsql.test.ts b/test/transactsql.test.ts index 96704ac58b..022514765b 100644 --- a/test/transactsql.test.ts +++ b/test/transactsql.test.ts @@ -47,23 +47,11 @@ describe('TransactSqlFormatter', () => { supportsIdentifiers(format, [`""-qq`, '[]']); supportsBetween(format); // Missing: `::` scope resolution operator (tested separately) - supportsOperators(format, [ - '%', - '&', - '|', - '^', - '~', - '!<', - '!>', - '+=', - '-=', - '*=', - '/=', - '%=', - '|=', - '&=', - '^=', - ]); + supportsOperators( + format, + ['%', '&', '|', '^', '~', '!<', '!>', '+=', '-=', '*=', '/=', '%=', '|=', '&=', '^='], + { any: true } + ); supportsJoin(format, { without: ['NATURAL'], supportsUsing: false, supportsApply: true }); supportsSetOperations(format, ['UNION', 'UNION ALL', 'EXCEPT', 'INTERSECT']); supportsParams(format, { named: ['@'], quoted: ['@""', '@[]'] }); diff --git a/test/trino.test.ts b/test/trino.test.ts index 216ed70708..8bf4e5bddd 100644 --- a/test/trino.test.ts +++ b/test/trino.test.ts @@ -51,7 +51,7 @@ describe('TrinoFormatter', () => { supportsIdentifiers(format, [`""-qq`]); supportsBetween(format); // Missing: '?' operator (for row patterns) - supportsOperators(format, ['%', '->', '=>', '||', '|', '^', '$'], ['AND', 'OR']); + supportsOperators(format, ['%', '->', '=>', '||', '|', '^', '$'], { any: true }); supportsIsDistinctFrom(format); supportsArrayLiterals(format, { withArrayPrefix: true }); supportsArrayAndMapAccessors(format); From a0a3c6b6dfde334cdb61cf96f96091da36db3d31 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 10:44:13 +0200 Subject: [PATCH 23/35] Remove unnecessary CREATE TABLE test --- test/bigquery.test.ts | 6 +----- test/features/createTable.ts | 14 -------------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/test/bigquery.test.ts b/test/bigquery.test.ts index 174b95ee12..888879fdb4 100644 --- a/test/bigquery.test.ts +++ b/test/bigquery.test.ts @@ -33,11 +33,7 @@ describe('BigQueryFormatter', () => { behavesLikeSqlFormatter(format); supportsComments(format, { hashComments: true }); supportsCreateView(format, { orReplace: true, materialized: true, ifNotExists: true }); - supportsCreateTable(format, { - orReplace: true, - ifNotExists: true, - dialectDoesntHaveVarchar: true, - }); + supportsCreateTable(format, { orReplace: true, ifNotExists: true }); supportsDropTable(format, { ifExists: true }); supportsAlterTable(format, { addColumn: true, diff --git a/test/features/createTable.ts b/test/features/createTable.ts index 223a8c981d..9b68e738bf 100644 --- a/test/features/createTable.ts +++ b/test/features/createTable.ts @@ -7,7 +7,6 @@ interface CreateTableConfig { ifNotExists?: boolean; columnComment?: boolean; tableComment?: boolean; - dialectDoesntHaveVarchar?: boolean; } export default function supportsCreateTable(format: FormatFn, cfg: CreateTableConfig = {}) { @@ -18,19 +17,6 @@ export default function supportsCreateTable(format: FormatFn, cfg: CreateTableCo `); }); - 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', () => { From bcd084de5e9dada529d41453fd7ab8853f77b50f Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 11:06:43 +0200 Subject: [PATCH 24/35] Remove all data types from N1QL --- src/languages/n1ql/n1ql.keywords.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/languages/n1ql/n1ql.keywords.ts b/src/languages/n1ql/n1ql.keywords.ts index 43f083ee44..b5e4a33ddd 100644 --- a/src/languages/n1ql/n1ql.keywords.ts +++ b/src/languages/n1ql/n1ql.keywords.ts @@ -6,11 +6,14 @@ export const keywords: string[] = [ 'ANALYZE', 'AND', 'ANY', + 'ARRAY', 'AS', 'ASC', 'AT', 'BEGIN', 'BETWEEN', + 'BINARY', + 'BOOLEAN', 'BREAK', 'BUCKET', 'BUILD', @@ -71,7 +74,6 @@ export const keywords: string[] = [ 'HASH', 'HAVING', 'IF', - 'ISOLATION', 'IGNORE', 'ILIKE', 'IN', @@ -85,6 +87,7 @@ export const keywords: string[] = [ 'INTERSECT', 'INTO', 'IS', + 'ISOLATION', 'JAVASCRIPT', 'JOIN', 'KEY', @@ -115,6 +118,8 @@ export const keywords: string[] = [ 'NTH_VALUE', 'NULL', 'NULLS', + 'NUMBER', + 'OBJECT', 'OFFSET', 'ON', 'OPTION', @@ -163,6 +168,7 @@ export const keywords: string[] = [ 'SOME', 'START', 'STATISTICS', + 'STRING', 'SYSTEM', 'THEN', 'TIES', @@ -201,11 +207,12 @@ export const keywords: string[] = [ ]; export const dataTypes: string[] = [ + // N1QL does not support any way of declaring types for columns. + // It does not support the CREATE TABLE statement nor the CAST() expression. + // + // It does have several keywords like ARRAY and OBJECT, which seem to refer to types, + // but they are used as operators. It also reserves several words like STRING and NUMBER, + // which it actually doesn't use. + // // https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/datatypes.html - 'ARRAY', - 'BINARY', - 'BOOLEAN', - 'NUMBER', - 'OBJECT', - 'STRING', ]; From 82b60bca0e5f3ab7e53d0fb642e8920525883320 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 11:18:53 +0200 Subject: [PATCH 25/35] Remove unnecessary dataTypeCase tests (using CAST) --- test/options/dataTypeCase.ts | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/test/options/dataTypeCase.ts b/test/options/dataTypeCase.ts index c35f2ab65e..62fd0fc5ce 100644 --- a/test/options/dataTypeCase.ts +++ b/test/options/dataTypeCase.ts @@ -27,38 +27,4 @@ export default function supportsDataTypeCase(format: FormatFn) { 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 - `); - }); } From 3b1cd758df64c44666bad235dcc78f1a143f0b36 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 11:31:38 +0200 Subject: [PATCH 26/35] Include parameterized types to dataTypeCase test --- test/options/dataTypeCase.ts | 37 ++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/test/options/dataTypeCase.ts b/test/options/dataTypeCase.ts index 62fd0fc5ce..d576d30374 100644 --- a/test/options/dataTypeCase.ts +++ b/test/options/dataTypeCase.ts @@ -4,27 +4,44 @@ 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 )'); + const result = format( + 'CREATE TABLE users ( user_id iNt PRIMARY KEY, total_earnings Decimal(5, 2) NOT NULL )' + ); expect(result).toBe(dedent` - CREATE TABLE users (id iNt PRIMARY KEY) + CREATE TABLE users ( + user_id iNt PRIMARY KEY, + total_earnings Decimal(5, 2) NOT NULL + ) `); }); it('converts data type keyword case to uppercase', () => { - const result = format('CREATE TABLE users ( id iNt PRIMARY KEY )', { - dataTypeCase: 'upper', - }); + const result = format( + 'CREATE TABLE users ( user_id iNt PRIMARY KEY, total_earnings Decimal(5, 2) NOT NULL )', + { + dataTypeCase: 'upper', + } + ); expect(result).toBe(dedent` - CREATE TABLE users (id INT PRIMARY KEY) + CREATE TABLE users ( + user_id INT PRIMARY KEY, + total_earnings DECIMAL(5, 2) NOT NULL + ) `); }); it('converts data type keyword case to lowercase', () => { - const result = format('CREATE TABLE users ( id iNt PRIMARY KEY )', { - dataTypeCase: 'lower', - }); + const result = format( + 'CREATE TABLE users ( user_id iNt PRIMARY KEY, total_earnings Decimal(5, 2) NOT NULL )', + { + dataTypeCase: 'lower', + } + ); expect(result).toBe(dedent` - CREATE TABLE users (id int PRIMARY KEY) + CREATE TABLE users ( + user_id int PRIMARY KEY, + total_earnings decimal(5, 2) NOT NULL + ) `); }); } From 49731eee4b4466a4f9d71c2e659d0639c0eddbd6 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 11:37:59 +0200 Subject: [PATCH 27/35] Remove string format parameters from BigQuery keywords These had been mistakenly added to keywords, but those values can only appear quoted instide string when calling the cast function: CAST(expr AS STRING FORMAT 'ASCII') See: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#format_bytes_as_string --- src/languages/bigquery/bigquery.keywords.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/languages/bigquery/bigquery.keywords.ts b/src/languages/bigquery/bigquery.keywords.ts index 1880e824ca..63d4bd5997 100644 --- a/src/languages/bigquery/bigquery.keywords.ts +++ b/src/languages/bigquery/bigquery.keywords.ts @@ -111,6 +111,7 @@ export const keywords: string[] = [ ]; export const dataTypes: string[] = [ + // https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types 'ARRAY', // parametric, ARRAY 'BOOL', 'BYTES', // parameterised, BYTES(Length) @@ -134,12 +135,4 @@ export const dataTypes: string[] = [ 'STRUCT', // parametric, STRUCT 'TIME', 'TIMEZONE', - - // https://cloud.google.com/bigquery/docs/reference/standard-sql/conversion_functions#formatting_syntax - 'HEX', - 'BASEX', - 'BASE64M', - 'ASCII', - 'UTF-8', - 'UTF8', ]; From 9bf2110abeac82d61c8051b8421b5a56e8110b1e Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 11:43:18 +0200 Subject: [PATCH 28/35] Simplify ARRAY[] + dataTypeCase tests Really just one small test is enough for this. --- test/features/arrayLiterals.ts | 78 ++-------------------------------- 1 file changed, 4 insertions(+), 74 deletions(-) diff --git a/test/features/arrayLiterals.ts b/test/features/arrayLiterals.ts index f7761b4bb3..9c91e45b92 100644 --- a/test/features/arrayLiterals.ts +++ b/test/features/arrayLiterals.ts @@ -32,84 +32,14 @@ export default function supportsArrayLiterals(format: FormatFn, cfg: ArrayLitera `); }); - it('supports preserving ARRAY[] literals keywords casing', () => { + it('dataTypeCase option affects ARRAY[] literal case', () => { 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', - } - ) + format(`SELECT ArrAy[1, 2]`, { + 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' - ]; `); }); } From 24bf4b0ef3da2a61303f96f5c98ae23f542ec397 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 11:55:09 +0200 Subject: [PATCH 29/35] Treat the "array" in ARRAY[] as keyword (not as data type) But elsewhere treat it as data type. --- src/lexer/disambiguateTokens.ts | 13 ++++--------- src/lexer/token.ts | 4 +--- src/parser/grammar.ne | 7 ------- test/features/arrayLiterals.ts | 23 ++++++++++++++++++++++- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/lexer/disambiguateTokens.ts b/src/lexer/disambiguateTokens.ts index 8c62da0253..0514fdf005 100644 --- a/src/lexer/disambiguateTokens.ts +++ b/src/lexer/disambiguateTokens.ts @@ -8,7 +8,7 @@ import { isReserved, Token, TokenType } from './token.js'; * * Converts RESERVED_DATA_TYPE tokens followed by "(" to RESERVED_PARAMETERIZED_DATA_TYPE. * - * When IDENTIFIER and RESERVED_KEYWORD token is followed by "[" + * When IDENTIFIER or RESERVED_DATA_TYPE token is followed by "[" * converts it to ARRAY_IDENTIFIER or ARRAY_KEYWORD accordingly. * * This is needed to avoid ambiguity in parser which expects function names @@ -21,7 +21,7 @@ export function disambiguateTokens(tokens: Token[]): Token[] { .map(funcNameToKeyword) .map(dataTypeToParameterizedDataType) .map(identToArrayIdent) - .map(keywordToArrayKeyword); + .map(dataTypeToArrayKeyword); } const dotKeywordToIdent = (token: Token, i: number, tokens: Token[]): Token => { @@ -64,17 +64,12 @@ const identToArrayIdent = (token: Token, i: number, tokens: Token[]): Token => { return token; }; -const keywordToArrayKeyword = (token: Token, i: number, tokens: Token[]): Token => { - if (token.type === TokenType.RESERVED_KEYWORD) { +const dataTypeToArrayKeyword = (token: Token, i: number, tokens: Token[]): Token => { + if (token.type === TokenType.RESERVED_DATA_TYPE) { const nextToken = nextNonCommentToken(tokens, i); 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 42490e9197..152102073f 100644 --- a/src/lexer/token.ts +++ b/src/lexer/token.ts @@ -14,8 +14,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 [ + ARRAY_KEYWORD = 'ARRAY_KEYWORD', // RESERVED_DATA_TYPE token in front of [ CASE = 'CASE', END = 'END', WHEN = 'WHEN', @@ -94,7 +93,6 @@ 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/grammar.ne b/src/parser/grammar.ne index 5ed25247f6..d2d50f6abc 100644 --- a/src/parser/grammar.ne +++ b/src/parser/grammar.ne @@ -213,13 +213,6 @@ 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, diff --git a/test/features/arrayLiterals.ts b/test/features/arrayLiterals.ts index 9c91e45b92..c62dfae25c 100644 --- a/test/features/arrayLiterals.ts +++ b/test/features/arrayLiterals.ts @@ -32,16 +32,37 @@ export default function supportsArrayLiterals(format: FormatFn, cfg: ArrayLitera `); }); - it('dataTypeCase option affects ARRAY[] literal case', () => { + it('dataTypeCase option does NOT affect ARRAY[] literal case', () => { expect( format(`SELECT ArrAy[1, 2]`, { dataTypeCase: 'upper', }) + ).toBe(dedent` + SELECT + ArrAy[1, 2] + `); + }); + + it('keywordCase option affects ARRAY[] literal case', () => { + expect( + format(`SELECT ArrAy[1, 2]`, { + keywordCase: 'upper', + }) ).toBe(dedent` SELECT ARRAY[1, 2] `); }); + + it('dataTypeCase option affects ARRAY type case', () => { + expect( + format(`CREATE TABLE foo ( items ArrAy )`, { + dataTypeCase: 'upper', + }) + ).toBe(dedent` + CREATE TABLE foo (items ARRAY) + `); + }); } if (cfg.withoutArrayPrefix) { From 55bd6530bcce3835330c22033c54ee15f3573789 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 12:11:57 +0200 Subject: [PATCH 30/35] Additional test for STRUCT and ARRAY in BigQuery --- test/bigquery.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/bigquery.test.ts b/test/bigquery.test.ts index 413cd95616..d891c10831 100644 --- a/test/bigquery.test.ts +++ b/test/bigquery.test.ts @@ -146,6 +146,14 @@ describe('BigQueryFormatter', () => { `); }); + it('STRUCT and ARRAY type case is affected by dataTypeCase option', () => { + expect(format('SELECT array>[(1, "foo")]', { dataTypeCase: 'upper' })) + .toBe(dedent` + SELECT + ARRAY>[(1, "foo")] + `); + }); + // TODO: Possibly incorrect formatting of STRUCT<>() and ARRAY<>() it('supports parametric STRUCT', () => { expect(format('SELECT STRUCT>([])')).toBe(dedent` From 04947c6e62cecb17c806d32c924d9a49734546cc Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 13:17:47 +0200 Subject: [PATCH 31/35] Update dataTypeCase docs --- docs/dataTypeCase.md | 49 +++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/docs/dataTypeCase.md b/docs/dataTypeCase.md index 97b4deb970..4c5d4407a4 100644 --- a/docs/dataTypeCase.md +++ b/docs/dataTypeCase.md @@ -2,10 +2,6 @@ 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 - `"preserve"` (default) preserves the original case. @@ -15,38 +11,35 @@ Note: Casing of function names like `VARCHAR(30)` are not modified - instead rel ### 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() - ) +CREATE TABLE user ( + id InTeGeR PRIMARY KEY, + first_name VarChaR(30) NOT NULL, + bio teXT, + is_email_verified BooL, + created_timestamp timestamP +); ``` ### 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() - ) +CREATE TABLE user ( + id INTEGER PRIMARY KEY, + first_name VARCHAR(30) NOT NULL, + bio TEXT, + is_email_verified BOOL, + created_timestamp TIMESTAMP +); ``` ### 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() - ) +CREATE TABLE user ( + id integer PRIMARY KEY, + first_name varchar(30) NOT NULL, + bio text, + is_email_verified bool, + created_timestamp timestamp +); ``` From aaa667e4850f376a0ac91e0a24c76ea3f08f4348 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 13:24:43 +0200 Subject: [PATCH 32/35] Better examples for functionCase option docs --- docs/functionCase.md | 45 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/docs/functionCase.md b/docs/functionCase.md index cb740d62fb..6240683251 100644 --- a/docs/functionCase.md +++ b/docs/functionCase.md @@ -11,38 +11,35 @@ Converts function names to upper- or 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() - ) +SELECT + Concat(Trim(first_name), ' ', Trim(last_name)) AS name, + Max(salary) AS max_pay +FROM + employee +WHERE + expires_at > 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() - ) +SELECT + CONCAT(TRIM(first_name), ' ', TRIM(last_name)) AS name, + MAX(salary) AS max_pay +FROM + employee +WHERE + expires_at > 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() - ) +SELECT + concat(trim(first_name), ' ', trim(last_name)) AS name, + max(salary) AS max_pay +FROM + employee +WHERE + expires_at > now() ``` From 2e0f53f4b12690dc0fba748f9c3638ea5dbcb7a8 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 13:28:43 +0200 Subject: [PATCH 33/35] Ensure that CAST() is cased as a function --- docs/functionCase.md | 9 ++++++--- test/options/functionCase.ts | 15 +++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/functionCase.md b/docs/functionCase.md index 6240683251..59537605eb 100644 --- a/docs/functionCase.md +++ b/docs/functionCase.md @@ -13,7 +13,8 @@ Converts function names to upper- or lowercase. ```sql SELECT Concat(Trim(first_name), ' ', Trim(last_name)) AS name, - Max(salary) AS max_pay + Max(salary) AS max_pay, + Cast(ssid AS INT) FROM employee WHERE @@ -25,7 +26,8 @@ WHERE ```sql SELECT CONCAT(TRIM(first_name), ' ', TRIM(last_name)) AS name, - MAX(salary) AS max_pay + MAX(salary) AS max_pay, + CAST(ssid AS INT) FROM employee WHERE @@ -37,7 +39,8 @@ WHERE ```sql SELECT concat(trim(first_name), ' ', trim(last_name)) AS name, - max(salary) AS max_pay + max(salary) AS max_pay, + cast(ssid AS INT) FROM employee WHERE diff --git a/test/options/functionCase.ts b/test/options/functionCase.ts index 437951fc5b..96f16ce489 100644 --- a/test/options/functionCase.ts +++ b/test/options/functionCase.ts @@ -4,34 +4,37 @@ 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'); + const result = format('SELECT MiN(price) AS min_price, Cast(item_code AS INT) FROM products'); expect(result).toBe(dedent` SELECT - MiN(price) AS min_price + MiN(price) AS min_price, + Cast(item_code AS INT) FROM products `); }); it('converts function names to uppercase', () => { - const result = format('SELECT MiN(price) AS min_price FROM products', { + const result = format('SELECT MiN(price) AS min_price, Cast(item_code AS INT) FROM products', { functionCase: 'upper', }); expect(result).toBe(dedent` SELECT - MIN(price) AS min_price + MIN(price) AS min_price, + CAST(item_code AS INT) FROM products `); }); it('converts function names to lowercase', () => { - const result = format('SELECT MiN(price) AS min_price FROM products', { + const result = format('SELECT MiN(price) AS min_price, Cast(item_code AS INT) FROM products', { functionCase: 'lower', }); expect(result).toBe(dedent` SELECT - min(price) AS min_price + min(price) AS min_price, + cast(item_code AS INT) FROM products `); From 9292b0121e43117490b17b3f99c74432f76f443c Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 13:31:17 +0200 Subject: [PATCH 34/35] Update identifierCase docs with regards to functionCase --- docs/identifierCase.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/identifierCase.md b/docs/identifierCase.md index 5826a89c30..883a6bb12f 100644 --- a/docs/identifierCase.md +++ b/docs/identifierCase.md @@ -6,7 +6,6 @@ This option doesn't yet support all types of identifiers: - prefixed variables like `@my_var` are not converted. - parameter placeholders like `:param` are not converted. -- function names like `count(*)` are not converted (this are currently governed by `keywordCase` option instead.) ## Options From d0a74067e98a4e9091247cc227e29242eef21c64 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Thu, 7 Dec 2023 13:32:50 +0200 Subject: [PATCH 35/35] Fix formatting in identifierCase docs code examples --- docs/identifierCase.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/identifierCase.md b/docs/identifierCase.md index 883a6bb12f..6d8ad300bc 100644 --- a/docs/identifierCase.md +++ b/docs/identifierCase.md @@ -25,7 +25,8 @@ from where Column6 and Column7 -group by Column4 +group by + Column4 ``` ### upper @@ -40,7 +41,8 @@ from where COLUMN6 and COLUMN7 -group by COLUMN4 +group by + COLUMN4 ``` ### lower @@ -55,5 +57,6 @@ from where column6 and column7 -group by column4 +group by + column4 ```