Appendix 2: CQL Grammar

Appendix 2: CQL Grammar

What follows is taken from a grammar snapshot with the tree building rules removed. It should give a fair sense of the syntax of CQL (but not semantic validation).

Operators and Literals

These are in order of priority lowest to highest

"UNION ALL" "UNION" "INTERSECT" "EXCEPT"
":=" "+=" "-=" "*=" "/=" "%=" "|=" "&=" "<<=" ">>="
"OR"
"AND"
"NOT"
"BETWEEN" "NOT BETWEEN" "<>" "!=" '=' "==" "LIKE" "NOT LIKE" "GLOB" "NOT GLOB" "MATCH" "NOT MATCH" "REGEXP" "NOT REGEXP" "IN" "NOT IN" "IS NOT" "IS" "IS TRUE" "IS FALSE" "IS NOT TRUE" "IS NOT FALSE"
"ISNULL" "NOTNULL"
'<' '>' ">=" "<="
"<<" ">>" '&' '|'
'+' '-'
'*' '/' '%'
"||" "->" "->>" ':' '.' '[' '~'
"COLLATE"
"UMINUS"
"@INCLUDE quoted-filename" "end of included file"

NOTE: The above varies considerably from the C binding order!!!

Literals:

ID        /* a name */
STRLIT    /* a string literal in SQL format e.g. 'it''s sql' */
CSTRLIT   /* a string literal in C format e.g. "hello, world\n" */
BLOBLIT   /* a blob literal in SQL format e.g. x'12ab' */
INTLIT    /* integer literal */
LONGLIT   /* long integer literal */
REALLIT   /* floating point literal */

Statement/Type Keywords

"@ATTRIBUTE" "@BEGIN_SCHEMA_REGION" "@BLOB_CREATE_KEY"
"@BLOB_CREATE_VAL" "@BLOB_GET_KEY" "@BLOB_GET_KEY_TYPE"
"@BLOB_GET_VAL" "@BLOB_GET_VAL_TYPE" "@BLOB_UPDATE_KEY"
"@BLOB_UPDATE_VAL" "@COLUMNS" "@CREATE"
"@DECLARE_DEPLOYABLE_REGION" "@DECLARE_SCHEMA_REGION"
"@DELETE" "@DUMMY_SEED" "@ECHO" "@EMIT_CONSTANTS"
"@EMIT_ENUMS" "@EMIT_GROUP" "@END_SCHEMA_REGION"
"@ENFORCE_NORMAL" "@ENFORCE_POP" "@ENFORCE_PUSH"
"@ENFORCE_RESET" "@ENFORCE_STRICT" "@EPONYMOUS" "@FILE"
"@ID" "@KEEP_TABLE_NAME_IN_ALIASES" "@LINE" "@MACRO"
"@MACRO_FILE" "@MACRO_LINE" "@OP" "@PREVIOUS_SCHEMA"
"@PROC" "@RC" "@RECREATE" "@SCHEMA_AD_HOC_MIGRATION"
"@SCHEMA_UPGRADE_SCRIPT" "@SCHEMA_UPGRADE_VERSION"
"@SENSITIVE" "@TEXT" "@UNSUB" "ABORT" "ACTION" "ADD"
"AFTER" "ALL" "ALTER" "ARGUMENTS" "AS" "ASC"
"AUTOINCREMENT" "BEFORE" "BEGIN" "BLOB" "BY" "CALL"
"CASCADE" "CASE" "CAST" "CATCH" "CHECK" "CLOSE" "COLUMN"
"COMMIT" "CONST" "CONSTRAINT" "CONTEXT COLUMN" "CONTEXT
TYPE" "CONTINUE" "CREATE" "CROSS" "CTE_TABLES" "CURRENT
ROW" "CURSOR HAS ROW" "CURSOR" "DECLARE" "DEFAULT"
"DEFERRABLE" "DEFERRED" "DELETE" "DESC" "DISTINCT"
"DISTINCTROW" "DO" "DROP" "ELSE IF" "ELSE" "ENCODE" "END"
"ENUM" "EXCLUDE CURRENT ROW" "EXCLUDE GROUP" "EXCLUDE NO
OTHERS" "EXCLUDE TIES" "EXCLUSIVE" "EXISTS" "EXPLAIN"
"EXPR" "FAIL" "FETCH" "FILTER" "FIRST" "FOLLOWING" "FOR
EACH ROW" "FOR" "FOREIGN" "FROM BLOB" "FROM" "FUNC"
"FUNCTION" "GROUP" "GROUPS" "HAVING" "HIDDEN" "IF" "IGNORE"
"IMMEDIATE" "INDEX" "INITIALLY" "INNER" "INOUT" "INSERT"
"INSTEAD" "INT" "INTEGER" "INTERFACE" "INTO" "JOIN" "KEY"
"LAST" "LEAVE" "LEFT" "LET" "LIMIT" "LONG" "LONG_INT"
"LONG_INTEGER" "LOOP" "NO" "NOT DEFERRABLE" "NOTHING"
"NULL" "NULLS" "OBJECT" "OF" "OFFSET" "ON CONFLICT" "ON"
"OPEN" "ORDER" "OUT" "OUTER" "OVER" "PARTITION" "PRECEDING"
"PRIMARY" "PRIVATE" "PROC" "PROCEDURE" "QUERY PLAN"
"QUERY_PARTS" "RAISE" "RANGE" "REAL" "RECURSIVE"
"REFERENCES" "RELEASE" "RENAME" "REPLACE" "RESTRICT"
"RETURN" "RIGHT" "ROLLBACK" "ROWID" "ROWS" "SAVEPOINT"
"SELECT" "SELECT_CORE" "SELECT_EXPR" "SET" "SIGN FUNCTION"
"STATEMENT" "STMT_LIST" "SWITCH" "TABLE" "TEMP" "TEXT"
"THEN" "THROW" "TO" "TRANSACTION" "TRIGGER" "TRY" "TYPE"
"TYPE_CHECK" "UNBOUNDED" "UNIQUE" "UPDATE" "UPSERT" "USING"
"VALUES" "VAR" "VIEW" "VIRTUAL" "WHEN" "WHERE" "WHILE"
"WINDOW" "WITH" "WITHOUT"

Rules

Note that in many cases the grammar is more generous than the overall language and errors have to be checked on top of this, often this is done on purpose because even when it’s possible it might be very inconvenient to do checks with syntax. For example the grammar cannot enforce non-duplicate ids in id lists, but it could enforce non-duplicate attributes in attribute lists. It chooses to do neither as they are easily done with semantic validation. Thus the grammar is not the final authority on what constitutes a valid program but it’s a good start.



program: top_level_stmts
  ;

  top_level_stmts:
    /* nil */
    | include_stmts
    | stmt_list
    | include_stmts stmt_list
   ;

include_section: "@INCLUDE quoted-filename" top_level_stmts "end of included file"
   ;

include_stmts:
    include_section
    | include_section include_stmts
    ;

opt_stmt_list:
  /* nil */
  | stmt_list
  ;

