use sql_dialect_fmt_syntax::SyntaxKind::*;
use crate::parser::{ContextualKeyword, Parser};
use super::{
at_block_start, at_stmt_terminator, balanced_parens, balanced_token_run_until, block_stmt,
call_stmt, column_list, dml, expr, name_ref, query_expr, type_name,
};
const CREATE_MODIFIER_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Materialized,
ContextualKeyword::Local,
ContextualKeyword::Global,
];
const NAMED_OBJECT_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Schema,
ContextualKeyword::Database,
ContextualKeyword::Stage,
ContextualKeyword::Sequence,
ContextualKeyword::Stream,
ContextualKeyword::Dynamic,
ContextualKeyword::Semantic,
ContextualKeyword::File,
ContextualKeyword::Tag,
];
const SEMANTIC_VIEW_CLAUSE_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Tables,
ContextualKeyword::Relationships,
ContextualKeyword::Facts,
ContextualKeyword::Dimensions,
ContextualKeyword::Metrics,
ContextualKeyword::AiSqlGeneration,
ContextualKeyword::AiQuestionCategorization,
ContextualKeyword::AiVerifiedQueries,
];
const SEMANTIC_VIEW_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Tables,
ContextualKeyword::Relationships,
ContextualKeyword::Facts,
ContextualKeyword::Dimensions,
ContextualKeyword::Metrics,
ContextualKeyword::Public,
ContextualKeyword::Private,
ContextualKeyword::Primary,
ContextualKeyword::Key,
ContextualKeyword::References,
ContextualKeyword::Synonyms,
ContextualKeyword::Labels,
ContextualKeyword::AiSqlGeneration,
ContextualKeyword::AiQuestionCategorization,
ContextualKeyword::AiVerifiedQueries,
ContextualKeyword::Question,
ContextualKeyword::VerifiedAt,
ContextualKeyword::OnboardingQuestion,
ContextualKeyword::VerifiedBy,
ContextualKeyword::Tag,
];
const DDL_CONSTRAINT_START_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Constraint,
ContextualKeyword::Primary,
ContextualKeyword::Unique,
ContextualKeyword::Foreign,
ContextualKeyword::Check,
];
const DDL_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Default,
ContextualKeyword::Primary,
ContextualKeyword::Key,
ContextualKeyword::Unique,
ContextualKeyword::Foreign,
ContextualKeyword::References,
ContextualKeyword::Constraint,
ContextualKeyword::Check,
ContextualKeyword::Collate,
ContextualKeyword::Comment,
ContextualKeyword::Cluster,
ContextualKeyword::Clone,
ContextualKeyword::Cascade,
ContextualKeyword::Restrict,
ContextualKeyword::Materialized,
ContextualKeyword::Masking,
ContextualKeyword::Policy,
ContextualKeyword::Access,
ContextualKeyword::Tag,
ContextualKeyword::AllowedValues,
ContextualKeyword::Propagate,
ContextualKeyword::ExemptOtherPolicies,
];
fn if_exists_clause(p: &mut Parser) {
if p.at(IF_KW) {
p.bump(IF_KW);
p.eat(NOT_KW);
p.eat(EXISTS_KW);
}
}
pub(super) fn create_stmt(p: &mut Parser) {
let m = p.start();
p.bump(CREATE_KW);
if p.at(OR_KW) {
p.bump(OR_KW);
if p.at(REPLACE_KW) {
p.bump(REPLACE_KW);
} else {
p.expect(ALTER_KW);
}
}
while !at_object_kind(p) && !at_stmt_terminator(p) && !at_create_body(p) {
if p.nth_any_contextual(0, CREATE_MODIFIER_CONTEXTUAL_WORDS) {
p.bump_as(CONTEXTUAL_KEYWORD);
} else {
p.bump_any();
}
}
if p.at(VIEW_KW) {
create_view(p);
} else if p.at(TABLE_KW) {
create_table(p);
} else if p.at(PROCEDURE_KW) || p.at(FUNCTION_KW) {
create_routine(p);
} else if at_policy_object_kind(p) {
create_policy(p);
} else if at_named_object_kind(p) {
create_object(p);
} else {
create_other(p);
}
m.complete(p, CREATE_STMT);
}
fn at_object_kind(p: &Parser) -> bool {
p.at(TABLE_KW)
|| p.at(VIEW_KW)
|| p.at(PROCEDURE_KW)
|| p.at(FUNCTION_KW)
|| at_policy_object_kind(p)
|| at_named_object_kind(p)
}
fn at_named_object_kind(p: &Parser) -> bool {
p.at(TASK_KW) || p.at(WAREHOUSE_KW) || p.nth_any_contextual(0, NAMED_OBJECT_CONTEXTUAL_WORDS)
}
fn at_policy_object_kind(p: &Parser) -> bool {
(p.nth_contextual(0, ContextualKeyword::Masking)
&& p.nth_contextual(1, ContextualKeyword::Policy))
|| (p.at(ROW_KW)
&& p.nth_contextual(1, ContextualKeyword::Access)
&& p.nth_contextual(2, ContextualKeyword::Policy))
}
fn create_object(p: &mut Parser) {
object_kind_words(p);
if_exists_clause(p);
if p.at_name() {
name_ref(p);
}
if p.at(L_PAREN) {
column_def_list(p);
}
p.eat(WITH_KW); object_property_region(p);
if at_create_body(p) {
p.bump(AS_KW);
create_body(p);
}
}
fn object_kind_words(p: &mut Parser) {
if p.nth_contextual(0, ContextualKeyword::File) {
p.bump_as(CONTEXTUAL_KEYWORD); if p.nth_contextual(0, ContextualKeyword::Format) {
p.bump_as(CONTEXTUAL_KEYWORD); }
} else if p.nth_contextual(0, ContextualKeyword::Dynamic) {
p.bump_as(CONTEXTUAL_KEYWORD); if p.at(TABLE_KW) {
p.bump(TABLE_KW);
}
} else if p.nth_contextual(0, ContextualKeyword::Semantic) {
p.bump_as(CONTEXTUAL_KEYWORD); if p.at(VIEW_KW) {
p.bump(VIEW_KW);
}
} else if p.nth_contextual(0, ContextualKeyword::Tag) {
p.bump_as(CONTEXTUAL_KEYWORD);
} else if p.at_keyword() {
p.bump_any();
} else {
p.bump_as(CONTEXTUAL_KEYWORD);
}
}
fn create_policy(p: &mut Parser) {
policy_kind_words(p);
if_exists_clause(p);
if p.at_name() {
name_ref(p);
}
while !at_stmt_terminator(p) {
bump_ddl_word(p);
}
}
fn policy_kind_words(p: &mut Parser) {
if p.nth_contextual(0, ContextualKeyword::Masking) {
p.bump_as(CONTEXTUAL_KEYWORD); if p.nth_contextual(0, ContextualKeyword::Policy) {
p.bump_as(CONTEXTUAL_KEYWORD); }
} else {
p.bump(ROW_KW);
if p.nth_contextual(0, ContextualKeyword::Access) {
p.bump_as(CONTEXTUAL_KEYWORD); }
if p.nth_contextual(0, ContextualKeyword::Policy) {
p.bump_as(CONTEXTUAL_KEYWORD); }
}
}
fn object_property_region(p: &mut Parser) {
while !at_create_body(p) && !at_stmt_terminator(p) {
if p.dialect().supports_semantic_view() && at_semantic_view_clause_start(p) {
semantic_view_clause(p);
} else if p.at(ON_KW) {
stream_source(p);
} else if p.at(AFTER_KW) {
task_after(p);
} else if p.at(WHEN_KW) {
let m = p.start();
p.bump(WHEN_KW);
expr(p);
m.complete(p, OBJECT_PROPERTY);
} else {
object_property(p);
}
}
}
fn semantic_view_clause(p: &mut Parser) {
let m = p.start();
if p.at(WITH_KW) {
p.bump(WITH_KW);
if p.nth_contextual(0, ContextualKeyword::Tag) {
p.bump_as(CONTEXTUAL_KEYWORD);
}
if p.at(L_PAREN) {
semantic_view_paren_list(p);
}
} else if p.at(COPY_KW) {
p.bump(COPY_KW);
p.eat(GRANTS_KW);
} else {
semantic_view_word(p);
if p.at(L_PAREN) {
semantic_view_paren_list(p);
} else if !at_semantic_view_clause_start(p)
&& !p.at(SEMICOLON)
&& !p.at_eof()
&& !at_create_body(p)
{
semantic_view_word(p);
}
}
m.complete(p, SEMANTIC_VIEW_CLAUSE);
}
fn at_semantic_view_clause_start(p: &Parser) -> bool {
p.nth_any_contextual(0, SEMANTIC_VIEW_CLAUSE_CONTEXTUAL_WORDS)
|| (p.at(WITH_KW) && p.nth_contextual(1, ContextualKeyword::Tag))
|| (p.at(COPY_KW) && p.nth_at(1, GRANTS_KW))
}
fn semantic_view_paren_list(p: &mut Parser) {
p.bump(L_PAREN);
if !p.at(R_PAREN) {
semantic_view_item(p);
while p.eat(COMMA) {
if p.at(R_PAREN) {
break;
}
semantic_view_item(p);
}
}
p.expect(R_PAREN);
}
fn semantic_view_item(p: &mut Parser) {
let m = p.start();
balanced_token_run_until(p, |p| p.at(COMMA) || p.at(R_PAREN), semantic_view_word);
m.complete(p, SEMANTIC_VIEW_ITEM);
}
fn semantic_view_word(p: &mut Parser) {
if is_semantic_view_contextual_word(p) {
p.bump_as(CONTEXTUAL_KEYWORD);
} else {
p.bump_any();
}
}
fn is_semantic_view_contextual_word(p: &Parser) -> bool {
p.nth_any_contextual(0, SEMANTIC_VIEW_CONTEXTUAL_WORDS)
}
fn object_property(p: &mut Parser) {
let m = p.start();
let is_allowed_values = p.nth_contextual(0, ContextualKeyword::AllowedValues);
if is_allowed_values {
p.bump_as(CONTEXTUAL_KEYWORD);
} else {
p.bump_any(); }
if p.eat(EQ) {
if p.at(L_PAREN) {
balanced_parens(p); } else if !at_create_body(p) && !at_stmt_terminator(p) {
p.bump_any(); }
while at_property_value_tail(p) {
p.bump_any();
}
} else if is_allowed_values {
while at_allowed_values_tail(p) {
p.bump_any();
}
} else {
while at_property_value_tail(p) {
p.bump_any();
}
}
m.complete(p, OBJECT_PROPERTY);
}
fn at_allowed_values_tail(p: &Parser) -> bool {
if p.at(COMMA) {
return true;
}
let starts_new_property = p.nth_at(1, EQ);
!starts_new_property
&& !p.at(SEMICOLON)
&& !p.at(ON_KW)
&& !p.at(AFTER_KW)
&& !p.at(WHEN_KW)
&& !at_create_body(p)
&& !p.at_eof()
&& (p.at(STRING) || p.at(INT_NUMBER) || p.at(FLOAT_NUMBER))
}
fn at_property_value_tail(p: &Parser) -> bool {
if p.at(WITH_KW) || p.at(BY_KW) {
return true;
}
let starts_new_property = p.nth_at(1, EQ);
!starts_new_property
&& !p.at(SEMICOLON)
&& !p.at(ON_KW)
&& !p.at(AFTER_KW)
&& !p.at(WHEN_KW)
&& !at_create_body(p)
&& !p.at_eof()
&& (p.at(INT_NUMBER) || p.at(FLOAT_NUMBER) || p.at(STRING) || p.at(VARIABLE))
}
fn stream_source(p: &mut Parser) {
let m = p.start();
p.bump(ON_KW);
if p.at(TABLE_KW) || p.at(VIEW_KW) {
p.bump_any();
} else if p.nth_contextual(0, ContextualKeyword::Stage) {
p.bump_as(CONTEXTUAL_KEYWORD);
} else if p.at_keyword() {
p.bump_any();
}
if p.at_name() {
name_ref(p);
}
if p.nth_contextual(0, ContextualKeyword::At) || p.nth_contextual(0, ContextualKeyword::Before)
{
p.bump_as(CONTEXTUAL_KEYWORD);
if p.at(L_PAREN) {
balanced_parens(p);
}
}
m.complete(p, STREAM_SOURCE);
}
fn task_after(p: &mut Parser) {
let m = p.start();
p.bump(AFTER_KW);
if p.at_name() {
name_ref(p);
while p.eat(COMMA) {
if p.at_name() {
name_ref(p);
} else {
break;
}
}
}
m.complete(p, TASK_AFTER);
}
fn create_body(p: &mut Parser) {
if p.at(SELECT_KW)
|| p.at(WITH_KW)
|| p.at(VALUES_KW)
|| (p.at(L_PAREN) && (p.nth_at(1, SELECT_KW) || p.nth_at(1, WITH_KW)))
{
query_expr(p);
} else if p.at(INSERT_KW) {
dml::insert_stmt(p);
} else if p.at(UPDATE_KW) {
dml::update_stmt(p);
} else if p.at(DELETE_KW) {
dml::delete_stmt(p);
} else if p.at(MERGE_KW) {
dml::merge_stmt(p);
} else if p.at(CALL_KW) {
call_stmt(p);
} else if at_block_start(p) {
block_stmt(p);
} else if !at_stmt_terminator(p) {
let m = p.start();
expr(p);
m.complete(p, EXPR_STMT);
}
}
fn create_other(p: &mut Parser) {
while !at_stmt_terminator(p) {
if at_create_query_body(p) {
p.bump(AS_KW);
query_expr(p);
return;
}
p.bump_any();
}
}
fn at_create_query_body(p: &Parser) -> bool {
p.at(AS_KW)
&& (p.nth_at(1, SELECT_KW)
|| p.nth_at(1, WITH_KW)
|| p.nth_at(1, VALUES_KW)
|| (p.nth_at(1, L_PAREN)
&& (p.nth_at(2, SELECT_KW) || p.nth_at(2, WITH_KW) || p.nth_at(2, VALUES_KW))))
}
fn at_create_body(p: &Parser) -> bool {
p.at(AS_KW)
&& (p.nth_at(1, SELECT_KW)
|| p.nth_at(1, WITH_KW)
|| p.nth_at(1, VALUES_KW)
|| p.nth_at(1, INSERT_KW)
|| p.nth_at(1, UPDATE_KW)
|| p.nth_at(1, DELETE_KW)
|| p.nth_at(1, MERGE_KW)
|| p.nth_at(1, CALL_KW)
|| p.nth_at(1, BEGIN_KW)
|| p.nth_at(1, L_PAREN))
}
fn create_routine(p: &mut Parser) {
p.bump_any(); name_ref(p);
if p.at(L_PAREN) {
column_def_list(p); }
let mut seen_returns = false;
while !at_routine_body(p) && !at_stmt_terminator(p) {
if !seen_returns && p.at(RETURNS_KW) {
routine_returns_clause(p);
seen_returns = true;
} else if p.at(LANGUAGE_KW) {
routine_language_clause(p);
} else {
p.bump_any();
}
}
if at_routine_body(p) {
p.bump(AS_KW);
if p.at(DOLLAR_STRING) || p.at(STRING) {
p.bump_any(); } else if at_block_start(p) {
block_stmt(p);
}
} else {
p.error("expected a routine body (AS $$ … $$, AS '…', or AS BEGIN … END)");
}
}
fn routine_returns_clause(p: &mut Parser) {
let m = p.start();
p.bump(RETURNS_KW);
if p.at(TABLE_KW) {
p.bump(TABLE_KW);
if p.at(L_PAREN) {
balanced_parens(p);
}
} else if !p.at(LANGUAGE_KW) && p.at_name() {
type_name(p);
} else {
p.error("expected a routine return type");
}
if p.eat(NOT_KW) {
p.expect(NULL_KW);
}
m.complete(p, ROUTINE_RETURNS_CLAUSE);
}
fn routine_language_clause(p: &mut Parser) {
let m = p.start();
p.bump(LANGUAGE_KW);
if p.at_name() || p.at_keyword() {
p.bump_any();
} else {
p.error("expected a routine language");
}
m.complete(p, ROUTINE_LANGUAGE_CLAUSE);
}
fn at_routine_body(p: &Parser) -> bool {
p.at(AS_KW)
&& (p.nth_at(1, DOLLAR_STRING)
|| p.nth_at(1, STRING)
|| p.nth_at(1, DECLARE_KW)
|| (p.nth_at(1, BEGIN_KW)
&& !p.nth_at(2, SEMICOLON)
&& !p.nth_contextual(2, ContextualKeyword::Transaction)
&& !p.nth_contextual(2, ContextualKeyword::Work)))
}
fn create_view(p: &mut Parser) {
p.bump(VIEW_KW);
if_exists_clause(p);
name_ref(p);
if p.at(L_PAREN) {
column_list(p);
}
while !p.at(AS_KW) && !p.at(SELECT_KW) && !p.at(WITH_KW) && !at_stmt_terminator(p) {
bump_ddl_word(p);
}
p.eat(AS_KW);
if p.at(SELECT_KW) || p.at(WITH_KW) || p.at(VALUES_KW) || p.at(L_PAREN) {
query_expr(p);
}
}
fn create_table(p: &mut Parser) {
p.bump(TABLE_KW);
if_exists_clause(p);
name_ref(p);
if p.at(L_PAREN) {
column_def_list(p);
}
if p.dialect().supports_delta_table_options()
&& (p.nth_contextual(0, ContextualKeyword::Shallow)
|| p.nth_contextual(0, ContextualKeyword::Deep))
&& p.nth_contextual(1, ContextualKeyword::Clone)
{
p.bump_as(CONTEXTUAL_KEYWORD);
}
if p.nth_contextual(0, ContextualKeyword::Clone) {
p.bump_as(CONTEXTUAL_KEYWORD); if p.at_name() {
name_ref(p);
}
}
while !p.at(AS_KW) && !at_stmt_terminator(p) {
if p.dialect().supports_delta_table_options() && at_databricks_table_option(p) {
databricks_table_option(p);
} else {
bump_ddl_word(p);
}
}
if p.eat(AS_KW) {
query_expr(p);
}
}
fn at_databricks_table_option(p: &Parser) -> bool {
p.at(USING_KW)
|| p.nth_contextual(0, ContextualKeyword::Location)
|| p.nth_contextual(0, ContextualKeyword::Tblproperties)
|| p.nth_contextual(0, ContextualKeyword::Options)
|| (p.nth_contextual(0, ContextualKeyword::Partitioned) && p.nth_at(1, BY_KW))
|| (p.nth_contextual(0, ContextualKeyword::Cluster) && p.nth_at(1, BY_KW))
}
fn databricks_table_option(p: &mut Parser) {
let m = p.start();
if p.at(USING_KW) {
p.bump(USING_KW);
if p.at_name() {
name_ref(p);
} else if !at_databricks_table_option_stop(p) {
p.bump_any();
}
} else if p.nth_contextual(0, ContextualKeyword::Location) {
p.bump_as(CONTEXTUAL_KEYWORD);
if !at_databricks_table_option_stop(p) {
p.bump_any();
}
} else if p.nth_contextual(0, ContextualKeyword::Tblproperties)
|| p.nth_contextual(0, ContextualKeyword::Options)
{
p.bump_as(CONTEXTUAL_KEYWORD);
if p.at(L_PAREN) {
balanced_parens(p);
} else {
databricks_table_option_tail(p);
}
} else if p.nth_contextual(0, ContextualKeyword::Partitioned)
|| p.nth_contextual(0, ContextualKeyword::Cluster)
{
p.bump_as(CONTEXTUAL_KEYWORD);
p.expect(BY_KW);
if p.at(L_PAREN) {
balanced_parens(p);
} else {
databricks_table_option_tail(p);
}
} else {
bump_ddl_word(p);
}
m.complete(p, OBJECT_PROPERTY);
}
fn databricks_table_option_tail(p: &mut Parser) {
while !at_databricks_table_option_stop(p) && !at_databricks_table_option(p) {
if p.at(L_PAREN) {
balanced_parens(p);
} else {
bump_ddl_word(p);
}
}
}
fn at_databricks_table_option_stop(p: &Parser) -> bool {
p.at(AS_KW) || at_stmt_terminator(p)
}
fn column_def_list(p: &mut Parser) {
let m = p.start();
p.bump(L_PAREN);
if !p.at(R_PAREN) {
column_def(p);
while p.eat(COMMA) {
if p.at(R_PAREN) {
break;
}
column_def(p);
}
}
p.expect(R_PAREN);
m.complete(p, COLUMN_DEF_LIST);
}
fn column_def(p: &mut Parser) {
let m = p.start();
let starts_with_constraint = p.nth_any_contextual(0, DDL_CONSTRAINT_START_CONTEXTUAL_WORDS);
if !starts_with_constraint && !p.at(COMMA) && !p.at(R_PAREN) && !p.at_eof() {
p.bump_any(); }
balanced_token_run_until(p, |p| p.at(COMMA) || p.at(R_PAREN), bump_ddl_word);
m.complete(p, COLUMN_DEF);
}
fn bump_ddl_word(p: &mut Parser) {
if p.at_keyword() {
p.bump_any(); } else if is_ddl_contextual_word(p) {
p.bump_as(CONTEXTUAL_KEYWORD); } else {
p.bump_any();
}
}
fn is_ddl_contextual_word(p: &Parser) -> bool {
p.nth_any_contextual(0, DDL_CONTEXTUAL_WORDS)
}
pub(super) fn drop_stmt(p: &mut Parser) {
let m = p.start();
p.bump(DROP_KW);
if !at_stmt_terminator(p) {
p.bump_any();
}
if_exists_clause(p);
if p.at_name() {
name_ref(p);
}
while !at_stmt_terminator(p) {
bump_ddl_word(p);
}
m.complete(p, DROP_STMT);
}
pub(super) fn alter_stmt(p: &mut Parser) {
let m = p.start();
p.bump(ALTER_KW);
while !at_stmt_terminator(p) {
p.bump_any();
}
m.complete(p, ALTER_STMT);
}
pub(super) fn at_comment_stmt(p: &Parser) -> bool {
p.nth_contextual(0, ContextualKeyword::Comment) && p.nth_at(1, ON_KW)
}
pub(super) fn comment_stmt(p: &mut Parser) {
let m = p.start();
p.bump_as(CONTEXTUAL_KEYWORD); while !at_stmt_terminator(p) {
p.bump_any();
}
m.complete(p, COMMENT_STMT);
}