diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 7aca095..b680392 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -18,7 +18,7 @@ jobs: - name: Publish parser on GitHub Pages run: | cp -r bindings queries src binding.gyp Cargo.toml \ - CMakeLists.txt go.mod grammar.js LICENSE Makefile \ + CMakeLists.txt go.mod grammar* LICENSE Makefile \ package.json Package.swift pyproject.toml setup.py \ tree-sitter.json \ docs diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 6785c55..77bac84 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -18,7 +18,7 @@ jobs: - run: mkdir -p parser-artifacts - run: | cp -r bindings queries src binding.gyp Cargo.toml \ - CMakeLists.txt go.mod grammar.js LICENSE Makefile \ + CMakeLists.txt go.mod grammar* LICENSE Makefile \ package.json Package.swift pyproject.toml setup.py \ tree-sitter.json \ parser-artifacts diff --git a/Cargo.toml b/Cargo.toml index 9fe69e3..f34cac7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" autoexamples = false build = "bindings/rust/build.rs" -include = ["LICENSE", "bindings/rust/*", "grammar.js", "queries/*", "src/*"] +include = ["LICENSE", "bindings/rust/*", "grammar*", "queries/*", "src/*"] [lib] path = "bindings/rust/lib.rs" diff --git a/Makefile b/Makefile index 6759dbb..dfa3e08 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ clean: .PHONY: generate generate: src/grammar.json src/parser.c -src/grammar.json src/parser.c: grammar.js queries/highlights.scm queries/indents.scm +src/grammar.json src/parser.c: $(shell find grammar* -name '*.js') queries/highlights.scm queries/indents.scm ${TS} generate ${TSFLAGS} .PHONY: regenerate diff --git a/grammar.js b/grammar.js index 617ee68..e7bcaeb 100644 --- a/grammar.js +++ b/grammar.js @@ -65,3713 +65,17 @@ module.exports = grammar({ ), ), - keyword_select: _ => make_keyword("select"), - keyword_delete: _ => make_keyword("delete"), - keyword_insert: _ => make_keyword("insert"), - keyword_replace: _ => make_keyword("replace"), - keyword_update: _ => make_keyword("update"), - keyword_truncate: _ => make_keyword("truncate"), - keyword_merge: _ => make_keyword("merge"), - keyword_show: _ => make_keyword("show"), - keyword_unload: _ => make_keyword("unload"), - keyword_into: _ => make_keyword("into"), - keyword_overwrite: _ => make_keyword("overwrite"), - keyword_values: _ => make_keyword("values"), - keyword_value: _ => make_keyword("value"), - keyword_matched: _ => make_keyword("matched"), - keyword_set: _ => make_keyword("set"), - keyword_from: _ => make_keyword("from"), - keyword_left: _ => make_keyword("left"), - keyword_right: _ => make_keyword("right"), - keyword_inner: _ => make_keyword("inner"), - keyword_full: _ => make_keyword("full"), - keyword_outer: _ => make_keyword("outer"), - keyword_cross: _ => make_keyword("cross"), - keyword_join: _ => make_keyword("join"), - keyword_lateral: _ => make_keyword("lateral"), - keyword_natural: _ => make_keyword("natural"), - keyword_on: _ => make_keyword("on"), - keyword_off: _ => make_keyword("off"), - keyword_where: _ => make_keyword("where"), - keyword_order: _ => make_keyword("order"), - keyword_group: _ => make_keyword("group"), - keyword_partition: _ => make_keyword("partition"), - keyword_by: _ => make_keyword("by"), - keyword_having: _ => make_keyword("having"), - keyword_desc: _ => make_keyword("desc"), - keyword_asc: _ => make_keyword("asc"), - keyword_limit: _ => make_keyword("limit"), - keyword_offset: _ => make_keyword("offset"), - keyword_primary: _ => make_keyword("primary"), - keyword_create: _ => make_keyword("create"), - keyword_alter: _ => make_keyword("alter"), - keyword_change: _ => make_keyword("change"), - keyword_analyze: _ => make_keyword("analyze"), - keyword_explain: _ => make_keyword("explain"), - keyword_verbose: _ => make_keyword("verbose"), - keyword_modify: _ => make_keyword("modify"), - keyword_drop: _ => make_keyword("drop"), - keyword_add: _ => make_keyword("add"), - keyword_table: _ => make_keyword("table"), - keyword_tables: _ => make_keyword("tables"), - keyword_view: _ => make_keyword("view"), - keyword_column: _ => make_keyword("column"), - keyword_columns: _ => make_keyword("columns"), - keyword_materialized: _ => make_keyword("materialized"), - keyword_tablespace: _ => make_keyword("tablespace"), - keyword_split: _ => make_keyword("split"), - keyword_tablets: _ => make_keyword("tablets"), - keyword_sequence: _ => make_keyword("sequence"), - keyword_increment: _ => make_keyword("increment"), - keyword_minvalue: _ => make_keyword("minvalue"), - keyword_maxvalue: _ => make_keyword("maxvalue"), - keyword_none: _ => make_keyword("none"), - keyword_owned: _ => make_keyword("owned"), - keyword_start: _ => make_keyword("start"), - keyword_restart: _ => make_keyword("restart"), - keyword_key: _ => make_keyword("key"), - keyword_duplicate: _ => make_keyword("duplicate"), - keyword_as: _ => make_keyword("as"), - keyword_distinct: _ => make_keyword("distinct"), - keyword_constraint: _ => make_keyword("constraint"), - keyword_filter: _ => make_keyword("filter"), - keyword_cast: _ => make_keyword("cast"), - keyword_separator: _ => make_keyword("separator"), - keyword_case: _ => make_keyword("case"), - keyword_when: _ => make_keyword("when"), - keyword_then: _ => make_keyword("then"), - keyword_else: _ => make_keyword("else"), - keyword_end: _ => make_keyword("end"), - keyword_in: _ => make_keyword("in"), - keyword_and: _ => make_keyword("and"), - keyword_or: _ => make_keyword("or"), - keyword_is: _ => make_keyword("is"), - keyword_not: _ => make_keyword("not"), - keyword_force: _ => make_keyword("force"), - keyword_ignore: _ => make_keyword("ignore"), - keyword_using: _ => make_keyword("using"), - keyword_use: _ => make_keyword("use"), - keyword_index: _ => make_keyword("index"), - keyword_include: _ => make_keyword("include"), - keyword_for: _ => make_keyword("for"), - keyword_if: _ => make_keyword("if"), - keyword_exists: _ => make_keyword("exists"), - keyword_auto_increment: _ => make_keyword("auto_increment"), - keyword_generated: _ => make_keyword("generated"), - keyword_always: _ => make_keyword("always"), - keyword_collate: _ => make_keyword("collate"), - keyword_character: _ => make_keyword("character"), - keyword_engine: _ => make_keyword("engine"), - keyword_default: _ => make_keyword("default"), - keyword_cascade: _ => make_keyword("cascade"), - keyword_restrict: _ => make_keyword("restrict"), - keyword_with: _ => make_keyword("with"), - keyword_without: _ => make_keyword("without"), - keyword_no: _ => make_keyword("no"), - keyword_data: _ => make_keyword("data"), - keyword_type: _ => make_keyword("type"), - keyword_rename: _ => make_keyword("rename"), - keyword_to: _ => make_keyword("to"), - keyword_database: _ => make_keyword("database"), - keyword_schema: _ => make_keyword("schema"), - keyword_owner: _ => make_keyword("owner"), - keyword_user: _ => make_keyword("user"), - keyword_admin: _ => make_keyword("admin"), - keyword_password: _ => make_keyword("password"), - keyword_encrypted: _ => make_keyword("encrypted"), - keyword_valid: _ => make_keyword("valid"), - keyword_until: _ => make_keyword("until"), - keyword_connection: _ => make_keyword("connection"), - keyword_role: _ => make_keyword("role"), - keyword_reset: _ => make_keyword("reset"), - keyword_temp: _ => make_keyword("temp"), - keyword_temporary: _ => make_keyword("temporary"), - keyword_unlogged: _ => make_keyword("unlogged"), - keyword_logged: _ => make_keyword("logged"), - keyword_cycle: _ => make_keyword("cycle"), - keyword_union: _ => make_keyword("union"), - keyword_all: _ => make_keyword("all"), - keyword_any: _ => make_keyword("any"), - keyword_some: _ => make_keyword("some"), - keyword_except: _ => make_keyword("except"), - keyword_intersect: _ => make_keyword("intersect"), - keyword_returning: _ => make_keyword("returning"), - keyword_begin: _ => make_keyword("begin"), - keyword_commit: _ => make_keyword("commit"), - keyword_rollback: _ => make_keyword("rollback"), - keyword_transaction: _ => make_keyword("transaction"), - keyword_over: _ => make_keyword("over"), - keyword_nulls: _ => make_keyword("nulls"), - keyword_first: _ => make_keyword("first"), - keyword_after: _ => make_keyword("after"), - keyword_before: _ => make_keyword("before"), - keyword_last: _ => make_keyword("last"), - keyword_window: _ => make_keyword("window"), - keyword_range: _ => make_keyword("range"), - keyword_rows: _ => make_keyword("rows"), - keyword_groups: _ => make_keyword("groups"), - keyword_between: _ => make_keyword("between"), - keyword_unbounded: _ => make_keyword("unbounded"), - keyword_preceding: _ => make_keyword("preceding"), - keyword_following: _ => make_keyword("following"), - keyword_exclude: _ => make_keyword("exclude"), - keyword_current: _ => make_keyword("current"), - keyword_row: _ => make_keyword("row"), - keyword_ties: _ => make_keyword("ties"), - keyword_others: _ => make_keyword("others"), - keyword_only: _ => make_keyword("only"), - keyword_unique: _ => make_keyword("unique"), - keyword_foreign: _ => make_keyword("foreign"), - keyword_references: _ => make_keyword("references"), - keyword_concurrently: _ => make_keyword("concurrently"), - keyword_btree: _ => make_keyword("btree"), - keyword_hash: _ => make_keyword("hash"), - keyword_gist: _ => make_keyword("gist"), - keyword_spgist: _ => make_keyword("spgist"), - keyword_gin: _ => make_keyword("gin"), - keyword_brin: _ => make_keyword("brin"), - keyword_like: _ => choice(make_keyword("like"),make_keyword("ilike")), - keyword_similar: _ => make_keyword("similar"), - keyword_unsigned: _ => make_keyword("unsigned"), - keyword_zerofill: _ => make_keyword("zerofill"), - keyword_conflict: _ => make_keyword("conflict"), - keyword_do: _ => make_keyword("do"), - keyword_nothing: _ => make_keyword("nothing"), - keyword_high_priority: _ => make_keyword("high_priority"), - keyword_low_priority: _ => make_keyword("low_priority"), - keyword_delayed: _ => make_keyword("delayed"), - keyword_recursive: _ => make_keyword("recursive"), - keyword_cascaded: _ => make_keyword("cascaded"), - keyword_local: _ => make_keyword("local"), - keyword_current_timestamp: _ => make_keyword("current_timestamp"), - keyword_check: _ => make_keyword("check"), - keyword_option: _ => make_keyword("option"), - keyword_vacuum: _ => make_keyword("vacuum"), - keyword_wait: _ => make_keyword("wait"), - keyword_nowait: _ => make_keyword("nowait"), - keyword_attribute: _ => make_keyword("attribute"), - keyword_authorization: _ => make_keyword("authorization"), - keyword_action: _ => make_keyword("action"), - keyword_extension: _ => make_keyword("extension"), - keyword_copy: _ => make_keyword("copy"), - keyword_stdin: _ => make_keyword("stdin"), - keyword_freeze: _ => make_keyword("freeze"), - keyword_escape: _ => make_keyword("escape"), - keyword_encoding: _ => make_keyword("encoding"), - keyword_force_quote: _ => make_keyword("force_quote"), - keyword_quote: _ => make_keyword("quote"), - keyword_force_null: _ => make_keyword("force_null"), - keyword_force_not_null: _ => make_keyword("force_not_null"), - keyword_header: _ => make_keyword("header"), - keyword_match: _ => make_keyword("match"), - keyword_program: _ => make_keyword("program"), - keyword_plain: _ => make_keyword("plain"), - keyword_extended: _ => make_keyword("extended"), - keyword_main: _ => make_keyword("main"), - keyword_storage: _ => make_keyword("storage"), - keyword_compression: _ => make_keyword("compression"), - keyword_while: _ => make_keyword("while"), - - keyword_trigger: _ => make_keyword('trigger'), - keyword_function: _ => make_keyword("function"), - keyword_returns: _ => make_keyword("returns"), - keyword_return: _ => make_keyword("return"), - keyword_setof: _ => make_keyword("setof"), - keyword_atomic: _ => make_keyword("atomic"), - keyword_declare: _ => make_keyword("declare"), - keyword_language: _ => make_keyword("language"), - keyword_immutable: _ => make_keyword("immutable"), - keyword_stable: _ => make_keyword("stable"), - keyword_volatile: _ => make_keyword("volatile"), - keyword_leakproof: _ => make_keyword("leakproof"), - keyword_parallel: _ => make_keyword("parallel"), - keyword_safe: _ => make_keyword("safe"), - keyword_unsafe: _ => make_keyword("unsafe"), - keyword_restricted: _ => make_keyword("restricted"), - keyword_called: _ => make_keyword("called"), - keyword_input: _ => make_keyword("input"), - keyword_strict: _ => make_keyword("strict"), - keyword_cost: _ => make_keyword("cost"), - keyword_rows: _ => make_keyword("rows"), - keyword_support: _ => make_keyword("support"), - keyword_definer: _ => make_keyword("definer"), - keyword_invoker: _ => make_keyword("invoker"), - keyword_security: _ => make_keyword("security"), - keyword_version: _ => make_keyword("version"), - keyword_extension: _ => make_keyword("extension"), - keyword_out: _ => make_keyword("out"), - keyword_inout: _ => make_keyword("inout"), - keyword_variadic: _ => make_keyword("variadic"), - keyword_ordinality: _ => make_keyword("ordinality"), - - keyword_session: _ => make_keyword("session"), - keyword_isolation: _ => make_keyword("isolation"), - keyword_level: _ => make_keyword("level"), - keyword_serializable: _ => make_keyword("serializable"), - keyword_repeatable: _ => make_keyword("repeatable"), - keyword_read: _ => make_keyword("read"), - keyword_write: _ => make_keyword("write"), - keyword_committed: _ => make_keyword("committed"), - keyword_uncommitted: _ => make_keyword("uncommitted"), - keyword_deferrable: _ => make_keyword("deferrable"), - keyword_names: _ => make_keyword("names"), - keyword_zone: _ => make_keyword("zone"), - keyword_immediate: _ => make_keyword("immediate"), - keyword_deferred: _ => make_keyword("deferred"), - keyword_constraints : _ => make_keyword("constraints"), - keyword_snapshot: _ => make_keyword("snapshot"), - keyword_characteristics: _ => make_keyword("characteristics"), - keyword_follows: _ => make_keyword("follows"), - keyword_precedes: _ => make_keyword("precedes"), - keyword_each: _ => make_keyword("each"), - keyword_instead: _ => make_keyword("instead"), - keyword_of: _ => make_keyword("of"), - keyword_initially: _ => make_keyword("initially"), - keyword_old: _ => make_keyword("old"), - keyword_new: _ => make_keyword("new"), - keyword_referencing: _ => make_keyword("referencing"), - keyword_statement: _ => make_keyword("statement"), - keyword_execute: _ => make_keyword("execute"), - keyword_procedure: _ => make_keyword("procedure"), - keyword_object_id: _ => make_keyword("object_id"), - - // Hive Keywords - keyword_external: _ => make_keyword("external"), - keyword_stored: _ => make_keyword("stored"), - keyword_virtual: _ => make_keyword("virtual"), - keyword_cached: _ => make_keyword("cached"), - keyword_uncached: _ => make_keyword("uncached"), - keyword_replication: _ => make_keyword("replication"), - keyword_tblproperties: _ => make_keyword("tblproperties"), - keyword_compute: _ => make_keyword("compute"), - keyword_stats: _ => make_keyword("stats"), - keyword_statistics: _ => make_keyword("statistics"), - keyword_optimize: _ => make_keyword("optimize"), - keyword_rewrite: _ => make_keyword("rewrite"), - keyword_bin_pack: _ => make_keyword("bin_pack"), - keyword_incremental: _ => make_keyword("incremental"), - keyword_location: _ => make_keyword("location"), - keyword_partitioned: _ => make_keyword("partitioned"), - keyword_comment: _ => make_keyword("comment"), - keyword_sort: _ => make_keyword("sort"), - keyword_format: _ => make_keyword("format"), - keyword_delimited: _ => make_keyword("delimited"), - keyword_delimiter: _ => make_keyword("delimiter"), - keyword_fields: _ => make_keyword("fields"), - keyword_terminated: _ => make_keyword("terminated"), - keyword_escaped: _ => make_keyword("escaped"), - keyword_lines: _ => make_keyword("lines"), - keyword_cache: _ => make_keyword("cache"), - keyword_metadata: _ => make_keyword("metadata"), - keyword_noscan: _ => make_keyword("noscan"), - - // Hive file formats - keyword_parquet: _ => make_keyword("parquet"), - keyword_rcfile: _ => make_keyword("rcfile"), - keyword_csv: _ => make_keyword("csv"), - keyword_textfile: _ => make_keyword("textfile"), - keyword_avro: _ => make_keyword("avro"), - keyword_sequencefile: _ => make_keyword("sequencefile"), - keyword_orc: _ => make_keyword("orc"), - keyword_avro: _ => make_keyword("avro"), - keyword_jsonfile: _ => make_keyword("jsonfile"), - - // Operators - is_not: $ => prec.left(seq($.keyword_is, $.keyword_not)), - not_like: $ => seq($.keyword_not, $.keyword_like), - similar_to: $ => seq($.keyword_similar, $.keyword_to), - not_similar_to: $ => seq($.keyword_not, $.keyword_similar, $.keyword_to), - distinct_from: $ => seq($.keyword_is, $.keyword_distinct, $.keyword_from), - not_distinct_from: $ => seq($.keyword_is, $.keyword_not, $.keyword_distinct, $.keyword_from), - - _temporary: $ => choice($.keyword_temp, $.keyword_temporary), - _not_null: $ => seq($.keyword_not, $.keyword_null), - _primary_key: $ => seq($.keyword_primary, $.keyword_key), - _if_exists: $ => seq($.keyword_if, $.keyword_exists), - _if_not_exists: $ => seq($.keyword_if, $.keyword_not, $.keyword_exists), - _or_replace: $ => seq($.keyword_or, $.keyword_replace), - _default_null: $ => seq($.keyword_default, $.keyword_null), - _current_row: $ => seq($.keyword_current, $.keyword_row), - _exclude_current_row: $ => seq($.keyword_exclude, $.keyword_current, $.keyword_row), - _exclude_group: $ => seq($.keyword_exclude, $.keyword_group), - _exclude_no_others: $ => seq($.keyword_exclude, $.keyword_no, $.keyword_others), - _exclude_ties: $ => seq($.keyword_exclude, $.keyword_ties), - _check_option: $ => seq($.keyword_check, $.keyword_option), - direction: $ => choice($.keyword_desc, $.keyword_asc), - - // Types - keyword_null: _ => make_keyword("null"), - keyword_true: _ => make_keyword("true"), - keyword_false: _ => make_keyword("false"), - - keyword_boolean: _ => make_keyword("boolean"), - keyword_bit: _ => make_keyword("bit"), - keyword_binary: _ => make_keyword("binary"), - keyword_varbinary: _ => make_keyword("varbinary"), - keyword_image: _ => make_keyword("image"), - - keyword_smallserial: _ => choice(make_keyword("smallserial"),make_keyword("serial2")), - keyword_serial: _ => choice(make_keyword("serial"),make_keyword("serial4")), - keyword_bigserial: _ => choice(make_keyword("bigserial"),make_keyword("serial8")), - keyword_tinyint: _ => choice(make_keyword("tinyint"),make_keyword("int1")), - keyword_smallint: _ => choice(make_keyword("smallint"),make_keyword("int2")), - keyword_mediumint: _ => choice(make_keyword("mediumint"),make_keyword("int3")), - keyword_int: _ => choice(make_keyword("int"), make_keyword("integer"), make_keyword("int4")), - keyword_bigint: _ => choice(make_keyword("bigint"),make_keyword("int8")), - keyword_decimal: _ => make_keyword("decimal"), - keyword_numeric: _ => make_keyword("numeric"), - keyword_real: _ => choice(make_keyword("real"),make_keyword("float4")), - keyword_float: _ => make_keyword("float"), - keyword_double: _ => make_keyword("double"), - keyword_precision: _ => make_keyword("precision"), - keyword_inet: _ => make_keyword("inet"), - - keyword_money: _ => make_keyword("money"), - keyword_smallmoney: _ => make_keyword("smallmoney"), - keyword_varying: _ => make_keyword("varying"), - - keyword_char: _ => choice(make_keyword("char"), make_keyword("character")), - keyword_nchar: _ => make_keyword("nchar"), - keyword_varchar: $ => choice( - make_keyword("varchar"), - seq( - make_keyword("character"), - $.keyword_varying, - ) - ), - keyword_nvarchar: _ => make_keyword("nvarchar"), - keyword_text: _ => make_keyword("text"), - keyword_string: _ => make_keyword("string"), - keyword_uuid: _ => make_keyword("uuid"), - - keyword_json: _ => make_keyword("json"), - keyword_jsonb: _ => make_keyword("jsonb"), - keyword_xml: _ => make_keyword("xml"), - - keyword_bytea: _ => make_keyword("bytea"), - - keyword_enum: _ => make_keyword("enum"), - - keyword_date: _ => make_keyword("date"), - keyword_datetime: _ => make_keyword("datetime"), - keyword_datetime2: _ => make_keyword("datetime2"), - keyword_smalldatetime: _ => make_keyword("smalldatetime"), - keyword_datetimeoffset: _ => make_keyword("datetimeoffset"), - keyword_time: _ => make_keyword("time"), - keyword_timestamp: _ => make_keyword("timestamp"), - keyword_timestamptz: _ => make_keyword('timestamptz'), - keyword_interval: _ => make_keyword("interval"), - - keyword_geometry: _ => make_keyword("geometry"), - keyword_geography: _ => make_keyword("geography"), - keyword_box2d: _ => make_keyword("box2d"), - keyword_box3d: _ => make_keyword("box3d"), - - keyword_oid: _ => make_keyword("oid"), - keyword_oids: _ => make_keyword("oids"), - keyword_name: _ => make_keyword("name"), - keyword_regclass: _ => make_keyword("regclass"), - keyword_regnamespace: _ => make_keyword("regnamespace"), - keyword_regproc: _ => make_keyword("regproc"), - keyword_regtype: _ => make_keyword("regtype"), - - keyword_array: _ => make_keyword("array"), // not included in _type since it's a constructor literal - - _type: $ => prec.left( - seq( - choice( - $.keyword_boolean, - $.bit, - $.binary, - $.varbinary, - $.keyword_image, - - $.keyword_smallserial, - $.keyword_serial, - $.keyword_bigserial, - - $.tinyint, - $.smallint, - $.mediumint, - $.int, - $.bigint, - $.decimal, - $.numeric, - $.double, - $.float, - - $.keyword_money, - $.keyword_smallmoney, - - $.char, - $.varchar, - $.nchar, - $.nvarchar, - $.numeric, - $.keyword_string, - $.keyword_text, - - $.keyword_uuid, - - $.keyword_json, - $.keyword_jsonb, - $.keyword_xml, - - $.keyword_bytea, - $.keyword_inet, - - $.enum, - - $.keyword_date, - $.keyword_datetime, - $.keyword_datetime2, - $.datetimeoffset, - $.keyword_smalldatetime, - $.time, - $.timestamp, - $.keyword_timestamptz, - $.keyword_interval, - - $.keyword_geometry, - $.keyword_geography, - $.keyword_box2d, - $.keyword_box3d, - - $.keyword_oid, - $.keyword_name, - $.keyword_regclass, - $.keyword_regnamespace, - $.keyword_regproc, - $.keyword_regtype, - - field("custom_type", $.object_reference) - ), - optional($.array_size_definition) - ), - ), - - array_size_definition: $ => prec.left( - choice( - seq($.keyword_array, optional($._array_size_definition)), - repeat1($._array_size_definition), - ), - ), - - _array_size_definition: $ => seq( - '[', - optional(field("size", alias($._integer, $.literal))), - ']' - ), - - tinyint: $ => unsigned_type($, parametric_type($, $.keyword_tinyint)), - smallint: $ => unsigned_type($, parametric_type($, $.keyword_smallint)), - mediumint: $ => unsigned_type($, parametric_type($, $.keyword_mediumint)), - int: $ => unsigned_type($, parametric_type($, $.keyword_int)), - bigint: $ => unsigned_type($, parametric_type($, $.keyword_bigint)), - - bit: $ => choice( - $.keyword_bit, - seq( - $.keyword_bit, - prec(0, parametric_type($, $.keyword_varying, ['precision'])), - ), - prec(1, parametric_type($, $.keyword_bit, ['precision'])), - ), - - binary: $ => parametric_type($, $.keyword_binary, ['precision']), - varbinary: $ => parametric_type($, $.keyword_varbinary, ['precision']), - - // TODO: should qualify against /\\b(0?[1-9]|[1-4][0-9]|5[0-4])\\b/g - float: $ => choice( - parametric_type($, $.keyword_float, ['precision']), - unsigned_type($, parametric_type($, $.keyword_float, ['precision', 'scale'])), - ), - - double: $ => choice( - make_keyword("float8"), - unsigned_type($, parametric_type($, $.keyword_double, ['precision', 'scale'])), - unsigned_type($, parametric_type($, seq($.keyword_double, $.keyword_precision), ['precision', 'scale'])), - unsigned_type($, parametric_type($, $.keyword_real, ['precision', 'scale'])), - ), - - decimal: $ => choice( - parametric_type($, $.keyword_decimal, ['precision']), - parametric_type($, $.keyword_decimal, ['precision', 'scale']), - ), - numeric: $ => choice( - parametric_type($, $.keyword_numeric, ['precision']), - parametric_type($, $.keyword_numeric, ['precision', 'scale']), - ), - char: $ => parametric_type($, $.keyword_char), - varchar: $ => parametric_type($, $.keyword_varchar), - nchar: $ => parametric_type($, $.keyword_nchar), - nvarchar: $ => parametric_type($, $.keyword_nvarchar), - - _include_time_zone: $ => seq( - choice($.keyword_with, $.keyword_without), - $.keyword_time, - $.keyword_zone, - ), - datetimeoffset: $ => parametric_type($, $.keyword_datetimeoffset), - time: $ => seq( - parametric_type($, $.keyword_time), - optional($._include_time_zone), - ), - timestamp: $ => seq( - parametric_type($, $.keyword_timestamp), - optional($._include_time_zone), - ), - timestamptz: $ => parametric_type($, $.keyword_timestamptz), - - enum: $ => seq( - $.keyword_enum, - paren_list(field("value", alias($._literal_string, $.literal)), true) - ), - - array: $ => seq( - $.keyword_array, - choice( - seq( - "[", - comma_list($._expression), - "]" - ), - seq( - "(", - $._dml_read, - ")", - ) - ) - ), - comment: _ => /--.*/, // https://stackoverflow.com/questions/13014947/regex-to-match-a-c-style-multiline-comment marginalia: _ => /\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\//, - transaction: $ => seq( - $.keyword_begin, - optional( - $.keyword_transaction, - ), - optional(';'), - repeat( - seq( - $.statement, - ';' - ), - ), - choice( - $._commit, - $._rollback, - ), - ), - - _commit: $ => seq( - $.keyword_commit, - optional( - $.keyword_transaction, - ), - ), - - _rollback: $ => seq( - $.keyword_rollback, - optional( - $.keyword_transaction, - ), - ), - - block: $ => seq( - $.keyword_begin, - optional(';'), - repeat( - seq( - $.statement, - ';' - ), - ), - $.keyword_end, - ), - - statement: $ => seq( - optional(seq( - $.keyword_explain, - optional($.keyword_analyze), - optional($.keyword_verbose), - )), - choice( - $._ddl_statement, - $._dml_write, - optional_parenthesis($._dml_read), - $.while_statement, - ), - ), - - while_statement: $ => prec.left(seq( - $.keyword_while, - optional_parenthesis($._expression), - choice( - seq( - $.statement, - optional(';'), - ), - seq( - $.keyword_begin, - repeat($.statement), - $.keyword_end, - ), - ), - )), - - var_declarations: $ => seq($.keyword_declare, repeat1($.var_declaration)), - var_declaration: $ => seq( - $.identifier, - $._type, - optional( - seq( - choice($.keyword_default, '='), - $.literal, - ), - ), - optional(','), - ), - - _ddl_statement: $ => choice( - $._create_statement, - $._alter_statement, - $._drop_statement, - $._rename_statement, - $._optimize_statement, - $._merge_statement, - $.comment_statement, - $.set_statement, - $.reset_statement, - ), - - _cte: $ => seq( - $.keyword_with, - optional($.keyword_recursive), - $.cte, - repeat( - seq( - ',', - $.cte, - ), - ), - ), - - _dml_write: $ => seq( - seq( - optional( - $._cte, - ), - choice( - $._delete_statement, - $._insert_statement, - $._update_statement, - $._truncate_statement, - $._copy_statement, - ), - ), - ), - - _dml_read: $ => seq( - optional(optional_parenthesis($._cte)), - optional_parenthesis( - choice( - $._select_statement, - $.set_operation, - $._show_statement, - $._unload_statement, - ), - ), - ), - - // athena - _unload_statement: $ => seq( - $.keyword_unload, - wrapped_in_parenthesis($._select_statement), - $.keyword_to, - $._single_quote_string, - $.storage_parameters, - ), - - _show_statement: $ => seq( - $.keyword_show, - choice( - $._show_create, - $.keyword_all, // Postgres - $._show_tables // trino/presto - ), - ), - - _show_tables: $ => seq( - $.keyword_tables, - optional(seq($.keyword_from, $._qualified_field)), - optional(seq($.keyword_like, $._expression)) - ), - - _show_create: $ => seq( - $.keyword_create, - choice( - // Trino/Presto/MySQL - $.keyword_schema, - $.keyword_table, - seq(optional($.keyword_materialized), $.keyword_view), - // MySQL - $.keyword_user, - $.keyword_trigger, - $.keyword_procedure, - $.keyword_function - ), - $.object_reference - ), - - cte: $ => seq( - $.identifier, - optional(paren_list(field("argument", $.identifier))), - $.keyword_as, - optional( - seq( - optional($.keyword_not), - $.keyword_materialized, - ), - ), - wrapped_in_parenthesis( - alias( - choice($._dml_read, $._dml_write), - $.statement, - ), - ), - ), - - set_operation: $ => seq( - $._select_statement, - repeat1( - seq( - field( - "operation", - choice( - seq($.keyword_union, optional($.keyword_all)), - $.keyword_except, - $.keyword_intersect, - ), - ), - $._select_statement, - ), - ), - ), - - _select_statement: $ => optional_parenthesis( - seq( - $.select, - optional( - seq( - $.keyword_into, - $.select_expression, - ), - ), - optional($.from), - ), - ), - - comment_statement: $ => seq( - $.keyword_comment, - $.keyword_on, - $._comment_target, - $.keyword_is, - choice( - $.keyword_null, - alias($._literal_string, $.literal), - ), - ), - - _argmode: $ => choice( - $.keyword_in, - $.keyword_out, - $.keyword_inout, - $.keyword_variadic, - seq($.keyword_in, $.keyword_out), - ), - - function_argument: $ => seq( - optional($._argmode), - optional($.identifier), - $._type, - optional( - seq( - choice($.keyword_default, '='), - $.literal, - ), - ), - ), - - function_arguments: $ => paren_list( - $.function_argument, - false, - ), - - _comment_target: $ => choice( - // TODO: access method - // TODO: aggregate - $.cast, - // TODO: collation - seq($.keyword_column, alias($._qualified_field, $.object_reference)), - // TODO: constraint (on domain) - // TODO: conversion - seq($.keyword_database, $.identifier), - // TODO: domain - seq($.keyword_extension, $.object_reference), - // TODO: event trigger - // TODO: foreign data wrapper - // TODO: foreign table - seq($.keyword_function, $.object_reference, optional($.function_arguments)), - seq($.keyword_index, $.object_reference), - // TODO: large object - seq($.keyword_materialized, $.keyword_view, $.object_reference), - // TODO: operator (|class|family) - // TODO: policy - // TODO: (procedural) language - // TODO: procedure - // TODO: publication - seq($.keyword_role, $.identifier), - // TODO: routine - // TODO: rule - seq($.keyword_schema, $.identifier), - seq($.keyword_sequence, $.object_reference), - // TODO: server - // TODO: statistics - // TODO: subscription - seq($.keyword_table, $.object_reference), - seq($.keyword_tablespace, $.identifier), - // TODO: text search (configuration|dictionary|parser|template) - // TODO: transform for - seq($.keyword_trigger, $.identifier, $.keyword_on, $.object_reference), - seq($.keyword_type, $.identifier), - seq($.keyword_view, $.object_reference), - ), - - select: $ => seq( - $.keyword_select, - seq( - optional($.keyword_distinct), - $.select_expression, - ), - ), - - select_expression: $ => seq( - $.term, - repeat( - seq( - ',', - $.term, - ), - ), - ), - - term: $ => seq( - field( - 'value', - choice( - $.all_fields, - $._expression, - ), - ), - optional($._alias), - ), - - _truncate_statement: $ => seq( - $.keyword_truncate, - optional($.keyword_table), - optional($.keyword_only), - comma_list($.object_reference), - optional($._drop_behavior), - ), - - _delete_statement: $ => seq( - $.delete, - alias($._delete_from, $.from), - optional($.returning), - ), - - _delete_from: $ => seq( - $.keyword_from, - optional( - $.keyword_only, - ), - $.object_reference, - optional($.where), - optional($.order_by), - optional($.limit), - ), - - delete: $ => seq( - $.keyword_delete, - optional($.index_hint), - ), - - _create_statement: $ => seq( - choice( - $.create_table, - $.create_view, - $.create_materialized_view, - $.create_index, - $.create_function, - $.create_type, - $.create_database, - $.create_role, - $.create_sequence, - $.create_extension, - $.create_trigger, - prec.left(seq( - $.create_schema, - repeat($._create_statement), - )), - ), - ), - - _table_settings: $ => choice( - $.table_partition, - $.stored_as, - $.storage_location, - $.table_sort, - $.row_format, - seq( - $.keyword_tblproperties, - paren_list($.table_option, true), - ), - seq($.keyword_without, $.keyword_oids), - $.storage_parameters, - $.table_option, - ), - - storage_parameters: $ => seq( - $.keyword_with, - paren_list( - seq($.identifier, optional(seq('=', choice($.literal, $.array)))), - true - ), - ), - - // left precedence because 'quoted' table options otherwise conflict with - // `create function` string bodies; if you remove this precedence you will - // have to also disable the `_literal_string` choice for the `name` field - // in =-assigned `table_option`s - create_table: $ => prec.left( - seq( - $.keyword_create, - optional( - choice( - $._temporary, - $.keyword_unlogged, - $.keyword_external, - ) - ), - $.keyword_table, - optional($._if_not_exists), - $.object_reference, - choice( - seq( - $.column_definitions, - repeat($._table_settings), - optional( - seq( - $.keyword_as, - $._select_statement, - ), - ) - ), - seq( - repeat($._table_settings), - seq( - $.keyword_as, - $.create_query, - ), - ), - ), - ), - ), - - reset_statement: $ => seq( - $.keyword_reset, - choice( - $.object_reference, - $.keyword_all, - seq($.keyword_session, $.keyword_authorization), - $.keyword_role, - ), - ), - - _transaction_mode: $ => seq( - $.keyword_isolation, - $.keyword_level, - choice( - $.keyword_serializable, - seq($.keyword_repeatable, $.keyword_read), - seq($.keyword_read, $.keyword_committed), - seq($.keyword_read, $.keyword_uncommitted), - ), - choice( - seq($.keyword_read, $.keyword_write), - seq($.keyword_read, $.keyword_only), - ), - optional($.keyword_not), - $.keyword_deferrable, - ), - - set_statement: $ => seq( - $.keyword_set, - choice( - seq( - optional(choice($.keyword_session, $.keyword_local)), - choice( - seq( - $.object_reference, - choice($.keyword_to, '='), - choice( - $.literal, - $.keyword_default, - $.identifier, - $.keyword_on, - $.keyword_off, - ), - ), - seq($.keyword_schema, $.literal), - seq($.keyword_names, $.literal), - seq($.keyword_time, $.keyword_zone, choice($.literal, $.keyword_local, $.keyword_default)), - seq($.keyword_session, $.keyword_authorization, choice($.identifier, $.keyword_default)), - seq($.keyword_role, choice($.identifier, $.keyword_none)), - ), - ), - seq($.keyword_constraints, choice($.keyword_all, comma_list($.identifier, true)), choice($.keyword_deferred, $.keyword_immediate)), - seq($.keyword_transaction, $._transaction_mode), - seq($.keyword_transaction, $.keyword_snapshot, $._transaction_mode), - seq($.keyword_session, $.keyword_characteristics, $.keyword_as, $.keyword_transaction, $._transaction_mode), - ), - ), - - create_query: $ => $._dml_read, - - create_view: $ => prec.right( - seq( - $.keyword_create, - optional($._or_replace), - optional($._temporary), - optional($.keyword_recursive), - $.keyword_view, - optional($._if_not_exists), - $.object_reference, - optional(paren_list($.identifier)), - $.keyword_as, - $.create_query, - optional( - seq( - $.keyword_with, - optional( - choice( - $.keyword_local, - $.keyword_cascaded, - ) - ), - $._check_option, - ), - ), - ), - ), - - create_materialized_view: $ => prec.right( - seq( - $.keyword_create, - optional($._or_replace), - $.keyword_materialized, - $.keyword_view, - optional($._if_not_exists), - $.object_reference, - $.keyword_as, - $.create_query, - optional( - choice( - seq( - $.keyword_with, - $.keyword_data, - ), - seq( - $.keyword_with, - $.keyword_no, - $.keyword_data, - ) - ) - ) - ), - ), - - // This is only used in create function statement, it is not needed to check - // the start tag match the end one. The usage of this syntax in other - // context is done by _dollar_string. - dollar_quote: () => /\$[^\$]*\$/, - - create_function: $ => seq( - $.keyword_create, - optional($._or_replace), - $.keyword_function, - $.object_reference, - $.function_arguments, - $.keyword_returns, - choice( - $._type, - seq($.keyword_setof, $._type), - seq($.keyword_table, $.column_definitions), - $.keyword_trigger, - ), - repeat( - choice( - $.function_language, - $.function_volatility, - $.function_leakproof, - $.function_security, - $.function_safety, - $.function_strictness, - $.function_cost, - $.function_rows, - $.function_support, - ), - ), - // ensure that there's only one function body -- other specifiers are less - // variable but the body can have all manner of conflicting stuff - $.function_body, - repeat( - choice( - $.function_language, - $.function_volatility, - $.function_leakproof, - $.function_security, - $.function_safety, - $.function_strictness, - $.function_cost, - $.function_rows, - $.function_support, - ), - ), - ), - - _function_return: $ => seq( - $.keyword_return, - $._expression, - ), - - function_declaration: $ => seq( - $.identifier, - $._type, - optional( - seq( - ':=', - choice( - wrapped_in_parenthesis($.statement), - // TODO are there more possibilities here? We can't use `_expression` since - // that includes subqueries - $.literal, - ), - ), - ), - ';', - ), - - _function_body_statement: $ => choice( - $.statement, - $._function_return, - ), + ...require("./grammar/keywords"), + ...require("./grammar/types"), + ...require("./grammar/column-lists"), + ...require("./grammar/expressions"), + ...require("./grammar/transactions"), + ...require("./grammar/statements"), - _tsql_function_body_statement: $ => seq( - optional($.keyword_as), - $.keyword_begin, - optional($.var_declarations), - choice( - repeat($.statement), - repeat1(seq( - $.keyword_begin, - repeat($.statement), - $.keyword_end, - )), - ), - $._function_return, - $.keyword_end, - ), - - function_body: $ => choice( - seq( - $._function_return, - ';' - ), - seq( - $.keyword_begin, - $.keyword_atomic, - repeat1( - seq( - $._function_body_statement, - ';', - ), - ), - $.keyword_end, - ), - seq( - $.keyword_as, - alias($._dollar_quoted_string_start_tag, $.dollar_quote), - optional( - seq( - $.keyword_declare, - repeat1( - $.function_declaration, - ), - ), - ), - $.keyword_begin, - repeat1( - seq( - $._function_body_statement, - ';', - ), - ), - $.keyword_end, - optional(';'), - alias($._dollar_quoted_string_end_tag, $.dollar_quote), - ), - seq( - $.keyword_as, - alias( - choice( - $._single_quote_string, - $._double_quote_string, - ), - $.literal - ), - ), - seq( - $.keyword_as, - alias($._dollar_quoted_string_start_tag, $.dollar_quote), - $._function_body_statement, - optional(';'), - alias($._dollar_quoted_string_end_tag, $.dollar_quote), - ), - $._tsql_function_body_statement, - ), - - function_language: $ => seq( - $.keyword_language, - // TODO Maybe we should do different version of function_body_statement in - // regard to the defined language to match either sql, plsql or - // plpgsql. Currently the function_body_statement support only sql. And - // maybe for other language the function_body should be a string. - $.identifier - ), - - function_volatility: $ => choice( - $.keyword_immutable, - $.keyword_stable, - $.keyword_volatile, - ), - - function_leakproof: $ => seq( - optional($.keyword_not), - $.keyword_leakproof, - ), - - function_security: $ => seq( - optional($.keyword_external), - $.keyword_security, - choice($.keyword_invoker, $.keyword_definer), - ), - - function_safety: $ => seq( - $.keyword_parallel, - choice( - $.keyword_safe, - $.keyword_unsafe, - $.keyword_restricted, - ), - ), - - function_strictness: $ => choice( - seq( - choice( - $.keyword_called, - seq( - $.keyword_returns, - $.keyword_null, - ), - ), - $.keyword_on, - $.keyword_null, - $.keyword_input, - ), - $.keyword_strict, - ), - - function_cost: $ => seq( - $.keyword_cost, - $._natural_number, - ), - - function_rows: $ => seq( - $.keyword_rows, - $._natural_number, - ), - - function_support: $ => seq( - $.keyword_support, - alias($._literal_string, $.literal), - ), - - _operator_class: $ => seq( - field("opclass", $.identifier), - optional( - field("opclass_parameters", wrapped_in_parenthesis(comma_list($.term))) - ) - ), - - composite_field: $ => seq( - wrapped_in_parenthesis( - comma_list( - alias($._index_field, $.field), - true, - ), - ), - optional($.keyword_hash), - ), - - _index_field: $ => seq( - choice( - field("expression", wrapped_in_parenthesis($._expression)), - field("function", $.invocation), - field("column", $._column), - ), - optional(seq($.keyword_collate, $.identifier)), - optional($._operator_class), - optional(choice($.keyword_hash, $.direction)), - optional( - seq( - $.keyword_nulls, - choice( - $.keyword_first, - $.keyword_last - ) - ) - ), - ), - - index_fields: $ => wrapped_in_parenthesis( - comma_list( - choice( - $.composite_field, - alias($._index_field, $.field), - ), - true, - ), - ), - tablespace: $ => seq($.keyword_tablespace, $.identifier), - tablet_split: $ => seq($.keyword_split, $.keyword_into, $._natural_number, $.keyword_tablets), - - covering_columns: $ => seq( - $.keyword_include, - $.index_fields, - ), - - create_index: $ => seq( - $.keyword_create, - optional($.keyword_unique), - $.keyword_index, - optional($.keyword_concurrently), - optional( - seq( - optional($._if_not_exists), - field("column", $._column), - ), - ), - $.keyword_on, - optional($.keyword_only), - seq( - $.object_reference, - optional( - seq( - $.keyword_using, - choice( - $.keyword_btree, - $.keyword_hash, - $.keyword_gist, - $.keyword_spgist, - $.keyword_gin, - $.keyword_brin - ), - ), - ), - $.index_fields - ), - optional($.covering_columns), - optional($.tablespace), - optional($.tablet_split), - optional( - $.where, - ), - ), - - create_schema: $ => prec.left(seq( - $.keyword_create, - $.keyword_schema, - choice( - seq( - optional($._if_not_exists), - $.identifier, - optional(seq($.keyword_authorization, $.identifier)), - ), - seq( - $.keyword_authorization, - $.identifier, - ), - ), - )), - - _with_settings: $ => seq( - field('name', $.identifier), - optional('='), - field('value', choice($.identifier, alias($._single_quote_string, $.literal))), - ), - - create_database: $ => prec.left(seq( - $.keyword_create, - $.keyword_database, - optional($._if_not_exists), - $.identifier, - optional($.keyword_with), - repeat( - $._with_settings - ), - )), - - create_role: $ => prec.left(seq( - $.keyword_create, - choice( - $.keyword_user, - $.keyword_role, - $.keyword_group, - ), - $.identifier, - optional($.keyword_with), - repeat( - choice( - $._user_access_role_config, - $._role_options, - ), - ), - )), - - _role_options: $ => choice( - field("option", $.identifier), - seq( - $.keyword_valid, - $.keyword_until, - field("valid_until", alias($._literal_string, $.literal)) - ), - seq( - $.keyword_connection, - $.keyword_limit, - field("connection_limit", alias($._integer, $.literal)) - ), - seq( - optional($.keyword_encrypted), - $.keyword_password, - choice( - field("password", alias($._literal_string, $.literal)), - $.keyword_null, - ), - ), - ), - - _user_access_role_config: $ => seq( - choice( - seq(optional($.keyword_in), $.keyword_role), - seq($.keyword_in, $.keyword_group), - $.keyword_admin, - $.keyword_user, - ), - comma_list($.identifier, true), - ), - - create_sequence: $ => seq( - $.keyword_create, - optional( - choice( - choice($.keyword_temporary, $.keyword_temp), - $.keyword_unlogged, - ) - ), - $.keyword_sequence, - optional($._if_not_exists), - $.object_reference, - repeat( - choice( - seq($.keyword_as, $._type), - seq($.keyword_increment, optional($.keyword_by), field("increment", alias($._integer, $.literal))), - seq($.keyword_minvalue, choice($.literal, seq($.keyword_no, $.keyword_minvalue))), - seq($.keyword_no, $.keyword_minvalue), - seq($.keyword_maxvalue, choice($.literal, seq($.keyword_no, $.keyword_maxvalue))), - seq($.keyword_no, $.keyword_maxvalue), - seq($.keyword_start, optional($.keyword_with), field("start", alias($._integer, $.literal))), - seq($.keyword_cache, field("cache", alias($._integer, $.literal))), - seq(optional($.keyword_no), $.keyword_cycle), - seq($.keyword_owned, $.keyword_by, choice($.keyword_none, $.object_reference)), - ) - ), - ), - - create_extension: $ => prec.left(seq( - $.keyword_create, - $.keyword_extension, - optional($._if_not_exists), - $.identifier, - optional($.keyword_with), - optional(seq($.keyword_schema, $.identifier)), - optional(seq($.keyword_version, choice($.identifier, alias($._literal_string, $.literal)))), - optional($.keyword_cascade), - )), - - create_trigger: $ => seq( - $.keyword_create, - optional($._or_replace), - // mariadb - optional(seq($.keyword_definer, '=', $.identifier)), - optional($.keyword_constraint), - // sqlite - optional($._temporary), - $.keyword_trigger, - // sqlite/mariadb - optional($._if_not_exists), - $.object_reference, - choice( - $.keyword_before, - $.keyword_after, - seq($.keyword_instead, $.keyword_of), - ), - $._create_trigger_event, - repeat(seq($.keyword_or, $._create_trigger_event)), - $.keyword_on, - $.object_reference, - repeat( - choice( - seq($.keyword_from, $.object_reference), - choice( - seq($.keyword_not, $.keyword_deferrable), - $.keyword_deferrable, - seq($.keyword_initially, $.keyword_immediate), - seq($.keyword_initially, $.keyword_deferred), - ), - seq($.keyword_referencing, choice($.keyword_old, $.keyword_new), $.keyword_table, optional($.keyword_as), $.identifier), - seq( - $.keyword_for, - optional($.keyword_each), - choice($.keyword_row, $.keyword_statement), - // mariadb - optional(seq(choice($.keyword_follows, $.keyword_precedes), $.identifier)), - ), - seq($.keyword_when, wrapped_in_parenthesis($._expression)), - ), - ), - $.keyword_execute, - choice($.keyword_function, $.keyword_procedure), - $.object_reference, - paren_list(field('parameter', $.term)), - ), - - _create_trigger_event: $ => choice( - $.keyword_insert, - seq( - $.keyword_update, - optional( - seq( - $.keyword_of, - comma_list($.identifier, true), - ), - ), - ), - $.keyword_delete, - $.keyword_truncate, - ), - - create_type: $ => prec.left(seq( - $.keyword_create, - $.keyword_type, - $.object_reference, - optional( - seq( - choice( - seq( - $.keyword_as, - $.column_definitions, - optional(seq($.keyword_collate, $.identifier)) - ), - seq( - $.keyword_as, - $.keyword_enum, - $.enum_elements, - ), - seq( - optional( - seq( - $.keyword_as, - $.keyword_range, - ) - ), - paren_list( - $._with_settings - ), - ), - ), - ), - ), - )), - - enum_elements: $ => seq( - paren_list(field("enum_element", alias($._literal_string, $.literal))), - ), - - _alter_statement: $ => seq( - choice( - $.alter_table, - $.alter_view, - $.alter_schema, - $.alter_type, - $.alter_index, - $.alter_database, - $.alter_role, - $.alter_sequence, - ), - ), - - _rename_statement: $ => seq( - $.keyword_rename, - choice( - $.keyword_table, - $.keyword_tables, - ), - optional($._if_exists), - $.object_reference, - optional( - choice( - $.keyword_nowait, - seq( - $.keyword_wait, - field('timeout', alias($._natural_number, $.literal)) - ) - ) - ), - $.keyword_to, - $.object_reference, - repeat( - seq( - ',', - $._rename_table_names, - ) - ), - ), - - _rename_table_names: $ => seq( - $.object_reference, - $.keyword_to, - $.object_reference, - ), - - alter_table: $ => seq( - $.keyword_alter, - $.keyword_table, - optional($._if_exists), - optional($.keyword_only), - $.object_reference, - choice( - seq( - $._alter_specifications, - repeat( - seq( - ",", - $._alter_specifications - ) - ) - ), - ), - ), - - _alter_specifications: $ => choice( - $.add_column, - $.add_constraint, - $.drop_constraint, - $.alter_column, - $.modify_column, - $.change_column, - $.drop_column, - $.rename_object, - $.rename_column, - $.set_schema, - $.change_ownership, - ), - - // TODO: optional `keyword_add` is necessary to allow for chained alter statements in t-sql - // maybe needs refactoring - add_column: $ => seq( - optional($.keyword_add), - optional( - $.keyword_column, - ), - optional($._if_not_exists), - $.column_definition, - optional($.column_position), - ), - - add_constraint: $ => seq( - $.keyword_add, - optional($.keyword_constraint), - $.identifier, - $.constraint, - ), - - drop_constraint: $ => seq( - $.keyword_drop, - $.keyword_constraint, - optional($._if_exists), - $.identifier, - optional($._drop_behavior), - ), - - alter_column: $ => seq( - // TODO constraint management - $.keyword_alter, - optional( - $.keyword_column, - ), - field('name', $.identifier), - choice( - seq( - choice( - $.keyword_set, - $.keyword_drop, - ), - $.keyword_not, - $.keyword_null, - ), - seq( - optional( - seq( - $.keyword_set, - $.keyword_data, - ), - ), - $.keyword_type, - field('type', $._type), - ), - seq( - $.keyword_set, - choice( - seq( - $.keyword_statistics, - field('statistics', $._integer) - ), - seq( - $.keyword_storage, - choice( - $.keyword_plain, - $.keyword_external, - $.keyword_extended, - $.keyword_main, - $.keyword_default, - ), - ), - seq( - $.keyword_compression, - field('compression_method', $._identifier) - ), - seq( - paren_list($._key_value_pair, true), - ), - seq( - $.keyword_default, - $._expression, - ), - ) - ), - seq( - $.keyword_drop, - $.keyword_default, - ), - ), - ), - - modify_column: $ => seq( - $.keyword_modify, - optional( - $.keyword_column, - ), - optional($._if_exists), - $.column_definition, - optional($.column_position), - ), - - change_column: $ => seq( - $.keyword_change, - optional( - $.keyword_column, - ), - optional($._if_exists), - field('old_name', $.identifier), - $.column_definition, - optional($.column_position), - ), - - column_position: $ => choice( - $.keyword_first, - seq( - $.keyword_after, - field('col_name', $.identifier), - ), - ), - - drop_column: $ => seq( - $.keyword_drop, - optional( - $.keyword_column, - ), - optional($._if_exists), - field('name', $.identifier), - ), - - rename_column: $ => seq( - $.keyword_rename, - optional( - $.keyword_column, - ), - field('old_name', $.identifier), - $.keyword_to, - field('new_name', $.identifier), - ), - - alter_view: $ => seq( - $.keyword_alter, - $.keyword_view, - optional($._if_exists), - $.object_reference, - choice( - // TODO Postgres allows a single "alter column" to set or drop default - $.rename_object, - $.rename_column, - $.set_schema, - $.change_ownership, - ), - ), - - alter_schema: $ => seq( - $.keyword_alter, - $.keyword_schema, - $.identifier, - choice( - $.keyword_rename, - $.keyword_owner, - ), - $.keyword_to, - $.identifier, - ), - - alter_database: $ => seq( - $.keyword_alter, - $.keyword_database, - $.identifier, - optional($.keyword_with), - choice( - seq($.rename_object), - seq($.change_ownership), - seq( - $.keyword_reset, - choice( - $.keyword_all, - field("configuration_parameter", $.identifier) - ), - ), - seq( - $.keyword_set, - choice( - seq($.keyword_tablespace, $.identifier), - $.set_configuration, - ), - ), - ), - ), - - alter_role: $ => prec.left(seq( - $.keyword_alter, - choice( - $.keyword_role, - $.keyword_group, - $.keyword_user, - ), - choice($.identifier, $.keyword_all), - choice( - $.rename_object, - seq(optional($.keyword_with),repeat($._role_options)), - seq( - optional(seq($.keyword_in, $.keyword_database, $.identifier)), - choice( - seq( - $.keyword_set, - $.set_configuration, - ), - seq( - $.keyword_reset, - choice( - $.keyword_all, - field("option", $.identifier), - )), - ), - ) - ), - )), - - set_configuration: $ => seq( - field("option", $.identifier), - choice( - seq($.keyword_from, $.keyword_current), - seq( - choice($.keyword_to, "="), - choice( - field("parameter", $.identifier), - $.literal, - $.keyword_default - ) - ) - ), - ), - - alter_index: $ => seq( - $.keyword_alter, - $.keyword_index, - optional($._if_exists), - $.identifier, - choice( - $.rename_object, - seq( - $.keyword_alter, - optional($.keyword_column), - alias($._natural_number, $.literal), - $.keyword_set, - $.keyword_statistics, - alias($._natural_number, $.literal), - ), - seq($.keyword_reset, paren_list($.identifier)), - seq( - $.keyword_set, - choice( - seq($.keyword_tablespace, $.identifier), - paren_list(seq($.identifier, '=', field("value", $.literal))) - ), - ), - ), - ), - - alter_sequence: $ => seq( - $.keyword_alter, - $.keyword_sequence, - optional($._if_exists), - $.object_reference, - choice( - repeat1( - choice( - seq($.keyword_as, $._type), - seq($.keyword_increment, optional($.keyword_by), $.literal), - seq($.keyword_minvalue, choice($.literal, seq($.keyword_no, $.keyword_minvalue))), - seq($.keyword_maxvalue, choice($.literal, seq($.keyword_no, $.keyword_maxvalue))), - seq($.keyword_start, optional($.keyword_with), field("start", alias($._integer, $.literal))), - seq($.keyword_restart, optional($.keyword_with), field("restart", alias($._integer, $.literal))), - seq($.keyword_cache, field("cache", alias($._integer, $.literal))), - seq(optional($.keyword_no), $.keyword_cycle), - seq($.keyword_owned, $.keyword_by, choice($.keyword_none, $.object_reference)), - ), - ), - $.rename_object, - $.change_ownership, - seq( - $.keyword_set, - choice( - choice($.keyword_logged, $.keyword_unlogged), - seq($.keyword_schema, $.identifier) - ), - ), - ), - ), - - alter_type: $ => seq( - $.keyword_alter, - $.keyword_type, - $.identifier, - choice( - $.change_ownership, - $.set_schema, - $.rename_object, - seq( - $.keyword_rename, - $.keyword_attribute, - $.identifier, - $.keyword_to, - $.identifier, - optional($._drop_behavior) - ), - seq( - $.keyword_add, - $.keyword_value, - optional($._if_not_exists), - alias($._single_quote_string,$.literal), - optional( - seq( - choice($.keyword_before, $.keyword_after), - alias($._single_quote_string,$.literal), - ) - ), - ), - seq( - $.keyword_rename, - $.keyword_value, - alias($._single_quote_string,$.literal), - $.keyword_to, - alias($._single_quote_string,$.literal), - ), - seq( - choice( - seq( - $.keyword_add, - $.keyword_attribute, - $.identifier, - $._type - ), - seq($.keyword_drop, - $.keyword_attribute, - optional($._if_exists), - $.identifier), - seq( - $.keyword_alter, - $.keyword_attribute, - $.identifier, - optional(seq($.keyword_set, $.keyword_data)), - $.keyword_type, - $._type - ), - ), - optional(seq($.keyword_collate, $.identifier)), - optional($._drop_behavior) - ) - ), - ), - - _drop_behavior: $ => choice( - $.keyword_cascade, - $.keyword_restrict, - ), - - _drop_statement: $ => seq( - choice( - $.drop_table, - $.drop_view, - $.drop_index, - $.drop_type, - $.drop_schema, - $.drop_database, - $.drop_role, - $.drop_sequence, - $.drop_extension, - $.drop_function, - ), - ), - - drop_table: $ => seq( - $.keyword_drop, - $.keyword_table, - optional($._if_exists), - $.object_reference, - optional($._drop_behavior), - ), - - drop_view: $ => seq( - $.keyword_drop, - $.keyword_view, - optional($._if_exists), - $.object_reference, - optional($._drop_behavior), - ), - - drop_schema: $ => seq( - $.keyword_drop, - $.keyword_schema, - optional($._if_exists), - $.identifier, - optional($._drop_behavior) - ), - - drop_database: $ => prec.left(seq( - $.keyword_drop, - $.keyword_database, - optional($._if_exists), - $.identifier, - optional($.keyword_with), - optional($.keyword_force), - )), - - drop_role: $ => seq( - $.keyword_drop, - choice( - $.keyword_group, - $.keyword_role, - $.keyword_user, - ), - optional($._if_exists), - $.identifier, - ), - - drop_type: $ => seq( - $.keyword_drop, - $.keyword_type, - optional($._if_exists), - $.object_reference, - optional($._drop_behavior), - ), - - drop_sequence: $ => seq( - $.keyword_drop, - $.keyword_sequence, - optional($._if_exists), - $.object_reference, - optional($._drop_behavior), - ), - - drop_index: $ => seq( - $.keyword_drop, - $.keyword_index, - optional($.keyword_concurrently), - optional($._if_exists), - field("name", $.identifier), - optional($._drop_behavior), - optional( - seq( - $.keyword_on, - $.object_reference, - ), - ), - ), - - drop_extension: $ => seq( - $.keyword_drop, - $.keyword_extension, - optional($._if_exists), - comma_list($.identifier, true), - optional(choice($.keyword_cascade, $.keyword_restrict)), - ), - - drop_function: $ => seq( - $.keyword_drop, - $.keyword_function, - optional($._if_exists), - $.object_reference, - optional($._drop_behavior), - ), - - rename_object: $ => seq( - $.keyword_rename, - $.keyword_to, - $.object_reference, - ), - - set_schema: $ => seq( - $.keyword_set, - $.keyword_schema, - field('schema', $.identifier), - ), - - change_ownership: $ => seq( - $.keyword_owner, - $.keyword_to, - $.identifier, - ), - - object_id: $ => seq( - $.keyword_object_id, - wrapped_in_parenthesis( - seq( - alias($._literal_string, $.literal), - optional( - seq( - ',', - alias($._literal_string, $.literal), - ), - ), - ), - ), - ), - - object_reference: $ => choice( - seq( - field('database', $.identifier), - '.', - field('schema', $.identifier), - '.', - field('name', $.identifier), - ), - seq( - field('schema', $.identifier), - '.', - field('name', $.identifier), - ), - field('name', $.identifier), - ), - - _copy_statement: $ => seq( - $.keyword_copy, - $.object_reference, - $._column_list, - $.keyword_from, - choice( - $.keyword_stdin, - alias($._literal_string, "filename"), - seq($.keyword_program, alias($._literal_string, "command")), - ), - optional($.keyword_with), - wrapped_in_parenthesis( - repeat1( - choice( - seq( - $.keyword_format, - choice( - $.keyword_csv, - $.keyword_binary, - $.keyword_text, - ), - ), - seq( - $.keyword_freeze, - choice( - $.keyword_true, - $.keyword_false - ) - ), - seq( - $.keyword_header, - choice( - $.keyword_true, - $.keyword_false, - $.keyword_match - ), - ), - seq( - choice( - $.keyword_delimiter, - $.keyword_null, - $.keyword_default, - $.keyword_escape, - $.keyword_quote, - $.keyword_encoding, - ), - alias($._literal_string, $.identifier) - ), - seq( - choice( - $.keyword_force_null, - $.keyword_force_not_null, - $.keyword_force_quote, - ), - $._column_list - ), - ), - ), - ), - optional($.where), - ), - - _insert_statement: $ => seq( - $.insert, - optional($.returning), - ), - - insert: $ => seq( - choice( - $.keyword_insert, - $.keyword_replace - ), - optional( - choice( - $.keyword_low_priority, - $.keyword_delayed, - $.keyword_high_priority, - ), - ), - optional($.keyword_ignore), - optional( - choice( - $.keyword_into, - $.keyword_overwrite, // Spark SQL - ), - ), - $.object_reference, - optional($.table_partition), // Spark SQL - optional( - seq( - $.keyword_as, - field('alias', $.identifier) - ), - ), - // TODO we need a test for `insert...set` - choice( - $._insert_values, - $._set_values, - ), - optional( - choice( - $._on_conflict, - $._on_duplicate_key_update, - ), - ), - ), - - _on_conflict: $ => seq( - $.keyword_on, - $.keyword_conflict, - seq( - $.keyword_do, - choice( - $.keyword_nothing, - seq( - $.keyword_update, - $._set_values, - optional($.where), - ), - ), - ), - ), - - _on_duplicate_key_update: $ => seq( - $.keyword_on, - $.keyword_duplicate, - $.keyword_key, - $.keyword_update, - $.assignment_list, - ), - - assignment_list: $ => seq( - $.assignment, - repeat(seq(',', $.assignment)), - ), - - _insert_values: $ => seq( - optional(alias($._column_list, $.list)), - choice( - seq( - $.keyword_values, - comma_list($.list, true), - ), - $._dml_read, - ), - ), - - _set_values: $ => seq( - $.keyword_set, - comma_list($.assignment, true), - ), - - _column_list: $ => paren_list(alias($._column, $.column), true), - _column: $ => choice( - $.identifier, - alias($._literal_string, $.literal), - ), - - _update_statement: $ => seq( - $.update, - optional($.returning), - ), - - _merge_statement: $=> seq( - $.keyword_merge, - $.keyword_into, - $.object_reference, - optional($._alias), - $.keyword_using, - choice( - $.subquery, - $.object_reference - ), - optional($._alias), - $.keyword_on, - optional_parenthesis(field("predicate", $._expression)), - repeat1($.when_clause) - ), - - when_clause: $ => prec.left(seq( - $.keyword_when, - optional($.keyword_not), - $.keyword_matched, - optional( - seq( - $.keyword_and, - optional_parenthesis(field("predicate", $._expression)) - ) - ), - $.keyword_then, - choice( - $.keyword_delete, - seq( - $.keyword_update, - $._set_values, - ), - seq( - $.keyword_insert, - $._insert_values - ), - optional($.where) - ) - )), - - _optimize_statement: $ => choice( - $._compute_stats, - $._vacuum_table, - $._optimize_table, - ), - - // Compute stats for Impala and Hive - _compute_stats: $ => prec.left(choice( - // Hive - seq( - $.keyword_analyze, - $.keyword_table, - $.object_reference, - optional($._partition_spec), - $.keyword_compute, - $.keyword_statistics, - optional( - seq( - $.keyword_for, - $.keyword_columns - ) - ), - optional( - seq( - $.keyword_cache, - $.keyword_metadata - ) - ), - optional($.keyword_noscan), - ), - // Impala - seq( - $.keyword_compute, - optional( - $.keyword_incremental, - ), - $.keyword_stats, - $.object_reference, - optional( - choice( - paren_list(repeat1($.field)), - $._partition_spec, - ) - ) - ), - )), - - _optimize_table: $ => choice( - // Athena/Iceberg - seq( - $.keyword_optimize, - $.object_reference, - $.keyword_rewrite, - $.keyword_data, - $.keyword_using, - $.keyword_bin_pack, - optional( - $.where, - ) - ), - // MariaDB Optimize - seq( - $.keyword_optimize, - optional( - choice( - $.keyword_local, - //$.keyword_no_write_to_binlog, - ) - ), - $.keyword_table, - $.object_reference, - repeat(seq(',', $.object_reference)), - ), - ), - - _vacuum_table: $ => prec.left(seq( - $.keyword_vacuum, - optional($._vacuum_option), - $.object_reference, - optional( - paren_list($.field) - ), - )), - - _vacuum_option: $ => choice( - seq($.keyword_full, optional(choice($.keyword_true, $.keyword_false))), - seq($.keyword_parallel, optional(choice($.keyword_true, $.keyword_false))), - seq($.keyword_analyze, optional(choice($.keyword_true, $.keyword_false))), - // seq($.keyword_freeze, choice($.keyword_true, $.keyword_false)), - // seq($.keyword_skip_locked, choice($.keyword_true, $.keyword_false)), - // seq($.keyword_truncate, choice($.keyword_true, $.keyword_false)), - // seq($.keyword_disable_page_skipping, choice($.keyword_true, $.keyword_false)), - // seq($.keyword_process_toast, choice($.keyword_true, $.keyword_false)), - // seq($.keyword_index_cleanup, choice($.keyword_auto, $.keyword_on, $.keyword_off)), - ), - - // TODO: this does not account for partitions specs like - // (partcol1='2022-01-01', hr=11) - // the second argument is not a $.table_option - _partition_spec: $ => seq( - $.keyword_partition, - paren_list($.table_option, true), - ), - - update: $ => seq( - $.keyword_update, - optional($.keyword_only), - choice( - $._mysql_update_statement, - $._postgres_update_statement, - ), - ), - - _mysql_update_statement: $ => prec(0, - seq( - comma_list($.relation, true), - repeat($.join), - $._set_values, - optional($.where), - ), - ), - - _postgres_update_statement: $ => prec(1, - seq( - $.relation, - $._set_values, - optional($.from), - ), - ), - - storage_location: $ => prec.right( - seq( - $.keyword_location, - field('path', alias($._literal_string, $.literal)), - optional( - seq( - $.keyword_cached, - $.keyword_in, - field('pool', alias($._literal_string, $.literal)), - optional( - choice( - $.keyword_uncached, - seq( - $.keyword_with, - $.keyword_replication, - '=', - field('value', alias($._natural_number, $.literal)), - ), - ), - ), - ) - ) - ), - ), - - row_format: $ => seq( - $.keyword_row, - $.keyword_format, - $.keyword_delimited, - optional( - seq( - $.keyword_fields, - $.keyword_terminated, - $.keyword_by, - field('fields_terminated_char', alias($._literal_string, $.literal)), - optional( - seq( - $.keyword_escaped, - $.keyword_by, - field('escaped_char', alias($._literal_string, $.literal)), - ) - ) - ) - ), - optional( - seq( - $.keyword_lines, - $.keyword_terminated, - $.keyword_by, - field('row_terminated_char', alias($._literal_string, $.literal)), - ) - ) - ), - - table_sort: $ => seq( - $.keyword_sort, - $.keyword_by, - paren_list($.identifier, true), - ), - - table_partition: $ => seq( - choice( - // Postgres/MySQL style - seq( - $.keyword_partition, - $.keyword_by, - choice( - $.keyword_range, - $.keyword_hash, - ) - ), - // Hive style - seq( - $.keyword_partitioned, - $.keyword_by, - ), - // Spark SQL - $.keyword_partition, - ), - choice( - paren_list($.identifier),// postgres & Impala (CTAS) - $.column_definitions, // impala/hive external tables - paren_list($._key_value_pair, true), // Spark SQL - ) - ), - - _key_value_pair: $ => seq( - field('key',$.identifier), - '=', - field('value', alias($._literal_string, $.literal)), - ), - - stored_as: $ => seq( - $.keyword_stored, - $.keyword_as, - choice( - $.keyword_parquet, - $.keyword_csv, - $.keyword_sequencefile, - $.keyword_textfile, - $.keyword_rcfile, - $.keyword_orc, - $.keyword_avro, - $.keyword_jsonfile, - ), - ), - - assignment: $ => seq( - field('left', - alias( - $._qualified_field, - $.field, - ), - ), - '=', - field('right', $._expression), - ), - - table_option: $ => choice( - seq($.keyword_default, $.keyword_character, $.keyword_set, $.identifier), - seq($.keyword_collate, $.identifier), - field('name', $.keyword_default), - seq( - field('name', choice($.keyword_engine, $.identifier, $._literal_string)), - '=', - field('value', choice($.identifier, $._literal_string)), - ), - ), - - column_definitions: $ => seq( - '(', - comma_list($.column_definition, true), - optional($.constraints), - ')', - ), - - column_definition: $ => prec.left(seq( - field('name', $._column), - field('type', $._type), - repeat($._column_constraint), - )), - - _column_comment: $ => seq( - $.keyword_comment, - alias($._literal_string, $.literal) - ), - - _column_constraint: $ => prec.left(choice( - choice( - $.keyword_null, - $._not_null, - ), - seq( - $.keyword_references, - $.object_reference, - paren_list($.identifier, true), - repeat( - seq( - $.keyword_on, - choice($.keyword_delete, $.keyword_update), - choice( - seq($.keyword_no, $.keyword_action), - $.keyword_restrict, - $.keyword_cascade, - seq( - $.keyword_set, - choice($.keyword_null, $.keyword_default), - optional(paren_list($.identifier, true)) - ), - ), - ), - ), - ), - $._default_expression, - $._primary_key, - $.keyword_auto_increment, - $.direction, - $._column_comment, - $._check_constraint, - seq( - optional(seq($.keyword_generated, $.keyword_always)), - $.keyword_as, - $._expression, - ), - choice( - $.keyword_stored, - $.keyword_virtual, - ), - $.keyword_unique - )), - - _check_constraint: $ => seq( - optional( - seq( - $.keyword_constraint, - $.literal - ) - ), - $.keyword_check, - wrapped_in_parenthesis($.binary_expression) - ), - - _default_expression: $ => seq( - $.keyword_default, - optional_parenthesis($._inner_default_expression), - ), - _inner_default_expression: $ => choice( - $.literal, - $.list, - $.cast, - $.binary_expression, - $.unary_expression, - $.array, - $.invocation, - $.keyword_current_timestamp, - alias($.implicit_cast, $.cast), - ), - - constraints: $ => seq( - ',', - $.constraint, - repeat( - seq(',', $.constraint), - ), - ), - - constraint: $ => choice( - $._constraint_literal, - $._key_constraint, - $._primary_key_constraint, - $._check_constraint - ), - - _constraint_literal: $ => seq( - $.keyword_constraint, - field('name', $.identifier), - choice( - seq( - $._primary_key, - $.ordered_columns, - ), - seq( - $._check_constraint - ) - ) - ), - - _primary_key_constraint: $ => seq( - $._primary_key, - $.ordered_columns, - ), - - _key_constraint: $ => seq( - choice( - seq( - $.keyword_unique, - optional( - choice( - $.keyword_index, - $.keyword_key, - seq($.keyword_nulls, optional($.keyword_not), $.keyword_distinct), - ), - ), - ), - seq(optional($.keyword_foreign), $.keyword_key, optional($._if_not_exists)), - $.keyword_index, - ), - optional(field('name', $.identifier)), - $.ordered_columns, - optional( - seq( - $.keyword_references, - $.object_reference, - paren_list($.identifier, true), - repeat( - seq( - $.keyword_on, - choice($.keyword_delete, $.keyword_update), - choice( - seq($.keyword_no, $.keyword_action), - $.keyword_restrict, - $.keyword_cascade, - seq( - $.keyword_set, - choice($.keyword_null, $.keyword_default), - optional(paren_list($.identifier, true)) - ), - ), - ), - ), - ), - ), - ), - - ordered_columns: $ => paren_list(alias($.ordered_column, $.column), true), - - ordered_column: $ => seq( - field('name', $._column), - optional($.direction), - ), - - all_fields: $ => seq( - optional( - seq( - $.object_reference, - '.', - ), - ), - '*', - ), - - - parameter: $ => /\?|(\$[0-9]+)/, - - case: $ => seq( - $.keyword_case, - choice( - // simplified CASE x WHEN - seq( - $._expression, - $.keyword_when, - $._expression, - $.keyword_then, - $._expression, - repeat( - seq( - $.keyword_when, - $._expression, - $.keyword_then, - $._expression, - ) - ), - ), - // standard CASE WHEN x, where x must be a predicate - seq( - $.keyword_when, - $._expression, - $.keyword_then, - $._expression, - repeat( - seq( - $.keyword_when, - $._expression, - $.keyword_then, - $._expression, - ) - ), - ), - ), - optional( - seq( - $.keyword_else, - $._expression, - ) - ), - $.keyword_end, - ), - - field: $ => field('name', $.identifier), - - _qualified_field: $ => seq( - optional( - seq( - optional_parenthesis($.object_reference), - '.', - ), - ), - field('name', $.identifier), - ), - - implicit_cast: $ => seq( - $._expression, - '::', - $._type, - ), - - // Postgres syntax for intervals - interval: $ => seq( - $.keyword_interval, - $._literal_string, - ), - - cast: $ => seq( - field('name', $.keyword_cast), - wrapped_in_parenthesis( - seq( - field('parameter', $._expression), - $.keyword_as, - $._type, - ), - ), - ), - - filter_expression : $ => seq( - $.keyword_filter, - wrapped_in_parenthesis($.where), - ), - - invocation: $ => prec(1, - seq( - $.object_reference, - choice( - // default invocation - paren_list( - seq( - optional($.keyword_distinct), - field( - 'parameter', - $.term, - ), - optional($.order_by) - ) - ), - // https://dev.mysql.com/doc/refman/8.4/en/date-and-time-functions.html#function_extract - paren_list( - seq( - field( - 'unit', - $.object_reference, - ), - $.keyword_from, - $.term - ) - ), - // _aggregate_function, e.g. group_concat - wrapped_in_parenthesis( - seq( - optional($.keyword_distinct), - field('parameter', $.term), - optional($.order_by), - optional(seq( - choice($.keyword_separator, ','), - alias($._literal_string, $.literal) - )), - optional($.limit), - ), - ), - ), - optional( - $.filter_expression - ) - ), - ), - - exists: $ => seq( - $.keyword_exists, - $.subquery, - ), - - partition_by: $ => seq( - $.keyword_partition, - $.keyword_by, - comma_list($._expression, true), - ), - - frame_definition: $ => seq( - choice( - seq( - $.keyword_unbounded, - $.keyword_preceding, - ), - seq( - field("start", - choice( - $.identifier, - $.binary_expression, - alias($._literal_string, $.literal), - alias($._integer, $.literal) - ) - ), - $.keyword_preceding, - ), - $._current_row, - seq( - field("end", - choice( - $.identifier, - $.binary_expression, - alias($._literal_string, $.literal), - alias($._integer, $.literal) - ) - ), - $.keyword_following, - ), - seq( - $.keyword_unbounded, - $.keyword_following, - ), - ), - ), - - window_frame: $ => seq( - choice( - $.keyword_range, - $.keyword_rows, - $.keyword_groups, - ), - - choice( - seq( - $.keyword_between, - $.frame_definition, - optional( - seq( - $.keyword_and, - $.frame_definition, - ) - ) - ), - seq( - $.frame_definition, - ) - ), - optional( - choice( - $._exclude_current_row, - $._exclude_group, - $._exclude_ties, - $._exclude_no_others, - ), - ), - ), - - window_clause: $ => seq( - $.keyword_window, - $.identifier, - $.keyword_as, - $.window_specification, - ), - - window_specification: $ => wrapped_in_parenthesis( - seq( - optional($.partition_by), - optional($.order_by), - optional($.window_frame), - ), - ), - - window_function: $ => seq( - $.invocation, - $.keyword_over, - choice( - $.identifier, - $.window_specification, - ), - ), - - _alias: $ => seq( - optional($.keyword_as), - field('alias', $.identifier), - ), - - from: $ => seq( - $.keyword_from, - optional( - $.keyword_only, - ), - comma_list($.relation, true), - optional($.index_hint), - repeat( - choice( - $.join, - $.cross_join, - $.lateral_join, - $.lateral_cross_join, - ), - ), - optional($.where), - optional($.group_by), - optional($.having), - optional($.window_clause), - optional($.order_by), - optional($.limit), - ), - - relation: $ => prec.right( - seq( - choice( - $.subquery, - $.invocation, - $.object_reference, - wrapped_in_parenthesis($.values), - ), - optional( - seq( - $._alias, - optional(alias($._column_list, $.list)), - ), - ), - ), - ), - - values: $ => seq( - $.keyword_values, - $.list, - optional( - repeat( - seq( - ',', - $.list, - ), - ), - ), - ), - - index_hint: $ => seq( - choice( - $.keyword_force, - $.keyword_use, - $.keyword_ignore, - ), - $.keyword_index, - optional( - seq( - $.keyword_for, - $.keyword_join, - ), - ), - wrapped_in_parenthesis( - field('index_name', $.identifier), - ), - ), - - join: $ => seq( - optional($.keyword_natural), - optional( - choice( - $.keyword_left, - seq($.keyword_full, $.keyword_outer), - seq($.keyword_left, $.keyword_outer), - $.keyword_right, - seq($.keyword_right, $.keyword_outer), - $.keyword_inner, - $.keyword_full, - ), - ), - $.keyword_join, - $.relation, - optional($.index_hint), - optional($.join), - choice( - seq( - $.keyword_on, - field("predicate", $._expression), - ), - seq( - $.keyword_using, - alias($._column_list, $.list), - ) - ) - ), - - cross_join: $ => prec.right( - seq( - $.keyword_cross, - $.keyword_join, - $.relation, - optional( - seq( - $.keyword_with, - $.keyword_ordinality, - optional( - seq( - $.keyword_as, - field("alias", $.identifier), - paren_list($.identifier), - ) - ) - ) - ) - ) - ), - - lateral_join: $ => seq( - optional( - choice( - // lateral joins cannot be right! - $.keyword_left, - seq($.keyword_left, $.keyword_outer), - $.keyword_inner, - ), - ), - $.keyword_join, - $.keyword_lateral, - choice( - $.invocation, - $.subquery, - ), - optional( - choice( - seq( - $.keyword_as, - field('alias', $.identifier), - ), - field('alias', $.identifier), - ), - ), - $.keyword_on, - choice( - $._expression, - $.keyword_true, - $.keyword_false, - ), - ), - - lateral_cross_join: $ => seq( - $.keyword_cross, - $.keyword_join, - $.keyword_lateral, - choice( - $.invocation, - $.subquery, - ), - optional( - choice( - seq( - $.keyword_as, - field('alias', $.identifier), - ), - field('alias', $.identifier), - ), - ), - ), - - where: $ => seq( - $.keyword_where, - field("predicate", $._expression), - ), - - group_by: $ => seq( - $.keyword_group, - $.keyword_by, - comma_list($._expression, true), - ), - - having: $ => seq( - $.keyword_having, - $._expression, - ), - - order_by: $ => prec.right(seq( - $.keyword_order, - $.keyword_by, - comma_list($.order_target, true), - )), - - order_target: $ => seq( - $._expression, - optional( - seq( - choice( - $.direction, - seq( - $.keyword_using, - choice('<', '>', '<=', '>='), - ), - ), - optional( - seq( - $.keyword_nulls, - choice( - $.keyword_first, - $.keyword_last, - ), - ), - ), - ), - ), - ), - - limit: $ => seq( - $.keyword_limit, - $.literal, - optional($.offset), - ), - - offset: $ => seq( - $.keyword_offset, - $.literal, - ), - - returning: $ => seq( - $.keyword_returning, - $.select_expression, - ), - - _expression: $ => prec(1, - choice( - $.literal, - alias( - $._qualified_field, - $.field, - ), - $.parameter, - $.list, - $.case, - $.window_function, - $.subquery, - $.cast, - alias($.implicit_cast, $.cast), - $.exists, - $.invocation, - $.binary_expression, - $.subscript, - $.unary_expression, - $.array, - $.interval, - $.between_expression, - $.parenthesized_expression, - $.object_id, - ) - ), - - parenthesized_expression: $ => prec(2, - wrapped_in_parenthesis($._expression) - ), - - subscript: $ => prec.left('binary_is', - seq( - field('expression', $._expression), - "[", - choice( - field('subscript', $._expression), - seq( - field('lower', $._expression), - ':', - field('upper', $._expression), - ), - ), - "]", - ), - ), - - op_other: $ => token( - choice( - '->', - '->>', - '#>', - '#>>', - '~', - '!~', - '~*', - '!~*', - '|', - '&', - '#', - '<<', - '>>', - '<<=', - '>>=', - '##', - '<->', - '@>', - '<@', - '&<', - '&>', - '|>>', - '<<|', - '&<|', - '|&>', - '<^', - '^>', - '?#', - '?-', - '?|', - '?-|', - '?||', - '@@', - '@@@', - '@?', - '#-', - '?&', - '?', - '-|-', - '||', - '^@', - ), - ), - - binary_expression: $ => choice( - ...[ - ['+', 'binary_plus'], - ['-', 'binary_plus'], - ['*', 'binary_times'], - ['/', 'binary_times'], - ['%', 'binary_times'], - ['^', 'binary_exp'], - ['=', 'binary_relation'], - ['<', 'binary_relation'], - ['<=', 'binary_relation'], - ['!=', 'binary_relation'], - ['>=', 'binary_relation'], - ['>', 'binary_relation'], - ['<>', 'binary_relation'], - [$.op_other, 'binary_other'], - [$.keyword_is, 'binary_is'], - [$.is_not, 'binary_is'], - [$.keyword_like, 'pattern_matching'], - [$.not_like, 'pattern_matching'], - [$.similar_to, 'pattern_matching'], - [$.not_similar_to, 'pattern_matching'], - // binary_is precedence disambiguates `(is not distinct from)` from an - // `is (not distinct from)` with a unary `not` - [$.distinct_from, 'binary_is'], - [$.not_distinct_from, 'binary_is'], - ].map(([operator, precedence]) => - prec.left(precedence, seq( - field('left', $._expression), - field('operator', operator), - field('right', $._expression) - )) - ), - ...[ - [$.keyword_and, 'clause_connective'], - [$.keyword_or, 'clause_disjunctive'], - ].map(([operator, precedence]) => - prec.left(precedence, seq( - field('left', $._expression), - field('operator', operator), - field('right', $._expression) - )) - ), - ...[ - [$.keyword_in, 'binary_in'], - [$.not_in, 'binary_in'], - ].map(([operator, precedence]) => - prec.left(precedence, seq( - field('left', $._expression), - field('operator', operator), - field('right', choice($.list, $.subquery)) - )) - ), - ), - - op_unary_other: $ => token( - choice( - '|/', - '||/', - '@', - '~', - '@-@', - '@@', - '#', - '?-', - '?|', - '!!', - ), - ), - - unary_expression: $ => choice( - ...[ - [$.keyword_not, 'unary_not'], - [$.bang, 'unary_not'], - [$.keyword_any, 'unary_not'], - [$.keyword_some, 'unary_not'], - [$.keyword_all, 'unary_not'], - [$.op_unary_other, 'unary_other'], - ].map(([operator, precedence]) => - prec.left(precedence, seq( - field('operator', operator), - field('operand', $._expression) - )) - ), - ), - - between_expression: $ => choice( - ...[ - [$.keyword_between, 'between'], - [seq($.keyword_not, $.keyword_between), 'between'], - ].map(([operator, precedence]) => - prec.left(precedence, seq( - field('left', $._expression), - field('operator', operator), - field('low', $._expression), - $.keyword_and, - field('high', $._expression) - )) - ), - ), - - not_in: $ => seq( - $.keyword_not, - $.keyword_in, - ), - - subquery: $ => wrapped_in_parenthesis( - $._dml_read - ), - - list: $ => paren_list($._expression), - - literal: $ => prec(2, - choice( - $._integer, - $._decimal_number, - $._literal_string, - $._bit_string, - $._string_casting, - $.keyword_true, - $.keyword_false, - $.keyword_null, - ), - ), - _double_quote_string: _ => /"[^"]*"/, - _backtick_quoted_string: _ => /`[^`]*`/, - // The norm specify that between two consecutive string must be a return, - // but this is good enough. - _single_quote_string: _ => seq(/([uU]&|[nN])?'([^']|'')*'/, repeat(/'([^']|'')*'/)), - - _postgres_escape_string: _ => /(e|E)'([^']|\\')*'/, - - _literal_string: $ => prec( - 1, - choice( - $._single_quote_string, - $._double_quote_string, - $._dollar_quoted_string, - $._postgres_escape_string, - ), - ), - _natural_number: _ => /\d+/, - _integer: $ => seq( - optional(choice("-", "+")), - /(0[xX][0-9A-Fa-f]+(_[0-9A-Fa-f]+)*)|(0[oO][0-7]+(_[0-7]+)*)|(0[bB][01]+(_[01]+)*)|(\d+(_\d+)*(e[+-]?\d+(_\d+)*)?)/ - ), - _decimal_number: $ => seq( - optional( - choice("-", "+")), - /((\d+(_\d+)*)?[.]\d+(_\d+)*(e[+-]?\d+(_\d+)*)?)|(\d+(_\d+)*[.](e[+-]?\d+(_\d+)*)?)/ - ), - _bit_string: $ => seq(/[bBxX]'([^']|'')*'/, repeat(/'([^']|'')*'/)), - // The identifier should be followed by a string (no parenthesis allowed) - _string_casting: $ => seq($.identifier, $._single_quote_string), - - bang: _ => '!', - - identifier: $ => choice( - $._identifier, - $._double_quote_string, - $._backtick_quoted_string, - $._tsql_parameter, - seq("`", $._identifier, "`"), - ), - _tsql_parameter: $ => seq('@', $._identifier), - // support nordic chars and umlaue - _identifier: _ => /[A-Za-z_\u00C0-\u017F][0-9A-Za-z_\u00C0-\u017F]*/, - } + } }); - -function unsigned_type($, type) { - return choice( - seq($.keyword_unsigned, type), - seq( - type, - optional($.keyword_unsigned), - optional($.keyword_zerofill), - ), - ) -} - -function optional_parenthesis(node) { - return prec.right( - choice( - node, - wrapped_in_parenthesis(node), - ), - ) -} - -function wrapped_in_parenthesis(node) { - if (node) { - return seq("(", node, ")"); - } - return seq("(", ")"); -} - -function parametric_type($, type, params = ['size']) { - return prec.right(1, - choice( - type, - seq( - type, - wrapped_in_parenthesis( - seq( - // first parameter is guaranteed, shift it out of the array - field(params.shift(), alias($._natural_number, $.literal)), - // then, fill in the ", next" until done - ...params.map(p => seq(',', field(p, alias($._natural_number, $.literal)))), - ), - ), - ), - ), - ) -} - -function comma_list(field, requireFirst) { - sequence = seq(field, repeat(seq(',', field))); - - if (requireFirst) { - return sequence; - } - - return optional(sequence); -} - -function paren_list(field, requireFirst) { - return wrapped_in_parenthesis( - comma_list(field, requireFirst), - ) -} - -function make_keyword(word) { - str = ""; - for (var i = 0; i < word.length; i++) { - str = str + "[" + word.charAt(i).toLowerCase() + word.charAt(i).toUpperCase() + "]"; - } - return new RegExp(str); -} diff --git a/grammar/column-lists.js b/grammar/column-lists.js new file mode 100644 index 0000000..f783e06 --- /dev/null +++ b/grammar/column-lists.js @@ -0,0 +1,190 @@ +const { comma_list, optional_parenthesis, paren_list, wrapped_in_parenthesis } = require("./helpers"); + +module.exports = { + + _column_list: $ => paren_list(alias($._column, $.column), true), + + _column: $ => choice( + $.identifier, + alias($._literal_string, $.literal), + ), + + _key_value_pair: $ => seq( + field('key',$.identifier), + '=', + field('value', alias($._literal_string, $.literal)), + ), + + column_definitions: $ => seq( + '(', + comma_list($.column_definition, true), + optional($.constraints), + ')', + ), + + column_definition: $ => prec.left(seq( + field('name', $._column), + field('type', $._type), + repeat($._column_constraint), + )), + + _column_comment: $ => seq( + $.keyword_comment, + alias($._literal_string, $.literal) + ), + + _column_constraint: $ => prec.left(choice( + choice( + $.keyword_null, + $._not_null, + ), + seq( + $.keyword_references, + $.object_reference, + paren_list($.identifier, true), + repeat( + seq( + $.keyword_on, + choice($.keyword_delete, $.keyword_update), + choice( + seq($.keyword_no, $.keyword_action), + $.keyword_restrict, + $.keyword_cascade, + seq( + $.keyword_set, + choice($.keyword_null, $.keyword_default), + optional(paren_list($.identifier, true)) + ), + ), + ), + ), + ), + $._default_expression, + $._primary_key, + $.keyword_auto_increment, + $.direction, + $._column_comment, + $._check_constraint, + seq( + optional(seq($.keyword_generated, $.keyword_always)), + $.keyword_as, + $._expression, + ), + choice( + $.keyword_stored, + $.keyword_virtual, + ), + $.keyword_unique + )), + + _check_constraint: $ => seq( + optional( + seq( + $.keyword_constraint, + $.literal + ) + ), + $.keyword_check, + wrapped_in_parenthesis($.binary_expression) + ), + + _default_expression: $ => seq( + $.keyword_default, + optional_parenthesis($._inner_default_expression), + ), + _inner_default_expression: $ => choice( + $.literal, + $.list, + $.cast, + $.binary_expression, + $.unary_expression, + $.array, + $.invocation, + $.keyword_current_timestamp, + alias($.implicit_cast, $.cast), + ), + + constraints: $ => seq( + ',', + $.constraint, + repeat( + seq(',', $.constraint), + ), + ), + + constraint: $ => choice( + $._constraint_literal, + $._key_constraint, + $._primary_key_constraint, + $._check_constraint + ), + + _constraint_literal: $ => seq( + $.keyword_constraint, + field('name', $.identifier), + choice( + seq( + $._primary_key, + $.ordered_columns, + ), + seq( + $._check_constraint + ) + ) + ), + + _primary_key_constraint: $ => seq( + $._primary_key, + $.ordered_columns, + ), + + _key_constraint: $ => seq( + choice( + seq( + $.keyword_unique, + optional( + choice( + $.keyword_index, + $.keyword_key, + seq($.keyword_nulls, optional($.keyword_not), $.keyword_distinct), + ), + ), + ), + seq(optional($.keyword_foreign), $.keyword_key, optional($._if_not_exists)), + $.keyword_index, + ), + optional(field('name', $.identifier)), + $.ordered_columns, + optional( + seq( + $.keyword_references, + $.object_reference, + paren_list($.identifier, true), + repeat( + seq( + $.keyword_on, + choice($.keyword_delete, $.keyword_update), + choice( + seq($.keyword_no, $.keyword_action), + $.keyword_restrict, + $.keyword_cascade, + seq( + $.keyword_set, + choice($.keyword_null, $.keyword_default), + optional(paren_list($.identifier, true)) + ), + ), + ), + ), + ), + ), + ), + + ordered_columns: $ => paren_list(alias($.ordered_column, $.column), true), + + ordered_column: $ => seq( + field('name', $._column), + optional($.direction), + ), + +}; diff --git a/grammar/expressions.js b/grammar/expressions.js new file mode 100644 index 0000000..0ade2f4 --- /dev/null +++ b/grammar/expressions.js @@ -0,0 +1,434 @@ +const { optional_parenthesis, paren_list, wrapped_in_parenthesis } = require("./helpers"); + +module.exports = { + + _expression: $ => prec(1, + choice( + $.literal, + alias( + $._qualified_field, + $.field, + ), + $.parameter, + $.list, + $.case, + $.window_function, + $.subquery, + $.cast, + alias($.implicit_cast, $.cast), + $.exists, + $.invocation, + $.binary_expression, + $.subscript, + $.unary_expression, + $.array, + $.interval, + $.between_expression, + $.parenthesized_expression, + $.object_id, + ) + ), + + object_reference: $ => choice( + seq( + field('database', $.identifier), + '.', + field('schema', $.identifier), + '.', + field('name', $.identifier), + ), + seq( + field('schema', $.identifier), + '.', + field('name', $.identifier), + ), + field('name', $.identifier), + ), + + field: $ => field('name', $.identifier), + + _qualified_field: $ => seq( + optional( + seq( + optional_parenthesis($.object_reference), + '.', + ), + ), + field('name', $.identifier), + ), + + parameter: $ => /\?|(\$[0-9]+)/, + + case: $ => seq( + $.keyword_case, + choice( + // simplified CASE x WHEN + seq( + $._expression, + $.keyword_when, + $._expression, + $.keyword_then, + $._expression, + repeat( + seq( + $.keyword_when, + $._expression, + $.keyword_then, + $._expression, + ) + ), + ), + // standard CASE WHEN x, where x must be a predicate + seq( + $.keyword_when, + $._expression, + $.keyword_then, + $._expression, + repeat( + seq( + $.keyword_when, + $._expression, + $.keyword_then, + $._expression, + ) + ), + ), + ), + optional( + seq( + $.keyword_else, + $._expression, + ) + ), + $.keyword_end, + ), + + implicit_cast: $ => seq( + $._expression, + '::', + $._type, + ), + + cast: $ => seq( + field('name', $.keyword_cast), + wrapped_in_parenthesis( + seq( + field('parameter', $._expression), + $.keyword_as, + $._type, + ), + ), + ), + + exists: $ => seq( + $.keyword_exists, + $.subquery, + ), + + invocation: $ => prec(1, + seq( + $.object_reference, + choice( + // default invocation + paren_list( + seq( + optional($.keyword_distinct), + field( + 'parameter', + $.term, + ), + optional($.order_by) + ) + ), + // https://dev.mysql.com/doc/refman/8.4/en/date-and-time-functions.html#function_extract + paren_list( + seq( + field( + 'unit', + $.object_reference, + ), + $.keyword_from, + $.term + ) + ), + // _aggregate_function, e.g. group_concat + wrapped_in_parenthesis( + seq( + optional($.keyword_distinct), + field('parameter', $.term), + optional($.order_by), + optional(seq( + choice($.keyword_separator, ','), + alias($._literal_string, $.literal) + )), + optional($.limit), + ), + ), + ), + optional( + $.filter_expression + ) + ), + ), + + filter_expression : $ => seq( + $.keyword_filter, + wrapped_in_parenthesis($.where), + ), + + parenthesized_expression: $ => prec(2, + wrapped_in_parenthesis($._expression) + ), + + subscript: $ => prec.left('binary_is', + seq( + field('expression', $._expression), + "[", + choice( + field('subscript', $._expression), + seq( + field('lower', $._expression), + ':', + field('upper', $._expression), + ), + ), + "]", + ), + ), + + op_other: $ => token( + choice( + '->', + '->>', + '#>', + '#>>', + '~', + '!~', + '~*', + '!~*', + '|', + '&', + '#', + '<<', + '>>', + '<<=', + '>>=', + '##', + '<->', + '@>', + '<@', + '&<', + '&>', + '|>>', + '<<|', + '&<|', + '|&>', + '<^', + '^>', + '?#', + '?-', + '?|', + '?-|', + '?||', + '@@', + '@@@', + '@?', + '#-', + '?&', + '?', + '-|-', + '||', + '^@', + ), + ), + + binary_expression: $ => choice( + ...[ + ['+', 'binary_plus'], + ['-', 'binary_plus'], + ['*', 'binary_times'], + ['/', 'binary_times'], + ['%', 'binary_times'], + ['^', 'binary_exp'], + ['=', 'binary_relation'], + ['<', 'binary_relation'], + ['<=', 'binary_relation'], + ['!=', 'binary_relation'], + ['>=', 'binary_relation'], + ['>', 'binary_relation'], + ['<>', 'binary_relation'], + [$.op_other, 'binary_other'], + [$.keyword_is, 'binary_is'], + [$.is_not, 'binary_is'], + [$.keyword_like, 'pattern_matching'], + [$.not_like, 'pattern_matching'], + [$.similar_to, 'pattern_matching'], + [$.not_similar_to, 'pattern_matching'], + // binary_is precedence disambiguates `(is not distinct from)` from an + // `is (not distinct from)` with a unary `not` + [$.distinct_from, 'binary_is'], + [$.not_distinct_from, 'binary_is'], + ].map(([operator, precedence]) => + prec.left(precedence, seq( + field('left', $._expression), + field('operator', operator), + field('right', $._expression) + )) + ), + ...[ + [$.keyword_and, 'clause_connective'], + [$.keyword_or, 'clause_disjunctive'], + ].map(([operator, precedence]) => + prec.left(precedence, seq( + field('left', $._expression), + field('operator', operator), + field('right', $._expression) + )) + ), + ...[ + [$.keyword_in, 'binary_in'], + [$.not_in, 'binary_in'], + ].map(([operator, precedence]) => + prec.left(precedence, seq( + field('left', $._expression), + field('operator', operator), + field('right', choice($.list, $.subquery)) + )) + ), + ), + + op_unary_other: $ => token( + choice( + '|/', + '||/', + '@', + '~', + '@-@', + '@@', + '#', + '?-', + '?|', + '!!', + ), + ), + + unary_expression: $ => choice( + ...[ + [$.keyword_not, 'unary_not'], + [$.bang, 'unary_not'], + [$.keyword_any, 'unary_not'], + [$.keyword_some, 'unary_not'], + [$.keyword_all, 'unary_not'], + [$.op_unary_other, 'unary_other'], + ].map(([operator, precedence]) => + prec.left(precedence, seq( + field('operator', operator), + field('operand', $._expression) + )) + ), + ), + + // Postgres syntax for intervals + interval: $ => seq( + $.keyword_interval, + $._literal_string, + ), + + between_expression: $ => choice( + ...[ + [$.keyword_between, 'between'], + [seq($.keyword_not, $.keyword_between), 'between'], + ].map(([operator, precedence]) => + prec.left(precedence, seq( + field('left', $._expression), + field('operator', operator), + field('low', $._expression), + $.keyword_and, + field('high', $._expression) + )) + ), + ), + + not_in: $ => seq( + $.keyword_not, + $.keyword_in, + ), + + subquery: $ => wrapped_in_parenthesis( + $._dml_read + ), + + list: $ => paren_list($._expression), + + literal: $ => prec(2, + choice( + $._integer, + $._decimal_number, + $._literal_string, + $._bit_string, + $._string_casting, + $.keyword_true, + $.keyword_false, + $.keyword_null, + ), + ), + _double_quote_string: _ => /"[^"]*"/, + _backtick_quoted_string: _ => /`[^`]*`/, + // The norm specify that between two consecutive string must be a return, + // but this is good enough. + _single_quote_string: _ => seq(/([uU]&|[nN])?'([^']|'')*'/, repeat(/'([^']|'')*'/)), + + _postgres_escape_string: _ => /(e|E)'([^']|\\')*'/, + + _literal_string: $ => prec( + 1, + choice( + $._single_quote_string, + $._double_quote_string, + $._dollar_quoted_string, + $._postgres_escape_string, + ), + ), + _natural_number: _ => /\d+/, + _integer: $ => seq( + optional(choice("-", "+")), + /(0[xX][0-9A-Fa-f]+(_[0-9A-Fa-f]+)*)|(0[oO][0-7]+(_[0-7]+)*)|(0[bB][01]+(_[01]+)*)|(\d+(_\d+)*(e[+-]?\d+(_\d+)*)?)/ + ), + _decimal_number: $ => seq( + optional( + choice("-", "+")), + /((\d+(_\d+)*)?[.]\d+(_\d+)*(e[+-]?\d+(_\d+)*)?)|(\d+(_\d+)*[.](e[+-]?\d+(_\d+)*)?)/ + ), + _bit_string: $ => seq(/[bBxX]'([^']|'')*'/, repeat(/'([^']|'')*'/)), + // The identifier should be followed by a string (no parenthesis allowed) + _string_casting: $ => seq($.identifier, $._single_quote_string), + + bang: _ => '!', + + identifier: $ => choice( + $._identifier, + $._double_quote_string, + $._backtick_quoted_string, + $._tsql_parameter, + seq("`", $._identifier, "`"), + ), + _tsql_parameter: $ => seq('@', $._identifier), + // support nordic chars and umlaue + _identifier: _ => /[A-Za-z_\u00C0-\u017F][0-9A-Za-z_\u00C0-\u017F]*/, + + object_id: $ => seq( + $.keyword_object_id, + wrapped_in_parenthesis( + seq( + alias($._literal_string, $.literal), + optional( + seq( + ',', + alias($._literal_string, $.literal), + ), + ), + ), + ), + ), + +}; diff --git a/grammar/helpers.js b/grammar/helpers.js new file mode 100644 index 0000000..b4662db --- /dev/null +++ b/grammar/helpers.js @@ -0,0 +1,47 @@ +function make_keyword(word) { + str = ""; + for (var i = 0; i < word.length; i++) { + str = str + "[" + word.charAt(i).toLowerCase() + word.charAt(i).toUpperCase() + "]"; + } + return new RegExp(str); +} + +function optional_parenthesis(node) { + return prec.right( + choice( + node, + wrapped_in_parenthesis(node), + ), + ) +} + +function wrapped_in_parenthesis(node) { + if (node) { + return seq("(", node, ")"); + } + return seq("(", ")"); +} + +function comma_list(field, requireFirst) { + sequence = seq(field, repeat(seq(',', field))); + + if (requireFirst) { + return sequence; + } + + return optional(sequence); +} + +function paren_list(field, requireFirst) { + return wrapped_in_parenthesis( + comma_list(field, requireFirst), + ) +} + +module.exports = { + make_keyword, + optional_parenthesis, + wrapped_in_parenthesis, + comma_list, + paren_list, +}; diff --git a/grammar/keywords.js b/grammar/keywords.js new file mode 100644 index 0000000..3a9862b --- /dev/null +++ b/grammar/keywords.js @@ -0,0 +1,417 @@ +const { make_keyword } = require("./helpers"); + +module.exports = { + + keyword_select: _ => make_keyword("select"), + keyword_delete: _ => make_keyword("delete"), + keyword_insert: _ => make_keyword("insert"), + keyword_replace: _ => make_keyword("replace"), + keyword_update: _ => make_keyword("update"), + keyword_truncate: _ => make_keyword("truncate"), + keyword_merge: _ => make_keyword("merge"), + keyword_show: _ => make_keyword("show"), + keyword_unload: _ => make_keyword("unload"), + keyword_into: _ => make_keyword("into"), + keyword_overwrite: _ => make_keyword("overwrite"), + keyword_values: _ => make_keyword("values"), + keyword_value: _ => make_keyword("value"), + keyword_matched: _ => make_keyword("matched"), + keyword_set: _ => make_keyword("set"), + keyword_from: _ => make_keyword("from"), + keyword_left: _ => make_keyword("left"), + keyword_right: _ => make_keyword("right"), + keyword_inner: _ => make_keyword("inner"), + keyword_full: _ => make_keyword("full"), + keyword_outer: _ => make_keyword("outer"), + keyword_cross: _ => make_keyword("cross"), + keyword_join: _ => make_keyword("join"), + keyword_lateral: _ => make_keyword("lateral"), + keyword_natural: _ => make_keyword("natural"), + keyword_on: _ => make_keyword("on"), + keyword_off: _ => make_keyword("off"), + keyword_where: _ => make_keyword("where"), + keyword_order: _ => make_keyword("order"), + keyword_group: _ => make_keyword("group"), + keyword_partition: _ => make_keyword("partition"), + keyword_by: _ => make_keyword("by"), + keyword_having: _ => make_keyword("having"), + keyword_desc: _ => make_keyword("desc"), + keyword_asc: _ => make_keyword("asc"), + keyword_limit: _ => make_keyword("limit"), + keyword_offset: _ => make_keyword("offset"), + keyword_primary: _ => make_keyword("primary"), + keyword_create: _ => make_keyword("create"), + keyword_alter: _ => make_keyword("alter"), + keyword_change: _ => make_keyword("change"), + keyword_analyze: _ => make_keyword("analyze"), + keyword_explain: _ => make_keyword("explain"), + keyword_verbose: _ => make_keyword("verbose"), + keyword_modify: _ => make_keyword("modify"), + keyword_drop: _ => make_keyword("drop"), + keyword_add: _ => make_keyword("add"), + keyword_table: _ => make_keyword("table"), + keyword_tables: _ => make_keyword("tables"), + keyword_view: _ => make_keyword("view"), + keyword_column: _ => make_keyword("column"), + keyword_columns: _ => make_keyword("columns"), + keyword_materialized: _ => make_keyword("materialized"), + keyword_tablespace: _ => make_keyword("tablespace"), + keyword_split: _ => make_keyword("split"), + keyword_tablets: _ => make_keyword("tablets"), + keyword_sequence: _ => make_keyword("sequence"), + keyword_increment: _ => make_keyword("increment"), + keyword_minvalue: _ => make_keyword("minvalue"), + keyword_maxvalue: _ => make_keyword("maxvalue"), + keyword_none: _ => make_keyword("none"), + keyword_owned: _ => make_keyword("owned"), + keyword_start: _ => make_keyword("start"), + keyword_restart: _ => make_keyword("restart"), + keyword_key: _ => make_keyword("key"), + keyword_duplicate: _ => make_keyword("duplicate"), + keyword_as: _ => make_keyword("as"), + keyword_distinct: _ => make_keyword("distinct"), + keyword_constraint: _ => make_keyword("constraint"), + keyword_filter: _ => make_keyword("filter"), + keyword_cast: _ => make_keyword("cast"), + keyword_separator: _ => make_keyword("separator"), + keyword_case: _ => make_keyword("case"), + keyword_when: _ => make_keyword("when"), + keyword_then: _ => make_keyword("then"), + keyword_else: _ => make_keyword("else"), + keyword_end: _ => make_keyword("end"), + keyword_in: _ => make_keyword("in"), + keyword_and: _ => make_keyword("and"), + keyword_or: _ => make_keyword("or"), + keyword_is: _ => make_keyword("is"), + keyword_not: _ => make_keyword("not"), + keyword_force: _ => make_keyword("force"), + keyword_ignore: _ => make_keyword("ignore"), + keyword_using: _ => make_keyword("using"), + keyword_use: _ => make_keyword("use"), + keyword_index: _ => make_keyword("index"), + keyword_include: _ => make_keyword("include"), + keyword_for: _ => make_keyword("for"), + keyword_if: _ => make_keyword("if"), + keyword_exists: _ => make_keyword("exists"), + keyword_auto_increment: _ => make_keyword("auto_increment"), + keyword_generated: _ => make_keyword("generated"), + keyword_always: _ => make_keyword("always"), + keyword_collate: _ => make_keyword("collate"), + keyword_character: _ => make_keyword("character"), + keyword_engine: _ => make_keyword("engine"), + keyword_default: _ => make_keyword("default"), + keyword_cascade: _ => make_keyword("cascade"), + keyword_restrict: _ => make_keyword("restrict"), + keyword_with: _ => make_keyword("with"), + keyword_without: _ => make_keyword("without"), + keyword_no: _ => make_keyword("no"), + keyword_data: _ => make_keyword("data"), + keyword_type: _ => make_keyword("type"), + keyword_rename: _ => make_keyword("rename"), + keyword_to: _ => make_keyword("to"), + keyword_database: _ => make_keyword("database"), + keyword_schema: _ => make_keyword("schema"), + keyword_owner: _ => make_keyword("owner"), + keyword_user: _ => make_keyword("user"), + keyword_admin: _ => make_keyword("admin"), + keyword_password: _ => make_keyword("password"), + keyword_encrypted: _ => make_keyword("encrypted"), + keyword_valid: _ => make_keyword("valid"), + keyword_until: _ => make_keyword("until"), + keyword_connection: _ => make_keyword("connection"), + keyword_role: _ => make_keyword("role"), + keyword_reset: _ => make_keyword("reset"), + keyword_temp: _ => make_keyword("temp"), + keyword_temporary: _ => make_keyword("temporary"), + keyword_unlogged: _ => make_keyword("unlogged"), + keyword_logged: _ => make_keyword("logged"), + keyword_cycle: _ => make_keyword("cycle"), + keyword_union: _ => make_keyword("union"), + keyword_all: _ => make_keyword("all"), + keyword_any: _ => make_keyword("any"), + keyword_some: _ => make_keyword("some"), + keyword_except: _ => make_keyword("except"), + keyword_intersect: _ => make_keyword("intersect"), + keyword_returning: _ => make_keyword("returning"), + keyword_begin: _ => make_keyword("begin"), + keyword_commit: _ => make_keyword("commit"), + keyword_rollback: _ => make_keyword("rollback"), + keyword_transaction: _ => make_keyword("transaction"), + keyword_over: _ => make_keyword("over"), + keyword_nulls: _ => make_keyword("nulls"), + keyword_first: _ => make_keyword("first"), + keyword_after: _ => make_keyword("after"), + keyword_before: _ => make_keyword("before"), + keyword_last: _ => make_keyword("last"), + keyword_window: _ => make_keyword("window"), + keyword_range: _ => make_keyword("range"), + keyword_rows: _ => make_keyword("rows"), + keyword_groups: _ => make_keyword("groups"), + keyword_between: _ => make_keyword("between"), + keyword_unbounded: _ => make_keyword("unbounded"), + keyword_preceding: _ => make_keyword("preceding"), + keyword_following: _ => make_keyword("following"), + keyword_exclude: _ => make_keyword("exclude"), + keyword_current: _ => make_keyword("current"), + keyword_row: _ => make_keyword("row"), + keyword_ties: _ => make_keyword("ties"), + keyword_others: _ => make_keyword("others"), + keyword_only: _ => make_keyword("only"), + keyword_unique: _ => make_keyword("unique"), + keyword_foreign: _ => make_keyword("foreign"), + keyword_references: _ => make_keyword("references"), + keyword_concurrently: _ => make_keyword("concurrently"), + keyword_btree: _ => make_keyword("btree"), + keyword_hash: _ => make_keyword("hash"), + keyword_gist: _ => make_keyword("gist"), + keyword_spgist: _ => make_keyword("spgist"), + keyword_gin: _ => make_keyword("gin"), + keyword_brin: _ => make_keyword("brin"), + keyword_like: _ => choice(make_keyword("like"),make_keyword("ilike")), + keyword_similar: _ => make_keyword("similar"), + keyword_unsigned: _ => make_keyword("unsigned"), + keyword_zerofill: _ => make_keyword("zerofill"), + keyword_conflict: _ => make_keyword("conflict"), + keyword_do: _ => make_keyword("do"), + keyword_nothing: _ => make_keyword("nothing"), + keyword_high_priority: _ => make_keyword("high_priority"), + keyword_low_priority: _ => make_keyword("low_priority"), + keyword_delayed: _ => make_keyword("delayed"), + keyword_recursive: _ => make_keyword("recursive"), + keyword_cascaded: _ => make_keyword("cascaded"), + keyword_local: _ => make_keyword("local"), + keyword_current_timestamp: _ => make_keyword("current_timestamp"), + keyword_check: _ => make_keyword("check"), + keyword_option: _ => make_keyword("option"), + keyword_vacuum: _ => make_keyword("vacuum"), + keyword_wait: _ => make_keyword("wait"), + keyword_nowait: _ => make_keyword("nowait"), + keyword_attribute: _ => make_keyword("attribute"), + keyword_authorization: _ => make_keyword("authorization"), + keyword_action: _ => make_keyword("action"), + keyword_extension: _ => make_keyword("extension"), + keyword_copy: _ => make_keyword("copy"), + keyword_stdin: _ => make_keyword("stdin"), + keyword_freeze: _ => make_keyword("freeze"), + keyword_escape: _ => make_keyword("escape"), + keyword_encoding: _ => make_keyword("encoding"), + keyword_force_quote: _ => make_keyword("force_quote"), + keyword_quote: _ => make_keyword("quote"), + keyword_force_null: _ => make_keyword("force_null"), + keyword_force_not_null: _ => make_keyword("force_not_null"), + keyword_header: _ => make_keyword("header"), + keyword_match: _ => make_keyword("match"), + keyword_program: _ => make_keyword("program"), + keyword_plain: _ => make_keyword("plain"), + keyword_extended: _ => make_keyword("extended"), + keyword_main: _ => make_keyword("main"), + keyword_storage: _ => make_keyword("storage"), + keyword_compression: _ => make_keyword("compression"), + keyword_while: _ => make_keyword("while"), + + keyword_trigger: _ => make_keyword('trigger'), + keyword_function: _ => make_keyword("function"), + keyword_returns: _ => make_keyword("returns"), + keyword_return: _ => make_keyword("return"), + keyword_setof: _ => make_keyword("setof"), + keyword_atomic: _ => make_keyword("atomic"), + keyword_declare: _ => make_keyword("declare"), + keyword_language: _ => make_keyword("language"), + keyword_immutable: _ => make_keyword("immutable"), + keyword_stable: _ => make_keyword("stable"), + keyword_volatile: _ => make_keyword("volatile"), + keyword_leakproof: _ => make_keyword("leakproof"), + keyword_parallel: _ => make_keyword("parallel"), + keyword_safe: _ => make_keyword("safe"), + keyword_unsafe: _ => make_keyword("unsafe"), + keyword_restricted: _ => make_keyword("restricted"), + keyword_called: _ => make_keyword("called"), + keyword_input: _ => make_keyword("input"), + keyword_strict: _ => make_keyword("strict"), + keyword_cost: _ => make_keyword("cost"), + keyword_rows: _ => make_keyword("rows"), + keyword_support: _ => make_keyword("support"), + keyword_definer: _ => make_keyword("definer"), + keyword_invoker: _ => make_keyword("invoker"), + keyword_security: _ => make_keyword("security"), + keyword_version: _ => make_keyword("version"), + keyword_extension: _ => make_keyword("extension"), + keyword_out: _ => make_keyword("out"), + keyword_inout: _ => make_keyword("inout"), + keyword_variadic: _ => make_keyword("variadic"), + keyword_ordinality: _ => make_keyword("ordinality"), + + keyword_session: _ => make_keyword("session"), + keyword_isolation: _ => make_keyword("isolation"), + keyword_level: _ => make_keyword("level"), + keyword_serializable: _ => make_keyword("serializable"), + keyword_repeatable: _ => make_keyword("repeatable"), + keyword_read: _ => make_keyword("read"), + keyword_write: _ => make_keyword("write"), + keyword_committed: _ => make_keyword("committed"), + keyword_uncommitted: _ => make_keyword("uncommitted"), + keyword_deferrable: _ => make_keyword("deferrable"), + keyword_names: _ => make_keyword("names"), + keyword_zone: _ => make_keyword("zone"), + keyword_immediate: _ => make_keyword("immediate"), + keyword_deferred: _ => make_keyword("deferred"), + keyword_constraints : _ => make_keyword("constraints"), + keyword_snapshot: _ => make_keyword("snapshot"), + keyword_characteristics: _ => make_keyword("characteristics"), + keyword_follows: _ => make_keyword("follows"), + keyword_precedes: _ => make_keyword("precedes"), + keyword_each: _ => make_keyword("each"), + keyword_instead: _ => make_keyword("instead"), + keyword_of: _ => make_keyword("of"), + keyword_initially: _ => make_keyword("initially"), + keyword_old: _ => make_keyword("old"), + keyword_new: _ => make_keyword("new"), + keyword_referencing: _ => make_keyword("referencing"), + keyword_statement: _ => make_keyword("statement"), + keyword_execute: _ => make_keyword("execute"), + keyword_procedure: _ => make_keyword("procedure"), + keyword_object_id: _ => make_keyword("object_id"), + + // Hive Keywords + keyword_external: _ => make_keyword("external"), + keyword_stored: _ => make_keyword("stored"), + keyword_virtual: _ => make_keyword("virtual"), + keyword_cached: _ => make_keyword("cached"), + keyword_uncached: _ => make_keyword("uncached"), + keyword_replication: _ => make_keyword("replication"), + keyword_tblproperties: _ => make_keyword("tblproperties"), + keyword_compute: _ => make_keyword("compute"), + keyword_stats: _ => make_keyword("stats"), + keyword_statistics: _ => make_keyword("statistics"), + keyword_optimize: _ => make_keyword("optimize"), + keyword_rewrite: _ => make_keyword("rewrite"), + keyword_bin_pack: _ => make_keyword("bin_pack"), + keyword_incremental: _ => make_keyword("incremental"), + keyword_location: _ => make_keyword("location"), + keyword_partitioned: _ => make_keyword("partitioned"), + keyword_comment: _ => make_keyword("comment"), + keyword_sort: _ => make_keyword("sort"), + keyword_format: _ => make_keyword("format"), + keyword_delimited: _ => make_keyword("delimited"), + keyword_delimiter: _ => make_keyword("delimiter"), + keyword_fields: _ => make_keyword("fields"), + keyword_terminated: _ => make_keyword("terminated"), + keyword_escaped: _ => make_keyword("escaped"), + keyword_lines: _ => make_keyword("lines"), + keyword_cache: _ => make_keyword("cache"), + keyword_metadata: _ => make_keyword("metadata"), + keyword_noscan: _ => make_keyword("noscan"), + + // Hive file formats + keyword_parquet: _ => make_keyword("parquet"), + keyword_rcfile: _ => make_keyword("rcfile"), + keyword_csv: _ => make_keyword("csv"), + keyword_textfile: _ => make_keyword("textfile"), + keyword_avro: _ => make_keyword("avro"), + keyword_sequencefile: _ => make_keyword("sequencefile"), + keyword_orc: _ => make_keyword("orc"), + keyword_avro: _ => make_keyword("avro"), + keyword_jsonfile: _ => make_keyword("jsonfile"), + + // Operators + is_not: $ => prec.left(seq($.keyword_is, $.keyword_not)), + not_like: $ => seq($.keyword_not, $.keyword_like), + similar_to: $ => seq($.keyword_similar, $.keyword_to), + not_similar_to: $ => seq($.keyword_not, $.keyword_similar, $.keyword_to), + distinct_from: $ => seq($.keyword_is, $.keyword_distinct, $.keyword_from), + not_distinct_from: $ => seq($.keyword_is, $.keyword_not, $.keyword_distinct, $.keyword_from), + + _temporary: $ => choice($.keyword_temp, $.keyword_temporary), + _not_null: $ => seq($.keyword_not, $.keyword_null), + _primary_key: $ => seq($.keyword_primary, $.keyword_key), + _if_exists: $ => seq($.keyword_if, $.keyword_exists), + _if_not_exists: $ => seq($.keyword_if, $.keyword_not, $.keyword_exists), + _or_replace: $ => seq($.keyword_or, $.keyword_replace), + _default_null: $ => seq($.keyword_default, $.keyword_null), + _current_row: $ => seq($.keyword_current, $.keyword_row), + _exclude_current_row: $ => seq($.keyword_exclude, $.keyword_current, $.keyword_row), + _exclude_group: $ => seq($.keyword_exclude, $.keyword_group), + _exclude_no_others: $ => seq($.keyword_exclude, $.keyword_no, $.keyword_others), + _exclude_ties: $ => seq($.keyword_exclude, $.keyword_ties), + _check_option: $ => seq($.keyword_check, $.keyword_option), + direction: $ => choice($.keyword_desc, $.keyword_asc), + + // Types + keyword_null: _ => make_keyword("null"), + keyword_true: _ => make_keyword("true"), + keyword_false: _ => make_keyword("false"), + + keyword_boolean: _ => make_keyword("boolean"), + keyword_bit: _ => make_keyword("bit"), + keyword_binary: _ => make_keyword("binary"), + keyword_varbinary: _ => make_keyword("varbinary"), + keyword_image: _ => make_keyword("image"), + + keyword_smallserial: _ => choice(make_keyword("smallserial"),make_keyword("serial2")), + keyword_serial: _ => choice(make_keyword("serial"),make_keyword("serial4")), + keyword_bigserial: _ => choice(make_keyword("bigserial"),make_keyword("serial8")), + keyword_tinyint: _ => choice(make_keyword("tinyint"),make_keyword("int1")), + keyword_smallint: _ => choice(make_keyword("smallint"),make_keyword("int2")), + keyword_mediumint: _ => choice(make_keyword("mediumint"),make_keyword("int3")), + keyword_int: _ => choice(make_keyword("int"), make_keyword("integer"), make_keyword("int4")), + keyword_bigint: _ => choice(make_keyword("bigint"),make_keyword("int8")), + keyword_decimal: _ => make_keyword("decimal"), + keyword_numeric: _ => make_keyword("numeric"), + keyword_real: _ => choice(make_keyword("real"),make_keyword("float4")), + keyword_float: _ => make_keyword("float"), + keyword_double: _ => make_keyword("double"), + keyword_precision: _ => make_keyword("precision"), + keyword_inet: _ => make_keyword("inet"), + + keyword_money: _ => make_keyword("money"), + keyword_smallmoney: _ => make_keyword("smallmoney"), + keyword_varying: _ => make_keyword("varying"), + + keyword_char: _ => choice(make_keyword("char"), make_keyword("character")), + keyword_nchar: _ => make_keyword("nchar"), + keyword_varchar: $ => choice( + make_keyword("varchar"), + seq( + make_keyword("character"), + $.keyword_varying, + ) + ), + keyword_nvarchar: _ => make_keyword("nvarchar"), + keyword_text: _ => make_keyword("text"), + keyword_string: _ => make_keyword("string"), + keyword_uuid: _ => make_keyword("uuid"), + + keyword_json: _ => make_keyword("json"), + keyword_jsonb: _ => make_keyword("jsonb"), + keyword_xml: _ => make_keyword("xml"), + + keyword_bytea: _ => make_keyword("bytea"), + + keyword_enum: _ => make_keyword("enum"), + + keyword_date: _ => make_keyword("date"), + keyword_datetime: _ => make_keyword("datetime"), + keyword_datetime2: _ => make_keyword("datetime2"), + keyword_smalldatetime: _ => make_keyword("smalldatetime"), + keyword_datetimeoffset: _ => make_keyword("datetimeoffset"), + keyword_time: _ => make_keyword("time"), + keyword_timestamp: _ => make_keyword("timestamp"), + keyword_timestamptz: _ => make_keyword('timestamptz'), + keyword_interval: _ => make_keyword("interval"), + + keyword_geometry: _ => make_keyword("geometry"), + keyword_geography: _ => make_keyword("geography"), + keyword_box2d: _ => make_keyword("box2d"), + keyword_box3d: _ => make_keyword("box3d"), + + keyword_oid: _ => make_keyword("oid"), + keyword_oids: _ => make_keyword("oids"), + keyword_name: _ => make_keyword("name"), + keyword_regclass: _ => make_keyword("regclass"), + keyword_regnamespace: _ => make_keyword("regnamespace"), + keyword_regproc: _ => make_keyword("regproc"), + keyword_regtype: _ => make_keyword("regtype"), + + keyword_array: _ => make_keyword("array"), // not included in _type since it's a constructor literal + +} diff --git a/grammar/statements/alter.js b/grammar/statements/alter.js new file mode 100644 index 0000000..4d6538c --- /dev/null +++ b/grammar/statements/alter.js @@ -0,0 +1,426 @@ +const { paren_list } = require("../helpers"); + +module.exports = { + + _alter_statement: $ => seq( + choice( + $.alter_table, + $.alter_view, + $.alter_schema, + $.alter_type, + $.alter_index, + $.alter_database, + $.alter_role, + $.alter_sequence, + ), + ), + + alter_table: $ => seq( + $.keyword_alter, + $.keyword_table, + optional($._if_exists), + optional($.keyword_only), + $.object_reference, + choice( + seq( + $._alter_specifications, + repeat( + seq( + ",", + $._alter_specifications + ) + ) + ), + ), + ), + + _alter_specifications: $ => choice( + $.add_column, + $.add_constraint, + $.drop_constraint, + $.alter_column, + $.modify_column, + $.change_column, + $.drop_column, + $.rename_object, + $.rename_column, + $.set_schema, + $.change_ownership, + ), + + // TODO: optional `keyword_add` is necessary to allow for chained alter statements in t-sql + // maybe needs refactoring + add_column: $ => seq( + optional($.keyword_add), + optional( + $.keyword_column, + ), + optional($._if_not_exists), + $.column_definition, + optional($.column_position), + ), + + add_constraint: $ => seq( + $.keyword_add, + optional($.keyword_constraint), + $.identifier, + $.constraint, + ), + + drop_constraint: $ => seq( + $.keyword_drop, + $.keyword_constraint, + optional($._if_exists), + $.identifier, + optional($._drop_behavior), + ), + + alter_column: $ => seq( + // TODO constraint management + $.keyword_alter, + optional( + $.keyword_column, + ), + field('name', $.identifier), + choice( + seq( + choice( + $.keyword_set, + $.keyword_drop, + ), + $.keyword_not, + $.keyword_null, + ), + seq( + optional( + seq( + $.keyword_set, + $.keyword_data, + ), + ), + $.keyword_type, + field('type', $._type), + ), + seq( + $.keyword_set, + choice( + seq( + $.keyword_statistics, + field('statistics', $._integer) + ), + seq( + $.keyword_storage, + choice( + $.keyword_plain, + $.keyword_external, + $.keyword_extended, + $.keyword_main, + $.keyword_default, + ), + ), + seq( + $.keyword_compression, + field('compression_method', $._identifier) + ), + seq( + paren_list($._key_value_pair, true), + ), + seq( + $.keyword_default, + $._expression, + ), + ) + ), + seq( + $.keyword_drop, + $.keyword_default, + ), + ), + ), + + modify_column: $ => seq( + $.keyword_modify, + optional( + $.keyword_column, + ), + optional($._if_exists), + $.column_definition, + optional($.column_position), + ), + + change_column: $ => seq( + $.keyword_change, + optional( + $.keyword_column, + ), + optional($._if_exists), + field('old_name', $.identifier), + $.column_definition, + optional($.column_position), + ), + + column_position: $ => choice( + $.keyword_first, + seq( + $.keyword_after, + field('col_name', $.identifier), + ), + ), + + drop_column: $ => seq( + $.keyword_drop, + optional( + $.keyword_column, + ), + optional($._if_exists), + field('name', $.identifier), + ), + + rename_column: $ => seq( + $.keyword_rename, + optional( + $.keyword_column, + ), + field('old_name', $.identifier), + $.keyword_to, + field('new_name', $.identifier), + ), + + alter_view: $ => seq( + $.keyword_alter, + $.keyword_view, + optional($._if_exists), + $.object_reference, + choice( + // TODO Postgres allows a single "alter column" to set or drop default + $.rename_object, + $.rename_column, + $.set_schema, + $.change_ownership, + ), + ), + + alter_schema: $ => seq( + $.keyword_alter, + $.keyword_schema, + $.identifier, + choice( + $.keyword_rename, + $.keyword_owner, + ), + $.keyword_to, + $.identifier, + ), + + alter_database: $ => seq( + $.keyword_alter, + $.keyword_database, + $.identifier, + optional($.keyword_with), + choice( + seq($.rename_object), + seq($.change_ownership), + seq( + $.keyword_reset, + choice( + $.keyword_all, + field("configuration_parameter", $.identifier) + ), + ), + seq( + $.keyword_set, + choice( + seq($.keyword_tablespace, $.identifier), + $.set_configuration, + ), + ), + ), + ), + + alter_role: $ => prec.left(seq( + $.keyword_alter, + choice( + $.keyword_role, + $.keyword_group, + $.keyword_user, + ), + choice($.identifier, $.keyword_all), + choice( + $.rename_object, + seq(optional($.keyword_with),repeat($._role_options)), + seq( + optional(seq($.keyword_in, $.keyword_database, $.identifier)), + choice( + seq( + $.keyword_set, + $.set_configuration, + ), + seq( + $.keyword_reset, + choice( + $.keyword_all, + field("option", $.identifier), + )), + ), + ) + ), + )), + + set_configuration: $ => seq( + field("option", $.identifier), + choice( + seq($.keyword_from, $.keyword_current), + seq( + choice($.keyword_to, "="), + choice( + field("parameter", $.identifier), + $.literal, + $.keyword_default + ) + ) + ), + ), + + alter_index: $ => seq( + $.keyword_alter, + $.keyword_index, + optional($._if_exists), + $.identifier, + choice( + $.rename_object, + seq( + $.keyword_alter, + optional($.keyword_column), + alias($._natural_number, $.literal), + $.keyword_set, + $.keyword_statistics, + alias($._natural_number, $.literal), + ), + seq($.keyword_reset, paren_list($.identifier)), + seq( + $.keyword_set, + choice( + seq($.keyword_tablespace, $.identifier), + paren_list(seq($.identifier, '=', field("value", $.literal))) + ), + ), + ), + ), + + alter_sequence: $ => seq( + $.keyword_alter, + $.keyword_sequence, + optional($._if_exists), + $.object_reference, + choice( + repeat1( + choice( + seq($.keyword_as, $._type), + seq($.keyword_increment, optional($.keyword_by), $.literal), + seq($.keyword_minvalue, choice($.literal, seq($.keyword_no, $.keyword_minvalue))), + seq($.keyword_maxvalue, choice($.literal, seq($.keyword_no, $.keyword_maxvalue))), + seq($.keyword_start, optional($.keyword_with), field("start", alias($._integer, $.literal))), + seq($.keyword_restart, optional($.keyword_with), field("restart", alias($._integer, $.literal))), + seq($.keyword_cache, field("cache", alias($._integer, $.literal))), + seq(optional($.keyword_no), $.keyword_cycle), + seq($.keyword_owned, $.keyword_by, choice($.keyword_none, $.object_reference)), + ), + ), + $.rename_object, + $.change_ownership, + seq( + $.keyword_set, + choice( + choice($.keyword_logged, $.keyword_unlogged), + seq($.keyword_schema, $.identifier) + ), + ), + ), + ), + + alter_type: $ => seq( + $.keyword_alter, + $.keyword_type, + $.identifier, + choice( + $.change_ownership, + $.set_schema, + $.rename_object, + seq( + $.keyword_rename, + $.keyword_attribute, + $.identifier, + $.keyword_to, + $.identifier, + optional($._drop_behavior) + ), + seq( + $.keyword_add, + $.keyword_value, + optional($._if_not_exists), + alias($._single_quote_string,$.literal), + optional( + seq( + choice($.keyword_before, $.keyword_after), + alias($._single_quote_string,$.literal), + ) + ), + ), + seq( + $.keyword_rename, + $.keyword_value, + alias($._single_quote_string,$.literal), + $.keyword_to, + alias($._single_quote_string,$.literal), + ), + seq( + choice( + seq( + $.keyword_add, + $.keyword_attribute, + $.identifier, + $._type + ), + seq($.keyword_drop, + $.keyword_attribute, + optional($._if_exists), + $.identifier), + seq( + $.keyword_alter, + $.keyword_attribute, + $.identifier, + optional(seq($.keyword_set, $.keyword_data)), + $.keyword_type, + $._type + ), + ), + optional(seq($.keyword_collate, $.identifier)), + optional($._drop_behavior) + ) + ), + ), + + _drop_behavior: $ => choice( + $.keyword_cascade, + $.keyword_restrict, + ), + + rename_object: $ => seq( + $.keyword_rename, + $.keyword_to, + $.object_reference, + ), + + set_schema: $ => seq( + $.keyword_set, + $.keyword_schema, + field('schema', $.identifier), + ), + + change_ownership: $ => seq( + $.keyword_owner, + $.keyword_to, + $.identifier, + ), + +}; diff --git a/grammar/statements/comment.js b/grammar/statements/comment.js new file mode 100644 index 0000000..29b51d5 --- /dev/null +++ b/grammar/statements/comment.js @@ -0,0 +1,54 @@ +module.exports = { + + comment_statement: $ => seq( + $.keyword_comment, + $.keyword_on, + $._comment_target, + $.keyword_is, + choice( + $.keyword_null, + alias($._literal_string, $.literal), + ), + ), + + _comment_target: $ => choice( + // TODO: access method + // TODO: aggregate + $.cast, + // TODO: collation + seq($.keyword_column, alias($._qualified_field, $.object_reference)), + // TODO: constraint (on domain) + // TODO: conversion + seq($.keyword_database, $.identifier), + // TODO: domain + seq($.keyword_extension, $.object_reference), + // TODO: event trigger + // TODO: foreign data wrapper + // TODO: foreign table + seq($.keyword_function, $.object_reference, optional($.function_arguments)), + seq($.keyword_index, $.object_reference), + // TODO: large object + seq($.keyword_materialized, $.keyword_view, $.object_reference), + // TODO: operator (|class|family) + // TODO: policy + // TODO: (procedural) language + // TODO: procedure + // TODO: publication + seq($.keyword_role, $.identifier), + // TODO: routine + // TODO: rule + seq($.keyword_schema, $.identifier), + seq($.keyword_sequence, $.object_reference), + // TODO: server + // TODO: statistics + // TODO: subscription + seq($.keyword_table, $.object_reference), + seq($.keyword_tablespace, $.identifier), + // TODO: text search (configuration|dictionary|parser|template) + // TODO: transform for + seq($.keyword_trigger, $.identifier, $.keyword_on, $.object_reference), + seq($.keyword_type, $.identifier), + seq($.keyword_view, $.object_reference), + ), + +}; diff --git a/grammar/statements/copy.js b/grammar/statements/copy.js new file mode 100644 index 0000000..b84424e --- /dev/null +++ b/grammar/statements/copy.js @@ -0,0 +1,67 @@ +const { wrapped_in_parenthesis } = require("../helpers"); + +module.exports = { + + _copy_statement: $ => seq( + $.keyword_copy, + $.object_reference, + $._column_list, + $.keyword_from, + choice( + $.keyword_stdin, + alias($._literal_string, "filename"), + seq($.keyword_program, alias($._literal_string, "command")), + ), + optional($.keyword_with), + wrapped_in_parenthesis( + repeat1( + choice( + seq( + $.keyword_format, + choice( + $.keyword_csv, + $.keyword_binary, + $.keyword_text, + ), + ), + seq( + $.keyword_freeze, + choice( + $.keyword_true, + $.keyword_false + ) + ), + seq( + $.keyword_header, + choice( + $.keyword_true, + $.keyword_false, + $.keyword_match + ), + ), + seq( + choice( + $.keyword_delimiter, + $.keyword_null, + $.keyword_default, + $.keyword_escape, + $.keyword_quote, + $.keyword_encoding, + ), + alias($._literal_string, $.identifier) + ), + seq( + choice( + $.keyword_force_null, + $.keyword_force_not_null, + $.keyword_force_quote, + ), + $._column_list + ), + ), + ), + ), + optional($.where), + ), + +}; diff --git a/grammar/statements/create-function.js b/grammar/statements/create-function.js new file mode 100644 index 0000000..9c62fda --- /dev/null +++ b/grammar/statements/create-function.js @@ -0,0 +1,246 @@ +const { paren_list, wrapped_in_parenthesis } = require("../helpers"); + +module.exports = { + + // This is only used in create function statement, it is not needed to check + // the start tag match the end one. The usage of this syntax in other + // context is done by _dollar_string. + dollar_quote: () => /\$[^\$]*\$/, + + create_function: $ => seq( + $.keyword_create, + optional($._or_replace), + $.keyword_function, + $.object_reference, + $.function_arguments, + $.keyword_returns, + choice( + $._type, + seq($.keyword_setof, $._type), + seq($.keyword_table, $.column_definitions), + $.keyword_trigger, + ), + repeat( + choice( + $.function_language, + $.function_volatility, + $.function_leakproof, + $.function_security, + $.function_safety, + $.function_strictness, + $.function_cost, + $.function_rows, + $.function_support, + ), + ), + // ensure that there's only one function body -- other specifiers are less + // variable but the body can have all manner of conflicting stuff + $.function_body, + repeat( + choice( + $.function_language, + $.function_volatility, + $.function_leakproof, + $.function_security, + $.function_safety, + $.function_strictness, + $.function_cost, + $.function_rows, + $.function_support, + ), + ), + ), + + _argmode: $ => choice( + $.keyword_in, + $.keyword_out, + $.keyword_inout, + $.keyword_variadic, + seq($.keyword_in, $.keyword_out), + ), + + function_argument: $ => seq( + optional($._argmode), + optional($.identifier), + $._type, + optional( + seq( + choice($.keyword_default, '='), + $.literal, + ), + ), + ), + + function_arguments: $ => paren_list( + $.function_argument, + false, + ), + + _function_return: $ => seq( + $.keyword_return, + $._expression, + ), + + function_declaration: $ => seq( + $.identifier, + $._type, + optional( + seq( + ':=', + choice( + wrapped_in_parenthesis($.statement), + // TODO are there more possibilities here? We can't use `_expression` since + // that includes subqueries + $.literal, + ), + ), + ), + ';', + ), + + _function_body_statement: $ => choice( + $.statement, + $._function_return, + ), + + _tsql_function_body_statement: $ => seq( + optional($.keyword_as), + $.keyword_begin, + optional($.var_declarations), + choice( + repeat($.statement), + repeat1(seq( + $.keyword_begin, + repeat($.statement), + $.keyword_end, + )), + ), + $._function_return, + $.keyword_end, + ), + + function_body: $ => choice( + seq( + $._function_return, + ';' + ), + seq( + $.keyword_begin, + $.keyword_atomic, + repeat1( + seq( + $._function_body_statement, + ';', + ), + ), + $.keyword_end, + ), + seq( + $.keyword_as, + alias($._dollar_quoted_string_start_tag, $.dollar_quote), + optional( + seq( + $.keyword_declare, + repeat1( + $.function_declaration, + ), + ), + ), + $.keyword_begin, + repeat1( + seq( + $._function_body_statement, + ';', + ), + ), + $.keyword_end, + optional(';'), + alias($._dollar_quoted_string_end_tag, $.dollar_quote), + ), + seq( + $.keyword_as, + alias( + choice( + $._single_quote_string, + $._double_quote_string, + ), + $.literal + ), + ), + seq( + $.keyword_as, + alias($._dollar_quoted_string_start_tag, $.dollar_quote), + $._function_body_statement, + optional(';'), + alias($._dollar_quoted_string_end_tag, $.dollar_quote), + ), + $._tsql_function_body_statement, + ), + + function_language: $ => seq( + $.keyword_language, + // TODO Maybe we should do different version of function_body_statement in + // regard to the defined language to match either sql, plsql or + // plpgsql. Currently the function_body_statement support only sql. And + // maybe for other language the function_body should be a string. + $.identifier + ), + + function_volatility: $ => choice( + $.keyword_immutable, + $.keyword_stable, + $.keyword_volatile, + ), + + function_leakproof: $ => seq( + optional($.keyword_not), + $.keyword_leakproof, + ), + + function_security: $ => seq( + optional($.keyword_external), + $.keyword_security, + choice($.keyword_invoker, $.keyword_definer), + ), + + function_safety: $ => seq( + $.keyword_parallel, + choice( + $.keyword_safe, + $.keyword_unsafe, + $.keyword_restricted, + ), + ), + + function_strictness: $ => choice( + seq( + choice( + $.keyword_called, + seq( + $.keyword_returns, + $.keyword_null, + ), + ), + $.keyword_on, + $.keyword_null, + $.keyword_input, + ), + $.keyword_strict, + ), + + function_cost: $ => seq( + $.keyword_cost, + $._natural_number, + ), + + function_rows: $ => seq( + $.keyword_rows, + $._natural_number, + ), + + function_support: $ => seq( + $.keyword_support, + alias($._literal_string, $.literal), + ), + +}; diff --git a/grammar/statements/create.js b/grammar/statements/create.js new file mode 100644 index 0000000..e241d98 --- /dev/null +++ b/grammar/statements/create.js @@ -0,0 +1,567 @@ +const { comma_list, paren_list, wrapped_in_parenthesis } = require("../helpers"); + +module.exports = { + + _create_statement: $ => seq( + choice( + $.create_table, + $.create_view, + $.create_materialized_view, + $.create_index, + $.create_function, + $.create_type, + $.create_database, + $.create_role, + $.create_sequence, + $.create_extension, + $.create_trigger, + prec.left(seq( + $.create_schema, + repeat($._create_statement), + )), + ), + ), + + // left precedence because 'quoted' table options otherwise conflict with + // `create function` string bodies; if you remove this precedence you will + // have to also disable the `_literal_string` choice for the `name` field + // in =-assigned `table_option`s + create_table: $ => prec.left( + seq( + $.keyword_create, + optional( + choice( + $._temporary, + $.keyword_unlogged, + $.keyword_external, + ) + ), + $.keyword_table, + optional($._if_not_exists), + $.object_reference, + choice( + seq( + $.column_definitions, + repeat($._table_settings), + optional( + seq( + $.keyword_as, + $._select_statement, + ), + ) + ), + seq( + repeat($._table_settings), + seq( + $.keyword_as, + $.create_query, + ), + ), + ), + ), + ), + + _table_settings: $ => choice( + $.table_partition, + $.stored_as, + $.storage_location, + $.table_sort, + $.row_format, + seq( + $.keyword_tblproperties, + paren_list($.table_option, true), + ), + seq($.keyword_without, $.keyword_oids), + $.storage_parameters, + $.table_option, + ), + + stored_as: $ => seq( + $.keyword_stored, + $.keyword_as, + choice( + $.keyword_parquet, + $.keyword_csv, + $.keyword_sequencefile, + $.keyword_textfile, + $.keyword_rcfile, + $.keyword_orc, + $.keyword_avro, + $.keyword_jsonfile, + ), + ), + + storage_parameters: $ => seq( + $.keyword_with, + paren_list( + seq($.identifier, optional(seq('=', choice($.literal, $.array)))), + true + ), + ), + + storage_location: $ => prec.right( + seq( + $.keyword_location, + field('path', alias($._literal_string, $.literal)), + optional( + seq( + $.keyword_cached, + $.keyword_in, + field('pool', alias($._literal_string, $.literal)), + optional( + choice( + $.keyword_uncached, + seq( + $.keyword_with, + $.keyword_replication, + '=', + field('value', alias($._natural_number, $.literal)), + ), + ), + ), + ) + ) + ), + ), + + row_format: $ => seq( + $.keyword_row, + $.keyword_format, + $.keyword_delimited, + optional( + seq( + $.keyword_fields, + $.keyword_terminated, + $.keyword_by, + field('fields_terminated_char', alias($._literal_string, $.literal)), + optional( + seq( + $.keyword_escaped, + $.keyword_by, + field('escaped_char', alias($._literal_string, $.literal)), + ) + ) + ) + ), + optional( + seq( + $.keyword_lines, + $.keyword_terminated, + $.keyword_by, + field('row_terminated_char', alias($._literal_string, $.literal)), + ) + ) + ), + + table_sort: $ => seq( + $.keyword_sort, + $.keyword_by, + paren_list($.identifier, true), + ), + + table_partition: $ => seq( + choice( + // Postgres/MySQL style + seq( + $.keyword_partition, + $.keyword_by, + choice( + $.keyword_range, + $.keyword_hash, + ) + ), + // Hive style + seq( + $.keyword_partitioned, + $.keyword_by, + ), + // Spark SQL + $.keyword_partition, + ), + choice( + paren_list($.identifier),// postgres & Impala (CTAS) + $.column_definitions, // impala/hive external tables + paren_list($._key_value_pair, true), // Spark SQL + ) + ), + + table_option: $ => choice( + seq($.keyword_default, $.keyword_character, $.keyword_set, $.identifier), + seq($.keyword_collate, $.identifier), + field('name', $.keyword_default), + seq( + field('name', choice($.keyword_engine, $.identifier, $._literal_string)), + '=', + field('value', choice($.identifier, $._literal_string)), + ), + ), + + create_query: $ => $._dml_read, + + create_view: $ => prec.right( + seq( + $.keyword_create, + optional($._or_replace), + optional($._temporary), + optional($.keyword_recursive), + $.keyword_view, + optional($._if_not_exists), + $.object_reference, + optional(paren_list($.identifier)), + $.keyword_as, + $.create_query, + optional( + seq( + $.keyword_with, + optional( + choice( + $.keyword_local, + $.keyword_cascaded, + ) + ), + $._check_option, + ), + ), + ), + ), + + create_materialized_view: $ => prec.right( + seq( + $.keyword_create, + optional($._or_replace), + $.keyword_materialized, + $.keyword_view, + optional($._if_not_exists), + $.object_reference, + $.keyword_as, + $.create_query, + optional( + choice( + seq( + $.keyword_with, + $.keyword_data, + ), + seq( + $.keyword_with, + $.keyword_no, + $.keyword_data, + ) + ) + ) + ), + ), + + _operator_class: $ => seq( + field("opclass", $.identifier), + optional( + field("opclass_parameters", wrapped_in_parenthesis(comma_list($.term))) + ) + ), + + composite_field: $ => seq( + wrapped_in_parenthesis( + comma_list( + alias($._index_field, $.field), + true, + ), + ), + optional($.keyword_hash), + ), + + _index_field: $ => seq( + choice( + field("expression", wrapped_in_parenthesis($._expression)), + field("function", $.invocation), + field("column", $._column), + ), + optional(seq($.keyword_collate, $.identifier)), + optional($._operator_class), + optional(choice($.keyword_hash, $.direction)), + optional( + seq( + $.keyword_nulls, + choice( + $.keyword_first, + $.keyword_last + ) + ) + ), + ), + + index_fields: $ => wrapped_in_parenthesis( + comma_list( + choice( + $.composite_field, + alias($._index_field, $.field), + ), + true, + ), + ), + tablespace: $ => seq($.keyword_tablespace, $.identifier), + tablet_split: $ => seq($.keyword_split, $.keyword_into, $._natural_number, $.keyword_tablets), + + covering_columns: $ => seq( + $.keyword_include, + $.index_fields, + ), + + create_index: $ => seq( + $.keyword_create, + optional($.keyword_unique), + $.keyword_index, + optional($.keyword_concurrently), + optional( + seq( + optional($._if_not_exists), + field("column", $._column), + ), + ), + $.keyword_on, + optional($.keyword_only), + seq( + $.object_reference, + optional( + seq( + $.keyword_using, + choice( + $.keyword_btree, + $.keyword_hash, + $.keyword_gist, + $.keyword_spgist, + $.keyword_gin, + $.keyword_brin + ), + ), + ), + $.index_fields + ), + optional($.covering_columns), + optional($.tablespace), + optional($.tablet_split), + optional( + $.where, + ), + ), + + ...require("./create-function"), + + create_schema: $ => prec.left(seq( + $.keyword_create, + $.keyword_schema, + choice( + seq( + optional($._if_not_exists), + $.identifier, + optional(seq($.keyword_authorization, $.identifier)), + ), + seq( + $.keyword_authorization, + $.identifier, + ), + ), + )), + + _with_settings: $ => seq( + field('name', $.identifier), + optional('='), + field('value', choice($.identifier, alias($._single_quote_string, $.literal))), + ), + + create_database: $ => prec.left(seq( + $.keyword_create, + $.keyword_database, + optional($._if_not_exists), + $.identifier, + optional($.keyword_with), + repeat( + $._with_settings + ), + )), + + create_role: $ => prec.left(seq( + $.keyword_create, + choice( + $.keyword_user, + $.keyword_role, + $.keyword_group, + ), + $.identifier, + optional($.keyword_with), + repeat( + choice( + $._user_access_role_config, + $._role_options, + ), + ), + )), + + _role_options: $ => choice( + field("option", $.identifier), + seq( + $.keyword_valid, + $.keyword_until, + field("valid_until", alias($._literal_string, $.literal)) + ), + seq( + $.keyword_connection, + $.keyword_limit, + field("connection_limit", alias($._integer, $.literal)) + ), + seq( + optional($.keyword_encrypted), + $.keyword_password, + choice( + field("password", alias($._literal_string, $.literal)), + $.keyword_null, + ), + ), + ), + + _user_access_role_config: $ => seq( + choice( + seq(optional($.keyword_in), $.keyword_role), + seq($.keyword_in, $.keyword_group), + $.keyword_admin, + $.keyword_user, + ), + comma_list($.identifier, true), + ), + + create_sequence: $ => seq( + $.keyword_create, + optional( + choice( + choice($.keyword_temporary, $.keyword_temp), + $.keyword_unlogged, + ) + ), + $.keyword_sequence, + optional($._if_not_exists), + $.object_reference, + repeat( + choice( + seq($.keyword_as, $._type), + seq($.keyword_increment, optional($.keyword_by), field("increment", alias($._integer, $.literal))), + seq($.keyword_minvalue, choice($.literal, seq($.keyword_no, $.keyword_minvalue))), + seq($.keyword_no, $.keyword_minvalue), + seq($.keyword_maxvalue, choice($.literal, seq($.keyword_no, $.keyword_maxvalue))), + seq($.keyword_no, $.keyword_maxvalue), + seq($.keyword_start, optional($.keyword_with), field("start", alias($._integer, $.literal))), + seq($.keyword_cache, field("cache", alias($._integer, $.literal))), + seq(optional($.keyword_no), $.keyword_cycle), + seq($.keyword_owned, $.keyword_by, choice($.keyword_none, $.object_reference)), + ) + ), + ), + + create_extension: $ => prec.left(seq( + $.keyword_create, + $.keyword_extension, + optional($._if_not_exists), + $.identifier, + optional($.keyword_with), + optional(seq($.keyword_schema, $.identifier)), + optional(seq($.keyword_version, choice($.identifier, alias($._literal_string, $.literal)))), + optional($.keyword_cascade), + )), + + create_trigger: $ => seq( + $.keyword_create, + optional($._or_replace), + // mariadb + optional(seq($.keyword_definer, '=', $.identifier)), + optional($.keyword_constraint), + // sqlite + optional($._temporary), + $.keyword_trigger, + // sqlite/mariadb + optional($._if_not_exists), + $.object_reference, + choice( + $.keyword_before, + $.keyword_after, + seq($.keyword_instead, $.keyword_of), + ), + $._create_trigger_event, + repeat(seq($.keyword_or, $._create_trigger_event)), + $.keyword_on, + $.object_reference, + repeat( + choice( + seq($.keyword_from, $.object_reference), + choice( + seq($.keyword_not, $.keyword_deferrable), + $.keyword_deferrable, + seq($.keyword_initially, $.keyword_immediate), + seq($.keyword_initially, $.keyword_deferred), + ), + seq($.keyword_referencing, choice($.keyword_old, $.keyword_new), $.keyword_table, optional($.keyword_as), $.identifier), + seq( + $.keyword_for, + optional($.keyword_each), + choice($.keyword_row, $.keyword_statement), + // mariadb + optional(seq(choice($.keyword_follows, $.keyword_precedes), $.identifier)), + ), + seq($.keyword_when, wrapped_in_parenthesis($._expression)), + ), + ), + $.keyword_execute, + choice($.keyword_function, $.keyword_procedure), + $.object_reference, + paren_list(field('parameter', $.term)), + ), + + _create_trigger_event: $ => choice( + $.keyword_insert, + seq( + $.keyword_update, + optional( + seq( + $.keyword_of, + comma_list($.identifier, true), + ), + ), + ), + $.keyword_delete, + $.keyword_truncate, + ), + + create_type: $ => prec.left(seq( + $.keyword_create, + $.keyword_type, + $.object_reference, + optional( + seq( + choice( + seq( + $.keyword_as, + $.column_definitions, + optional(seq($.keyword_collate, $.identifier)) + ), + seq( + $.keyword_as, + $.keyword_enum, + $.enum_elements, + ), + seq( + optional( + seq( + $.keyword_as, + $.keyword_range, + ) + ), + paren_list( + $._with_settings + ), + ), + ), + ), + ), + )), + + enum_elements: $ => seq( + paren_list(field("enum_element", alias($._literal_string, $.literal))), + ), + +}; diff --git a/grammar/statements/delete.js b/grammar/statements/delete.js new file mode 100644 index 0000000..367f423 --- /dev/null +++ b/grammar/statements/delete.js @@ -0,0 +1,25 @@ +module.exports = { + + _delete_statement: $ => seq( + $.delete, + alias($._delete_from, $.from), + optional($.returning), + ), + + _delete_from: $ => seq( + $.keyword_from, + optional( + $.keyword_only, + ), + $.object_reference, + optional($.where), + optional($.order_by), + optional($.limit), + ), + + delete: $ => seq( + $.keyword_delete, + optional($.index_hint), + ), + +}; diff --git a/grammar/statements/drop.js b/grammar/statements/drop.js new file mode 100644 index 0000000..9db79e8 --- /dev/null +++ b/grammar/statements/drop.js @@ -0,0 +1,111 @@ +const { comma_list } = require("../helpers"); + +module.exports = { + + _drop_statement: $ => seq( + choice( + $.drop_table, + $.drop_view, + $.drop_index, + $.drop_type, + $.drop_schema, + $.drop_database, + $.drop_role, + $.drop_sequence, + $.drop_extension, + $.drop_function, + ), + ), + + drop_table: $ => seq( + $.keyword_drop, + $.keyword_table, + optional($._if_exists), + $.object_reference, + optional($._drop_behavior), + ), + + drop_view: $ => seq( + $.keyword_drop, + $.keyword_view, + optional($._if_exists), + $.object_reference, + optional($._drop_behavior), + ), + + drop_schema: $ => seq( + $.keyword_drop, + $.keyword_schema, + optional($._if_exists), + $.identifier, + optional($._drop_behavior) + ), + + drop_database: $ => prec.left(seq( + $.keyword_drop, + $.keyword_database, + optional($._if_exists), + $.identifier, + optional($.keyword_with), + optional($.keyword_force), + )), + + drop_role: $ => seq( + $.keyword_drop, + choice( + $.keyword_group, + $.keyword_role, + $.keyword_user, + ), + optional($._if_exists), + $.identifier, + ), + + drop_type: $ => seq( + $.keyword_drop, + $.keyword_type, + optional($._if_exists), + $.object_reference, + optional($._drop_behavior), + ), + + drop_sequence: $ => seq( + $.keyword_drop, + $.keyword_sequence, + optional($._if_exists), + $.object_reference, + optional($._drop_behavior), + ), + + drop_index: $ => seq( + $.keyword_drop, + $.keyword_index, + optional($.keyword_concurrently), + optional($._if_exists), + field("name", $.identifier), + optional($._drop_behavior), + optional( + seq( + $.keyword_on, + $.object_reference, + ), + ), + ), + + drop_extension: $ => seq( + $.keyword_drop, + $.keyword_extension, + optional($._if_exists), + comma_list($.identifier, true), + optional(choice($.keyword_cascade, $.keyword_restrict)), + ), + + drop_function: $ => seq( + $.keyword_drop, + $.keyword_function, + optional($._if_exists), + $.object_reference, + optional($._drop_behavior), + ), + +}; diff --git a/grammar/statements/index.js b/grammar/statements/index.js new file mode 100644 index 0000000..9002acf --- /dev/null +++ b/grammar/statements/index.js @@ -0,0 +1,156 @@ +const { optional_parenthesis, wrapped_in_parenthesis } = require("../helpers"); + +module.exports = { + + block: $ => seq( + $.keyword_begin, + optional(';'), + repeat( + seq( + $.statement, + ';' + ), + ), + $.keyword_end, + ), + + statement: $ => seq( + optional(seq( + $.keyword_explain, + optional($.keyword_analyze), + optional($.keyword_verbose), + )), + choice( + $._ddl_statement, + $._dml_write, + optional_parenthesis($._dml_read), + $.while_statement, + ), + ), + + while_statement: $ => prec.left(seq( + $.keyword_while, + optional_parenthesis($._expression), + choice( + seq( + $.statement, + optional(';'), + ), + seq( + $.keyword_begin, + repeat($.statement), + $.keyword_end, + ), + ), + )), + + var_declarations: $ => seq($.keyword_declare, repeat1($.var_declaration)), + var_declaration: $ => seq( + $.identifier, + $._type, + optional( + seq( + choice($.keyword_default, '='), + $.literal, + ), + ), + optional(','), + ), + + _ddl_statement: $ => choice( + $._create_statement, + $._alter_statement, + $._drop_statement, + $._rename_statement, + $._optimize_statement, + $._merge_statement, + $.comment_statement, + $.set_statement, + $.reset_statement, + ), + + ...require("./create"), + ...require("./alter"), + ...require("./drop"), + ...require("./rename"), + ...require("./optimize"), + ...require("./merge"), + ...require("./comment"), + + _dml_write: $ => seq( + seq( + optional( + $._cte, + ), + choice( + $._delete_statement, + $._insert_statement, + $._update_statement, + $._truncate_statement, + $._copy_statement, + ), + ), + ), + + ...require("./delete"), + ...require("./insert"), + ...require("./update"), + ...require("./truncate"), + ...require("./copy"), + + _dml_read: $ => seq( + optional(optional_parenthesis($._cte)), + optional_parenthesis( + choice( + $._select_statement, + $.set_operation, + $._show_statement, + $._unload_statement, + ), + ), + ), + + ...require("./select"), + ...require("./set"), + + _show_statement: $ => seq( + $.keyword_show, + choice( + $._show_create, + $.keyword_all, // Postgres + $._show_tables // trino/presto + ), + ), + + _show_create: $ => seq( + $.keyword_create, + choice( + // Trino/Presto/MySQL + $.keyword_schema, + $.keyword_table, + seq(optional($.keyword_materialized), $.keyword_view), + // MySQL + $.keyword_user, + $.keyword_trigger, + $.keyword_procedure, + $.keyword_function + ), + $.object_reference + ), + + _show_tables: $ => seq( + $.keyword_tables, + optional(seq($.keyword_from, $._qualified_field)), + optional(seq($.keyword_like, $._expression)) + ), + + // athena + _unload_statement: $ => seq( + $.keyword_unload, + wrapped_in_parenthesis($._select_statement), + $.keyword_to, + $._single_quote_string, + $.storage_parameters, + ), + +}; diff --git a/grammar/statements/insert.js b/grammar/statements/insert.js new file mode 100644 index 0000000..abd5ded --- /dev/null +++ b/grammar/statements/insert.js @@ -0,0 +1,106 @@ +const { comma_list } = require("../helpers"); + +module.exports = { + + _insert_statement: $ => seq( + $.insert, + optional($.returning), + ), + + insert: $ => seq( + choice( + $.keyword_insert, + $.keyword_replace + ), + optional( + choice( + $.keyword_low_priority, + $.keyword_delayed, + $.keyword_high_priority, + ), + ), + optional($.keyword_ignore), + optional( + choice( + $.keyword_into, + $.keyword_overwrite, // Spark SQL + ), + ), + $.object_reference, + optional($.table_partition), // Spark SQL + optional( + seq( + $.keyword_as, + field('alias', $.identifier) + ), + ), + // TODO we need a test for `insert...set` + choice( + $._insert_values, + $._set_values, + ), + optional( + choice( + $._on_conflict, + $._on_duplicate_key_update, + ), + ), + ), + + _on_conflict: $ => seq( + $.keyword_on, + $.keyword_conflict, + seq( + $.keyword_do, + choice( + $.keyword_nothing, + seq( + $.keyword_update, + $._set_values, + optional($.where), + ), + ), + ), + ), + + _on_duplicate_key_update: $ => seq( + $.keyword_on, + $.keyword_duplicate, + $.keyword_key, + $.keyword_update, + $.assignment_list, + ), + + assignment_list: $ => seq( + $.assignment, + repeat(seq(',', $.assignment)), + ), + + assignment: $ => seq( + field('left', + alias( + $._qualified_field, + $.field, + ), + ), + '=', + field('right', $._expression), + ), + + _insert_values: $ => seq( + optional(alias($._column_list, $.list)), + choice( + seq( + $.keyword_values, + comma_list($.list, true), + ), + $._dml_read, + ), + ), + + _set_values: $ => seq( + $.keyword_set, + comma_list($.assignment, true), + ), + +} diff --git a/grammar/statements/merge.js b/grammar/statements/merge.js new file mode 100644 index 0000000..7c06248 --- /dev/null +++ b/grammar/statements/merge.js @@ -0,0 +1,46 @@ +const { optional_parenthesis } = require("../helpers"); + +module.exports = { + + _merge_statement: $=> seq( + $.keyword_merge, + $.keyword_into, + $.object_reference, + optional($._alias), + $.keyword_using, + choice( + $.subquery, + $.object_reference + ), + optional($._alias), + $.keyword_on, + optional_parenthesis(field("predicate", $._expression)), + repeat1($.when_clause) + ), + + when_clause: $ => prec.left(seq( + $.keyword_when, + optional($.keyword_not), + $.keyword_matched, + optional( + seq( + $.keyword_and, + optional_parenthesis(field("predicate", $._expression)) + ) + ), + $.keyword_then, + choice( + $.keyword_delete, + seq( + $.keyword_update, + $._set_values, + ), + seq( + $.keyword_insert, + $._insert_values + ), + optional($.where) + ) + )), + +}; diff --git a/grammar/statements/optimize.js b/grammar/statements/optimize.js new file mode 100644 index 0000000..76f5b35 --- /dev/null +++ b/grammar/statements/optimize.js @@ -0,0 +1,109 @@ +const { paren_list } = require("../helpers"); + +module.exports = { + + _optimize_statement: $ => choice( + $._compute_stats, + $._vacuum_table, + $._optimize_table, + ), + + // Compute stats for Impala and Hive + _compute_stats: $ => prec.left(choice( + // Hive + seq( + $.keyword_analyze, + $.keyword_table, + $.object_reference, + optional($._partition_spec), + $.keyword_compute, + $.keyword_statistics, + optional( + seq( + $.keyword_for, + $.keyword_columns + ) + ), + optional( + seq( + $.keyword_cache, + $.keyword_metadata + ) + ), + optional($.keyword_noscan), + ), + // Impala + seq( + $.keyword_compute, + optional( + $.keyword_incremental, + ), + $.keyword_stats, + $.object_reference, + optional( + choice( + paren_list(repeat1($.field)), + $._partition_spec, + ) + ) + ), + )), + + _optimize_table: $ => choice( + // Athena/Iceberg + seq( + $.keyword_optimize, + $.object_reference, + $.keyword_rewrite, + $.keyword_data, + $.keyword_using, + $.keyword_bin_pack, + optional( + $.where, + ) + ), + // MariaDB Optimize + seq( + $.keyword_optimize, + optional( + choice( + $.keyword_local, + //$.keyword_no_write_to_binlog, + ) + ), + $.keyword_table, + $.object_reference, + repeat(seq(',', $.object_reference)), + ), + ), + + _vacuum_table: $ => prec.left(seq( + $.keyword_vacuum, + optional($._vacuum_option), + $.object_reference, + optional( + paren_list($.field) + ), + )), + + _vacuum_option: $ => choice( + seq($.keyword_full, optional(choice($.keyword_true, $.keyword_false))), + seq($.keyword_parallel, optional(choice($.keyword_true, $.keyword_false))), + seq($.keyword_analyze, optional(choice($.keyword_true, $.keyword_false))), + // seq($.keyword_freeze, choice($.keyword_true, $.keyword_false)), + // seq($.keyword_skip_locked, choice($.keyword_true, $.keyword_false)), + // seq($.keyword_truncate, choice($.keyword_true, $.keyword_false)), + // seq($.keyword_disable_page_skipping, choice($.keyword_true, $.keyword_false)), + // seq($.keyword_process_toast, choice($.keyword_true, $.keyword_false)), + // seq($.keyword_index_cleanup, choice($.keyword_auto, $.keyword_on, $.keyword_off)), + ), + + // TODO: this does not account for partitions specs like + // (partcol1='2022-01-01', hr=11) + // the second argument is not a $.table_option + _partition_spec: $ => seq( + $.keyword_partition, + paren_list($.table_option, true), + ), + +}; diff --git a/grammar/statements/rename.js b/grammar/statements/rename.js new file mode 100644 index 0000000..69f7709 --- /dev/null +++ b/grammar/statements/rename.js @@ -0,0 +1,36 @@ +module.exports = { + + _rename_statement: $ => seq( + $.keyword_rename, + choice( + $.keyword_table, + $.keyword_tables, + ), + optional($._if_exists), + $.object_reference, + optional( + choice( + $.keyword_nowait, + seq( + $.keyword_wait, + field('timeout', alias($._natural_number, $.literal)) + ) + ) + ), + $.keyword_to, + $.object_reference, + repeat( + seq( + ',', + $._rename_table_names, + ) + ), + ), + + _rename_table_names: $ => seq( + $.object_reference, + $.keyword_to, + $.object_reference, + ), + +}; diff --git a/grammar/statements/select.js b/grammar/statements/select.js new file mode 100644 index 0000000..c63f926 --- /dev/null +++ b/grammar/statements/select.js @@ -0,0 +1,441 @@ +const { comma_list, optional_parenthesis, paren_list, wrapped_in_parenthesis } = require("../helpers"); + +module.exports = { + + _cte: $ => seq( + $.keyword_with, + optional($.keyword_recursive), + $.cte, + repeat( + seq( + ',', + $.cte, + ), + ), + ), + + cte: $ => seq( + $.identifier, + optional(paren_list(field("argument", $.identifier))), + $.keyword_as, + optional( + seq( + optional($.keyword_not), + $.keyword_materialized, + ), + ), + wrapped_in_parenthesis( + alias( + choice($._dml_read, $._dml_write), + $.statement, + ), + ), + ), + + set_operation: $ => seq( + $._select_statement, + repeat1( + seq( + field( + "operation", + choice( + seq($.keyword_union, optional($.keyword_all)), + $.keyword_except, + $.keyword_intersect, + ), + ), + $._select_statement, + ), + ), + ), + + _select_statement: $ => optional_parenthesis( + seq( + $.select, + optional( + seq( + $.keyword_into, + $.select_expression, + ), + ), + optional($.from), + ), + ), + + select: $ => seq( + $.keyword_select, + seq( + optional($.keyword_distinct), + $.select_expression, + ), + ), + + select_expression: $ => seq( + $.term, + repeat( + seq( + ',', + $.term, + ), + ), + ), + + term: $ => seq( + field( + 'value', + choice( + $.all_fields, + $._expression, + ), + ), + optional($._alias), + ), + + all_fields: $ => seq( + optional( + seq( + $.object_reference, + '.', + ), + ), + '*', + ), + + partition_by: $ => seq( + $.keyword_partition, + $.keyword_by, + comma_list($._expression, true), + ), + + frame_definition: $ => seq( + choice( + seq( + $.keyword_unbounded, + $.keyword_preceding, + ), + seq( + field("start", + choice( + $.identifier, + $.binary_expression, + alias($._literal_string, $.literal), + alias($._integer, $.literal) + ) + ), + $.keyword_preceding, + ), + $._current_row, + seq( + field("end", + choice( + $.identifier, + $.binary_expression, + alias($._literal_string, $.literal), + alias($._integer, $.literal) + ) + ), + $.keyword_following, + ), + seq( + $.keyword_unbounded, + $.keyword_following, + ), + ), + ), + + window_frame: $ => seq( + choice( + $.keyword_range, + $.keyword_rows, + $.keyword_groups, + ), + + choice( + seq( + $.keyword_between, + $.frame_definition, + optional( + seq( + $.keyword_and, + $.frame_definition, + ) + ) + ), + seq( + $.frame_definition, + ) + ), + optional( + choice( + $._exclude_current_row, + $._exclude_group, + $._exclude_ties, + $._exclude_no_others, + ), + ), + ), + + window_clause: $ => seq( + $.keyword_window, + $.identifier, + $.keyword_as, + $.window_specification, + ), + + window_specification: $ => wrapped_in_parenthesis( + seq( + optional($.partition_by), + optional($.order_by), + optional($.window_frame), + ), + ), + + window_function: $ => seq( + $.invocation, + $.keyword_over, + choice( + $.identifier, + $.window_specification, + ), + ), + + _alias: $ => seq( + optional($.keyword_as), + field('alias', $.identifier), + ), + + from: $ => seq( + $.keyword_from, + optional( + $.keyword_only, + ), + comma_list($.relation, true), + optional($.index_hint), + repeat( + choice( + $.join, + $.cross_join, + $.lateral_join, + $.lateral_cross_join, + ), + ), + optional($.where), + optional($.group_by), + optional($.having), + optional($.window_clause), + optional($.order_by), + optional($.limit), + ), + + relation: $ => prec.right( + seq( + choice( + $.subquery, + $.invocation, + $.object_reference, + wrapped_in_parenthesis($.values), + ), + optional( + seq( + $._alias, + optional(alias($._column_list, $.list)), + ), + ), + ), + ), + + values: $ => seq( + $.keyword_values, + $.list, + optional( + repeat( + seq( + ',', + $.list, + ), + ), + ), + ), + + index_hint: $ => seq( + choice( + $.keyword_force, + $.keyword_use, + $.keyword_ignore, + ), + $.keyword_index, + optional( + seq( + $.keyword_for, + $.keyword_join, + ), + ), + wrapped_in_parenthesis( + field('index_name', $.identifier), + ), + ), + + join: $ => seq( + optional($.keyword_natural), + optional( + choice( + $.keyword_left, + seq($.keyword_full, $.keyword_outer), + seq($.keyword_left, $.keyword_outer), + $.keyword_right, + seq($.keyword_right, $.keyword_outer), + $.keyword_inner, + $.keyword_full, + ), + ), + $.keyword_join, + $.relation, + optional($.index_hint), + optional($.join), + choice( + seq( + $.keyword_on, + field("predicate", $._expression), + ), + seq( + $.keyword_using, + alias($._column_list, $.list), + ) + ) + ), + + cross_join: $ => prec.right( + seq( + $.keyword_cross, + $.keyword_join, + $.relation, + optional( + seq( + $.keyword_with, + $.keyword_ordinality, + optional( + seq( + $.keyword_as, + field("alias", $.identifier), + paren_list($.identifier), + ) + ) + ) + ) + ) + ), + + lateral_join: $ => seq( + optional( + choice( + // lateral joins cannot be right! + $.keyword_left, + seq($.keyword_left, $.keyword_outer), + $.keyword_inner, + ), + ), + $.keyword_join, + $.keyword_lateral, + choice( + $.invocation, + $.subquery, + ), + optional( + choice( + seq( + $.keyword_as, + field('alias', $.identifier), + ), + field('alias', $.identifier), + ), + ), + $.keyword_on, + choice( + $._expression, + $.keyword_true, + $.keyword_false, + ), + ), + + lateral_cross_join: $ => seq( + $.keyword_cross, + $.keyword_join, + $.keyword_lateral, + choice( + $.invocation, + $.subquery, + ), + optional( + choice( + seq( + $.keyword_as, + field('alias', $.identifier), + ), + field('alias', $.identifier), + ), + ), + ), + + where: $ => seq( + $.keyword_where, + field("predicate", $._expression), + ), + + group_by: $ => seq( + $.keyword_group, + $.keyword_by, + comma_list($._expression, true), + ), + + having: $ => seq( + $.keyword_having, + $._expression, + ), + + order_by: $ => prec.right(seq( + $.keyword_order, + $.keyword_by, + comma_list($.order_target, true), + )), + + order_target: $ => seq( + $._expression, + optional( + seq( + choice( + $.direction, + seq( + $.keyword_using, + choice('<', '>', '<=', '>='), + ), + ), + optional( + seq( + $.keyword_nulls, + choice( + $.keyword_first, + $.keyword_last, + ), + ), + ), + ), + ), + ), + + limit: $ => seq( + $.keyword_limit, + $.literal, + optional($.offset), + ), + + offset: $ => seq( + $.keyword_offset, + $.literal, + ), + + returning: $ => seq( + $.keyword_returning, + $.select_expression, + ), + +}; diff --git a/grammar/statements/set.js b/grammar/statements/set.js new file mode 100644 index 0000000..6a6febd --- /dev/null +++ b/grammar/statements/set.js @@ -0,0 +1,63 @@ +const { comma_list } = require("../helpers"); + +module.exports = { + + set_statement: $ => seq( + $.keyword_set, + choice( + seq( + optional(choice($.keyword_session, $.keyword_local)), + choice( + seq( + $.object_reference, + choice($.keyword_to, '='), + choice( + $.literal, + $.keyword_default, + $.identifier, + $.keyword_on, + $.keyword_off, + ), + ), + seq($.keyword_schema, $.literal), + seq($.keyword_names, $.literal), + seq($.keyword_time, $.keyword_zone, choice($.literal, $.keyword_local, $.keyword_default)), + seq($.keyword_session, $.keyword_authorization, choice($.identifier, $.keyword_default)), + seq($.keyword_role, choice($.identifier, $.keyword_none)), + ), + ), + seq($.keyword_constraints, choice($.keyword_all, comma_list($.identifier, true)), choice($.keyword_deferred, $.keyword_immediate)), + seq($.keyword_transaction, $._transaction_mode), + seq($.keyword_transaction, $.keyword_snapshot, $._transaction_mode), + seq($.keyword_session, $.keyword_characteristics, $.keyword_as, $.keyword_transaction, $._transaction_mode), + ), + ), + + _transaction_mode: $ => seq( + $.keyword_isolation, + $.keyword_level, + choice( + $.keyword_serializable, + seq($.keyword_repeatable, $.keyword_read), + seq($.keyword_read, $.keyword_committed), + seq($.keyword_read, $.keyword_uncommitted), + ), + choice( + seq($.keyword_read, $.keyword_write), + seq($.keyword_read, $.keyword_only), + ), + optional($.keyword_not), + $.keyword_deferrable, + ), + + reset_statement: $ => seq( + $.keyword_reset, + choice( + $.object_reference, + $.keyword_all, + seq($.keyword_session, $.keyword_authorization), + $.keyword_role, + ), + ), + +}; diff --git a/grammar/statements/truncate.js b/grammar/statements/truncate.js new file mode 100644 index 0000000..2a7333c --- /dev/null +++ b/grammar/statements/truncate.js @@ -0,0 +1,13 @@ +const { comma_list } = require("../helpers"); + +module.exports = { + + _truncate_statement: $ => seq( + $.keyword_truncate, + optional($.keyword_table), + optional($.keyword_only), + comma_list($.object_reference), + optional($._drop_behavior), + ), + +}; diff --git a/grammar/statements/update.js b/grammar/statements/update.js new file mode 100644 index 0000000..3132206 --- /dev/null +++ b/grammar/statements/update.js @@ -0,0 +1,36 @@ +const { comma_list } = require("../helpers"); + +module.exports = { + + _update_statement: $ => seq( + $.update, + optional($.returning), + ), + + update: $ => seq( + $.keyword_update, + optional($.keyword_only), + choice( + $._mysql_update_statement, + $._postgres_update_statement, + ), + ), + + _mysql_update_statement: $ => prec(0, + seq( + comma_list($.relation, true), + repeat($.join), + $._set_values, + optional($.where), + ), + ), + + _postgres_update_statement: $ => prec(1, + seq( + $.relation, + $._set_values, + optional($.from), + ), + ), + +}; diff --git a/grammar/transactions.js b/grammar/transactions.js new file mode 100644 index 0000000..ceceedb --- /dev/null +++ b/grammar/transactions.js @@ -0,0 +1,35 @@ +module.exports = { + + transaction: $ => seq( + $.keyword_begin, + optional( + $.keyword_transaction, + ), + optional(';'), + repeat( + seq( + $.statement, + ';' + ), + ), + choice( + $._commit, + $._rollback, + ), + ), + + _commit: $ => seq( + $.keyword_commit, + optional( + $.keyword_transaction, + ), + ), + + _rollback: $ => seq( + $.keyword_rollback, + optional( + $.keyword_transaction, + ), + ), + +}; diff --git a/grammar/types.js b/grammar/types.js new file mode 100644 index 0000000..7165e94 --- /dev/null +++ b/grammar/types.js @@ -0,0 +1,202 @@ +const { make_keyword, comma_list, paren_list, wrapped_in_parenthesis } = require("./helpers"); + +module.exports = { + + _type: $ => prec.left( + seq( + choice( + $.keyword_boolean, + $.bit, + $.binary, + $.varbinary, + $.keyword_image, + + $.keyword_smallserial, + $.keyword_serial, + $.keyword_bigserial, + + $.tinyint, + $.smallint, + $.mediumint, + $.int, + $.bigint, + $.decimal, + $.numeric, + $.double, + $.float, + + $.keyword_money, + $.keyword_smallmoney, + + $.char, + $.varchar, + $.nchar, + $.nvarchar, + $.numeric, + $.keyword_string, + $.keyword_text, + + $.keyword_uuid, + + $.keyword_json, + $.keyword_jsonb, + $.keyword_xml, + + $.keyword_bytea, + $.keyword_inet, + + $.enum, + + $.keyword_date, + $.keyword_datetime, + $.keyword_datetime2, + $.datetimeoffset, + $.keyword_smalldatetime, + $.time, + $.timestamp, + $.keyword_timestamptz, + $.keyword_interval, + + $.keyword_geometry, + $.keyword_geography, + $.keyword_box2d, + $.keyword_box3d, + + $.keyword_oid, + $.keyword_name, + $.keyword_regclass, + $.keyword_regnamespace, + $.keyword_regproc, + $.keyword_regtype, + + field("custom_type", $.object_reference) + ), + optional($.array_size_definition) + ), + ), + + array_size_definition: $ => prec.left( + choice( + seq($.keyword_array, optional($._array_size_definition)), + repeat1($._array_size_definition), + ), + ), + + _array_size_definition: $ => seq( + '[', + optional(field("size", alias($._integer, $.literal))), + ']' + ), + + tinyint: $ => unsigned_type($, parametric_type($, $.keyword_tinyint)), + smallint: $ => unsigned_type($, parametric_type($, $.keyword_smallint)), + mediumint: $ => unsigned_type($, parametric_type($, $.keyword_mediumint)), + int: $ => unsigned_type($, parametric_type($, $.keyword_int)), + bigint: $ => unsigned_type($, parametric_type($, $.keyword_bigint)), + + bit: $ => choice( + $.keyword_bit, + seq( + $.keyword_bit, + prec(0, parametric_type($, $.keyword_varying, ['precision'])), + ), + prec(1, parametric_type($, $.keyword_bit, ['precision'])), + ), + + binary: $ => parametric_type($, $.keyword_binary, ['precision']), + varbinary: $ => parametric_type($, $.keyword_varbinary, ['precision']), + + // TODO: should qualify against /\\b(0?[1-9]|[1-4][0-9]|5[0-4])\\b/g + float: $ => choice( + parametric_type($, $.keyword_float, ['precision']), + unsigned_type($, parametric_type($, $.keyword_float, ['precision', 'scale'])), + ), + + double: $ => choice( + make_keyword("float8"), + unsigned_type($, parametric_type($, $.keyword_double, ['precision', 'scale'])), + unsigned_type($, parametric_type($, seq($.keyword_double, $.keyword_precision), ['precision', 'scale'])), + unsigned_type($, parametric_type($, $.keyword_real, ['precision', 'scale'])), + ), + + decimal: $ => choice( + parametric_type($, $.keyword_decimal, ['precision']), + parametric_type($, $.keyword_decimal, ['precision', 'scale']), + ), + numeric: $ => choice( + parametric_type($, $.keyword_numeric, ['precision']), + parametric_type($, $.keyword_numeric, ['precision', 'scale']), + ), + char: $ => parametric_type($, $.keyword_char), + varchar: $ => parametric_type($, $.keyword_varchar), + nchar: $ => parametric_type($, $.keyword_nchar), + nvarchar: $ => parametric_type($, $.keyword_nvarchar), + + _include_time_zone: $ => seq( + choice($.keyword_with, $.keyword_without), + $.keyword_time, + $.keyword_zone, + ), + datetimeoffset: $ => parametric_type($, $.keyword_datetimeoffset), + time: $ => seq( + parametric_type($, $.keyword_time), + optional($._include_time_zone), + ), + timestamp: $ => seq( + parametric_type($, $.keyword_timestamp), + optional($._include_time_zone), + ), + timestamptz: $ => parametric_type($, $.keyword_timestamptz), + + enum: $ => seq( + $.keyword_enum, + paren_list(field("value", alias($._literal_string, $.literal)), true) + ), + + array: $ => seq( + $.keyword_array, + choice( + seq( + "[", + comma_list($._expression), + "]" + ), + seq( + "(", + $._dml_read, + ")", + ) + ) + ), + +}; + +function unsigned_type($, type) { + return choice( + seq($.keyword_unsigned, type), + seq( + type, + optional($.keyword_unsigned), + optional($.keyword_zerofill), + ), + ) +} + +function parametric_type($, type, params = ['size']) { + return prec.right(1, + choice( + type, + seq( + type, + wrapped_in_parenthesis( + seq( + // first parameter is guaranteed, shift it out of the array + field(params.shift(), alias($._natural_number, $.literal)), + // then, fill in the ", next" until done + ...params.map(p => seq(',', field(p, alias($._natural_number, $.literal)))), + ), + ), + ), + ), + ) +} diff --git a/package.json b/package.json index 5b94c0e..f0d0653 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ ], "files": [ "grammar.js", + "grammar/**", "binding.gyp", "prebuilds/**", "bindings/node/*",