non_expr_macro_ref:
  stmt_list_macro_ref
  | cte_tables_macro_ref
  | select_core_macro_ref
  | select_expr_macro_ref
  | query_parts_macro_ref
  ;

stmt_list_macro_ref:
  "ID!"
  | "ID!" '(' opt_macro_args ')'
  ;

expr_macro_ref:
  "ID!"
  | "ID!" '(' opt_macro_args ')'
  | basic_expr ':' "ID!" '(' opt_macro_args ')'
  | basic_expr ':' "ID!"
  ;

query_parts_macro_ref:
  "ID!"
  | "ID!" '(' opt_macro_args ')'
  ;

cte_tables_macro_ref:
  "ID!"
  | "ID!" '(' opt_macro_args ')'
  ;

select_core_macro_ref:
  "ID!"
  | "ID!" '(' opt_macro_args ')'
  ;

select_expr_macro_ref:
  "ID!"
  | "ID!" '(' opt_macro_args ')'
  ;

stmt_list:
  stmt
  | stmt_list_macro_ref ';'
  | stmt_list stmt
  | stmt_list stmt_list_macro_ref ';'
  ;

stmt:
  misc_attrs any_stmt ';'
  ;

expr_stmt: expr
  ;

any_stmt:
    alter_table_add_column_stmt
  | expr_stmt
  | begin_schema_region_stmt
  | begin_trans_stmt
  | blob_get_key_type_stmt
  | blob_get_val_type_stmt
  | blob_get_key_stmt
  | blob_get_val_stmt
  | blob_create_key_stmt
  | blob_create_val_stmt
  | blob_update_key_stmt
  | blob_update_val_stmt
  | call_stmt
  | close_stmt
  | commit_return_stmt
  | commit_trans_stmt
  | const_stmt
  | continue_stmt
  | create_index_stmt
  | create_proc_stmt
  | create_table_stmt
  | create_trigger_stmt
  | create_view_stmt
  | create_virtual_table_stmt
  | declare_deployable_region_stmt
  | declare_enum_stmt
  | declare_const_stmt
  | declare_group_stmt
  | declare_func_stmt
  | declare_select_func_stmt
  | declare_out_call_stmt
  | declare_proc_no_check_stmt
  | declare_proc_stmt
  | declare_interface_stmt
  | declare_schema_region_stmt
  | declare_vars_stmt
  | declare_forward_read_cursor_stmt
  | declare_fetched_value_cursor_stmt
  | declare_type_stmt
  | delete_stmt
  | drop_index_stmt
  | drop_table_stmt
  | drop_trigger_stmt
  | drop_view_stmt
  | echo_stmt
  | emit_enums_stmt
  | emit_group_stmt
  | emit_constants_stmt
  | end_schema_region_stmt
  | enforce_normal_stmt
  | enforce_pop_stmt
  | enforce_push_stmt
  | enforce_reset_stmt
  | enforce_strict_stmt
  | explain_stmt
  | select_nothing_stmt
  | fetch_call_stmt
  | fetch_stmt
  | fetch_values_stmt
  | fetch_cursor_from_blob_stmt
  | guard_stmt
  | if_stmt
  | insert_stmt
  | leave_stmt
  | let_stmt
  | loop_stmt
  | macro_def_stmt
  | op_stmt
  | out_stmt
  | out_union_stmt
  | out_union_parent_child_stmt
  | previous_schema_stmt
  | proc_savepoint_stmt
  | release_savepoint_stmt
  | return_stmt
  | rollback_return_stmt
  | rollback_trans_stmt
  | savepoint_stmt
  | select_stmt
  | schema_ad_hoc_migration_stmt
  | schema_unsub_stmt
  | schema_upgrade_script_stmt
  | schema_upgrade_version_stmt
  | set_stmt
  | switch_stmt
  | throw_stmt
  | trycatch_stmt
  | update_cursor_stmt
  | update_stmt
  | upsert_stmt
  | while_stmt
  | with_delete_stmt
  | with_insert_stmt
  | with_update_stmt
  | with_upsert_stmt
  | keep_table_name_in_aliases_stmt
  ;

explain_stmt:
  "EXPLAIN" opt_query_plan explain_target
  ;

opt_query_plan:
  /* nil */
  | "QUERY PLAN"
  ;

explain_target: select_stmt
  | update_stmt
  | delete_stmt
  | with_delete_stmt
  | with_insert_stmt
  | with_update_stmt
  | with_upsert_stmt
  | insert_stmt
  | upsert_stmt
  | drop_table_stmt
  | drop_view_stmt
  | drop_index_stmt
  | drop_trigger_stmt
  | begin_trans_stmt
  | commit_trans_stmt
  ;

previous_schema_stmt:
  "@PREVIOUS_SCHEMA"
  ;

schema_upgrade_script_stmt:
  "@SCHEMA_UPGRADE_SCRIPT"
  ;

schema_upgrade_version_stmt:
  "@SCHEMA_UPGRADE_VERSION" '(' "integer-literal" ')'
  ;

set_stmt:
  "SET" sql_name ":=" expr
  | "SET" sql_name "FROM" "CURSOR" name
  | "SET" sql_name '[' arg_list ']' ":=" expr
  ;

let_stmt:
  "LET" sql_name ":=" expr
  ;

const_stmt:
  "CONST" sql_name ":=" expr
  ;

version_attrs_opt_recreate:
  /* nil */
  | "@RECREATE"  opt_delete_plain_attr
  | "@RECREATE" '(' name ')'  opt_delete_plain_attr
  | version_attrs
  ;

opt_delete_plain_attr:
  /* nil */
  | "@DELETE"
  ;

opt_version_attrs:
  /* nil */
  | version_attrs
  ;

version_attrs:
  "@CREATE" version_annotation opt_version_attrs
  | "@DELETE" version_annotation opt_version_attrs
  ;

opt_delete_version_attr:
  /* nil */
  | "@DELETE" version_annotation
  ;

drop_table_stmt:
  "DROP" "TABLE" "IF" "EXISTS" sql_name
  | "DROP" "TABLE" sql_name
  ;

drop_view_stmt:
  "DROP" "VIEW" "IF" "EXISTS" sql_name
  | "DROP" "VIEW" sql_name
  ;

drop_index_stmt:
  "DROP" "INDEX" "IF" "EXISTS" sql_name
  | "DROP" "INDEX" sql_name
  ;

drop_trigger_stmt:
  "DROP" "TRIGGER" "IF" "EXISTS" sql_name
  | "DROP" "TRIGGER" sql_name
  ;

create_virtual_table_stmt: "CREATE" "VIRTUAL" "TABLE" opt_vtab_flags sql_name
                           "USING" name opt_module_args
                           "AS" '(' col_key_list ')' opt_delete_version_attr ;

opt_module_args: /* nil */
  | '(' misc_attr_value_list ')'
  | '(' "ARGUMENTS" "FOLLOWING" ')'
  ;

create_table_prefix_opt_temp:
  "CREATE" opt_temp "TABLE" ;

