Merge pull request #2957 from DeusData/distill/2246-sqlite-grammar

fix(grammar): stop flagging valid SQLite migrations as partially parsed
This commit is contained in:
Martin Vogel
2026-11-12 22:57:52 +01:00
committed by GitHub
43 changed files with 2791533 additions and 1293065 deletions
+9 -4
View File
@@ -60,8 +60,8 @@ License summary:
smart-coin language, written for this project because no usable public
grammar exists; its source and corpus tests live in
`tools/tree-sitter-chialisp/`.
- Eleven further grammars (`arkts`, `assembly`, `cfml`,
`cfscript`, `dotenv`, `javascript`, `pascal`, `pine`, `qml`, `tsx`, `vbnet`) are
- Twelve further grammars (`arkts`, `assembly`, `cfml`,
`cfscript`, `dotenv`, `javascript`, `pascal`, `pine`, `qml`, `sql`, `tsx`, `vbnet`) are
self-maintained forks that retain their original upstream authors' licenses —
see the manifest for per-grammar provenance. `arkts` is a first-party derivative of
[tree-sitter/tree-sitter-typescript](https://github.com/tree-sitter/tree-sitter-typescript)
@@ -79,8 +79,13 @@ License summary:
([Isopod/tree-sitter-pascal](https://github.com/Isopod/tree-sitter-pascal),
MIT, (c) 2018 Benjamin Gray) is the upstream grammar with one (c) 2026
DeusData patch that accepts non-ASCII letters in identifiers; its grammar
source lives in `tools/tree-sitter-pascal/`. `vbnet` is described in its own
section below.
source lives in `tools/tree-sitter-pascal/`. `sql`
([DerekStride/tree-sitter-sql](https://github.com/DerekStride/tree-sitter-sql),
MIT, (c) 2021 Derek Stride) is the upstream grammar with one (c) 2026
DeusData patch that accepts the SQLite syntax of real migration files
(PRAGMA, AUTOINCREMENT, WITHOUT ROWID, trigger bodies, virtual tables); its
grammar source lives in `tools/tree-sitter-sql/`. `vbnet` is described in its
own section below.
### tree-sitter-vbnet (with CodeGraph's real-world VB.NET patch)
File diff suppressed because one or more lines are too long
+1318106 -1293057
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -43,6 +43,9 @@ FORKS = { # self-maintained forks: vendored LICENSE must match the original ups
# same routing rule as arkts -- upstream's MIT verbatim, byte-verified.
"pascal": "Isopod/tree-sitter-pascal",
"qml": "yuja/tree-sitter-qmljs",
# sql: our SQLite-migration fork (#2246) of the pin we vendored before;
# same routing rule as arkts -- upstream's MIT verbatim, byte-verified.
"sql": "DerekStride/tree-sitter-sql",
"tsx": "tree-sitter/tree-sitter-typescript",
# vbnet: tree-sitter-vbnet + CodeGraph's real-world VB.NET patch; same
# routing rule as arkts -- `LICENSE` is upstream's MIT verbatim and is the
+2 -2
View File
@@ -5,7 +5,7 @@ c5cfb43042b6b72045f4ba997834d0a7786d2793d91680868b5815b39f14fc78 internal/cbm/v
b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea internal/cbm/vendored/common/tree_sitter/alloc.h
31e60a1bff6f715afacce03b5b70efe42b58371b4f9595dd4af52a577ff9608c internal/cbm/vendored/common/tree_sitter/array.h
180b893c8734778fd32f372dfbc27bd6ad1cd2221f26150b31256ff6716320d2 internal/cbm/vendored/common/tree_sitter/parser.h
e47220380048a65e4ae290c5368f1eaafe024c98d83ffd2fd16b98a8ce3833d5 internal/cbm/vendored/grammars/MANIFEST.md
e7478214041a2186a67160ccad3f08ddfcb8259b073172a19b9a2c947c260263 internal/cbm/vendored/grammars/MANIFEST.md
ad8425038de519f8c4e3e9339feebf99dfad8a6002dcf79227d91402780a32cc internal/cbm/vendored/grammars/ada/LICENSE
02805ec13939b749c891567be36bf024b09034e04c80683a1ae667272458d549 internal/cbm/vendored/grammars/ada/parser.c
115a75d000bef9c70c4de7dfbb7f2a80b90cbb14ba265056dc7abb1ef3b9b6db internal/cbm/vendored/grammars/ada/tree_sitter/alloc.h
@@ -754,7 +754,7 @@ c3d045abbc0a8dec1b00737c0668f6dbb028d248521b74e79778c726671925ed internal/cbm/v
02d139d44303b0b0b85a899839dbc1346c478e7318d1f2971fad2b38fd57cb73 internal/cbm/vendored/grammars/sosl/tree_sitter/array.h
2db740ea1be6b71014d2be1385491f01bf2d15b607e61c5b30b4128535efe68a internal/cbm/vendored/grammars/sosl/tree_sitter/parser.h
3b3e4e4252d5d7d1c18e1257005f23242bf0580ad619204fd093c99b8c56748d internal/cbm/vendored/grammars/sql/LICENSE
ccee90722c4d0a13b65c534dede1434107c693e405cebb9044199c66a9be0865 internal/cbm/vendored/grammars/sql/parser.c
3c64e5c415d00f7d935b7f9f0516f7be04801b241bc5da2e5e71ca31cf337887 internal/cbm/vendored/grammars/sql/parser.c
11378bf572fa0f3ff7bc4d704e49ecd3543c26b3987c7e6e5cef342afbcabe2f internal/cbm/vendored/grammars/sql/scanner.c
115a75d000bef9c70c4de7dfbb7f2a80b90cbb14ba265056dc7abb1ef3b9b6db internal/cbm/vendored/grammars/sql/tree_sitter/alloc.h
02d139d44303b0b0b85a899839dbc1346c478e7318d1f2971fad2b38fd57cb73 internal/cbm/vendored/grammars/sql/tree_sitter/array.h
+175
View File
@@ -2178,6 +2178,172 @@ TEST(vbnet_vendored_grammar_still_flags_broken_source) {
PASS();
}
/* ── SQLite migrations (#2246) ───────────────────────────────────────────────
* The vendored SQL grammar is a self-maintained fork of DerekStride/tree-sitter-sql
* (tools/tree-sitter-sql + patches/sqlite-dialect.patch). Upstream rejected
* forms that valid SQLite migrations use, so those files were reported
* parse_partial. The first two tests and their inputs are Kerf's, from #2246,
* carried over as written; the others pin the remaining forms seen in real
* SQLite migration sets (vaultwarden, atuin), plus controls that keep "fixed"
* from meaning "stopped reporting errors". */
static const char *SQLITE_VALID_MIGRATION =
"PRAGMA foreign_keys = ON;\n"
"CREATE TABLE IF NOT EXISTS registrations (\n"
" id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
" name TEXT NOT NULL\n"
");\n"
"CREATE TRIGGER IF NOT EXISTS registrations_guard\n"
"BEFORE UPDATE ON registrations\n"
"BEGIN\n"
" SELECT RAISE(ABORT, 'registrations are immutable');\n"
"END;\n"
"CREATE UNIQUE INDEX IF NOT EXISTS registrations_name ON registrations(name)\n"
" WHERE name IS NOT NULL;\n";
static const char *SQLITE_MALFORMED =
"CREATE TABLE broken ???;\n";
TEST(sqlite_valid_migration_not_flagged) {
CBMFileResult *r = do_extract(SQLITE_VALID_MIGRATION, CBM_LANG_SQL, "migration.sql");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_FALSE(r->parse_incomplete);
ASSERT_EQ(r->error_region_count, 0);
ASSERT_NULL(r->error_ranges);
cbm_free_result(r);
PASS();
}
TEST(sqlite_malformed_still_flagged) {
CBMFileResult *r = do_extract(SQLITE_MALFORMED, CBM_LANG_SQL, "broken.sql");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_TRUE(r->parse_incomplete);
ASSERT_TRUE(r->error_region_count > 0);
cbm_free_result(r);
PASS();
}
/* `key` is not reserved in SQLite, so it is a common column name. After the
* first column the grammar also accepts MySQL's `KEY name (cols)` index there,
* which took `key TEXT NOT NULL` for a malformed index (vaultwarden, atuin-kv). */
static const char *SQLITE_KEY_COLUMN = "CREATE TABLE kv (\n"
" namespace TEXT NOT NULL,\n"
" key TEXT NOT NULL,\n"
" value TEXT NOT NULL\n"
");\n"
"CREATE UNIQUE INDEX idx_kv ON kv (namespace, key);\n";
/* Control: MySQL's KEY index inside CREATE TABLE still parses as an index. */
static const char *MYSQL_KEY_INDEX = "CREATE TABLE t (\n"
" id INT NOT NULL,\n"
" KEY idx_id (id)\n"
");\n";
static const char *SQLITE_WITHOUT_ROWID = "CREATE TABLE history_uploads (\n"
" user_id TEXT NOT NULL,\n"
" history_id TEXT NOT NULL,\n"
" PRIMARY KEY (user_id, history_id)\n"
") WITHOUT ROWID;\n";
/* atuin's ai_session schema: a trigger with an unparenthesized WHEN condition
* and an INSERT OR IGNORE body, then an fts5 virtual table. */
static const char *SQLITE_TRIGGER_WHEN_AND_FTS5 =
"CREATE TABLE sessions (id INTEGER PRIMARY KEY, root_harness INTEGER);\n"
"CREATE TABLE regrouped (session INTEGER PRIMARY KEY);\n"
"CREATE TRIGGER sessions_regrouped AFTER UPDATE OF root_harness ON sessions\n"
"WHEN OLD.root_harness IS NOT NEW.root_harness\n"
"BEGIN\n"
" INSERT OR IGNORE INTO regrouped (session) VALUES (NEW.id);\n"
"END;\n"
"CREATE VIRTUAL TABLE messages_fts USING fts5(\n"
" title,\n"
" body,\n"
" tokenize = 'unicode61',\n"
" content = '',\n"
" contentless_delete = 1\n"
");\n";
static const char *SQLITE_PRAGMA_FORMS = "PRAGMA journal_mode = WAL;\n"
"PRAGMA main.cache_size = -2000;\n"
"PRAGMA table_info(registrations);\n"
"PRAGMA optimize;\n";
/* Invalid SQLite (its ALTER TABLE has no DROP INDEX), shipped in a real down
* migration (atuin-scripts): it must stay flagged. */
static const char *SQLITE_ALTER_DROP_INDEX = "alter table scripts drop index name_uniq_idx;\n";
static int sqlite_has_labeled_def(CBMFileResult *r, const char *label, const char *name) {
for (int i = 0; i < r->defs.count; i++) {
const CBMDefinition *d = &r->defs.items[i];
if (d->label && d->name && strcmp(d->label, label) == 0 && strcmp(d->name, name) == 0) {
return 1;
}
}
return 0;
}
/* 0 when the file parsed without any error region; the ASSERTs return 1. */
static int sqlite_check_clean(CBMFileResult *r) {
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_FALSE(r->parse_incomplete);
ASSERT_EQ(r->error_region_count, 0);
ASSERT_NULL(r->error_ranges);
return 0;
}
TEST(sqlite_key_column_not_flagged_issue2246) {
CBMFileResult *r = do_extract(SQLITE_KEY_COLUMN, CBM_LANG_SQL, "create_kv_db.up.sql");
ASSERT_EQ(sqlite_check_clean(r), 0);
ASSERT_TRUE(sqlite_has_labeled_def(r, "Table", "kv"));
cbm_free_result(r);
PASS();
}
TEST(mysql_key_index_in_create_table_still_clean_issue2246) {
CBMFileResult *r = do_extract(MYSQL_KEY_INDEX, CBM_LANG_SQL, "mysql_schema.sql");
ASSERT_EQ(sqlite_check_clean(r), 0);
ASSERT_TRUE(sqlite_has_labeled_def(r, "Table", "t"));
cbm_free_result(r);
PASS();
}
TEST(sqlite_without_rowid_not_flagged_issue2246) {
CBMFileResult *r = do_extract(SQLITE_WITHOUT_ROWID, CBM_LANG_SQL, "0003_queue_owners.sql");
ASSERT_EQ(sqlite_check_clean(r), 0);
ASSERT_TRUE(sqlite_has_labeled_def(r, "Table", "history_uploads"));
cbm_free_result(r);
PASS();
}
TEST(sqlite_trigger_when_body_and_fts5_not_flagged_issue2246) {
CBMFileResult *r = do_extract(SQLITE_TRIGGER_WHEN_AND_FTS5, CBM_LANG_SQL, "0001_init.sql");
ASSERT_EQ(sqlite_check_clean(r), 0);
ASSERT_TRUE(sqlite_has_labeled_def(r, "Table", "regrouped"));
/* The virtual table is a table: it is extracted like one. */
ASSERT_TRUE(sqlite_has_labeled_def(r, "Table", "messages_fts"));
cbm_free_result(r);
PASS();
}
TEST(sqlite_pragma_forms_not_flagged_issue2246) {
CBMFileResult *r = do_extract(SQLITE_PRAGMA_FORMS, CBM_LANG_SQL, "pragmas.sql");
ASSERT_EQ(sqlite_check_clean(r), 0);
cbm_free_result(r);
PASS();
}
TEST(sqlite_alter_table_drop_index_still_flagged_issue2246) {
CBMFileResult *r = do_extract(SQLITE_ALTER_DROP_INDEX, CBM_LANG_SQL, "unique_names.down.sql");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_TRUE(r->parse_incomplete);
ASSERT_TRUE(r->error_region_count > 0);
cbm_free_result(r);
PASS();
}
SUITE(parse_coverage) {
RUN_TEST(source_line_count_one_convention);
RUN_TEST(c_ifdef_split_brace_sets_parse_incomplete);
@@ -2243,4 +2409,13 @@ SUITE(parse_coverage) {
RUN_TEST(pascal_broken_source_still_flagged_issue2211);
RUN_TEST(vbnet_vendored_grammar_parses_real_vb_clean);
RUN_TEST(vbnet_vendored_grammar_still_flags_broken_source);
RUN_TEST(sqlite_valid_migration_not_flagged);
RUN_TEST(sqlite_malformed_still_flagged);
RUN_TEST(sqlite_key_column_not_flagged_issue2246);
RUN_TEST(mysql_key_index_in_create_table_still_clean_issue2246);
RUN_TEST(sqlite_without_rowid_not_flagged_issue2246);
RUN_TEST(sqlite_trigger_when_body_and_fts5_not_flagged_issue2246);
RUN_TEST(sqlite_pragma_forms_not_flagged_issue2246);
RUN_TEST(sqlite_alter_table_drop_index_still_flagged_issue2246);
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Derek Stride
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+126
View File
@@ -0,0 +1,126 @@
/*
* cbm self-maintained fork of tree-sitter-sql (SQLite migration syntax, #2246)
*
* Copyright (c) 2021 Derek Stride (tree-sitter-sql, MIT)
* Copyright (c) 2026 DeusData (the "cbm fork (#2246)" hunks in grammar/)
*
* The VENDORED parser ships upstream's MIT LICENSE byte-identical; our copyright
* for the patch lives here, with the source, and in THIRD_PARTY.md.
*
* Fork provenance (pinned; the commit this repo vendored before the fork):
* DerekStride/tree-sitter-sql @ 851e9cb257ba7c66cc8c14214a31c44d2f1e954e
* (v0.3.11; upstream's gh-pages "deploy: 3c99273f9b32" commit, whose grammar
* sources are byte-identical to main @ 3c99273f9b32a00e38a737e8a0dd37397f931916)
* grammar.js -> this file = upstream + this header
* grammar/*.js -> upstream + patches/sqlite-dialect.patch
* src/scanner.c -> upstream + the start_tag leak fix listed under "Local
* source patches" in internal/cbm/vendored/grammars/MANIFEST.md
*
* The patch (and nothing else) differs from upstream. Upstream rejected forms
* that valid SQLite migrations use, so those files became ERROR nodes and were
* flagged parse_partial: PRAGMA; AUTOINCREMENT (upstream's own c042cde1e2a6,
* after our pin); a column named `key` after the first column; WITHOUT ROWID;
* a trigger's BEGIN ... END body and its unparenthesized WHEN; INSERT OR
* <conflict>; CREATE VIRTUAL TABLE ... USING module(...), aliased to
* create_table. Every upstream node keeps its type and shape; the new nodes
* are pragma_statement and module_argument.
* On an upstream bump: re-fetch the pinned grammar.js and grammar/, re-apply the
* patch, regenerate, re-run test/corpus plus upstream's corpus -- and drop each
* hunk upstream has absorbed.
*
* Regenerate with exactly this CLI version (ABI 15, the runtime ceiling; the
* output is pinned by SHA-256 in scripts/vendored-checksums.txt):
* cd tools/tree-sitter-sql
* npx tree-sitter-cli@0.25.10 generate
* npx tree-sitter-cli@0.25.10 test
* cp src/parser.c ../../internal/cbm/vendored/grammars/sql/parser.c
*/
import keyword_rules from "./grammar/keywords.js";
import type_rules from "./grammar/types.js";
import column_list_rules from "./grammar/column-lists.js";
import expression_rules from "./grammar/expressions.js";
import transaction_rules from "./grammar/transactions.js";
import statement_rules from "./grammar/statements/index.js";
export default grammar({
name: 'sql',
extras: $ => [
/\s\n/,
/\s/,
$.comment,
$.marginalia,
],
externals: $ => [
$._dollar_quoted_string_start_tag,
$._dollar_quoted_string_end_tag,
$._dollar_quoted_string,
],
conflicts: $ => [
[$.object_reference, $._qualified_field],
[$.field, $._qualified_field],
[$._column, $._qualified_field],
[$.object_reference],
[$.between_expression, $.binary_expression],
[$.time],
[$.timestamp],
],
precedences: $ => [
[
'binary_is',
'unary_not',
'binary_exp',
'binary_times',
'binary_plus',
'unary_other',
'binary_other',
'binary_in',
'binary_compare',
'binary_relation',
'pattern_matching',
'between',
'clause_connective',
'clause_disjunctive',
],
],
word: $ => $._identifier,
rules: {
program: $ => seq(
// any number of transactions, statements, or blocks with a terminating ;
repeat(
seq(
choice(
$.transaction,
$.statement,
$.block,
),
';',
),
),
// optionally, a single statement without a terminating ;
optional(
$.statement,
),
),
comment: _ => /--.*/,
// https://stackoverflow.com/questions/13014947/regex-to-match-a-c-style-multiline-comment
marginalia: _ => /\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\//,
...keyword_rules,
...type_rules,
...column_list_rules,
...expression_rules,
...transaction_rules,
...statement_rules,
}
});
@@ -0,0 +1,206 @@
import { comma_list, optional_parenthesis, paren_list, wrapped_in_parenthesis } from "./helpers.js";
export default {
_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(
// cbm fork (#2246): SQLite does not reserve `key`, so it is a common column
// name, but after the first column it lexes as the keyword of MySQL's
// `KEY name (cols)` index. Accept that keyword as a column name too.
field('name', choice($._column, alias($.keyword_key, $.identifier))),
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,
),
// cbm fork (#2246): with `key` also a column name (column_definition), a
// type keyword after KEY now lexes as that type, so accept the types that
// take no arguments as index names too (MySQL `KEY name (name)`). A type
// that takes arguments (int, varchar, time, ...) is not in the list.
optional(field('name', choice(
$.identifier,
...[
'boolean', 'image', 'smallserial', 'serial', 'bigserial', 'money',
'smallmoney', 'string', 'text', 'uuid', 'json', 'jsonb', 'xml', 'bytea',
'inet', 'date', 'datetime', 'datetime2', 'smalldatetime', 'timestamptz',
'interval', 'geometry', 'geography', 'box2d', 'box3d', 'oid', 'name',
'regclass', 'regnamespace', 'regproc', 'regtype',
].map((type) => alias($['keyword_' + type], $.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),
),
};
@@ -0,0 +1,436 @@
import { optional_parenthesis, paren_list, wrapped_in_parenthesis } from "./helpers.js";
export default {
_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'],
[$.keyword_rlike, 'pattern_matching'],
[$.not_rlike, '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),
),
),
),
),
),
};
+39
View File
@@ -0,0 +1,39 @@
export function make_keyword(word) {
let str = "";
for (var i = 0; i < word.length; i++) {
str = str + "[" + word.charAt(i).toLowerCase() + word.charAt(i).toUpperCase() + "]";
}
return new RegExp(str);
}
export function optional_parenthesis(node) {
return prec.right(
choice(
node,
wrapped_in_parenthesis(node),
),
)
}
export function wrapped_in_parenthesis(node) {
if (node) {
return seq("(", node, ")");
}
return seq("(", ")");
}
export function comma_list(field, requireFirst) {
let sequence = seq(field, repeat(seq(',', field)));
if (requireFirst) {
return sequence;
}
return optional(sequence);
}
export function paren_list(field, requireFirst) {
return wrapped_in_parenthesis(
comma_list(field, requireFirst),
)
}
+436
View File
@@ -0,0 +1,436 @@
import { make_keyword } from "./helpers.js";
export default {
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_policy: _ => make_keyword("policy"),
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_enable: _ => make_keyword("enable"),
keyword_disable: _ => make_keyword("disable"),
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"),
// cbm fork (#2246): SQLite AUTOINCREMENT. Upstream c042cde1e2a6 (2026-11-08,
// after our pin) made exactly this change; drop the hunk on a refresh past it.
keyword_auto_increment: _ => choice(make_keyword("auto_increment"),make_keyword("autoincrement")),
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_refresh: _ => make_keyword("refresh"),
keyword_rename: _ => make_keyword("rename"),
keyword_to: _ => make_keyword("to"),
keyword_database: _ => make_keyword("database"),
keyword_schema: _ => make_keyword("schema"),
keyword_public: _ => make_keyword("public"),
keyword_owner: _ => make_keyword("owner"),
keyword_user: _ => make_keyword("user"),
keyword_current_user: _ => make_keyword("current_user"),
keyword_session_user: _ => make_keyword("session_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_current_role: _ => make_keyword("current_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_permissive: _ => make_keyword("permissive"),
keyword_restrictive: _ => make_keyword("restrictive"),
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_rlike: _ => choice(make_keyword("rlike"),make_keyword("regexp")),
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"),
// cbm fork (#2246): SQLite PRAGMA, WITHOUT ROWID and INSERT OR <conflict>
keyword_pragma: _ => make_keyword("pragma"),
keyword_rowid: _ => make_keyword("rowid"),
keyword_abort: _ => make_keyword("abort"),
keyword_fail: _ => make_keyword("fail"),
// 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),
not_rlike: $ => seq($.keyword_not, $.keyword_rlike),
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
}
@@ -0,0 +1,504 @@
import { paren_list } from "../helpers.js";
export default {
_alter_statement: $ => seq(
choice(
$.alter_table,
$.alter_view,
$.alter_materialized_view,
$.alter_schema,
$.alter_type,
$.alter_index,
$.alter_database,
$.alter_role,
$.alter_sequence,
$.alter_policy,
),
),
alter_table: $ => seq(
$.keyword_alter,
$.keyword_table,
optional($._if_exists),
optional($.keyword_only),
$.object_reference,
choice(
seq(
$._alter_specifications,
repeat(
seq(
",",
$._alter_specifications
)
)
),
seq(
choice(
$.keyword_enable,
$.keyword_disable,
seq(optional($.keyword_no), $.keyword_force),
),
$.keyword_row,
$.keyword_level,
$.keyword_security,
),
),
),
_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_materialized_view: $ => seq(
$.keyword_alter,
$.keyword_materialized,
$.keyword_view,
optional($._if_exists),
$.object_reference,
choice(
$.rename_object,
$.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)
),
),
),
),
// Postgres row level security
alter_policy: $ => prec.right(
seq(
$.keyword_alter,
$.keyword_policy,
$.object_reference,
$.keyword_on,
$.object_reference,
choice(
$.rename_object,
seq(
optional(
seq(
$.keyword_to,
choice(
$.object_reference,
$.keyword_public,
$.keyword_current_role,
$.keyword_current_user,
$.keyword_session_user,
),
repeat(
seq(
',',
choice(
$.object_reference,
$.keyword_public,
$.keyword_current_role,
$.keyword_current_user,
$.keyword_session_user,
),
),
),
),
),
optional(
seq(
$.keyword_using,
$.parenthesized_expression,
),
),
optional(
seq(
$.keyword_with,
$.keyword_check,
$.parenthesized_expression,
),
),
),
),
),
),
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,
),
};
@@ -0,0 +1,54 @@
export default {
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
seq($.keyword_procedure, $.object_reference, optional($.function_arguments)),
// 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),
),
};
@@ -0,0 +1,67 @@
import { wrapped_in_parenthesis } from "../helpers.js";
export default {
_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),
),
};
@@ -0,0 +1,246 @@
import { paren_list, wrapped_in_parenthesis } from "../helpers.js";
export default {
// 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),
),
};
@@ -0,0 +1,103 @@
export default {
create_procedure: $ => seq(
$.keyword_create,
optional($._or_replace),
// mariadb/mysql
optional(seq($.keyword_definer, '=', $.identifier)),
$.keyword_procedure,
optional($._if_not_exists),
$.object_reference,
optional($.function_arguments),
repeat(
choice(
$.function_language,
$.function_security,
$.function_safety,
),
),
$.procedure_body,
repeat(
choice(
$.function_language,
$.function_security,
$.function_safety,
),
),
),
procedure_body: $ => choice(
// BEGIN ATOMIC block (SQL standard)
seq(
$.keyword_begin,
$.keyword_atomic,
repeat1(
seq(
$._function_body_statement,
';',
),
),
$.keyword_end,
),
// Dollar-quoted with optional DECLARE (PostgreSQL)
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),
),
// String literal body
seq(
$.keyword_as,
alias(
choice(
$._single_quote_string,
$._double_quote_string,
),
$.literal
),
),
// Dollar-quoted single statement (PostgreSQL)
seq(
$.keyword_as,
alias($._dollar_quoted_string_start_tag, $.dollar_quote),
$._function_body_statement,
optional(';'),
alias($._dollar_quoted_string_end_tag, $.dollar_quote),
),
// T-SQL style (no required RETURN)
$._tsql_procedure_body_statement,
),
_tsql_procedure_body_statement: $ => seq(
optional($.keyword_as),
$.keyword_begin,
optional($.var_declarations),
choice(
repeat($.statement),
repeat1(seq(
$.keyword_begin,
repeat($.statement),
$.keyword_end,
)),
),
$.keyword_end,
),
};
@@ -0,0 +1,675 @@
import { comma_list, paren_list, wrapped_in_parenthesis } from "../helpers.js";
import create_function_rules from "./create-function.js";
import create_procedure_rules from "./create-procedure.js";
export default {
_create_statement: $ => seq(
choice(
$.create_table,
alias($._create_virtual_table, $.create_table), // cbm fork (#2246)
$.create_view,
$.create_materialized_view,
$.create_index,
$.create_function,
$.create_procedure,
$.create_type,
$.create_database,
$.create_role,
$.create_sequence,
$.create_extension,
$.create_trigger,
$.create_policy,
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),
seq($.keyword_without, $.keyword_rowid), // cbm fork (#2246): SQLite
$.storage_parameters,
$.table_option,
),
// cbm fork (#2246): SQLite CREATE VIRTUAL TABLE [IF NOT EXISTS] name
// USING module[(arguments)], e.g. an fts5 full-text index. It is a table,
// so it is aliased to create_table and extracted like any other.
_create_virtual_table: $ => prec.right(seq(
$.keyword_create,
$.keyword_virtual,
$.keyword_table,
optional($._if_not_exists),
$.object_reference,
$.keyword_using,
field('module', $.identifier),
optional(paren_list($.module_argument, true)),
)),
// a column (fts5 `body`, `body UNINDEXED`, rtree `minX`) or an option
// (fts5 `tokenize = 'unicode61'`, `content = ''`, `contentless_delete = 1`)
module_argument: $ => choice(
seq(field('name', $.identifier), repeat($.identifier)),
seq(field('name', $.identifier), '=', field('value', choice($.literal, $.identifier))),
),
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,
),
),
...create_function_rules,
...create_procedure_rules,
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)),
),
),
choice(
seq(
$.keyword_execute,
choice($.keyword_function, $.keyword_procedure),
$.object_reference,
paren_list(field('parameter', $.term)),
),
// cbm fork (#2246): SQLite runs a BEGIN ... END body, after a WHEN
// condition SQLite does not parenthesize
seq(
optional(seq($.keyword_when, $._expression)),
$.block,
),
),
),
_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))),
),
// Postgres row level security
create_policy: $ => prec.right(
seq(
$.keyword_create,
$.keyword_policy,
$.object_reference,
$.keyword_on,
$.object_reference,
optional(
seq(
$.keyword_as,
choice(
$.keyword_permissive,
$.keyword_restrictive,
),
),
),
optional(
seq(
$.keyword_for,
choice(
$.keyword_all,
$.keyword_select,
$.keyword_insert,
$.keyword_update,
$.keyword_delete,
),
),
),
optional(
seq(
$.keyword_to,
choice(
$.object_reference,
$.keyword_public,
$.keyword_current_role,
$.keyword_current_user,
$.keyword_session_user,
),
repeat(
seq(
',',
choice(
$.object_reference,
$.keyword_public,
$.keyword_current_role,
$.keyword_current_user,
$.keyword_session_user,
),
),
),
),
),
optional(
seq(
$.keyword_using,
$.parenthesized_expression,
),
),
optional(
seq(
$.keyword_with,
$.keyword_check,
$.parenthesized_expression,
),
),
),
),
};
@@ -0,0 +1,25 @@
export default {
_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),
),
};
@@ -0,0 +1,130 @@
import { comma_list } from "../helpers.js";
export default {
_drop_statement: $ => seq(
choice(
$.drop_table,
$.drop_view,
$.drop_materialized_view,
$.drop_index,
$.drop_type,
$.drop_schema,
$.drop_database,
$.drop_role,
$.drop_sequence,
$.drop_extension,
$.drop_function,
$.drop_procedure,
),
),
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_materialized_view: $ => seq(
$.keyword_drop,
$.keyword_materialized,
$.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),
),
drop_procedure: $ => seq(
$.keyword_drop,
$.keyword_procedure,
optional($._if_exists),
$.object_reference,
optional($._drop_behavior),
),
};
@@ -0,0 +1,189 @@
import { optional_parenthesis, wrapped_in_parenthesis } from "../helpers.js";
import create_rules from "./create.js";
import alter_rules from "./alter.js";
import drop_rules from "./drop.js";
import rename_rules from "./rename.js";
import optimize_rules from "./optimize.js";
import merge_rules from "./merge.js";
import comment_rules from "./comment.js";
import delete_rules from "./delete.js";
import insert_rules from "./insert.js";
import update_rules from "./update.js";
import truncate_rules from "./truncate.js";
import copy_rules from "./copy.js";
import select_rules from "./select.js";
import set_rules from "./set.js";
import refresh_rules from "./refresh.js";
export default {
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,
$._refresh_statement,
$.comment_statement,
$.set_statement,
$.reset_statement,
$.pragma_statement, // cbm fork (#2246)
),
// cbm fork (#2246): SQLite PRAGMA [schema.]name [= value | (value)]
pragma_statement: $ => prec.right(seq(
$.keyword_pragma,
$.object_reference,
optional(
choice(
seq('=', field('value', $._pragma_value)),
wrapped_in_parenthesis(field('value', $._pragma_value)),
),
),
)),
_pragma_value: $ => choice($.literal, $.identifier),
...create_rules,
...alter_rules,
...drop_rules,
...rename_rules,
...optimize_rules,
...merge_rules,
...refresh_rules,
...comment_rules,
_dml_write: $ => seq(
seq(
optional(
$._cte,
),
choice(
$._delete_statement,
$._insert_statement,
$._update_statement,
$._truncate_statement,
$._copy_statement,
),
),
),
...delete_rules,
...insert_rules,
...update_rules,
...truncate_rules,
...copy_rules,
_dml_read: $ => seq(
optional(optional_parenthesis($._cte)),
optional_parenthesis(
choice(
$._select_statement,
$.set_operation,
$._show_statement,
$._unload_statement,
),
),
),
...select_rules,
...set_rules,
_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,
),
};
@@ -0,0 +1,119 @@
import { comma_list } from "../helpers.js";
export default {
_insert_statement: $ => seq(
$.insert,
optional($.returning),
),
insert: $ => seq(
choice(
$.keyword_insert,
$.keyword_replace
),
// cbm fork (#2246): SQLite INSERT OR {ABORT | FAIL | IGNORE | REPLACE | ROLLBACK}
optional(
seq(
$.keyword_or,
choice(
$.keyword_abort,
$.keyword_fail,
$.keyword_ignore,
$.keyword_replace,
$.keyword_rollback,
),
),
),
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),
),
}
@@ -0,0 +1,46 @@
import { optional_parenthesis } from "../helpers.js";
export default {
_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)
)
)),
};
@@ -0,0 +1,109 @@
import { paren_list } from "../helpers.js";
export default {
_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),
),
};
@@ -0,0 +1,20 @@
export default {
_refresh_statement: ($) => $.refresh_materialized_view,
refresh_materialized_view: ($) =>
prec.right(
seq(
$.keyword_refresh,
$.keyword_materialized,
$.keyword_view,
optional($.keyword_concurrently),
$.object_reference,
optional(
choice(
seq($.keyword_with, $.keyword_data),
seq($.keyword_with, $.keyword_no, $.keyword_data),
),
),
),
),
};
@@ -0,0 +1,36 @@
export default {
_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,
),
};
@@ -0,0 +1,441 @@
import { comma_list, optional_parenthesis, paren_list, wrapped_in_parenthesis } from "../helpers.js";
export default {
_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,
),
};
@@ -0,0 +1,63 @@
import { comma_list } from "../helpers.js";
export default {
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,
),
),
};
@@ -0,0 +1,13 @@
import { comma_list } from "../helpers.js";
export default {
_truncate_statement: $ => seq(
$.keyword_truncate,
optional($.keyword_table),
optional($.keyword_only),
comma_list($.object_reference),
optional($._drop_behavior),
),
};
@@ -0,0 +1,36 @@
import { comma_list } from "../helpers.js";
export default {
_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),
),
),
};
@@ -0,0 +1,35 @@
export default {
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,
),
),
};
+202
View File
@@ -0,0 +1,202 @@
import { make_keyword, comma_list, paren_list, wrapped_in_parenthesis } from "./helpers.js";
export default {
_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)))),
),
),
),
),
)
}
+10
View File
@@ -0,0 +1,10 @@
{
"name": "tree-sitter-sql-cbm",
"version": "0.3.11-cbm.1",
"description": "cbm fork of tree-sitter-sql @ 851e9cb (SQLite migration syntax, #2246)",
"license": "MIT",
"type": "module",
"devDependencies": {
"tree-sitter-cli": "0.25.10"
}
}
@@ -0,0 +1,187 @@
diff --git a/grammar/column-lists.js b/grammar/column-lists.js
index 55d257d70e83c364d875494961944439118fd8b5..2b99c49354039a628fce8b92a7b00119eed80207 100644
--- a/grammar/column-lists.js
+++ b/grammar/column-lists.js
@@ -23,7 +23,10 @@ export default {
),
column_definition: $ => prec.left(seq(
- field('name', $._column),
+ // cbm fork (#2246): SQLite does not reserve `key`, so it is a common column
+ // name, but after the first column it lexes as the keyword of MySQL's
+ // `KEY name (cols)` index. Accept that keyword as a column name too.
+ field('name', choice($._column, alias($.keyword_key, $.identifier))),
field('type', $._type),
repeat($._column_constraint),
)),
@@ -153,7 +156,20 @@ export default {
seq(optional($.keyword_foreign), $.keyword_key, optional($._if_not_exists)),
$.keyword_index,
),
- optional(field('name', $.identifier)),
+ // cbm fork (#2246): with `key` also a column name (column_definition), a
+ // type keyword after KEY now lexes as that type, so accept the types that
+ // take no arguments as index names too (MySQL `KEY name (name)`). A type
+ // that takes arguments (int, varchar, time, ...) is not in the list.
+ optional(field('name', choice(
+ $.identifier,
+ ...[
+ 'boolean', 'image', 'smallserial', 'serial', 'bigserial', 'money',
+ 'smallmoney', 'string', 'text', 'uuid', 'json', 'jsonb', 'xml', 'bytea',
+ 'inet', 'date', 'datetime', 'datetime2', 'smalldatetime', 'timestamptz',
+ 'interval', 'geometry', 'geography', 'box2d', 'box3d', 'oid', 'name',
+ 'regclass', 'regnamespace', 'regproc', 'regtype',
+ ].map((type) => alias($['keyword_' + type], $.identifier)),
+ ))),
$.ordered_columns,
optional(
seq(
diff --git a/grammar/keywords.js b/grammar/keywords.js
index 6be9e8940972d69a3c5804cce56666e59be23fd2..a88aef4e2b85037c56f9eb4d2f27b57846b7fdb4 100644
--- a/grammar/keywords.js
+++ b/grammar/keywords.js
@@ -96,7 +96,9 @@ export default {
keyword_for: _ => make_keyword("for"),
keyword_if: _ => make_keyword("if"),
keyword_exists: _ => make_keyword("exists"),
- keyword_auto_increment: _ => make_keyword("auto_increment"),
+ // cbm fork (#2246): SQLite AUTOINCREMENT. Upstream c042cde1e2a6 (2026-11-08,
+ // after our pin) made exactly this change; drop the hunk on a refresh past it.
+ keyword_auto_increment: _ => choice(make_keyword("auto_increment"),make_keyword("autoincrement")),
keyword_generated: _ => make_keyword("generated"),
keyword_always: _ => make_keyword("always"),
keyword_collate: _ => make_keyword("collate"),
@@ -282,6 +284,11 @@ export default {
keyword_execute: _ => make_keyword("execute"),
keyword_procedure: _ => make_keyword("procedure"),
keyword_object_id: _ => make_keyword("object_id"),
+ // cbm fork (#2246): SQLite PRAGMA, WITHOUT ROWID and INSERT OR <conflict>
+ keyword_pragma: _ => make_keyword("pragma"),
+ keyword_rowid: _ => make_keyword("rowid"),
+ keyword_abort: _ => make_keyword("abort"),
+ keyword_fail: _ => make_keyword("fail"),
// Hive Keywords
keyword_external: _ => make_keyword("external"),
diff --git a/grammar/statements/create.js b/grammar/statements/create.js
index 41f54a244e283c38b7fec7b1f1d6e5e8f595e8a8..388c2e8838fc95e1b34877a3eff47ceb2d0d84ee 100644
--- a/grammar/statements/create.js
+++ b/grammar/statements/create.js
@@ -8,6 +8,7 @@ export default {
_create_statement: $ => seq(
choice(
$.create_table,
+ alias($._create_virtual_table, $.create_table), // cbm fork (#2246)
$.create_view,
$.create_materialized_view,
$.create_index,
@@ -77,10 +78,32 @@ export default {
paren_list($.table_option, true),
),
seq($.keyword_without, $.keyword_oids),
+ seq($.keyword_without, $.keyword_rowid), // cbm fork (#2246): SQLite
$.storage_parameters,
$.table_option,
),
+ // cbm fork (#2246): SQLite CREATE VIRTUAL TABLE [IF NOT EXISTS] name
+ // USING module[(arguments)], e.g. an fts5 full-text index. It is a table,
+ // so it is aliased to create_table and extracted like any other.
+ _create_virtual_table: $ => prec.right(seq(
+ $.keyword_create,
+ $.keyword_virtual,
+ $.keyword_table,
+ optional($._if_not_exists),
+ $.object_reference,
+ $.keyword_using,
+ field('module', $.identifier),
+ optional(paren_list($.module_argument, true)),
+ )),
+
+ // a column (fts5 `body`, `body UNINDEXED`, rtree `minX`) or an option
+ // (fts5 `tokenize = 'unicode61'`, `content = ''`, `contentless_delete = 1`)
+ module_argument: $ => choice(
+ seq(field('name', $.identifier), repeat($.identifier)),
+ seq(field('name', $.identifier), '=', field('value', choice($.literal, $.identifier))),
+ ),
+
stored_as: $ => seq(
$.keyword_stored,
$.keyword_as,
@@ -512,10 +535,20 @@ export default {
seq($.keyword_when, wrapped_in_parenthesis($._expression)),
),
),
- $.keyword_execute,
- choice($.keyword_function, $.keyword_procedure),
- $.object_reference,
- paren_list(field('parameter', $.term)),
+ choice(
+ seq(
+ $.keyword_execute,
+ choice($.keyword_function, $.keyword_procedure),
+ $.object_reference,
+ paren_list(field('parameter', $.term)),
+ ),
+ // cbm fork (#2246): SQLite runs a BEGIN ... END body, after a WHEN
+ // condition SQLite does not parenthesize
+ seq(
+ optional(seq($.keyword_when, $._expression)),
+ $.block,
+ ),
+ ),
),
_create_trigger_event: $ => choice(
diff --git a/grammar/statements/index.js b/grammar/statements/index.js
index 94f0ec628add92d8a258f3441dbc85e551290dec..8f15fbd249086005255a4aee01acbf6f007f4ba4 100644
--- a/grammar/statements/index.js
+++ b/grammar/statements/index.js
@@ -84,8 +84,23 @@ export default {
$.comment_statement,
$.set_statement,
$.reset_statement,
+ $.pragma_statement, // cbm fork (#2246)
),
+ // cbm fork (#2246): SQLite PRAGMA [schema.]name [= value | (value)]
+ pragma_statement: $ => prec.right(seq(
+ $.keyword_pragma,
+ $.object_reference,
+ optional(
+ choice(
+ seq('=', field('value', $._pragma_value)),
+ wrapped_in_parenthesis(field('value', $._pragma_value)),
+ ),
+ ),
+ )),
+
+ _pragma_value: $ => choice($.literal, $.identifier),
+
...create_rules,
...alter_rules,
...drop_rules,
diff --git a/grammar/statements/insert.js b/grammar/statements/insert.js
index f241d126b4a5bfcccaad193ca956ccbefa2ae1a6..d78c104c4aae51a467b63ad6aaeb82d95051b827 100644
--- a/grammar/statements/insert.js
+++ b/grammar/statements/insert.js
@@ -12,6 +12,19 @@ export default {
$.keyword_insert,
$.keyword_replace
),
+ // cbm fork (#2246): SQLite INSERT OR {ABORT | FAIL | IGNORE | REPLACE | ROLLBACK}
+ optional(
+ seq(
+ $.keyword_or,
+ choice(
+ $.keyword_abort,
+ $.keyword_fail,
+ $.keyword_ignore,
+ $.keyword_replace,
+ $.keyword_rollback,
+ ),
+ ),
+ ),
optional(
choice(
$.keyword_low_priority,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+189
View File
@@ -0,0 +1,189 @@
#include "tree_sitter/parser.h"
#include <stdlib.h>
#include <string.h>
#include <wctype.h>
enum TokenType {
DOLLAR_QUOTED_STRING_START_TAG,
DOLLAR_QUOTED_STRING_END_TAG,
DOLLAR_QUOTED_STRING
};
#define MALLOC_STRING_SIZE 1024
typedef struct LexerState {
char* start_tag;
} LexerState;
void *tree_sitter_sql_external_scanner_create() {
LexerState *state = malloc(sizeof(LexerState));
state->start_tag = NULL;
return state;
}
void tree_sitter_sql_external_scanner_destroy(void *payload) {
LexerState *state = (LexerState*)payload;
if (state->start_tag != NULL) {
free(state->start_tag);
state->start_tag = NULL;
}
free(payload);
}
static char* add_char(char* text, size_t* text_size, char c, int index) {
if (text == NULL) {
text = malloc(sizeof(char) * MALLOC_STRING_SIZE);
*text_size = MALLOC_STRING_SIZE;
}
// will break when indexes advances more than MALLOC_STRING_SIZE
if (index + 1 >= *text_size) {
*text_size += MALLOC_STRING_SIZE;
char* tmp = malloc(*text_size * sizeof(char));
strncpy(tmp, text, *text_size);
free(text);
text = tmp;
}
text[index] = c;
text[index + 1] = '\0';
return text;
}
static char* scan_dollar_string_tag(TSLexer *lexer) {
char* tag = NULL;
int index = 0;
size_t* text_size = malloc(sizeof(size_t));
*text_size = 0;
if (lexer->lookahead == '$') {
tag = add_char(tag, text_size, '$', index);
lexer->advance(lexer, false);
} else {
free(text_size);
return NULL;
}
while (lexer->lookahead != '$' && !iswspace(lexer->lookahead) && !lexer->eof(lexer)) {
tag = add_char(tag, text_size, lexer->lookahead, ++index);
lexer->advance(lexer, false);
}
if (lexer->lookahead == '$') {
tag = add_char(tag, text_size, lexer->lookahead, ++index);
lexer->advance(lexer, false);
free(text_size);
return tag;
} else {
free(tag);
free(text_size);
return NULL;
}
}
bool tree_sitter_sql_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) {
LexerState *state = (LexerState*)payload;
if (valid_symbols[DOLLAR_QUOTED_STRING_START_TAG] && state->start_tag == NULL) {
while (iswspace(lexer->lookahead)) lexer->advance(lexer, true);
char* start_tag = scan_dollar_string_tag(lexer);
if (start_tag == NULL) {
return false;
}
if (state->start_tag != NULL) {
free(state->start_tag);
state->start_tag = NULL;
}
state->start_tag = start_tag;
lexer->result_symbol = DOLLAR_QUOTED_STRING_START_TAG;
return true;
}
if (valid_symbols[DOLLAR_QUOTED_STRING_END_TAG] && state->start_tag != NULL) {
while (iswspace(lexer->lookahead)) lexer->advance(lexer, true);
char* end_tag = scan_dollar_string_tag(lexer);
if (end_tag != NULL && strcmp(end_tag, state->start_tag) == 0) {
free(state->start_tag);
state->start_tag = NULL;
lexer->result_symbol = DOLLAR_QUOTED_STRING_END_TAG;
free(end_tag);
return true;
}
if (end_tag != NULL) {
free(end_tag);
}
return false;
}
if (valid_symbols[DOLLAR_QUOTED_STRING]) {
lexer->mark_end(lexer);
while (iswspace(lexer->lookahead)) lexer->advance(lexer, true);
char* start_tag = scan_dollar_string_tag(lexer);
if (start_tag == NULL) {
return false;
}
if (state->start_tag != NULL && strcmp(state->start_tag, start_tag) == 0) {
return false;
}
char* end_tag = NULL;
while (true) {
if (lexer->eof(lexer)) {
free(start_tag);
free(end_tag);
return false;
}
end_tag = scan_dollar_string_tag(lexer);
if (end_tag == NULL) {
lexer->advance(lexer, false);
continue;
}
if (strcmp(end_tag, start_tag) == 0) {
free(start_tag);
free(end_tag);
lexer->mark_end(lexer);
lexer->result_symbol = DOLLAR_QUOTED_STRING;
return true;
}
free(end_tag);
end_tag = NULL;
}
}
return false;
}
unsigned tree_sitter_sql_external_scanner_serialize(void *payload, char *buffer) {
LexerState *state = (LexerState *)payload;
if (state == NULL || state->start_tag == NULL) {
return 0;
}
// + 1 for the '\0'
int tag_length = strlen(state->start_tag) + 1;
if (tag_length >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) {
return 0;
}
memcpy(buffer, state->start_tag, tag_length);
if (state->start_tag != NULL) {
free(state->start_tag);
state->start_tag = NULL;
}
return tag_length;
}
void tree_sitter_sql_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) {
LexerState *state = (LexerState *)payload;
free(state->start_tag); /* deserialize is called repeatedly; free the prior tag first */
state->start_tag = NULL;
// A length of 1 can't exists.
if (length > 1) {
state->start_tag = malloc(length);
memcpy(state->start_tag, buffer, length);
}
}
@@ -0,0 +1,54 @@
#ifndef TREE_SITTER_ALLOC_H_
#define TREE_SITTER_ALLOC_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
// Allow clients to override allocation functions
#ifdef TREE_SITTER_REUSE_ALLOCATOR
extern void *(*ts_current_malloc)(size_t size);
extern void *(*ts_current_calloc)(size_t count, size_t size);
extern void *(*ts_current_realloc)(void *ptr, size_t size);
extern void (*ts_current_free)(void *ptr);
#ifndef ts_malloc
#define ts_malloc ts_current_malloc
#endif
#ifndef ts_calloc
#define ts_calloc ts_current_calloc
#endif
#ifndef ts_realloc
#define ts_realloc ts_current_realloc
#endif
#ifndef ts_free
#define ts_free ts_current_free
#endif
#else
#ifndef ts_malloc
#define ts_malloc malloc
#endif
#ifndef ts_calloc
#define ts_calloc calloc
#endif
#ifndef ts_realloc
#define ts_realloc realloc
#endif
#ifndef ts_free
#define ts_free free
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_ALLOC_H_
@@ -0,0 +1,291 @@
#ifndef TREE_SITTER_ARRAY_H_
#define TREE_SITTER_ARRAY_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "./alloc.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4101)
#elif defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
#define Array(T) \
struct { \
T *contents; \
uint32_t size; \
uint32_t capacity; \
}
/// Initialize an array.
#define array_init(self) \
((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL)
/// Create an empty array.
#define array_new() \
{ NULL, 0, 0 }
/// Get a pointer to the element at a given `index` in the array.
#define array_get(self, _index) \
(assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index])
/// Get a pointer to the first element in the array.
#define array_front(self) array_get(self, 0)
/// Get a pointer to the last element in the array.
#define array_back(self) array_get(self, (self)->size - 1)
/// Clear the array, setting its size to zero. Note that this does not free any
/// memory allocated for the array's contents.
#define array_clear(self) ((self)->size = 0)
/// Reserve `new_capacity` elements of space in the array. If `new_capacity` is
/// less than the array's current capacity, this function has no effect.
#define array_reserve(self, new_capacity) \
_array__reserve((Array *)(self), array_elem_size(self), new_capacity)
/// Free any memory allocated for this array. Note that this does not free any
/// memory allocated for the array's contents.
#define array_delete(self) _array__delete((Array *)(self))
/// Push a new `element` onto the end of the array.
#define array_push(self, element) \
(_array__grow((Array *)(self), 1, array_elem_size(self)), \
(self)->contents[(self)->size++] = (element))
/// Increase the array's size by `count` elements.
/// New elements are zero-initialized.
#define array_grow_by(self, count) \
do { \
if ((count) == 0) break; \
_array__grow((Array *)(self), count, array_elem_size(self)); \
memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \
(self)->size += (count); \
} while (0)
/// Append all elements from one array to the end of another.
#define array_push_all(self, other) \
array_extend((self), (other)->size, (other)->contents)
/// Append `count` elements to the end of the array, reading their values from the
/// `contents` pointer.
#define array_extend(self, count, contents) \
_array__splice( \
(Array *)(self), array_elem_size(self), (self)->size, \
0, count, contents \
)
/// Remove `old_count` elements from the array starting at the given `index`. At
/// the same index, insert `new_count` new elements, reading their values from the
/// `new_contents` pointer.
#define array_splice(self, _index, old_count, new_count, new_contents) \
_array__splice( \
(Array *)(self), array_elem_size(self), _index, \
old_count, new_count, new_contents \
)
/// Insert one `element` into the array at the given `index`.
#define array_insert(self, _index, element) \
_array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element))
/// Remove one element from the array at the given `index`.
#define array_erase(self, _index) \
_array__erase((Array *)(self), array_elem_size(self), _index)
/// Pop the last element off the array, returning the element by value.
#define array_pop(self) ((self)->contents[--(self)->size])
/// Assign the contents of one array to another, reallocating if necessary.
#define array_assign(self, other) \
_array__assign((Array *)(self), (const Array *)(other), array_elem_size(self))
/// Swap one array with another
#define array_swap(self, other) \
_array__swap((Array *)(self), (Array *)(other))
/// Get the size of the array contents
#define array_elem_size(self) (sizeof *(self)->contents)
/// Search a sorted array for a given `needle` value, using the given `compare`
/// callback to determine the order.
///
/// If an existing element is found to be equal to `needle`, then the `index`
/// out-parameter is set to the existing value's index, and the `exists`
/// out-parameter is set to true. Otherwise, `index` is set to an index where
/// `needle` should be inserted in order to preserve the sorting, and `exists`
/// is set to false.
#define array_search_sorted_with(self, compare, needle, _index, _exists) \
_array__search_sorted(self, 0, compare, , needle, _index, _exists)
/// Search a sorted array for a given `needle` value, using integer comparisons
/// of a given struct field (specified with a leading dot) to determine the order.
///
/// See also `array_search_sorted_with`.
#define array_search_sorted_by(self, field, needle, _index, _exists) \
_array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists)
/// Insert a given `value` into a sorted array, using the given `compare`
/// callback to determine the order.
#define array_insert_sorted_with(self, compare, value) \
do { \
unsigned _index, _exists; \
array_search_sorted_with(self, compare, &(value), &_index, &_exists); \
if (!_exists) array_insert(self, _index, value); \
} while (0)
/// Insert a given `value` into a sorted array, using integer comparisons of
/// a given struct field (specified with a leading dot) to determine the order.
///
/// See also `array_search_sorted_by`.
#define array_insert_sorted_by(self, field, value) \
do { \
unsigned _index, _exists; \
array_search_sorted_by(self, field, (value) field, &_index, &_exists); \
if (!_exists) array_insert(self, _index, value); \
} while (0)
// Private
typedef Array(void) Array;
/// This is not what you're looking for, see `array_delete`.
static inline void _array__delete(Array *self) {
if (self->contents) {
ts_free(self->contents);
self->contents = NULL;
self->size = 0;
self->capacity = 0;
}
}
/// This is not what you're looking for, see `array_erase`.
static inline void _array__erase(Array *self, size_t element_size,
uint32_t index) {
assert(index < self->size);
char *contents = (char *)self->contents;
memmove(contents + index * element_size, contents + (index + 1) * element_size,
(self->size - index - 1) * element_size);
self->size--;
}
/// This is not what you're looking for, see `array_reserve`.
static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) {
if (new_capacity > self->capacity) {
if (self->contents) {
self->contents = ts_realloc(self->contents, new_capacity * element_size);
} else {
self->contents = ts_malloc(new_capacity * element_size);
}
self->capacity = new_capacity;
}
}
/// This is not what you're looking for, see `array_assign`.
static inline void _array__assign(Array *self, const Array *other, size_t element_size) {
_array__reserve(self, element_size, other->size);
self->size = other->size;
memcpy(self->contents, other->contents, self->size * element_size);
}
/// This is not what you're looking for, see `array_swap`.
static inline void _array__swap(Array *self, Array *other) {
Array swap = *other;
*other = *self;
*self = swap;
}
/// This is not what you're looking for, see `array_push` or `array_grow_by`.
static inline void _array__grow(Array *self, uint32_t count, size_t element_size) {
uint32_t new_size = self->size + count;
if (new_size > self->capacity) {
uint32_t new_capacity = self->capacity * 2;
if (new_capacity < 8) new_capacity = 8;
if (new_capacity < new_size) new_capacity = new_size;
_array__reserve(self, element_size, new_capacity);
}
}
/// This is not what you're looking for, see `array_splice`.
static inline void _array__splice(Array *self, size_t element_size,
uint32_t index, uint32_t old_count,
uint32_t new_count, const void *elements) {
uint32_t new_size = self->size + new_count - old_count;
uint32_t old_end = index + old_count;
uint32_t new_end = index + new_count;
assert(old_end <= self->size);
_array__reserve(self, element_size, new_size);
char *contents = (char *)self->contents;
if (self->size > old_end) {
memmove(
contents + new_end * element_size,
contents + old_end * element_size,
(self->size - old_end) * element_size
);
}
if (new_count > 0) {
if (elements) {
memcpy(
(contents + index * element_size),
elements,
new_count * element_size
);
} else {
memset(
(contents + index * element_size),
0,
new_count * element_size
);
}
}
self->size += new_count - old_count;
}
/// A binary search routine, based on Rust's `std::slice::binary_search_by`.
/// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`.
#define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \
do { \
*(_index) = start; \
*(_exists) = false; \
uint32_t size = (self)->size - *(_index); \
if (size == 0) break; \
int comparison; \
while (size > 1) { \
uint32_t half_size = size / 2; \
uint32_t mid_index = *(_index) + half_size; \
comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \
if (comparison <= 0) *(_index) = mid_index; \
size -= half_size; \
} \
comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \
if (comparison == 0) *(_exists) = true; \
else if (comparison < 0) *(_index) += 1; \
} while (0)
/// Helper macro for the `_sorted_by` routines below. This takes the left (existing)
/// parameter by reference in order to work with the generic sorting function above.
#define _compare_int(a, b) ((int)*(a) - (int)(b))
#ifdef _MSC_VER
#pragma warning(pop)
#elif defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_ARRAY_H_
@@ -0,0 +1,286 @@
#ifndef TREE_SITTER_PARSER_H_
#define TREE_SITTER_PARSER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define ts_builtin_sym_error ((TSSymbol)-1)
#define ts_builtin_sym_end 0
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
#ifndef TREE_SITTER_API_H_
typedef uint16_t TSStateId;
typedef uint16_t TSSymbol;
typedef uint16_t TSFieldId;
typedef struct TSLanguage TSLanguage;
typedef struct TSLanguageMetadata {
uint8_t major_version;
uint8_t minor_version;
uint8_t patch_version;
} TSLanguageMetadata;
#endif
typedef struct {
TSFieldId field_id;
uint8_t child_index;
bool inherited;
} TSFieldMapEntry;
// Used to index the field and supertype maps.
typedef struct {
uint16_t index;
uint16_t length;
} TSMapSlice;
typedef struct {
bool visible;
bool named;
bool supertype;
} TSSymbolMetadata;
typedef struct TSLexer TSLexer;
struct TSLexer {
int32_t lookahead;
TSSymbol result_symbol;
void (*advance)(TSLexer *, bool);
void (*mark_end)(TSLexer *);
uint32_t (*get_column)(TSLexer *);
bool (*is_at_included_range_start)(const TSLexer *);
bool (*eof)(const TSLexer *);
void (*log)(const TSLexer *, const char *, ...);
};
typedef enum {
TSParseActionTypeShift,
TSParseActionTypeReduce,
TSParseActionTypeAccept,
TSParseActionTypeRecover,
} TSParseActionType;
typedef union {
struct {
uint8_t type;
TSStateId state;
bool extra;
bool repetition;
} shift;
struct {
uint8_t type;
uint8_t child_count;
TSSymbol symbol;
int16_t dynamic_precedence;
uint16_t production_id;
} reduce;
uint8_t type;
} TSParseAction;
typedef struct {
uint16_t lex_state;
uint16_t external_lex_state;
} TSLexMode;
typedef struct {
uint16_t lex_state;
uint16_t external_lex_state;
uint16_t reserved_word_set_id;
} TSLexerMode;
typedef union {
TSParseAction action;
struct {
uint8_t count;
bool reusable;
} entry;
} TSParseActionEntry;
typedef struct {
int32_t start;
int32_t end;
} TSCharacterRange;
struct TSLanguage {
uint32_t abi_version;
uint32_t symbol_count;
uint32_t alias_count;
uint32_t token_count;
uint32_t external_token_count;
uint32_t state_count;
uint32_t large_state_count;
uint32_t production_id_count;
uint32_t field_count;
uint16_t max_alias_sequence_length;
const uint16_t *parse_table;
const uint16_t *small_parse_table;
const uint32_t *small_parse_table_map;
const TSParseActionEntry *parse_actions;
const char * const *symbol_names;
const char * const *field_names;
const TSMapSlice *field_map_slices;
const TSFieldMapEntry *field_map_entries;
const TSSymbolMetadata *symbol_metadata;
const TSSymbol *public_symbol_map;
const uint16_t *alias_map;
const TSSymbol *alias_sequences;
const TSLexerMode *lex_modes;
bool (*lex_fn)(TSLexer *, TSStateId);
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
TSSymbol keyword_capture_token;
struct {
const bool *states;
const TSSymbol *symbol_map;
void *(*create)(void);
void (*destroy)(void *);
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
unsigned (*serialize)(void *, char *);
void (*deserialize)(void *, const char *, unsigned);
} external_scanner;
const TSStateId *primary_state_ids;
const char *name;
const TSSymbol *reserved_words;
uint16_t max_reserved_word_set_size;
uint32_t supertype_count;
const TSSymbol *supertype_symbols;
const TSMapSlice *supertype_map_slices;
const TSSymbol *supertype_map_entries;
TSLanguageMetadata metadata;
};
static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) {
uint32_t index = 0;
uint32_t size = len - index;
while (size > 1) {
uint32_t half_size = size / 2;
uint32_t mid_index = index + half_size;
const TSCharacterRange *range = &ranges[mid_index];
if (lookahead >= range->start && lookahead <= range->end) {
return true;
} else if (lookahead > range->end) {
index = mid_index;
}
size -= half_size;
}
const TSCharacterRange *range = &ranges[index];
return (lookahead >= range->start && lookahead <= range->end);
}
/*
* Lexer Macros
*/
#ifdef _MSC_VER
#define UNUSED __pragma(warning(suppress : 4101))
#else
#define UNUSED __attribute__((unused))
#endif
#define START_LEXER() \
bool result = false; \
bool skip = false; \
UNUSED \
bool eof = false; \
int32_t lookahead; \
goto start; \
next_state: \
lexer->advance(lexer, skip); \
start: \
skip = false; \
lookahead = lexer->lookahead;
#define ADVANCE(state_value) \
{ \
state = state_value; \
goto next_state; \
}
#define ADVANCE_MAP(...) \
{ \
static const uint16_t map[] = { __VA_ARGS__ }; \
for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \
if (map[i] == lookahead) { \
state = map[i + 1]; \
goto next_state; \
} \
} \
}
#define SKIP(state_value) \
{ \
skip = true; \
state = state_value; \
goto next_state; \
}
#define ACCEPT_TOKEN(symbol_value) \
result = true; \
lexer->result_symbol = symbol_value; \
lexer->mark_end(lexer);
#define END_STATE() return result;
/*
* Parse Table Macros
*/
#define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT)
#define STATE(id) id
#define ACTIONS(id) id
#define SHIFT(state_value) \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = (state_value) \
} \
}}
#define SHIFT_REPEAT(state_value) \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = (state_value), \
.repetition = true \
} \
}}
#define SHIFT_EXTRA() \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.extra = true \
} \
}}
#define REDUCE(symbol_name, children, precedence, prod_id) \
{{ \
.reduce = { \
.type = TSParseActionTypeReduce, \
.symbol = symbol_name, \
.child_count = children, \
.dynamic_precedence = precedence, \
.production_id = prod_id \
}, \
}}
#define RECOVER() \
{{ \
.type = TSParseActionTypeRecover \
}}
#define ACCEPT_INPUT() \
{{ \
.type = TSParseActionTypeAccept \
}}
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_PARSER_H_
@@ -0,0 +1,507 @@
================================================================================
SQLite: PRAGMA statements
================================================================================
PRAGMA foreign_keys = ON;
PRAGMA journal_mode = WAL;
PRAGMA main.cache_size = -2000;
PRAGMA table_info(registrations);
PRAGMA optimize;
--------------------------------------------------------------------------------
(program
(statement
(pragma_statement
(keyword_pragma)
(object_reference
(identifier))
(identifier)))
(statement
(pragma_statement
(keyword_pragma)
(object_reference
(identifier))
(identifier)))
(statement
(pragma_statement
(keyword_pragma)
(object_reference
(identifier)
(identifier))
(literal)))
(statement
(pragma_statement
(keyword_pragma)
(object_reference
(identifier))
(identifier)))
(statement
(pragma_statement
(keyword_pragma)
(object_reference
(identifier)))))
================================================================================
SQLite: AUTOINCREMENT column
================================================================================
CREATE TABLE IF NOT EXISTS registrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
--------------------------------------------------------------------------------
(program
(statement
(create_table
(keyword_create)
(keyword_table)
(keyword_if)
(keyword_not)
(keyword_exists)
(object_reference
(identifier))
(column_definitions
(column_definition
(identifier)
(int
(keyword_int))
(keyword_primary)
(keyword_key)
(keyword_auto_increment))
(column_definition
(identifier)
(keyword_text)
(keyword_not)
(keyword_null))))))
================================================================================
SQLite: a column named key after the first column
================================================================================
CREATE TABLE kv (
namespace TEXT NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL
);
--------------------------------------------------------------------------------
(program
(statement
(create_table
(keyword_create)
(keyword_table)
(object_reference
(identifier))
(column_definitions
(column_definition
(identifier)
(keyword_text)
(keyword_not)
(keyword_null))
(column_definition
(identifier)
(keyword_text)
(keyword_not)
(keyword_null))
(column_definition
(identifier)
(keyword_text)
(keyword_not)
(keyword_null))))))
================================================================================
MySQL: KEY index named after a type keyword is still an index
================================================================================
CREATE TABLE t (
id INT NOT NULL,
KEY name (id),
UNIQUE KEY date (id)
);
--------------------------------------------------------------------------------
(program
(statement
(create_table
(keyword_create)
(keyword_table)
(object_reference
(identifier))
(column_definitions
(column_definition
(identifier)
(int
(keyword_int))
(keyword_not)
(keyword_null))
(constraints
(constraint
(keyword_key)
(identifier)
(ordered_columns
(column
(identifier))))
(constraint
(keyword_unique)
(keyword_key)
(identifier)
(ordered_columns
(column
(identifier)))))))))
================================================================================
SQLite: WITHOUT ROWID table
================================================================================
CREATE TABLE history_uploads (
user_id TEXT NOT NULL,
history_id TEXT NOT NULL,
PRIMARY KEY (user_id, history_id)
) WITHOUT ROWID;
--------------------------------------------------------------------------------
(program
(statement
(create_table
(keyword_create)
(keyword_table)
(object_reference
(identifier))
(column_definitions
(column_definition
(identifier)
(keyword_text)
(keyword_not)
(keyword_null))
(column_definition
(identifier)
(keyword_text)
(keyword_not)
(keyword_null))
(constraints
(constraint
(keyword_primary)
(keyword_key)
(ordered_columns
(column
(identifier))
(column
(identifier))))))
(keyword_without)
(keyword_rowid))))
================================================================================
SQLite: trigger with a BEGIN END body
================================================================================
CREATE TRIGGER IF NOT EXISTS registrations_guard
BEFORE UPDATE ON registrations
BEGIN
SELECT RAISE(ABORT, 'registrations are immutable');
END;
--------------------------------------------------------------------------------
(program
(statement
(create_trigger
(keyword_create)
(keyword_trigger)
(keyword_if)
(keyword_not)
(keyword_exists)
(object_reference
(identifier))
(keyword_before)
(keyword_update)
(keyword_on)
(object_reference
(identifier))
(block
(keyword_begin)
(statement
(select
(keyword_select)
(select_expression
(term
(invocation
(object_reference
(identifier))
(term
(field
(identifier)))
(term
(literal)))))))
(keyword_end)))))
================================================================================
SQLite: trigger with a WHEN condition and INSERT OR IGNORE
================================================================================
CREATE TRIGGER sessions_regrouped AFTER UPDATE OF root_harness ON sessions
WHEN OLD.root_harness IS NOT NEW.root_harness
BEGIN
INSERT OR IGNORE INTO regrouped (session) VALUES (NEW.id);
END;
--------------------------------------------------------------------------------
(program
(statement
(create_trigger
(keyword_create)
(keyword_trigger)
(object_reference
(identifier))
(keyword_after)
(keyword_update)
(keyword_of)
(identifier)
(keyword_on)
(object_reference
(identifier))
(keyword_when)
(binary_expression
(field
(object_reference
(identifier))
(identifier))
(is_not
(keyword_is)
(keyword_not))
(field
(object_reference
(identifier))
(identifier)))
(block
(keyword_begin)
(statement
(insert
(keyword_insert)
(keyword_or)
(keyword_ignore)
(keyword_into)
(object_reference
(identifier))
(list
(column
(identifier)))
(keyword_values)
(list
(field
(object_reference
(identifier))
(identifier)))))
(keyword_end)))))
================================================================================
SQLite: trigger with a parenthesized WHEN condition
================================================================================
CREATE TRIGGER tg AFTER INSERT ON x FOR EACH ROW WHEN (NEW.a > 1)
BEGIN
UPDATE y SET b = 1;
DELETE FROM z WHERE id = NEW.id;
END;
--------------------------------------------------------------------------------
(program
(statement
(create_trigger
(keyword_create)
(keyword_trigger)
(object_reference
(identifier))
(keyword_after)
(keyword_insert)
(keyword_on)
(object_reference
(identifier))
(keyword_for)
(keyword_each)
(keyword_row)
(keyword_when)
(parenthesized_expression
(binary_expression
(field
(object_reference
(identifier))
(identifier))
(literal)))
(block
(keyword_begin)
(statement
(update
(keyword_update)
(relation
(object_reference
(identifier)))
(keyword_set)
(assignment
(field
(identifier))
(literal))))
(statement
(delete
(keyword_delete))
(from
(keyword_from)
(object_reference
(identifier))
(where
(keyword_where)
(binary_expression
(field
(identifier))
(field
(object_reference
(identifier))
(identifier))))))
(keyword_end)))))
================================================================================
Postgres: trigger WHEN condition before EXECUTE FUNCTION is unchanged
================================================================================
CREATE TRIGGER tg BEFORE UPDATE ON x FOR EACH ROW
WHEN (OLD.a IS DISTINCT FROM NEW.a) EXECUTE FUNCTION f();
--------------------------------------------------------------------------------
(program
(statement
(create_trigger
(keyword_create)
(keyword_trigger)
(object_reference
(identifier))
(keyword_before)
(keyword_update)
(keyword_on)
(object_reference
(identifier))
(keyword_for)
(keyword_each)
(keyword_row)
(keyword_when)
(binary_expression
(field
(object_reference
(identifier))
(identifier))
(distinct_from
(keyword_is)
(keyword_distinct)
(keyword_from))
(field
(object_reference
(identifier))
(identifier)))
(keyword_execute)
(keyword_function)
(object_reference
(identifier)))))
================================================================================
SQLite: virtual tables
================================================================================
CREATE VIRTUAL TABLE messages_fts USING fts5(
title,
body UNINDEXED,
tokenize = 'unicode61',
content = '',
contentless_delete = 1
);
CREATE VIRTUAL TABLE IF NOT EXISTS boxes USING rtree(id, minx, maxx);
--------------------------------------------------------------------------------
(program
(statement
(create_table
(keyword_create)
(keyword_virtual)
(keyword_table)
(object_reference
(identifier))
(keyword_using)
(identifier)
(module_argument
(identifier))
(module_argument
(identifier)
(identifier))
(module_argument
(identifier)
(literal))
(module_argument
(identifier)
(literal))
(module_argument
(identifier)
(literal))))
(statement
(create_table
(keyword_create)
(keyword_virtual)
(keyword_table)
(keyword_if)
(keyword_not)
(keyword_exists)
(object_reference
(identifier))
(keyword_using)
(identifier)
(module_argument
(identifier))
(module_argument
(identifier))
(module_argument
(identifier)))))
================================================================================
SQLite: INSERT OR conflict clause
================================================================================
INSERT OR REPLACE INTO t (a) VALUES (1);
INSERT OR ROLLBACK INTO t (a) VALUES (2);
--------------------------------------------------------------------------------
(program
(statement
(insert
(keyword_insert)
(keyword_or)
(keyword_replace)
(keyword_into)
(object_reference
(identifier))
(list
(column
(identifier)))
(keyword_values)
(list
(literal))))
(statement
(insert
(keyword_insert)
(keyword_or)
(keyword_rollback)
(keyword_into)
(object_reference
(identifier))
(list
(column
(identifier)))
(keyword_values)
(list
(literal)))))
+16
View File
@@ -0,0 +1,16 @@
{
"grammars": [
{
"name": "sql",
"camelcase": "SQL",
"scope": "source.sql",
"path": ".",
"file-types": ["sql"]
}
],
"metadata": {
"version": "0.3.11-cbm.1",
"license": "MIT",
"description": "cbm fork of tree-sitter-sql @ 851e9cb (SQLite migration syntax)"
}
}