From 2487b58dbeb14bf95cacc02c3cf9d766c83b5c19 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 5 Oct 2022 15:15:09 +0300 Subject: [PATCH 1/6] Replace extending of Formatter with DialectOptions object --- src/FormatOptions.ts | 3 -- src/dialect.ts | 44 ++++++++++++++++ src/formatter/Formatter.ts | 47 +++-------------- src/languages/sql/sql.formatter.ts | 51 ++++++++---------- src/sqlFormatter.ts | 84 ++++++++++++++++-------------- src/utils.ts | 8 --- 6 files changed, 118 insertions(+), 119 deletions(-) create mode 100644 src/dialect.ts diff --git a/src/FormatOptions.ts b/src/FormatOptions.ts index dec524289b..c785b009fb 100644 --- a/src/FormatOptions.ts +++ b/src/FormatOptions.ts @@ -1,7 +1,5 @@ // import only type to avoid ESLint no-cycle rule producing an error -import type { SqlLanguage } from './sqlFormatter.js'; import { ParamItems } from './formatter/Params.js'; -import Formatter from './formatter/Formatter.js'; import { ParamTypes } from './lexer/TokenizerOptions.js'; export type IndentStyle = 'standard' | 'tabularLeft' | 'tabularRight'; @@ -13,7 +11,6 @@ export type CommaPosition = 'before' | 'after' | 'tabular'; export type LogicalOperatorNewline = 'before' | 'after'; export interface FormatOptions { - language: SqlLanguage | typeof Formatter; tabWidth: number; useTabs: boolean; keywordCase: KeywordCase; diff --git a/src/dialect.ts b/src/dialect.ts new file mode 100644 index 0000000000..04caf302b7 --- /dev/null +++ b/src/dialect.ts @@ -0,0 +1,44 @@ +import { + DialectFormatOptions, + ProcessedDialectFormatOptions, +} from './formatter/ExpressionFormatter.js'; +import Tokenizer from './lexer/Tokenizer.js'; +import { TokenizerOptions } from './lexer/TokenizerOptions.js'; + +export interface DialectOptions { + tokenizer: TokenizerOptions; + formatOptions: DialectFormatOptions; +} + +export interface Dialect { + tokenizer: Tokenizer; + formatOptions: ProcessedDialectFormatOptions; +} + +const cache = new Map(); + +/** + * Factory function for building Dialect objects. + * When called repeatedly with same options object returns the cached Dialect, + * to avoid the cost of creating it again. + */ +export const createDialect = (options: DialectOptions): Dialect => { + let dialect = cache.get(options); + if (!dialect) { + dialect = dialectFromOptions(options); + cache.set(options, dialect); + } + return dialect; +}; + +const dialectFromOptions = (dialectOptions: DialectOptions): Dialect => ({ + tokenizer: new Tokenizer(dialectOptions.tokenizer), + formatOptions: processDialectFormatOptions(dialectOptions.formatOptions), +}); + +const processDialectFormatOptions = ( + options: DialectFormatOptions +): ProcessedDialectFormatOptions => ({ + alwaysDenseOperators: options.alwaysDenseOperators || [], + onelineClauses: Object.fromEntries(options.onelineClauses.map(name => [name, true])), +}); diff --git a/src/formatter/Formatter.ts b/src/formatter/Formatter.ts index fd319d11f1..26d5c8da42 100644 --- a/src/formatter/Formatter.ts +++ b/src/formatter/Formatter.ts @@ -1,62 +1,29 @@ import { FormatOptions } from '../FormatOptions.js'; import { indentString } from './config.js'; import Params from './Params.js'; -import Tokenizer from '../lexer/Tokenizer.js'; import { createParser } from '../parser/createParser.js'; import { StatementNode } from '../parser/ast.js'; -import { cacheInClassField } from '../utils.js'; +import { Dialect } from '../dialect.js'; import formatCommaPositions from './formatCommaPositions.js'; import formatAliasPositions from './formatAliasPositions.js'; -import ExpressionFormatter, { - DialectFormatOptions, - ProcessedDialectFormatOptions, -} from './ExpressionFormatter.js'; +import ExpressionFormatter from './ExpressionFormatter.js'; import Layout, { WS } from './Layout.js'; import Indentation from './Indentation.js'; /** Main formatter class that produces a final output string from list of tokens */ export default class Formatter { + private dialect: Dialect; private cfg: FormatOptions; private params: Params; - constructor(cfg: FormatOptions) { + constructor(dialect: Dialect, cfg: FormatOptions) { + this.dialect = dialect; this.cfg = cfg; this.params = new Params(this.cfg.params); } - /** - * SQL Tokenizer for this formatter, provided by subclasses. - */ - protected tokenizer(): Tokenizer { - throw new Error('tokenizer() not implemented by subclass'); - } - - // Cache the tokenizer for each class (each SQL dialect) - // So we wouldn't need to recreate the tokenizer, which is kinda expensive, - // for each call to format() function. - private cachedTokenizer(): Tokenizer { - return cacheInClassField(this.constructor, 'cachedTokenizer', () => this.tokenizer()); - } - - /** - * Dialect-specific formatting configuration, provided by subclass. - */ - protected formatOptions(): DialectFormatOptions { - throw new Error('formatOptions() not implemented by sybclass'); - } - - private cachedFormatOptions(): ProcessedDialectFormatOptions { - return cacheInClassField(this.constructor, 'cachedFormatOptions', () => { - const opts = this.formatOptions(); - return { - alwaysDenseOperators: opts.alwaysDenseOperators || [], - onelineClauses: Object.fromEntries(opts.onelineClauses.map(name => [name, true])), - }; - }); - } - /** * Formats an SQL query. * @param {string} query - The SQL query string to be formatted @@ -71,7 +38,7 @@ export default class Formatter { } private parse(query: string): StatementNode[] { - return createParser(this.cachedTokenizer()).parse(query, this.cfg.paramTypes || {}); + return createParser(this.dialect.tokenizer).parse(query, this.cfg.paramTypes || {}); } private formatAst(statements: StatementNode[]): string { @@ -83,7 +50,7 @@ export default class Formatter { private formatStatement(statement: StatementNode): string { const layout = new ExpressionFormatter({ cfg: this.cfg, - dialectCfg: this.cachedFormatOptions(), + dialectCfg: this.dialect.formatOptions, params: this.params, layout: new Layout(new Indentation(indentString(this.cfg))), }).format(statement.children); diff --git a/src/languages/sql/sql.formatter.ts b/src/languages/sql/sql.formatter.ts index 51f31ddb76..dfdfd11243 100644 --- a/src/languages/sql/sql.formatter.ts +++ b/src/languages/sql/sql.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './sql.functions.js'; import { keywords } from './sql.keywords.js'; @@ -75,29 +73,24 @@ const reservedPhrases = expandPhrases([ '{ROWS | RANGE} BETWEEN', ]); -export default class SqlFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - stringTypes: [ - { quote: "''-qq-bs", prefixes: ['N', 'U&'] }, - { quote: "''-raw", prefixes: ['X'], requirePrefix: true }, - ], - identTypes: [`""-qq`, '``'], - paramTypes: { positional: true }, - operators: ['||'], - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const sql: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + stringTypes: [ + { quote: "''-qq-bs", prefixes: ['N', 'U&'] }, + { quote: "''-raw", prefixes: ['X'], requirePrefix: true }, + ], + identTypes: [`""-qq`, '``'], + paramTypes: { positional: true }, + operators: ['||'], + }, + formatOptions: { + onelineClauses, + }, +}; diff --git a/src/sqlFormatter.ts b/src/sqlFormatter.ts index 108079db55..6983dd1e03 100644 --- a/src/sqlFormatter.ts +++ b/src/sqlFormatter.ts @@ -1,46 +1,52 @@ -import BigQueryFormatter from './languages/bigquery/bigquery.formatter.js'; -import Db2Formatter from './languages/db2/db2.formatter.js'; -import HiveFormatter from './languages/hive/hive.formatter.js'; -import MariaDbFormatter from './languages/mariadb/mariadb.formatter.js'; -import MySqlFormatter from './languages/mysql/mysql.formatter.js'; -import N1qlFormatter from './languages/n1ql/n1ql.formatter.js'; -import PlSqlFormatter from './languages/plsql/plsql.formatter.js'; -import PostgreSqlFormatter from './languages/postgresql/postgresql.formatter.js'; -import RedshiftFormatter from './languages/redshift/redshift.formatter.js'; -import SparkFormatter from './languages/spark/spark.formatter.js'; -import SqliteFormatter from './languages/sqlite/sqlite.formatter.js'; -import SqlFormatter from './languages/sql/sql.formatter.js'; -import TrinoFormatter from './languages/trino/trino.formatter.js'; -import TransactSqlFormatter from './languages/transactsql/transactsql.formatter.js'; -import SingleStoreDbFormatter from './languages/singlestoredb/singlestoredb.formatter.js'; -import SnowflakeFormatter from './languages/snowflake/snowflake.formatter.js'; +// import BigQueryFormatter from './languages/bigquery/bigquery.formatter.js'; +// import Db2Formatter from './languages/db2/db2.formatter.js'; +// import HiveFormatter from './languages/hive/hive.formatter.js'; +// import MariaDbFormatter from './languages/mariadb/mariadb.formatter.js'; +// import MySqlFormatter from './languages/mysql/mysql.formatter.js'; +// import N1qlFormatter from './languages/n1ql/n1ql.formatter.js'; +// import PlSqlFormatter from './languages/plsql/plsql.formatter.js'; +// import PostgreSqlFormatter from './languages/postgresql/postgresql.formatter.js'; +// import RedshiftFormatter from './languages/redshift/redshift.formatter.js'; +// import SparkFormatter from './languages/spark/spark.formatter.js'; +// import SqliteFormatter from './languages/sqlite/sqlite.formatter.js'; +import { sql } from './languages/sql/sql.formatter.js'; +// import TrinoFormatter from './languages/trino/trino.formatter.js'; +// import TransactSqlFormatter from './languages/transactsql/transactsql.formatter.js'; +// import SingleStoreDbFormatter from './languages/singlestoredb/singlestoredb.formatter.js'; +// import SnowflakeFormatter from './languages/snowflake/snowflake.formatter.js'; import { FormatOptions } from './FormatOptions.js'; import { ParamItems } from './formatter/Params.js'; +import { createDialect, DialectOptions } from './dialect.js'; +import Formatter from './formatter/Formatter.js'; export const formatters = { - bigquery: BigQueryFormatter, - db2: Db2Formatter, - hive: HiveFormatter, - mariadb: MariaDbFormatter, - mysql: MySqlFormatter, - n1ql: N1qlFormatter, - plsql: PlSqlFormatter, - postgresql: PostgreSqlFormatter, - redshift: RedshiftFormatter, - singlestoredb: SingleStoreDbFormatter, - snowflake: SnowflakeFormatter, - spark: SparkFormatter, - sql: SqlFormatter, - sqlite: SqliteFormatter, - transactsql: TransactSqlFormatter, - trino: TrinoFormatter, - tsql: TransactSqlFormatter, // alias for transactsql + // bigquery: BigQueryFormatter, + // db2: Db2Formatter, + // hive: HiveFormatter, + // mariadb: MariaDbFormatter, + // mysql: MySqlFormatter, + // n1ql: N1qlFormatter, + // plsql: PlSqlFormatter, + // postgresql: PostgreSqlFormatter, + // redshift: RedshiftFormatter, + // singlestoredb: SingleStoreDbFormatter, + // snowflake: SnowflakeFormatter, + // spark: SparkFormatter, + sql, + // sqlite: SqliteFormatter, + // transactsql: TransactSqlFormatter, + // trino: TrinoFormatter, + // tsql: TransactSqlFormatter, // alias for transactsql }; export type SqlLanguage = keyof typeof formatters; export const supportedDialects = Object.keys(formatters); -const defaultOptions: FormatOptions = { +export interface FormatOptionsWithLanguage extends FormatOptions { + language: SqlLanguage | DialectOptions; +} + +const defaultOptions: FormatOptionsWithLanguage = { language: 'sql', tabWidth: 2, useTabs: false, @@ -59,10 +65,10 @@ const defaultOptions: FormatOptions = { * Format whitespace in a query to make it easier to read. * * @param {string} query - input SQL query string - * @param {FormatOptions} cfg Configuration options (see docs in README) + * @param {Partial} cfg Configuration options (see docs in README) * @return {string} formatted query */ -export const format = (query: string, cfg: Partial = {}): string => { +export const format = (query: string, cfg: Partial = {}): string => { if (typeof query !== 'string') { throw new Error('Invalid query argument. Expected string, instead got ' + typeof query); } @@ -72,15 +78,15 @@ export const format = (query: string, cfg: Partial = {}): string ...cfg, }); - const FormatterCls = + const dialectOptions: DialectOptions = typeof options.language === 'string' ? formatters[options.language] : options.language; - return new FormatterCls(options).format(query); + return new Formatter(createDialect(dialectOptions), options).format(query); }; export class ConfigError extends Error {} -function validateConfig(cfg: FormatOptions): FormatOptions { +function validateConfig(cfg: FormatOptionsWithLanguage): FormatOptionsWithLanguage { if (typeof cfg.language === 'string' && !supportedDialects.includes(cfg.language)) { throw new ConfigError(`Unsupported SQL dialect: ${cfg.language}`); } diff --git a/src/utils.ts b/src/utils.ts index 546cebea39..25eef752f4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -30,11 +30,3 @@ export const isMultiline = (text: string): boolean => /\n/.test(text); // type Foo = { foo?: string, bar: number }; // export type Optional = Pick, K> & Omit; - -// Caches return value of a function in specified field in class object/function -export const cacheInClassField = (cls: any, fieldName: string, fn: () => T): T => { - if (!cls[fieldName]) { - cls[fieldName] = fn(); - } - return cls[fieldName]; -}; From dbe46fae09c69089321dae4b1a599d991cceb60e Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 5 Oct 2022 15:32:57 +0300 Subject: [PATCH 2/6] Convert rest of the dialects --- src/languages/bigquery/bigquery.formatter.ts | 71 +++--- src/languages/db2/db2.formatter.ts | 53 ++-- src/languages/hive/hive.formatter.ts | 47 ++-- src/languages/mariadb/mariadb.formatter.ts | 75 +++--- src/languages/mysql/mysql.formatter.ts | 75 +++--- src/languages/n1ql/n1ql.formatter.ts | 57 ++--- src/languages/plsql/plsql.formatter.ts | 93 ++++--- .../postgresql/postgresql.formatter.ts | 233 +++++++++--------- src/languages/redshift/redshift.formatter.ts | 77 +++--- .../singlestoredb/singlestoredb.formatter.ts | 67 +++-- .../snowflake/snowflake.formatter.ts | 85 +++---- src/languages/spark/spark.formatter.ts | 61 ++--- src/languages/sqlite/sqlite.formatter.ts | 57 ++--- .../transactsql/transactsql.formatter.ts | 87 +++---- src/languages/trino/trino.formatter.ts | 87 +++---- src/sqlFormatter.ts | 62 ++--- test/sqlFormatter.test.ts | 6 +- 17 files changed, 594 insertions(+), 699 deletions(-) diff --git a/src/languages/bigquery/bigquery.formatter.ts b/src/languages/bigquery/bigquery.formatter.ts index a735b987b5..22952c05ff 100644 --- a/src/languages/bigquery/bigquery.formatter.ts +++ b/src/languages/bigquery/bigquery.formatter.ts @@ -1,6 +1,4 @@ -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; +import { DialectOptions } from '../../dialect.js'; import { EOF_TOKEN, isToken, TokenType, Token } from '../../lexer/token.js'; import { expandPhrases } from '../../expandPhrases.js'; import { keywords } from './bigquery.keywords.js'; @@ -155,42 +153,37 @@ const reservedPhrases = expandPhrases([ ]); // https://cloud.google.com/bigquery/docs/reference/#standard-sql-reference -export default class BigQueryFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - extraParens: ['[]'], - stringTypes: [ - // The triple-quoted strings are listed first, so they get matched first. - // Otherwise the first two quotes of """ will get matched as an empty "" string. - { quote: '""".."""', prefixes: ['R', 'B', 'RB', 'BR'] }, - { quote: "'''..'''", prefixes: ['R', 'B', 'RB', 'BR'] }, - '""-bs', - "''-bs", - { quote: '""-raw', prefixes: ['R', 'B', 'RB', 'BR'], requirePrefix: true }, - { quote: "''-raw", prefixes: ['R', 'B', 'RB', 'BR'], requirePrefix: true }, - ], - identTypes: ['``'], - identChars: { dashes: true }, - paramTypes: { positional: true, named: ['@'], quoted: ['@'] }, - lineCommentTypes: ['--', '#'], - operators: ['&', '|', '^', '~', '>>', '<<', '||'], - postProcess, - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const bigquery: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + extraParens: ['[]'], + stringTypes: [ + // The triple-quoted strings are listed first, so they get matched first. + // Otherwise the first two quotes of """ will get matched as an empty "" string. + { quote: '""".."""', prefixes: ['R', 'B', 'RB', 'BR'] }, + { quote: "'''..'''", prefixes: ['R', 'B', 'RB', 'BR'] }, + '""-bs', + "''-bs", + { quote: '""-raw', prefixes: ['R', 'B', 'RB', 'BR'], requirePrefix: true }, + { quote: "''-raw", prefixes: ['R', 'B', 'RB', 'BR'], requirePrefix: true }, + ], + identTypes: ['``'], + identChars: { dashes: true }, + paramTypes: { positional: true, named: ['@'], quoted: ['@'] }, + lineCommentTypes: ['--', '#'], + operators: ['&', '|', '^', '~', '>>', '<<', '||'], + postProcess, + }, + formatOptions: { + onelineClauses, + }, +}; function postProcess(tokens: Token[]): Token[] { return detectArraySubscripts(combineParameterizedTypes(tokens)); diff --git a/src/languages/db2/db2.formatter.ts b/src/languages/db2/db2.formatter.ts index 7fdef5c684..05a33a6666 100644 --- a/src/languages/db2/db2.formatter.ts +++ b/src/languages/db2/db2.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './db2.functions.js'; import { keywords } from './db2.keywords.js'; @@ -178,30 +176,25 @@ const reservedPhrases = expandPhrases([ ]); // https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_72/db2/rbafzintro.htm -export default class Db2Formatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - stringTypes: [ - { quote: "''-qq", prefixes: ['G', 'N', 'U&'] }, - { quote: "''-raw", prefixes: ['X', 'BX', 'GX', 'UX'], requirePrefix: true }, - ], - identTypes: [`""-qq`], - paramTypes: { positional: true, named: [':'] }, - paramChars: { first: '@#$', rest: '@#$' }, - operators: ['**', '¬=', '¬>', '¬<', '!>', '!<', '||'], - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const db2: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + stringTypes: [ + { quote: "''-qq", prefixes: ['G', 'N', 'U&'] }, + { quote: "''-raw", prefixes: ['X', 'BX', 'GX', 'UX'], requirePrefix: true }, + ], + identTypes: [`""-qq`], + paramTypes: { positional: true, named: [':'] }, + paramChars: { first: '@#$', rest: '@#$' }, + operators: ['**', '¬=', '¬>', '¬<', '!>', '!<', '||'], + }, + formatOptions: { + onelineClauses, + }, +}; diff --git a/src/languages/hive/hive.formatter.ts b/src/languages/hive/hive.formatter.ts index 997c829a6b..8facba9157 100644 --- a/src/languages/hive/hive.formatter.ts +++ b/src/languages/hive/hive.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './hive.functions.js'; import { keywords } from './hive.keywords.js'; @@ -84,27 +82,22 @@ const reservedJoins = expandPhrases([ const reservedPhrases = expandPhrases(['{ROWS | RANGE} BETWEEN']); // https://cwiki.apache.org/confluence/display/Hive/LanguageManual -export default class HiveFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - extraParens: ['[]'], - stringTypes: ['""-bs', "''-bs"], - identTypes: ['``'], - variableTypes: [{ quote: '{}', prefixes: ['$'], requirePrefix: true }], - operators: ['%', '~', '^', '|', '&', '<=>', '==', '!', '||'], - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const hive: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + extraParens: ['[]'], + stringTypes: ['""-bs', "''-bs"], + identTypes: ['``'], + variableTypes: [{ quote: '{}', prefixes: ['$'], requirePrefix: true }], + operators: ['%', '~', '^', '|', '&', '<=>', '==', '!', '||'], + }, + formatOptions: { + onelineClauses, + }, +}; diff --git a/src/languages/mariadb/mariadb.formatter.ts b/src/languages/mariadb/mariadb.formatter.ts index 89ceac0dd7..294af6d346 100644 --- a/src/languages/mariadb/mariadb.formatter.ts +++ b/src/languages/mariadb/mariadb.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { EOF_TOKEN, isToken, Token, TokenType } from '../../lexer/token.js'; import { keywords } from './mariadb.keywords.js'; import { functions } from './mariadb.functions.js'; @@ -266,44 +264,39 @@ const reservedPhrases = expandPhrases([ ]); // For reference: https://mariadb.com/kb/en/sql-statements-structure/ -export default class MariaDbFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - supportsXor: true, - reservedKeywords: keywords, - reservedFunctionNames: functions, - // TODO: support _ char set prefixes such as _utf8, _latin1, _binary, _utf8mb4, etc. - stringTypes: [ - '""-qq-bs', - "''-qq-bs", - { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, - ], - identTypes: ['``'], - identChars: { first: '$', rest: '$', allowFirstCharNumber: true }, - variableTypes: [ - { regex: '@@?[A-Za-z0-9_.$]+' }, - { quote: '""-qq-bs', prefixes: ['@'], requirePrefix: true }, - { quote: "''-qq-bs", prefixes: ['@'], requirePrefix: true }, - { quote: '``', prefixes: ['@'], requirePrefix: true }, - ], - paramTypes: { positional: true }, - lineCommentTypes: ['--', '#'], - operators: ['%', ':=', '&', '|', '^', '~', '<<', '>>', '<=>', '&&', '||', '!'], - postProcess, - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const mariadb: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + supportsXor: true, + reservedKeywords: keywords, + reservedFunctionNames: functions, + // TODO: support _ char set prefixes such as _utf8, _latin1, _binary, _utf8mb4, etc. + stringTypes: [ + '""-qq-bs', + "''-qq-bs", + { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, + ], + identTypes: ['``'], + identChars: { first: '$', rest: '$', allowFirstCharNumber: true }, + variableTypes: [ + { regex: '@@?[A-Za-z0-9_.$]+' }, + { quote: '""-qq-bs', prefixes: ['@'], requirePrefix: true }, + { quote: "''-qq-bs", prefixes: ['@'], requirePrefix: true }, + { quote: '``', prefixes: ['@'], requirePrefix: true }, + ], + paramTypes: { positional: true }, + lineCommentTypes: ['--', '#'], + operators: ['%', ':=', '&', '|', '^', '~', '<<', '>>', '<=>', '&&', '||', '!'], + postProcess, + }, + formatOptions: { + onelineClauses, + }, +}; function postProcess(tokens: Token[]) { return tokens.map((token, i) => { diff --git a/src/languages/mysql/mysql.formatter.ts b/src/languages/mysql/mysql.formatter.ts index a21e102cd3..43b5aeb682 100644 --- a/src/languages/mysql/mysql.formatter.ts +++ b/src/languages/mysql/mysql.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { EOF_TOKEN, isToken, Token, TokenType } from '../../lexer/token.js'; import { keywords } from './mysql.keywords.js'; import { functions } from './mysql.functions.js'; @@ -233,44 +231,39 @@ const reservedPhrases = expandPhrases([ ]); // https://dev.mysql.com/doc/refman/8.0/en/ -export default class MySqlFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - supportsXor: true, - reservedKeywords: keywords, - reservedFunctionNames: functions, - // TODO: support _ char set prefixes such as _utf8, _latin1, _binary, _utf8mb4, etc. - stringTypes: [ - '""-qq-bs', - { quote: "''-qq-bs", prefixes: ['N'] }, - { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, - ], - identTypes: ['``'], - identChars: { first: '$', rest: '$', allowFirstCharNumber: true }, - variableTypes: [ - { regex: '@@?[A-Za-z0-9_.$]+' }, - { quote: '""-qq-bs', prefixes: ['@'], requirePrefix: true }, - { quote: "''-qq-bs", prefixes: ['@'], requirePrefix: true }, - { quote: '``', prefixes: ['@'], requirePrefix: true }, - ], - paramTypes: { positional: true }, - lineCommentTypes: ['--', '#'], - operators: ['%', ':=', '&', '|', '^', '~', '<<', '>>', '<=>', '->', '->>', '&&', '||', '!'], - postProcess, - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const mysql: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + supportsXor: true, + reservedKeywords: keywords, + reservedFunctionNames: functions, + // TODO: support _ char set prefixes such as _utf8, _latin1, _binary, _utf8mb4, etc. + stringTypes: [ + '""-qq-bs', + { quote: "''-qq-bs", prefixes: ['N'] }, + { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, + ], + identTypes: ['``'], + identChars: { first: '$', rest: '$', allowFirstCharNumber: true }, + variableTypes: [ + { regex: '@@?[A-Za-z0-9_.$]+' }, + { quote: '""-qq-bs', prefixes: ['@'], requirePrefix: true }, + { quote: "''-qq-bs", prefixes: ['@'], requirePrefix: true }, + { quote: '``', prefixes: ['@'], requirePrefix: true }, + ], + paramTypes: { positional: true }, + lineCommentTypes: ['--', '#'], + operators: ['%', ':=', '&', '|', '^', '~', '<<', '>>', '<=>', '->', '->>', '&&', '||', '!'], + postProcess, + }, + formatOptions: { + onelineClauses, + }, +}; function postProcess(tokens: Token[]) { return tokens.map((token, i) => { diff --git a/src/languages/n1ql/n1ql.formatter.ts b/src/languages/n1ql/n1ql.formatter.ts index 7e7029ca96..a3331b4453 100644 --- a/src/languages/n1ql/n1ql.formatter.ts +++ b/src/languages/n1ql/n1ql.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './n1ql.functions.js'; import { keywords } from './n1ql.keywords.js'; @@ -85,32 +83,27 @@ const reservedJoins = expandPhrases(['JOIN', '{LEFT | RIGHT} [OUTER] JOIN', 'INN const reservedPhrases = expandPhrases(['{ROWS | RANGE | GROUPS} BETWEEN']); // For reference: http://docs.couchbase.com.s3-website-us-west-1.amazonaws.com/server/6.0/n1ql/n1ql-language-reference/index.html -export default class N1qlFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - supportsXor: true, - reservedKeywords: keywords, - reservedFunctionNames: functions, - // NOTE: single quotes are actually not supported in N1QL, - // but we support them anyway as all other SQL dialects do, - // which simplifies writing tests that are shared between all dialects. - stringTypes: ['""-bs', "''-bs"], - identTypes: ['``'], - extraParens: ['[]', '{}'], - paramTypes: { positional: true, numbered: ['$'], named: ['$'] }, - lineCommentTypes: ['#', '--'], - operators: ['%', '==', ':', '||'], - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const n1ql: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + supportsXor: true, + reservedKeywords: keywords, + reservedFunctionNames: functions, + // NOTE: single quotes are actually not supported in N1QL, + // but we support them anyway as all other SQL dialects do, + // which simplifies writing tests that are shared between all dialects. + stringTypes: ['""-bs', "''-bs"], + identTypes: ['``'], + extraParens: ['[]', '{}'], + paramTypes: { positional: true, numbered: ['$'], named: ['$'] }, + lineCommentTypes: ['#', '--'], + operators: ['%', '==', ':', '||'], + }, + formatOptions: { + onelineClauses, + }, +}; diff --git a/src/languages/plsql/plsql.formatter.ts b/src/languages/plsql/plsql.formatter.ts index 9879c2dd66..78d76302d3 100644 --- a/src/languages/plsql/plsql.formatter.ts +++ b/src/languages/plsql/plsql.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { EOF_TOKEN, isReserved, isToken, Token, TokenType } from '../../lexer/token.js'; import { keywords } from './plsql.keywords.js'; import { functions } from './plsql.functions.js'; @@ -83,53 +81,48 @@ const reservedPhrases = expandPhrases([ '{ROWS | RANGE} BETWEEN', ]); -export default class PlSqlFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - supportsXor: true, - reservedKeywords: keywords, - reservedFunctionNames: functions, - stringTypes: [ - { quote: "''-qq", prefixes: ['N'] }, - { quote: "q''", prefixes: ['N'] }, - ], - // PL/SQL doesn't actually support escaping of quotes in identifiers, - // but for the sake of simpler testing we'll support this anyway - // as all other SQL dialects with "identifiers" do. - identTypes: [`""-qq`], - identChars: { rest: '$#' }, - variableTypes: [{ regex: '&{1,2}[A-Za-z][A-Za-z0-9_$#]*' }], - paramTypes: { numbered: [':'], named: [':'] }, - paramChars: {}, // Empty object used on purpose to not allow $ and # chars as specified in identChars - operators: [ - '**', - ':=', - '%', - '~=', - '^=', - // '..', // Conflicts with float followed by dot (so "2..3" gets parsed as ["2.", ".", "3"]) - '>>', - '<<', - '=>', - '@', - '||', - ], - postProcess, - }); - } - - formatOptions(): DialectFormatOptions { - return { - alwaysDenseOperators: ['@'], - onelineClauses, - }; - } -} +export const plsql: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + supportsXor: true, + reservedKeywords: keywords, + reservedFunctionNames: functions, + stringTypes: [ + { quote: "''-qq", prefixes: ['N'] }, + { quote: "q''", prefixes: ['N'] }, + ], + // PL/SQL doesn't actually support escaping of quotes in identifiers, + // but for the sake of simpler testing we'll support this anyway + // as all other SQL dialects with "identifiers" do. + identTypes: [`""-qq`], + identChars: { rest: '$#' }, + variableTypes: [{ regex: '&{1,2}[A-Za-z][A-Za-z0-9_$#]*' }], + paramTypes: { numbered: [':'], named: [':'] }, + paramChars: {}, // Empty object used on purpose to not allow $ and # chars as specified in identChars + operators: [ + '**', + ':=', + '%', + '~=', + '^=', + // '..', // Conflicts with float followed by dot (so "2..3" gets parsed as ["2.", ".", "3"]) + '>>', + '<<', + '=>', + '@', + '||', + ], + postProcess, + }, + formatOptions: { + alwaysDenseOperators: ['@'], + onelineClauses, + }, +}; function postProcess(tokens: Token[]) { let previousReservedToken: Token = EOF_TOKEN; diff --git a/src/languages/postgresql/postgresql.formatter.ts b/src/languages/postgresql/postgresql.formatter.ts index 5ad88bd086..cd7d9db57f 100644 --- a/src/languages/postgresql/postgresql.formatter.ts +++ b/src/languages/postgresql/postgresql.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Formatter from '../../formatter/Formatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './postgresql.functions.js'; import { keywords } from './postgresql.keywords.js'; @@ -251,120 +249,115 @@ const reservedPhrases = expandPhrases([ ]); // https://www.postgresql.org/docs/14/index.html -export default class PostgreSqlFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - nestedBlockComments: true, - extraParens: ['[]'], - stringTypes: [ - '$$', - { quote: "''-qq", prefixes: ['U&'] }, - { quote: "''-bs", prefixes: ['E'], requirePrefix: true }, - { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, - ], - identTypes: [{ quote: '""-qq', prefixes: ['U&'] }], - identChars: { rest: '$' }, - paramTypes: { numbered: ['$'] }, - operators: [ - // Arithmetic - '%', - '^', - '|/', - '||/', - '@', - // Assignment - ':=', - // Bitwise - '&', - '|', - '#', - '~', - '<<', - '>>', - // Byte comparison - '~>~', - '~<~', - '~>=~', - '~<=~', - // Geometric - '@-@', - '@@', - '##', - '<->', - '&&', - '&<', - '&>', - '<<|', - '&<|', - '|>>', - '|&>', - '<^', - '^>', - '?#', - '?-', - '?|', - '?-|', - '?||', - '@>', - '<@', - '~=', - // JSON - '?', - '@?', - '?&', - '->', - '->>', - '#>', - '#>>', - '#-', - // Named function params - '=>', - // Network address - '>>=', - '<<=', - // Pattern matching - '~~', - '~~*', - '!~~', - '!~~*', - // POSIX RegExp - '~', - '~*', - '!~', - '!~*', - // Range/multirange - '-|-', - // String concatenation - '||', - // Text search - '@@@', - '!!', - // Trigram/trigraph - '<%', - '%>', - '<<%', - '%>>', - '<<->', - '<->>', - '<<<->', - '<->>>', - // Type cast - '::', - ], - }); - } - - formatOptions(): DialectFormatOptions { - return { - alwaysDenseOperators: ['::'], - onelineClauses, - }; - } -} +export const postgresql: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + nestedBlockComments: true, + extraParens: ['[]'], + stringTypes: [ + '$$', + { quote: "''-qq", prefixes: ['U&'] }, + { quote: "''-bs", prefixes: ['E'], requirePrefix: true }, + { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, + ], + identTypes: [{ quote: '""-qq', prefixes: ['U&'] }], + identChars: { rest: '$' }, + paramTypes: { numbered: ['$'] }, + operators: [ + // Arithmetic + '%', + '^', + '|/', + '||/', + '@', + // Assignment + ':=', + // Bitwise + '&', + '|', + '#', + '~', + '<<', + '>>', + // Byte comparison + '~>~', + '~<~', + '~>=~', + '~<=~', + // Geometric + '@-@', + '@@', + '##', + '<->', + '&&', + '&<', + '&>', + '<<|', + '&<|', + '|>>', + '|&>', + '<^', + '^>', + '?#', + '?-', + '?|', + '?-|', + '?||', + '@>', + '<@', + '~=', + // JSON + '?', + '@?', + '?&', + '->', + '->>', + '#>', + '#>>', + '#-', + // Named function params + '=>', + // Network address + '>>=', + '<<=', + // Pattern matching + '~~', + '~~*', + '!~~', + '!~~*', + // POSIX RegExp + '~', + '~*', + '!~', + '!~*', + // Range/multirange + '-|-', + // String concatenation + '||', + // Text search + '@@@', + '!!', + // Trigram/trigraph + '<%', + '%>', + '<<%', + '%>>', + '<<->', + '<->>', + '<<<->', + '<->>>', + // Type cast + '::', + ], + }, + formatOptions: { + alwaysDenseOperators: ['::'], + onelineClauses, + }, +}; diff --git a/src/languages/redshift/redshift.formatter.ts b/src/languages/redshift/redshift.formatter.ts index 8d14294bc7..bbda2856cf 100644 --- a/src/languages/redshift/redshift.formatter.ts +++ b/src/languages/redshift/redshift.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Formatter from '../../formatter/Formatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './redshift.functions.js'; import { keywords } from './redshift.keywords.js'; @@ -142,42 +140,37 @@ const reservedPhrases = expandPhrases([ ]); // https://docs.aws.amazon.com/redshift/latest/dg/cm_chap_SQLCommandRef.html -export default class RedshiftFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - stringTypes: ["''-qq"], - identTypes: [`""-qq`], - identChars: { first: '#' }, - paramTypes: { numbered: ['$'] }, - operators: [ - '^', - '%', - '@', - '|/', - '||/', - '&', - '|', - // '#', conflicts with first char of identifier - '~', - '<<', - '>>', - '||', - '::', - ], - }); - } - - formatOptions(): DialectFormatOptions { - return { - alwaysDenseOperators: ['::'], - onelineClauses, - }; - } -} +export const redshift: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + stringTypes: ["''-qq"], + identTypes: [`""-qq`], + identChars: { first: '#' }, + paramTypes: { numbered: ['$'] }, + operators: [ + '^', + '%', + '@', + '|/', + '||/', + '&', + '|', + // '#', conflicts with first char of identifier + '~', + '<<', + '>>', + '||', + '::', + ], + }, + formatOptions: { + alwaysDenseOperators: ['::'], + onelineClauses, + }, +}; diff --git a/src/languages/singlestoredb/singlestoredb.formatter.ts b/src/languages/singlestoredb/singlestoredb.formatter.ts index 1c4bd31076..4a91b4e159 100644 --- a/src/languages/singlestoredb/singlestoredb.formatter.ts +++ b/src/languages/singlestoredb/singlestoredb.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { EOF_TOKEN, isToken, Token, TokenType } from '../../lexer/token.js'; import { keywords } from './singlestoredb.keywords.js'; import { functions } from './singlestoredb.functions.js'; @@ -234,40 +232,35 @@ const reservedPhrases = expandPhrases([ '{ROWS | RANGE} BETWEEN', ]); -export default class SingleStoreDbFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - // TODO: support _binary"some string" prefix - stringTypes: [ - '""-qq-bs', - "''-qq-bs", - { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, - ], - identTypes: ['``'], - identChars: { first: '$', rest: '$', allowFirstCharNumber: true }, - variableTypes: [ - { regex: '@@?[A-Za-z0-9_$]+' }, - { quote: '``', prefixes: ['@'], requirePrefix: true }, - ], - lineCommentTypes: ['--', '#'], - operators: [':=', '&', '|', '^', '~', '<<', '>>', '<=>', '&&', '||'], - postProcess, - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const singlestoredb: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + // TODO: support _binary"some string" prefix + stringTypes: [ + '""-qq-bs', + "''-qq-bs", + { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, + ], + identTypes: ['``'], + identChars: { first: '$', rest: '$', allowFirstCharNumber: true }, + variableTypes: [ + { regex: '@@?[A-Za-z0-9_$]+' }, + { quote: '``', prefixes: ['@'], requirePrefix: true }, + ], + lineCommentTypes: ['--', '#'], + operators: [':=', '&', '|', '^', '~', '<<', '>>', '<=>', '&&', '||'], + postProcess, + }, + formatOptions: { + onelineClauses, + }, +}; function postProcess(tokens: Token[]) { return tokens.map((token, i) => { diff --git a/src/languages/snowflake/snowflake.formatter.ts b/src/languages/snowflake/snowflake.formatter.ts index b0dbaaf87a..3934e5b63b 100644 --- a/src/languages/snowflake/snowflake.formatter.ts +++ b/src/languages/snowflake/snowflake.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Formatter from '../../formatter/Formatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './snowflake.functions.js'; import { keywords } from './snowflake.keywords.js'; @@ -296,46 +294,41 @@ const reservedPhrases = expandPhrases([ 'ON {UPDATE | DELETE} [SET NULL | SET DEFAULT]', ]); -export default class SnowflakeFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - stringTypes: ['$$', `''-qq-bs`], - identTypes: ['""-qq'], - variableTypes: [ - // for accessing columns at certain positons in the table - { regex: '[$][1-9]\\d*' }, - // identifier style syntax - { regex: '[$][_a-zA-Z][_a-zA-Z0-9$]*' }, - ], - extraParens: ['[]'], - identChars: { rest: '$' }, - lineCommentTypes: ['--', '//'], - operators: [ - // Modulo - '%', - // Type cast - '::', - // String concat - '||', - // Get Path - ':', - // Generators: https://docs.snowflake.com/en/sql-reference/functions/generator.html#generator - '=>', - ], - }); - } - - formatOptions(): DialectFormatOptions { - return { - alwaysDenseOperators: [':', '::'], - onelineClauses, - }; - } -} +export const snowflake: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + stringTypes: ['$$', `''-qq-bs`], + identTypes: ['""-qq'], + variableTypes: [ + // for accessing columns at certain positons in the table + { regex: '[$][1-9]\\d*' }, + // identifier style syntax + { regex: '[$][_a-zA-Z][_a-zA-Z0-9$]*' }, + ], + extraParens: ['[]'], + identChars: { rest: '$' }, + lineCommentTypes: ['--', '//'], + operators: [ + // Modulo + '%', + // Type cast + '::', + // String concat + '||', + // Get Path + ':', + // Generators: https://docs.snowflake.com/en/sql-reference/functions/generator.html#generator + '=>', + ], + }, + formatOptions: { + alwaysDenseOperators: [':', '::'], + onelineClauses, + }, +}; diff --git a/src/languages/spark/spark.formatter.ts b/src/languages/spark/spark.formatter.ts index 22734370c9..cf80d5e5a9 100644 --- a/src/languages/spark/spark.formatter.ts +++ b/src/languages/spark/spark.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { EOF_TOKEN, isToken, Token, TokenType } from '../../lexer/token.js'; import { keywords } from './spark.keywords.js'; import { functions } from './spark.functions.js'; @@ -120,37 +118,32 @@ const reservedPhrases = expandPhrases([ ]); // http://spark.apache.org/docs/latest/sql-programming-guide.html -export default class SparkFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - supportsXor: true, - reservedKeywords: keywords, - reservedFunctionNames: functions, - extraParens: ['[]'], - stringTypes: [ - "''-bs", - '""-bs', - { quote: "''-raw", prefixes: ['R', 'X'], requirePrefix: true }, - { quote: '""-raw', prefixes: ['R', 'X'], requirePrefix: true }, - ], - identTypes: ['``'], - variableTypes: [{ quote: '{}', prefixes: ['$'], requirePrefix: true }], - operators: ['%', '~', '^', '|', '&', '<=>', '==', '!', '||', '->'], - postProcess, - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const spark: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + supportsXor: true, + reservedKeywords: keywords, + reservedFunctionNames: functions, + extraParens: ['[]'], + stringTypes: [ + "''-bs", + '""-bs', + { quote: "''-raw", prefixes: ['R', 'X'], requirePrefix: true }, + { quote: '""-raw', prefixes: ['R', 'X'], requirePrefix: true }, + ], + identTypes: ['``'], + variableTypes: [{ quote: '{}', prefixes: ['$'], requirePrefix: true }], + operators: ['%', '~', '^', '|', '&', '<=>', '==', '!', '||', '->'], + postProcess, + }, + formatOptions: { + onelineClauses, + }, +}; function postProcess(tokens: Token[]) { return tokens.map((token, i) => { diff --git a/src/languages/sqlite/sqlite.formatter.ts b/src/languages/sqlite/sqlite.formatter.ts index 5c4c9efe9f..4e292dd80b 100644 --- a/src/languages/sqlite/sqlite.formatter.ts +++ b/src/languages/sqlite/sqlite.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './sqlite.functions.js'; import { keywords } from './sqlite.keywords.js'; @@ -64,32 +62,27 @@ const reservedPhrases = expandPhrases([ '{ROWS | RANGE | GROUPS} BETWEEN', ]); -export default class SqliteFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - stringTypes: [ - "''-qq", - { quote: "''-raw", prefixes: ['X'], requirePrefix: true }, - // Depending on context SQLite also supports double-quotes for strings, - // and single-quotes for identifiers. - ], - identTypes: [`""-qq`, '``', '[]'], - // https://www.sqlite.org/lang_expr.html#parameters - paramTypes: { positional: true, numbered: ['?'], named: [':', '@', '$'] }, - operators: ['%', '~', '&', '|', '<<', '>>', '==', '->', '->>', '||'], - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const sqlite: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + stringTypes: [ + "''-qq", + { quote: "''-raw", prefixes: ['X'], requirePrefix: true }, + // Depending on context SQLite also supports double-quotes for strings, + // and single-quotes for identifiers. + ], + identTypes: [`""-qq`, '``', '[]'], + // https://www.sqlite.org/lang_expr.html#parameters + paramTypes: { positional: true, numbered: ['?'], named: [':', '@', '$'] }, + operators: ['%', '~', '&', '|', '<<', '>>', '==', '->', '->>', '||'], + }, + formatOptions: { + onelineClauses, + }, +}; diff --git a/src/languages/transactsql/transactsql.formatter.ts b/src/languages/transactsql/transactsql.formatter.ts index cbda06b6f5..42c19fff83 100644 --- a/src/languages/transactsql/transactsql.formatter.ts +++ b/src/languages/transactsql/transactsql.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Formatter from '../../formatter/Formatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './transactsql.functions.js'; import { keywords } from './transactsql.keywords.js'; @@ -221,47 +219,42 @@ const reservedPhrases = expandPhrases([ ]); // https://docs.microsoft.com/en-us/sql/t-sql/language-reference?view=sql-server-ver15 -export default class TransactSqlFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - nestedBlockComments: true, - stringTypes: [{ quote: "''-qq", prefixes: ['N'] }], - identTypes: [`""-qq`, '[]'], - identChars: { first: '#@', rest: '#@$' }, - paramTypes: { named: ['@'], quoted: ['@'] }, - operators: [ - '%', - '&', - '|', - '^', - '~', - '!<', - '!>', - '+=', - '-=', - '*=', - '/=', - '%=', - '|=', - '&=', - '^=', - '::', - ], - // TODO: Support for money constants - }); - } - - formatOptions(): DialectFormatOptions { - return { - alwaysDenseOperators: ['::'], - onelineClauses, - }; - } -} +export const transactsql: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + nestedBlockComments: true, + stringTypes: [{ quote: "''-qq", prefixes: ['N'] }], + identTypes: [`""-qq`, '[]'], + identChars: { first: '#@', rest: '#@$' }, + paramTypes: { named: ['@'], quoted: ['@'] }, + operators: [ + '%', + '&', + '|', + '^', + '~', + '!<', + '!>', + '+=', + '-=', + '*=', + '/=', + '%=', + '|=', + '&=', + '^=', + '::', + ], + // TODO: Support for money constants + }, + formatOptions: { + alwaysDenseOperators: ['::'], + onelineClauses, + }, +}; diff --git a/src/languages/trino/trino.formatter.ts b/src/languages/trino/trino.formatter.ts index 3965dd9011..ad9bb616d6 100644 --- a/src/languages/trino/trino.formatter.ts +++ b/src/languages/trino/trino.formatter.ts @@ -1,7 +1,5 @@ +import { DialectOptions } from '../../dialect.js'; import { expandPhrases } from '../../expandPhrases.js'; -import Formatter from '../../formatter/Formatter.js'; -import { DialectFormatOptions } from '../../formatter/ExpressionFormatter.js'; -import Tokenizer from '../../lexer/Tokenizer.js'; import { functions } from './trino.functions.js'; import { keywords } from './trino.keywords.js'; @@ -124,47 +122,42 @@ const reservedJoins = expandPhrases([ const reservedPhrases = expandPhrases(['{ROWS | RANGE | GROUPS} BETWEEN']); -export default class TrinoFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - reservedSelect, - reservedClauses: [...reservedClauses, ...onelineClauses], - reservedSetOperations, - reservedJoins, - reservedPhrases, - reservedKeywords: keywords, - reservedFunctionNames: functions, - // Trino also supports {- ... -} parenthesis. - // The formatting of these currently works out as a result of { and - - // not getting a space added in-between. - // https://trino.io/docs/current/sql/match-recognize.html#row-pattern-syntax - extraParens: ['[]', '{}'], - // https://trino.io/docs/current/language/types.html#string - // https://trino.io/docs/current/language/types.html#varbinary - stringTypes: [ - { quote: "''-qq", prefixes: ['U&'] }, - { quote: "''-raw", prefixes: ['X'], requirePrefix: true }, - ], - // https://trino.io/docs/current/language/reserved.html - identTypes: ['""-qq'], - paramTypes: { positional: true }, - operators: [ - '%', - '->', - ':', - '||', - // Row pattern syntax - '|', - '^', - '$', - // '?', conflicts with positional placeholders - ], - }); - } - - formatOptions(): DialectFormatOptions { - return { - onelineClauses, - }; - } -} +export const trino: DialectOptions = { + tokenizer: { + reservedSelect, + reservedClauses: [...reservedClauses, ...onelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + reservedKeywords: keywords, + reservedFunctionNames: functions, + // Trino also supports {- ... -} parenthesis. + // The formatting of these currently works out as a result of { and - + // not getting a space added in-between. + // https://trino.io/docs/current/sql/match-recognize.html#row-pattern-syntax + extraParens: ['[]', '{}'], + // https://trino.io/docs/current/language/types.html#string + // https://trino.io/docs/current/language/types.html#varbinary + stringTypes: [ + { quote: "''-qq", prefixes: ['U&'] }, + { quote: "''-raw", prefixes: ['X'], requirePrefix: true }, + ], + // https://trino.io/docs/current/language/reserved.html + identTypes: ['""-qq'], + paramTypes: { positional: true }, + operators: [ + '%', + '->', + ':', + '||', + // Row pattern syntax + '|', + '^', + '$', + // '?', conflicts with positional placeholders + ], + }, + formatOptions: { + onelineClauses, + }, +}; diff --git a/src/sqlFormatter.ts b/src/sqlFormatter.ts index 6983dd1e03..f9b34d1344 100644 --- a/src/sqlFormatter.ts +++ b/src/sqlFormatter.ts @@ -1,19 +1,19 @@ -// import BigQueryFormatter from './languages/bigquery/bigquery.formatter.js'; -// import Db2Formatter from './languages/db2/db2.formatter.js'; -// import HiveFormatter from './languages/hive/hive.formatter.js'; -// import MariaDbFormatter from './languages/mariadb/mariadb.formatter.js'; -// import MySqlFormatter from './languages/mysql/mysql.formatter.js'; -// import N1qlFormatter from './languages/n1ql/n1ql.formatter.js'; -// import PlSqlFormatter from './languages/plsql/plsql.formatter.js'; -// import PostgreSqlFormatter from './languages/postgresql/postgresql.formatter.js'; -// import RedshiftFormatter from './languages/redshift/redshift.formatter.js'; -// import SparkFormatter from './languages/spark/spark.formatter.js'; -// import SqliteFormatter from './languages/sqlite/sqlite.formatter.js'; +import { bigquery } from './languages/bigquery/bigquery.formatter.js'; +import { db2 } from './languages/db2/db2.formatter.js'; +import { hive } from './languages/hive/hive.formatter.js'; +import { mariadb } from './languages/mariadb/mariadb.formatter.js'; +import { mysql } from './languages/mysql/mysql.formatter.js'; +import { n1ql } from './languages/n1ql/n1ql.formatter.js'; +import { plsql } from './languages/plsql/plsql.formatter.js'; +import { postgresql } from './languages/postgresql/postgresql.formatter.js'; +import { redshift } from './languages/redshift/redshift.formatter.js'; +import { spark } from './languages/spark/spark.formatter.js'; +import { sqlite } from './languages/sqlite/sqlite.formatter.js'; import { sql } from './languages/sql/sql.formatter.js'; -// import TrinoFormatter from './languages/trino/trino.formatter.js'; -// import TransactSqlFormatter from './languages/transactsql/transactsql.formatter.js'; -// import SingleStoreDbFormatter from './languages/singlestoredb/singlestoredb.formatter.js'; -// import SnowflakeFormatter from './languages/snowflake/snowflake.formatter.js'; +import { trino } from './languages/trino/trino.formatter.js'; +import { transactsql } from './languages/transactsql/transactsql.formatter.js'; +import { singlestoredb } from './languages/singlestoredb/singlestoredb.formatter.js'; +import { snowflake } from './languages/snowflake/snowflake.formatter.js'; import { FormatOptions } from './FormatOptions.js'; import { ParamItems } from './formatter/Params.js'; @@ -21,23 +21,23 @@ import { createDialect, DialectOptions } from './dialect.js'; import Formatter from './formatter/Formatter.js'; export const formatters = { - // bigquery: BigQueryFormatter, - // db2: Db2Formatter, - // hive: HiveFormatter, - // mariadb: MariaDbFormatter, - // mysql: MySqlFormatter, - // n1ql: N1qlFormatter, - // plsql: PlSqlFormatter, - // postgresql: PostgreSqlFormatter, - // redshift: RedshiftFormatter, - // singlestoredb: SingleStoreDbFormatter, - // snowflake: SnowflakeFormatter, - // spark: SparkFormatter, + bigquery, + db2, + hive, + mariadb, + mysql, + n1ql, + plsql, + postgresql, + redshift, + singlestoredb, + snowflake, + spark, sql, - // sqlite: SqliteFormatter, - // transactsql: TransactSqlFormatter, - // trino: TrinoFormatter, - // tsql: TransactSqlFormatter, // alias for transactsql + sqlite, + transactsql, + trino, + tsql: transactsql, // alias for transactsql }; export type SqlLanguage = keyof typeof formatters; export const supportedDialects = Object.keys(formatters); diff --git a/test/sqlFormatter.test.ts b/test/sqlFormatter.test.ts index 8492136ccc..a6e5ddd861 100644 --- a/test/sqlFormatter.test.ts +++ b/test/sqlFormatter.test.ts @@ -1,7 +1,7 @@ import dedent from 'dedent-js'; import { format, SqlLanguage } from '../src/sqlFormatter.js'; -import SqliteFormatter from '../src/languages/sqlite/sqlite.formatter.js'; +import { sqlite } from '../src/languages/sqlite/sqlite.formatter.js'; describe('sqlFormatter', () => { it('throws error when unsupported language parameter specified', () => { @@ -56,8 +56,8 @@ describe('sqlFormatter', () => { }).toThrow('aliasAs config is no more supported.'); }); - it('allows passing Formatter class as a language parameter', () => { - expect(format('SELECT [foo], `bar`;', { language: SqliteFormatter })).toBe(dedent` + it('allows passing Dialect config object as a language parameter', () => { + expect(format('SELECT [foo], `bar`;', { language: sqlite })).toBe(dedent` SELECT [foo], \`bar\`; From f4101aaea3fd5815caa15a742ca07a2c60af667c Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 5 Oct 2022 15:34:37 +0300 Subject: [PATCH 3/6] Rename tokenizer field to tokenizerOptions --- src/dialect.ts | 4 ++-- src/languages/bigquery/bigquery.formatter.ts | 2 +- src/languages/db2/db2.formatter.ts | 2 +- src/languages/hive/hive.formatter.ts | 2 +- src/languages/mariadb/mariadb.formatter.ts | 2 +- src/languages/mysql/mysql.formatter.ts | 2 +- src/languages/n1ql/n1ql.formatter.ts | 2 +- src/languages/plsql/plsql.formatter.ts | 2 +- src/languages/postgresql/postgresql.formatter.ts | 2 +- src/languages/redshift/redshift.formatter.ts | 2 +- src/languages/singlestoredb/singlestoredb.formatter.ts | 2 +- src/languages/snowflake/snowflake.formatter.ts | 2 +- src/languages/spark/spark.formatter.ts | 2 +- src/languages/sql/sql.formatter.ts | 2 +- src/languages/sqlite/sqlite.formatter.ts | 2 +- src/languages/transactsql/transactsql.formatter.ts | 2 +- src/languages/trino/trino.formatter.ts | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/dialect.ts b/src/dialect.ts index 04caf302b7..ddcafa2455 100644 --- a/src/dialect.ts +++ b/src/dialect.ts @@ -6,7 +6,7 @@ import Tokenizer from './lexer/Tokenizer.js'; import { TokenizerOptions } from './lexer/TokenizerOptions.js'; export interface DialectOptions { - tokenizer: TokenizerOptions; + tokenizerOptions: TokenizerOptions; formatOptions: DialectFormatOptions; } @@ -32,7 +32,7 @@ export const createDialect = (options: DialectOptions): Dialect => { }; const dialectFromOptions = (dialectOptions: DialectOptions): Dialect => ({ - tokenizer: new Tokenizer(dialectOptions.tokenizer), + tokenizer: new Tokenizer(dialectOptions.tokenizerOptions), formatOptions: processDialectFormatOptions(dialectOptions.formatOptions), }); diff --git a/src/languages/bigquery/bigquery.formatter.ts b/src/languages/bigquery/bigquery.formatter.ts index 22952c05ff..ab0c8ac241 100644 --- a/src/languages/bigquery/bigquery.formatter.ts +++ b/src/languages/bigquery/bigquery.formatter.ts @@ -154,7 +154,7 @@ const reservedPhrases = expandPhrases([ // https://cloud.google.com/bigquery/docs/reference/#standard-sql-reference export const bigquery: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/db2/db2.formatter.ts b/src/languages/db2/db2.formatter.ts index 05a33a6666..8afd15b27c 100644 --- a/src/languages/db2/db2.formatter.ts +++ b/src/languages/db2/db2.formatter.ts @@ -177,7 +177,7 @@ const reservedPhrases = expandPhrases([ // https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_72/db2/rbafzintro.htm export const db2: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/hive/hive.formatter.ts b/src/languages/hive/hive.formatter.ts index 8facba9157..a7cb419278 100644 --- a/src/languages/hive/hive.formatter.ts +++ b/src/languages/hive/hive.formatter.ts @@ -83,7 +83,7 @@ const reservedPhrases = expandPhrases(['{ROWS | RANGE} BETWEEN']); // https://cwiki.apache.org/confluence/display/Hive/LanguageManual export const hive: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/mariadb/mariadb.formatter.ts b/src/languages/mariadb/mariadb.formatter.ts index 294af6d346..687e86d33b 100644 --- a/src/languages/mariadb/mariadb.formatter.ts +++ b/src/languages/mariadb/mariadb.formatter.ts @@ -265,7 +265,7 @@ const reservedPhrases = expandPhrases([ // For reference: https://mariadb.com/kb/en/sql-statements-structure/ export const mariadb: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/mysql/mysql.formatter.ts b/src/languages/mysql/mysql.formatter.ts index 43b5aeb682..d448dd0bbc 100644 --- a/src/languages/mysql/mysql.formatter.ts +++ b/src/languages/mysql/mysql.formatter.ts @@ -232,7 +232,7 @@ const reservedPhrases = expandPhrases([ // https://dev.mysql.com/doc/refman/8.0/en/ export const mysql: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/n1ql/n1ql.formatter.ts b/src/languages/n1ql/n1ql.formatter.ts index a3331b4453..cde6404700 100644 --- a/src/languages/n1ql/n1ql.formatter.ts +++ b/src/languages/n1ql/n1ql.formatter.ts @@ -84,7 +84,7 @@ const reservedPhrases = expandPhrases(['{ROWS | RANGE | GROUPS} BETWEEN']); // For reference: http://docs.couchbase.com.s3-website-us-west-1.amazonaws.com/server/6.0/n1ql/n1ql-language-reference/index.html export const n1ql: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/plsql/plsql.formatter.ts b/src/languages/plsql/plsql.formatter.ts index 78d76302d3..bcb4258b40 100644 --- a/src/languages/plsql/plsql.formatter.ts +++ b/src/languages/plsql/plsql.formatter.ts @@ -82,7 +82,7 @@ const reservedPhrases = expandPhrases([ ]); export const plsql: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/postgresql/postgresql.formatter.ts b/src/languages/postgresql/postgresql.formatter.ts index cd7d9db57f..b057b38aa9 100644 --- a/src/languages/postgresql/postgresql.formatter.ts +++ b/src/languages/postgresql/postgresql.formatter.ts @@ -250,7 +250,7 @@ const reservedPhrases = expandPhrases([ // https://www.postgresql.org/docs/14/index.html export const postgresql: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/redshift/redshift.formatter.ts b/src/languages/redshift/redshift.formatter.ts index bbda2856cf..ca4b32c744 100644 --- a/src/languages/redshift/redshift.formatter.ts +++ b/src/languages/redshift/redshift.formatter.ts @@ -141,7 +141,7 @@ const reservedPhrases = expandPhrases([ // https://docs.aws.amazon.com/redshift/latest/dg/cm_chap_SQLCommandRef.html export const redshift: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/singlestoredb/singlestoredb.formatter.ts b/src/languages/singlestoredb/singlestoredb.formatter.ts index 4a91b4e159..447251e3ea 100644 --- a/src/languages/singlestoredb/singlestoredb.formatter.ts +++ b/src/languages/singlestoredb/singlestoredb.formatter.ts @@ -233,7 +233,7 @@ const reservedPhrases = expandPhrases([ ]); export const singlestoredb: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/snowflake/snowflake.formatter.ts b/src/languages/snowflake/snowflake.formatter.ts index 3934e5b63b..cdc599b43a 100644 --- a/src/languages/snowflake/snowflake.formatter.ts +++ b/src/languages/snowflake/snowflake.formatter.ts @@ -295,7 +295,7 @@ const reservedPhrases = expandPhrases([ ]); export const snowflake: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/spark/spark.formatter.ts b/src/languages/spark/spark.formatter.ts index cf80d5e5a9..e68ad94817 100644 --- a/src/languages/spark/spark.formatter.ts +++ b/src/languages/spark/spark.formatter.ts @@ -119,7 +119,7 @@ const reservedPhrases = expandPhrases([ // http://spark.apache.org/docs/latest/sql-programming-guide.html export const spark: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/sql/sql.formatter.ts b/src/languages/sql/sql.formatter.ts index dfdfd11243..5b4ba2fd84 100644 --- a/src/languages/sql/sql.formatter.ts +++ b/src/languages/sql/sql.formatter.ts @@ -74,7 +74,7 @@ const reservedPhrases = expandPhrases([ ]); export const sql: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/sqlite/sqlite.formatter.ts b/src/languages/sqlite/sqlite.formatter.ts index 4e292dd80b..ca7ba5177f 100644 --- a/src/languages/sqlite/sqlite.formatter.ts +++ b/src/languages/sqlite/sqlite.formatter.ts @@ -63,7 +63,7 @@ const reservedPhrases = expandPhrases([ ]); export const sqlite: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/transactsql/transactsql.formatter.ts b/src/languages/transactsql/transactsql.formatter.ts index 42c19fff83..9bfbf385bd 100644 --- a/src/languages/transactsql/transactsql.formatter.ts +++ b/src/languages/transactsql/transactsql.formatter.ts @@ -220,7 +220,7 @@ const reservedPhrases = expandPhrases([ // https://docs.microsoft.com/en-us/sql/t-sql/language-reference?view=sql-server-ver15 export const transactsql: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, diff --git a/src/languages/trino/trino.formatter.ts b/src/languages/trino/trino.formatter.ts index ad9bb616d6..61be6684b1 100644 --- a/src/languages/trino/trino.formatter.ts +++ b/src/languages/trino/trino.formatter.ts @@ -123,7 +123,7 @@ const reservedJoins = expandPhrases([ const reservedPhrases = expandPhrases(['{ROWS | RANGE | GROUPS} BETWEEN']); export const trino: DialectOptions = { - tokenizer: { + tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...onelineClauses], reservedSetOperations, From 8767899a7724c4363708f04d524afa3ea3898e91 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 5 Oct 2022 15:38:02 +0300 Subject: [PATCH 4/6] Export DialectOptions instead of Formatter and Tokenizer classes --- src/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9a9a64b3da..101c8691ec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,5 @@ export type { LogicalOperatorNewline, FormatOptions, } from './FormatOptions.js'; -export { default as Formatter } from './formatter/Formatter.js'; -export { default as Tokenizer } from './lexer/Tokenizer.js'; +export { DialectOptions } from './dialect.js'; export { expandPhrases } from './expandPhrases.js'; From d2a5e00f7ffe907b1b29e267fffcc3d92936205b Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 5 Oct 2022 15:43:16 +0300 Subject: [PATCH 5/6] Document the use of DialectOptions object in public API --- docs/language.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/language.md b/docs/language.md index 8e9c7327da..2f17d37231 100644 --- a/docs/language.md +++ b/docs/language.md @@ -20,34 +20,35 @@ Specifies the SQL dialect to use. - `"sqlite"` - [SQLite][sqlite] - `"transactsql"` or `"tsql"` - [SQL Server Transact-SQL][tsql] - `"trino"` - [Trino][] / [Presto][] -- custom formatter class (see below) +- custom SQL dialect configuration object (see below) The default `"sql"` dialect is meant for cases where you don't know which dialect of SQL you're about to format. It's not an auto-detection, it just supports a subset of features common enough in many SQL implementations. This might or might not work for your specific dialect. Better to always pick something more specific if possible. -### Custom formatter class (experimental) +### Custom dialect configuration (experimental) -The language parameter can also be used to specify a custom formatter implementation: +The language parameter can also be used to specify a custom SQL dialect configuration: ```ts -import { format, Formatter, Tokenizer } from 'sql-formatter'; +import { format, DialectOptions } from 'sql-formatter'; -class MyFormatter extends Formatter { - tokenizer() { - return new Tokenizer({ - // See source code for examples of tokenizer config options - // For example: src/languages/sqlite/sqlite.formatter.ts - }); - } -} +const myDialect: DialectOptions { + tokenizerOptions: { + // See source code for examples of tokenizer config options + // For example: src/languages/sqlite/sqlite.formatter.ts + }, + formatOptions: { + // ... + }, +}; -const result = format('SELECT * FROM tbl', { language: MyFormatter }); +const result = format('SELECT * FROM tbl', { language: myDialect }); ``` **NB!** This functionality is experimental and there are no stability guarantees for this API. -The API of Formatter and Tokenizer classes can (and likely will) change in non-major releases. +The DialectOptions interface can (and likely will) change in non-major releases. You likely only want to use this if your other alternative is to fork SQL Formatter. [standard sql]: https://en.wikipedia.org/wiki/SQL:2011 From b413e69adb21a301c7157778a857acd0c3498b6c Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Wed, 5 Oct 2022 15:54:19 +0300 Subject: [PATCH 6/6] Use export type for re-exporting types --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 101c8691ec..ed35822556 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,5 +6,5 @@ export type { LogicalOperatorNewline, FormatOptions, } from './FormatOptions.js'; -export { DialectOptions } from './dialect.js'; +export type { DialectOptions } from './dialect.js'; export { expandPhrases } from './expandPhrases.js';