create_table_stmt:
  create_table_prefix_opt_temp opt_if_not_exists sql_name '(' col_key_list ')' opt_no_rowid version_attrs_opt_recreate
  ;

opt_temp:
  /* nil */
  | "TEMP"
  ;

opt_if_not_exists:
  /* nil */
  | "IF" "NOT" "EXISTS"
  ;

opt_no_rowid:
  /* nil */
  | "WITHOUT" "ROWID"
  ;

opt_vtab_flags:
  /* nil */
  | "IF" "NOT" "EXISTS"
  | "@EPONYMOUS"
  | "@EPONYMOUS" "IF" "NOT" "EXISTS"
  | "IF" "NOT" "EXISTS" "@EPONYMOUS"
  ;

col_key_list:
  col_key_def
  | col_key_def ',' col_key_list
  ;

col_key_def:
  col_def
  | pk_def
  | fk_def
  | unq_def
  | check_def
  | shape_def
  ;

check_def:
  "CONSTRAINT" name "CHECK" '(' expr ')'
  | "CHECK" '(' expr ')'
  ;

shape_exprs:
  shape_expr ',' shape_exprs
  | shape_expr
  ;

shape_expr:
  sql_name
  | '-' sql_name
  ;

shape_def:
    shape_def_base
  | shape_def_base '(' shape_exprs ')'
  ;

shape_def_base:
    "LIKE" sql_name
  | "LIKE" name "ARGUMENTS"
  ;

sql_name:
  name
  | "`quoted identifier`"
  ;

misc_attr_key:
  name
  | name ':' name
  ;

cql_attr_key:
  name
  | name ':' name
  ;

misc_attr_value_list:
  misc_attr_value
  | misc_attr_value ',' misc_attr_value_list
  ;

misc_attr_value:
  sql_name
  | any_literal
  | const_expr
  | '(' misc_attr_value_list ')'
  | '-' num_literal
  | '+' num_literal
  ;

misc_attr:
  "@ATTRIBUTE" '(' misc_attr_key ')'
  | "@ATTRIBUTE" '(' misc_attr_key '=' misc_attr_value ')'
  | '[' '[' cql_attr_key ']' ']'
  | '[' '[' cql_attr_key  '=' misc_attr_value ']' ']'
  ;

misc_attrs:
  /* nil */
  | misc_attr misc_attrs
  ;

col_def:
  misc_attrs sql_name data_type_any col_attrs
  ;

pk_def:
  "CONSTRAINT" sql_name "PRIMARY" "KEY" '(' indexed_columns ')' opt_conflict_clause
  | "PRIMARY" "KEY" '(' indexed_columns ')' opt_conflict_clause
  ;

opt_conflict_clause:
  /* nil */
  | conflict_clause
  ;

conflict_clause:
  "ON CONFLICT" "ROLLBACK"
  | "ON CONFLICT" "ABORT"
  | "ON CONFLICT" "FAIL"
  | "ON CONFLICT" "IGNORE"
  | "ON CONFLICT" "REPLACE"
  ;

opt_fk_options:
  /* nil */
  | fk_options
  ;

fk_options:
  fk_on_options
  | fk_deferred_options
  | fk_on_options fk_deferred_options
  ;

fk_on_options:
  "ON" "DELETE" fk_action
  | "ON" "UPDATE" fk_action
  | "ON" "UPDATE" fk_action "ON" "DELETE" fk_action
  | "ON" "DELETE" fk_action "ON" "UPDATE" fk_action
  ;

fk_action:
  "SET" "NULL"
  | "SET" "DEFAULT"
  | "CASCADE"
  | "RESTRICT"
  | "NO" "ACTION"
  ;

fk_deferred_options:
  "DEFERRABLE" fk_initial_state
  | "NOT DEFERRABLE" fk_initial_state
  ;

fk_initial_state:
  /* nil */
  | "INITIALLY" "DEFERRED"
  | "INITIALLY" "IMMEDIATE"
  ;

fk_def:
  "CONSTRAINT" sql_name "FOREIGN" "KEY" '(' sql_name_list ')' fk_target_options
  | "FOREIGN" "KEY" '(' sql_name_list ')' fk_target_options
  ;

fk_target_options:
  "REFERENCES" sql_name '(' sql_name_list ')' opt_fk_options
  ;

unq_def:
  "CONSTRAINT" sql_name "UNIQUE" '(' indexed_columns ')' opt_conflict_clause
  | "UNIQUE" '(' indexed_columns ')' opt_conflict_clause
  ;

opt_unique:
  /* nil */
  | "UNIQUE"
  ;

indexed_column:
  expr opt_asc_desc
  ;

indexed_columns:
  indexed_column
  | indexed_column ',' indexed_columns
  ;

create_index_stmt:
  "CREATE" opt_unique "INDEX" opt_if_not_exists sql_name "ON" sql_name '(' indexed_columns ')' opt_where opt_delete_version_attr
  ;

name:
  "ID"
  | "TEXT"
  | "TRIGGER"
  | "ROWID"
  | "REPLACE"
  | "KEY"
  | "VIRTUAL"
  | "TYPE"
  | "HIDDEN"
  | "PRIVATE"
  | "FIRST"
  | "LAST"
  | "ADD"
  | "AFTER"
  | "BEFORE"
  | "VIEW"
  | "INDEX"
  | "COLUMN"
  | "EXPR"
  | "STMT_LIST"
  | "QUERY_PARTS"
  | "CTE_TABLES"
  | "SELECT_CORE"
  | "SELECT_EXPR"
  | "@ID" '(' text_args ')'
  ;

loose_name:
  name
  | "CALL"
  | "SET"
  | "BOOL"
  | "INT"
  | "LONG"
  | "REAL"
  | "BLOB"
  | "OBJECT"
  | "RIGHT"
  | "LEFT"
  ;

loose_name_or_type:
  loose_name
  | "ALL"
  | loose_name '<' loose_name '>'
  ;

opt_sql_name:
  /* nil */
  | sql_name
  ;

name_list:
  name
  |  name ',' name_list
  ;

sql_name_list:
  sql_name
  |  sql_name ',' sql_name_list
  ;

opt_name_list:
  /* nil */
  | name_list
  ;

opt_sql_name_list:
  /* nil */
  | sql_name_list
  ;

cte_binding_list:
  cte_binding
  | cte_binding ',' cte_binding_list
  ;

cte_binding: name name
  | name "AS" name
  ;

col_attrs:
  /* nil */
  | not_null opt_conflict_clause col_attrs
  | "PRIMARY" "KEY" opt_conflict_clause col_attrs
  | "PRIMARY" "KEY" opt_conflict_clause "AUTOINCREMENT" col_attrs
  | "DEFAULT" '-' num_literal col_attrs
  | "DEFAULT" '+' num_literal col_attrs
  | "DEFAULT" num_literal col_attrs
  | "DEFAULT" const_expr col_attrs
  | "DEFAULT" str_literal col_attrs
  | "COLLATE" name col_attrs
  | "CHECK" '(' expr ')' col_attrs
  | "UNIQUE" opt_conflict_clause col_attrs
  | "HIDDEN" col_attrs
  | "@SENSITIVE" col_attrs
  | "@CREATE" version_annotation col_attrs
  | "@DELETE" version_annotation col_attrs
  | fk_target_options col_attrs
  ;

version_annotation:
  '(' "integer-literal" ',' name ')'
  | '(' "integer-literal" ',' name ':' name ')'
  | '(' "integer-literal" ')'
  ;

opt_kind:
  /* nil */
  | '<' name '>'
  ;

data_type_numeric:
  "INT" opt_kind
  | "INTEGER" opt_kind
  | "REAL" opt_kind
  | "LONG" opt_kind
  | "BOOL" opt_kind
  | "LONG" "INTEGER" opt_kind
  | "LONG" "INT" opt_kind
  | "LONG_INT" opt_kind
  | "LONG_INTEGER" opt_kind
  ;

data_type_any:
  data_type_numeric
  | "TEXT"  opt_kind
  | "BLOB"  opt_kind
  | "OBJECT" opt_kind
  | "OBJECT" '<' name "CURSOR" '>'
  | "OBJECT" '<' name "SET" '>'
  | "ID"
  | "@ID" '(' text_args ')'
  ;

not_null: "NOT" "NULL" | '!'
  ;

data_type_with_options:
  data_type_any
  | data_type_any not_null
  | data_type_any "@SENSITIVE"
  | data_type_any "@SENSITIVE" not_null
  | data_type_any not_null "@SENSITIVE"
  ;

str_literal:
  str_chain
  ;

str_chain:
  str_leaf
  | str_leaf str_chain
  ;

str_leaf:
  "sql-string-literal"
  | "c-string-literal"
  ;

num_literal:
  "integer-literal"
  | "long-literal"
  | "real-literal"
  | "TRUE"
  | "FALSE"
  ;

const_expr:
  "CONST" '(' expr ')'
  ;

any_literal:
  str_literal
  | num_literal
  | "NULL"
  | "@FILE" '(' str_literal ')'
  | "@LINE"
  | "@MACRO_LINE"
  | "@MACRO_FILE"
  | "@PROC"
  | "@TEXT" '(' text_args ')'
  | "sql-blob-literal"
  ;

text_args:
   text_arg
   | text_arg ',' text_args
   ;

text_arg : expr | non_expr_macro_ref ;

raise_expr:
  "RAISE" '(' "IGNORE" ')'
  | "RAISE" '(' "ROLLBACK" ','  expr ')'
  | "RAISE" '(' "ABORT" ','  expr ')'
  | "RAISE" '(' "FAIL" ','  expr ')'
  ;

opt_distinct:
  /* nil */
  | "DISTINCT"
  ;

simple_call:
  loose_name '(' opt_distinct arg_list ')' opt_filter_clause
  | "GLOB" '(' opt_distinct arg_list ')' opt_filter_clause
  | "LIKE" '(' opt_distinct arg_list ')' opt_filter_clause
  ;

call:
  simple_call
  | basic_expr ':' simple_call
  | basic_expr ':' loose_name
  | basic_expr ':' '(' arg_list ')'
  ;

basic_expr:
  name
  | "`quoted identifier`"
  | expr_macro_ref
  | '*'
  | "@RC"
  | basic_expr '.' sql_name
  | basic_expr '.' '*'
  | any_literal
  | const_expr
  | '(' expr ')'
  | call
  | window_func_inv
  | raise_expr
  | '(' select_stmt ')'
  | '(' select_stmt "IF" "NOTHING" expr ')'
  | '(' select_stmt "IF" "NOTHING" "THEN" expr ')'
  | '(' select_stmt "IF" "NOTHING" "OR" "NULL" expr ')'
  | '(' select_stmt "IF" "NOTHING" "OR" "NULL" "THEN" expr ')'
  | '(' select_stmt "IF" "NOTHING" "THROW"')'
  | '(' select_stmt "IF" "NOTHING" "THEN" "THROW"')'
  | "EXISTS" '(' select_stmt ')'
  | "CASE" expr case_list "END"
  | "CASE" expr case_list "ELSE" expr "END"
  | "CASE" case_list "END"
  | "CASE" case_list "ELSE" expr "END"
  | "CAST" '(' expr "AS" data_type_any ')'
  | "TYPE_CHECK" '(' expr "AS" data_type_with_options ')'
  | basic_expr '[' arg_list ']'
  | basic_expr '~' data_type_any '~'
  | basic_expr "->" basic_expr
  | basic_expr "->>" '~' data_type_any '~' basic_expr
  ;

math_expr:
  basic_expr
  | math_expr '&' math_expr
  | math_expr '|' math_expr
  | math_expr "<<" math_expr
  | math_expr ">>"  math_expr
  | math_expr '+' math_expr
  | math_expr '-' math_expr
  | math_expr '*' math_expr
  | math_expr '/' math_expr
  | math_expr '%' math_expr
  | math_expr "IS NOT TRUE"
  | math_expr "IS NOT FALSE"
  | math_expr "ISNULL"
  | math_expr "NOTNULL"
  | math_expr "IS TRUE"
  | math_expr "IS FALSE"
  | '-' math_expr
  | '+' math_expr
  | '~' math_expr
  | "NOT" math_expr
  | math_expr '=' math_expr
  | math_expr "==" math_expr
  | math_expr '<' math_expr
  | math_expr '>' math_expr
  | math_expr "<>" math_expr
  | math_expr "!=" math_expr
  | math_expr ">=" math_expr
  | math_expr "<=" math_expr
  | math_expr "NOT IN" '(' opt_expr_list ')'
  | math_expr "NOT IN" '(' select_stmt ')'
  | math_expr "IN" '(' opt_expr_list ')'
  | math_expr "IN" '(' select_stmt ')'
  | math_expr "LIKE" math_expr
  | math_expr "NOT LIKE" math_expr
  | math_expr "MATCH" math_expr
  | math_expr "NOT MATCH" math_expr
  | math_expr "REGEXP" math_expr
  | math_expr "NOT REGEXP" math_expr
  | math_expr "GLOB" math_expr
  | math_expr "NOT GLOB" math_expr
  | math_expr "BETWEEN" math_expr "AND" math_expr
  | math_expr "NOT BETWEEN" math_expr "AND" math_expr
  | math_expr "IS NOT" math_expr
  | math_expr "IS" math_expr
  | math_expr "||" math_expr
  | math_expr "COLLATE" name
  ;

expr:
  math_expr
  | expr "AND" expr
  | expr "OR" expr
  | expr ":=" expr
  | expr "+=" expr
  | expr "-=" expr
  | expr "/=" expr
  | expr "*=" expr
  | expr "%=" expr
  | expr "&=" expr
  | expr "|=" expr
  | expr "<<=" expr
  | expr ">>=" expr
  ;

case_list:
  "WHEN" expr "THEN" expr
  | "WHEN" expr "THEN" expr case_list
  ;

arg_expr: expr
  | shape_arguments
  ;

arg_exprs:
  arg_expr
  | arg_expr ',' arg_exprs
  ;

arg_list:
  /* nil */
  | arg_exprs
  ;

opt_expr_list:
  /* nil */
  | expr_list
  ;

expr_list:
  expr
  | expr ',' expr_list
  ;

shape_arguments:
  "FROM" name
  | "FROM" name shape_def
  | "FROM" "ARGUMENTS"
  | "FROM" "ARGUMENTS" shape_def
  ;

column_calculation:
  "@COLUMNS" '(' col_calcs ')'
  | "@COLUMNS" '(' "DISTINCT" col_calcs ')'
  ;

col_calcs:
  col_calc
  | col_calc ',' col_calcs
  ;

col_calc:
  name
  | shape_def
  | name shape_def
  | name '.' name
  ;

cte_tables:
  cte_table
  | cte_table ',' cte_tables
  ;

cte_decl:
  name '(' name_list ')'
  | name '(' '*' ')'
  | name
  ;

shared_cte:
  call_stmt
  | call_stmt "USING" cte_binding_list
  ;

cte_table:
  cte_decl "AS" '(' select_stmt ')'
  | cte_decl "AS" '(' shared_cte')'
  | '(' call_stmt ')'
  | '(' call_stmt "USING" cte_binding_list ')'
  | cte_decl "LIKE" '(' select_stmt ')'
  | cte_decl "LIKE" sql_name
  | cte_tables_macro_ref
  ;

with_prefix:
  "WITH" cte_tables
  | "WITH" "RECURSIVE" cte_tables
  ;

with_select_stmt:
  with_prefix select_stmt_no_with
  ;

select_nothing_stmt:
  "SELECT" "NOTHING"
  ;

select_stmt:
  with_select_stmt
  | select_stmt_no_with
  ;

select_stmt_no_with:
  select_core_list opt_orderby opt_limit opt_offset
  ;

select_core_list:
  select_core
  | select_core compound_operator select_core_list
  | select_core_macro_ref
  | select_core_macro_ref compound_operator select_core_list
  ;

values:
  '(' insert_list ')'
  | '(' insert_list ')' ',' values
  ;

select_core:
  "SELECT" select_opts select_expr_list opt_from_query_parts opt_where opt_groupby opt_having opt_select_window
  | "VALUES" values
  ;

compound_operator:
  "UNION"
  | "UNION ALL"
  | "INTERSECT"
  | "EXCEPT"
  ;

window_func_inv:
  simple_call "OVER" window_name_or_defn
  ;

opt_filter_clause:
  /* nil */
  | "FILTER" '(' opt_where ')'
  ;

window_name_or_defn: window_defn
  | name
  ;

window_defn:
  '(' opt_partition_by opt_orderby opt_frame_spec ')'
  ;

opt_frame_spec:
  /* nil */
  | frame_type frame_boundary_opts frame_exclude
  ;

frame_type:
  "RANGE"
  | "ROWS"
  | "GROUPS"
  ;

frame_exclude:
  /* nil */
  | "EXCLUDE NO OTHERS"
  | "EXCLUDE CURRENT ROW"
  | "EXCLUDE GROUP"
  | "EXCLUDE TIES"
  ;

frame_boundary_opts:
  frame_boundary
  | "BETWEEN" frame_boundary_start "AND" frame_boundary_end
  ;

frame_boundary_start:
  "UNBOUNDED" "PRECEDING"
  | expr "PRECEDING"
  | "CURRENT ROW"
  | expr "FOLLOWING"
  ;

frame_boundary_end:
  expr "PRECEDING"
  | "CURRENT ROW"
  | expr "FOLLOWING"
  | "UNBOUNDED" "FOLLOWING"
  ;

frame_boundary:
  "UNBOUNDED" "PRECEDING"
  | expr "PRECEDING"
  | "CURRENT ROW"
  ;

opt_partition_by:
  /* nil */
  | "PARTITION" "BY" expr_list
  ;

opt_select_window:
  /* nil */
  | window_clause
  ;

window_clause:
  "WINDOW" window_name_defn_list
  ;

window_name_defn_list:
  window_name_defn
  | window_name_defn ',' window_name_defn_list
  ;

window_name_defn:
  name "AS" window_defn
  ;

region_spec:
    name
  | name "PRIVATE"
  ;

region_list:
  region_spec ',' region_list
  | region_spec
  ;

declare_schema_region_stmt:
  "@DECLARE_SCHEMA_REGION" name
  | "@DECLARE_SCHEMA_REGION" name "USING" region_list
  ;

declare_deployable_region_stmt:
  "@DECLARE_DEPLOYABLE_REGION"  name
  | "@DECLARE_DEPLOYABLE_REGION" name "USING" region_list
  ;

begin_schema_region_stmt:
  "@BEGIN_SCHEMA_REGION" name
  ;

end_schema_region_stmt:
  "@END_SCHEMA_REGION"
  ;

schema_unsub_stmt:
  "@UNSUB"  '(' sql_name ')'
  ;

schema_ad_hoc_migration_stmt:
  "@SCHEMA_AD_HOC_MIGRATION" version_annotation
  | "@SCHEMA_AD_HOC_MIGRATION" "FOR" "@RECREATE" '(' name ',' name ')'
  ;

emit_enums_stmt:
  "@EMIT_ENUMS" opt_name_list
  ;

emit_group_stmt:
  "@EMIT_GROUP" opt_name_list
  ;

emit_constants_stmt:
  "@EMIT_CONSTANTS" name_list
  ;

opt_from_query_parts:
  /* nil */
  | "FROM" query_parts
  ;

opt_where:
  /* nil */
  | "WHERE" expr
  ;

opt_groupby:
  /* nil */
  | "GROUP" "BY" groupby_list
  ;

groupby_list:
  groupby_item
  | groupby_item ',' groupby_list
  ;

groupby_item:
  expr
  ;

opt_asc_desc:
  /* nil */
  | "ASC"  opt_nullsfirst_nullslast
  | "DESC"  opt_nullsfirst_nullslast
  ;

opt_nullsfirst_nullslast:
  /* nil */
  | "NULLS" "FIRST"
  | "NULLS" "LAST"
  ;

opt_having:
  /* nil */
  | "HAVING" expr
  ;

opt_orderby:
  /* nil */
  | "ORDER" "BY" orderby_list
  ;

orderby_list:
  orderby_item
  | orderby_item ',' orderby_list
  ;

orderby_item:
  expr opt_asc_desc
  ;

opt_limit:
  /* nil */
  | "LIMIT" expr
  ;

opt_offset:
  /* nil */
  | "OFFSET" expr
  ;

select_opts:
  /* nil */
  | "ALL"
  | "DISTINCT"
  | "DISTINCTROW"
  ;

select_expr_list:
  select_expr
  | select_expr ',' select_expr_list
  | select_expr_macro_ref
  | select_expr_macro_ref ',' select_expr_list
  ;

select_expr:
  expr opt_as_alias
  | column_calculation
  ;

opt_as_alias:
  /* nil */
  | as_alias
  ;

as_alias:
  "AS" sql_name
  | sql_name
  ;

query_parts:
  table_or_subquery_list
  | join_clause
  ;

table_or_subquery_list:
  table_or_subquery
  | table_or_subquery ',' table_or_subquery_list
  ;

join_clause:
  table_or_subquery join_target_list
  ;

join_target_list:
  join_target
  | join_target join_target_list
  ;

table_or_subquery:
  sql_name opt_as_alias
  | '(' select_stmt ')' opt_as_alias
  | '(' shared_cte ')' opt_as_alias
  | table_function opt_as_alias
  | '(' query_parts ')'
  |  query_parts_macro_ref opt_as_alias
  ;

join_type:
  /* nil */
  | "LEFT"
  | "RIGHT"
  | "LEFT" "OUTER"
  | "RIGHT" "OUTER"
  | "INNER"
  | "CROSS"
  ;

join_target: join_type "JOIN" table_or_subquery opt_join_cond
  ;

opt_join_cond:
  /* nil */
  | join_cond
  ;

join_cond:
  "ON" expr
  | "USING" '(' name_list ')'
  ;

table_function:
  name '(' arg_list ')'
  ;

create_view_stmt:
  "CREATE" opt_temp "VIEW" opt_if_not_exists sql_name "AS" select_stmt opt_delete_version_attr
  | "CREATE" opt_temp "VIEW" opt_if_not_exists sql_name '(' name_list ')' "AS" select_stmt opt_delete_version_attr
  ;

with_delete_stmt:
  with_prefix delete_stmt
  ;

delete_stmt:
  "DELETE" "FROM" sql_name opt_where
  ;

opt_insert_dummy_spec:
  /* nil */
  | "@DUMMY_SEED" '(' expr ')' dummy_modifier
  ;

dummy_modifier:
  /* nil */
  | "@DUMMY_NULLABLES"
  | "@DUMMY_DEFAULTS"
  | "@DUMMY_NULLABLES" "@DUMMY_DEFAULTS"
  | "@DUMMY_DEFAULTS" "@DUMMY_NULLABLES"
  ;

insert_stmt_type:
  "INSERT" "INTO"
  | "INSERT" "OR" "REPLACE" "INTO"
  | "INSERT" "OR" "IGNORE" "INTO"
  | "INSERT" "OR" "ROLLBACK" "INTO"
  | "INSERT" "OR" "ABORT" "INTO"
  | "INSERT" "OR" "FAIL" "INTO"
  | "REPLACE" "INTO"
  ;

with_insert_stmt:
  with_prefix insert_stmt
  ;

opt_column_spec:
  /* nil */
  | '(' opt_sql_name_list ')'
  | '(' shape_def ')'
  ;

column_spec:
  '(' sql_name_list ')'
  | '(' shape_def ')'
  ;

from_shape:
  "FROM" "CURSOR" name opt_column_spec
  | "FROM" name opt_column_spec
  | "FROM" "ARGUMENTS" opt_column_spec
  ;

insert_stmt:
  insert_stmt_type sql_name opt_column_spec select_stmt opt_insert_dummy_spec
  | insert_stmt_type sql_name opt_column_spec from_shape opt_insert_dummy_spec
  | insert_stmt_type sql_name "DEFAULT" "VALUES"
  | insert_stmt_type sql_name "USING" select_stmt
  | insert_stmt_type sql_name "USING" expr_names opt_insert_dummy_spec
  ;

insert_list_item:
  expr
  | shape_arguments
  ;

insert_list:
  /* nil */
  | insert_list_item
  | insert_list_item ',' insert_list
  ;

basic_update_stmt:
  "UPDATE" opt_sql_name "SET" update_list opt_from_query_parts opt_where
  ;

with_update_stmt:
  with_prefix update_stmt
  ;

update_stmt:
  "UPDATE" sql_name "SET" update_list opt_from_query_parts opt_where opt_orderby opt_limit
  | "UPDATE" sql_name "SET" column_spec '=' '(' insert_list ')' opt_from_query_parts opt_where opt_orderby opt_limit
  ;

update_entry:
  sql_name '=' expr
  ;

update_list:
  update_entry
  | update_entry ',' update_list
  ;

with_upsert_stmt:
  with_prefix upsert_stmt
  ;

upsert_stmt:
  insert_stmt "ON CONFLICT" conflict_target "DO" "NOTHING"
  | insert_stmt "ON CONFLICT" conflict_target "DO" basic_update_stmt
  ;

update_cursor_stmt:
  "UPDATE" "CURSOR" name opt_column_spec "FROM" "VALUES" '(' insert_list ')'
  | "UPDATE" "CURSOR" name opt_column_spec from_shape
  | "UPDATE" "CURSOR" name "USING" expr_names
  ;

conflict_target:
  /* nil */
  | '(' indexed_columns ')' opt_where
  ;

function: "FUNC" | "FUNCTION"
  ;

declare_out_call_stmt:
  "DECLARE" "OUT" call_stmt
  ;

declare_enum_stmt:
  "DECLARE" "ENUM" name data_type_numeric '(' enum_values ')'
  ;

enum_values:
    enum_value
  | enum_value ',' enum_values
  ;

enum_value:
    name
  | name '=' expr
  ;

declare_const_stmt:
  "DECLARE" "CONST" "GROUP" name '(' const_values ')'
  ;

declare_group_stmt:
  "DECLARE" "GROUP" name "BEGIN" simple_variable_decls "END"
  ;

simple_variable_decls:
  declare_vars_stmt ';'
  | declare_vars_stmt ';' simple_variable_decls
  ;

const_values:
   const_value
  | const_value ',' const_values
  ;

const_value:  name '=' expr
  ;

declare_select_func_stmt:
   "DECLARE" "SELECT" function name '(' params ')' data_type_with_options
  | "DECLARE" "SELECT" function name '(' params ')' '(' typed_names ')'
  | "DECLARE" "SELECT" function name "NO" "CHECK" data_type_with_options
  | "DECLARE" "SELECT" function name "NO" "CHECK" '(' typed_names ')'
  ;

declare_func_stmt:
  "DECLARE" function loose_name '(' func_params ')' data_type_with_options
  | "DECLARE" function loose_name '(' func_params ')' "CREATE" data_type_with_options
  | "DECLARE" function loose_name "NO" "CHECK" data_type_with_options
  | "DECLARE" function loose_name "NO" "CHECK" "CREATE" data_type_with_options
  ;

procedure: "PROC" | "PROCEDURE"
  ;

declare_proc_no_check_stmt:
  "DECLARE" procedure loose_name "NO" "CHECK"
  ;

declare_proc_stmt:
  "DECLARE" procedure loose_name '(' params ')'
  | "DECLARE" procedure loose_name '(' params ')' '(' typed_names ')'
  | "DECLARE" procedure loose_name '(' params ')' "USING" "TRANSACTION"
  | "DECLARE" procedure loose_name '(' params ')' "OUT" '(' typed_names ')'
  | "DECLARE" procedure loose_name '(' params ')' "OUT" '(' typed_names ')' "USING" "TRANSACTION"
  | "DECLARE" procedure loose_name '(' params ')' "OUT" "UNION" '(' typed_names ')'
  | "DECLARE" procedure loose_name '(' params ')' "OUT" "UNION" '(' typed_names ')' "USING" "TRANSACTION"
  ;

declare_interface_stmt:
  "DECLARE" "INTERFACE" name '(' typed_names ')'
  | "INTERFACE" name '(' typed_names ')'
  ;

create_proc_stmt:
  "CREATE" procedure loose_name '(' params ')' "BEGIN" opt_stmt_list "END"
  | procedure loose_name '(' params ')' "BEGIN" opt_stmt_list "END"
  ;

inout:
  "IN"
  | "OUT"
  | "INOUT"
  ;

typed_name:
  sql_name data_type_with_options
  | shape_def
  | name shape_def
  ;

typed_names:
  typed_name
  | typed_name ',' typed_names
  ;

func_param:
  param
  | name "CURSOR"
  ;

func_params:
  /* nil */
  | func_param
  |  func_param ',' func_params
  ;

param:
  sql_name data_type_with_options
  | inout sql_name data_type_with_options
  | shape_def
  | name shape_def
  ;

params:
  /* nil */
  | param
  |  param ',' params
  ;

declare_value_cursor:
  "DECLARE" name "CURSOR" shape_def
  | "CURSOR" name shape_def
  | "DECLARE" name "CURSOR" "LIKE" select_stmt
  | "CURSOR" name "LIKE" select_stmt
  | "DECLARE" name "CURSOR" "LIKE" '(' typed_names ')'
  | "CURSOR" name "LIKE" '(' typed_names ')'
  ;

declare_forward_read_cursor_stmt:
  "DECLARE" name "CURSOR" "FOR" select_stmt
  | "CURSOR" name "FOR" select_stmt
  | "DECLARE" name "CURSOR" "FOR" explain_stmt
  | "CURSOR" name "FOR" explain_stmt
  | "DECLARE" name "CURSOR" "FOR" call_stmt
  | "CURSOR" name "FOR" call_stmt
  | "DECLARE" name "CURSOR" "FOR" expr
  | "CURSOR" name "FOR" expr
  ;

declare_fetched_value_cursor_stmt:
  "DECLARE" name "CURSOR" "FETCH" "FROM" call_stmt
  | "CURSOR" name "FETCH" "FROM" call_stmt
  ;

declare_type_stmt:
  "DECLARE" name "TYPE" data_type_with_options
  | "TYPE" name data_type_with_options
  ;

declare_vars_stmt:
  "DECLARE" sql_name_list data_type_with_options
  | "VAR" name_list data_type_with_options
  | declare_value_cursor
  ;

call_stmt: "CALL" loose_name '(' arg_list ')'
  ;

while_stmt:
  "WHILE" expr "BEGIN" opt_stmt_list "END"
  ;

switch_stmt:
  "SWITCH" expr switch_case switch_cases
  | "SWITCH" expr "ALL" "VALUES" switch_case switch_cases
  ;

switch_case:
  "WHEN" expr_list "THEN" stmt_list
  | "WHEN" expr_list "THEN" "NOTHING"
  ;

switch_cases:
  switch_case switch_cases
  | "ELSE" stmt_list "END"
  | "END"
  ;

loop_stmt:
  "LOOP" fetch_stmt "BEGIN" opt_stmt_list "END"
  ;

leave_stmt:
  "LEAVE"
  ;

return_stmt:
  "RETURN"
  ;

rollback_return_stmt:
  "ROLLBACK" "RETURN"
  ;

commit_return_stmt:
  "COMMIT" "RETURN"
  ;

throw_stmt:
  "THROW"
  ;

trycatch_stmt:
  "BEGIN" "TRY" opt_stmt_list "END" "TRY" ';' "BEGIN" "CATCH" opt_stmt_list "END" "CATCH"
  | "TRY" opt_stmt_list "CATCH" opt_stmt_list "END"
  ;

continue_stmt:
  "CONTINUE"
  ;

fetch_stmt:
  "FETCH" name "INTO" name_list
  | "FETCH" name
  ;

fetch_cursor_from_blob_stmt:
  "FETCH" name "FROM BLOB" expr
  ;

fetch_values_stmt:
  "FETCH" name opt_column_spec "FROM" "VALUES" '(' insert_list ')' opt_insert_dummy_spec
  | "FETCH" name opt_column_spec from_shape opt_insert_dummy_spec
  | "FETCH" name "USING" expr_names opt_insert_dummy_spec
  ;

expr_names:
  expr_name
  |  expr_name ',' expr_names
  ;

expr_name: expr as_alias
  ;

fetch_call_stmt:
  "FETCH" name opt_column_spec "FROM" call_stmt
  ;

close_stmt:
  "CLOSE" name
  ;

out_stmt:
  "OUT" name
  ;

out_union_stmt:
  "OUT" "UNION" name
  ;

out_union_parent_child_stmt:
  "OUT" "UNION" call_stmt "JOIN" child_results
  ;

child_results:
   child_result
   | child_result "AND" child_results
   ;

child_result:
  call_stmt "USING" '(' name_list ')'
  | call_stmt "USING" '(' name_list ')' "AS" name
  ;

if_stmt:
  "IF" expr "THEN" opt_stmt_list opt_elseif_list opt_else "END" "IF"
  | "IF" expr "THEN" opt_stmt_list opt_elseif_list opt_else "END"
  ;

opt_else:
  /* nil */
  | "ELSE" opt_stmt_list
  ;

elseif_item:
  "ELSE IF" expr "THEN" opt_stmt_list
  ;

elseif_list:
  elseif_item
  | elseif_item elseif_list
  ;

opt_elseif_list:
  /* nil */
  | elseif_list
  ;

control_stmt:
  commit_return_stmt
  | continue_stmt
  | leave_stmt
  | return_stmt
  | rollback_return_stmt
  | throw_stmt

guard_stmt:
  "IF" expr control_stmt
  ;

transaction_mode:
  /* nil */
  | "DEFERRED"
  | "IMMEDIATE"
  | "EXCLUSIVE"
  ;

begin_trans_stmt:
  "BEGIN" transaction_mode "TRANSACTION"
  | "BEGIN" transaction_mode
  ;

rollback_trans_stmt:
  "ROLLBACK"
  | "ROLLBACK" "TRANSACTION"
  | "ROLLBACK" "TO" savepoint_name
  | "ROLLBACK" "TRANSACTION" "TO" savepoint_name
  | "ROLLBACK" "TO" "SAVEPOINT" savepoint_name
  | "ROLLBACK" "TRANSACTION" "TO" "SAVEPOINT" savepoint_name
  ;

commit_trans_stmt:
  "COMMIT" "TRANSACTION"
  | "COMMIT"
  ;

proc_savepoint_stmt:  procedure "SAVEPOINT" "BEGIN" opt_stmt_list "END"
  ;

savepoint_name:
  "@PROC"
  | name
  ;

savepoint_stmt:
  "SAVEPOINT" savepoint_name
  ;

release_savepoint_stmt:
  "RELEASE" savepoint_name
  | "RELEASE" "SAVEPOINT" savepoint_name
  ;

echo_stmt:
  "@ECHO" name ',' str_literal
  | "@ECHO" name ',' "@TEXT" '(' text_args ')'
  ;

alter_table_add_column_stmt:
  "ALTER" "TABLE" sql_name "ADD" "COLUMN" col_def
  ;

create_trigger_stmt:
  "CREATE" opt_temp "TRIGGER" opt_if_not_exists trigger_def opt_delete_version_attr
  ;

trigger_def:
  sql_name trigger_condition trigger_operation "ON" sql_name trigger_action
  ;

trigger_condition:
  /* nil */
  | "BEFORE"
  | "AFTER"
  | "INSTEAD" "OF"
 ;

trigger_operation:
  "DELETE"
  | "INSERT"
  | "UPDATE" opt_of
  ;

opt_of:
  /* nil */
  | "OF" name_list
  ;

trigger_action:
  opt_foreachrow opt_when_expr "BEGIN" trigger_stmts "END"
  ;

opt_foreachrow:
  /* nil */
  | "FOR EACH ROW"
  ;

opt_when_expr:
  /* nil */
  | "WHEN" expr
  ;

trigger_stmts:
  trigger_stmt
  | trigger_stmt  trigger_stmts
  ;

trigger_stmt:
  trigger_update_stmt ';'
  | trigger_insert_stmt ';'
  | trigger_delete_stmt ';'
  | trigger_select_stmt ';'
  ;

trigger_select_stmt:
  select_stmt_no_with
  ;

trigger_insert_stmt:
  insert_stmt
  ;

trigger_delete_stmt:
  delete_stmt
  ;

trigger_update_stmt:
  basic_update_stmt
  ;

enforcement_options:
  "FOREIGN" "KEY" "ON" "UPDATE"
  | "FOREIGN" "KEY" "ON" "DELETE"
  | "JOIN"
  | "UPSERT" "STATEMENT"
  | "WINDOW" function
  | "WITHOUT" "ROWID"
  | "TRANSACTION"
  | "SELECT" "IF" "NOTHING"
  | "INSERT" "SELECT"
  | "TABLE" "FUNCTION"
  | "ENCODE" "CONTEXT COLUMN"
  | "ENCODE" "CONTEXT TYPE" "INTEGER"
  | "ENCODE" "CONTEXT TYPE" "LONG_INTEGER"
  | "ENCODE" "CONTEXT TYPE" "REAL"
  | "ENCODE" "CONTEXT TYPE" "BOOL"
  | "ENCODE" "CONTEXT TYPE" "TEXT"
  | "ENCODE" "CONTEXT TYPE" "BLOB"
  | "IS TRUE"
  | "CAST"
  | "SIGN FUNCTION"
  | "CURSOR HAS ROW"
  | "UPDATE" "FROM"
  | "AND" "OR" "NOT" "NULL" "CHECK"
  ;

enforce_strict_stmt:
  "@ENFORCE_STRICT" enforcement_options
  ;

enforce_normal_stmt:
  "@ENFORCE_NORMAL" enforcement_options
  ;

enforce_reset_stmt:
  "@ENFORCE_RESET"
  ;

enforce_push_stmt:
  "@ENFORCE_PUSH"
  ;

enforce_pop_stmt:
  "@ENFORCE_POP"
  ;

opt_use_offset:
  /* nil */
  | "OFFSET"
  ;

blob_get_key_type_stmt:
  "@BLOB_GET_KEY_TYPE" name
  ;

blob_get_val_type_stmt:
  "@BLOB_GET_VAL_TYPE" name
  ;

blob_get_key_stmt:
  "@BLOB_GET_KEY" name opt_use_offset
  ;

blob_get_val_stmt:
  "@BLOB_GET_VAL" name opt_use_offset
  ;

blob_create_key_stmt:
  "@BLOB_CREATE_KEY" name opt_use_offset
  ;

blob_create_val_stmt:
  "@BLOB_CREATE_VAL" name opt_use_offset
  ;

blob_update_key_stmt:
  "@BLOB_UPDATE_KEY" name opt_use_offset
  ;

blob_update_val_stmt:
  "@BLOB_UPDATE_VAL" name opt_use_offset
  ;

keep_table_name_in_aliases_stmt:
  "@KEEP_TABLE_NAME_IN_ALIASES"

expr_macro_def:
  "@MACRO" '(' "EXPR" ')' name '!' '(' opt_macro_formals ')'

stmt_list_macro_def:
  "@MACRO" '(' "STMT_LIST" ')' name '!' '(' opt_macro_formals ')'
  ;

query_parts_macro_def:
  "@MACRO" '(' "QUERY_PARTS" ')' name '!' '(' opt_macro_formals ')'
  ;

cte_tables_macro_def:
  "@MACRO" '(' "CTE_TABLES" ')' name '!' '(' opt_macro_formals ')'
  ;

select_core_macro_def:
  "@MACRO" '(' "SELECT_CORE" ')' name '!' '(' opt_macro_formals ')'
  ;

select_expr_macro_def:
  "@MACRO" '(' "SELECT_EXPR" ')' name '!' '(' opt_macro_formals ')'
  ;

op_stmt: "@OP" data_type_any ':' loose_name loose_name_or_type "AS" loose_name
  | "@OP" "CURSOR" ':' loose_name loose_name_or_type "AS" loose_name
  | "@OP" "NULL" ':' loose_name loose_name_or_type "AS" loose_name
  ;

macro_def_stmt:
  expr_macro_def "BEGIN" expr "END"
  | stmt_list_macro_def "BEGIN" stmt_list "END"
  | query_parts_macro_def "BEGIN" query_parts "END"
  | cte_tables_macro_def "BEGIN" cte_tables "END"
  | select_core_macro_def "BEGIN" select_core_list "END"
  | select_expr_macro_def "BEGIN" select_expr_list "END"
  ;

opt_macro_args:
   /* nil */
   | macro_args
   ;

macro_arg:
 expr
 | "BEGIN" stmt_list "END"
 |  stmt_list_macro_ref
 | "FROM" '(' query_parts ')'
 |  query_parts_macro_ref
 | "WITH" '(' cte_tables ')'
 |  cte_tables_macro_ref
 | "ALL" '(' select_core_list ')'
 |  select_core_macro_ref
 | "SELECT" '(' select_expr_list ')'
 |  select_expr_macro_ref
 ;

macro_args:
   macro_arg
  | macro_arg ',' macro_args
  ;

opt_macro_formals:
   /* nil */
   | macro_formals
   ;

macro_formals:
   macro_formal
  | macro_formal ',' macro_formals
  ;

macro_formal: name '!' macro_type
  ;

macro_type:
   "EXPR"
  | "STMT_LIST"
  | "QUERY_PARTS"
  | "CTE_TABLES"
  | "SELECT_CORE"
  | "SELECT_EXPR"
  ;