use sql_dialect_fmt_syntax::SyntaxKind::*;
use crate::parser::{ContextualKeyword, Parser};
use super::{at_stmt_terminator, name_ref};
const OBJECT_TYPE_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Schema,
ContextualKeyword::Database,
ContextualKeyword::Stage,
ContextualKeyword::Sequence,
ContextualKeyword::Stream,
];
const GRANTEE_KIND_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Role,
ContextualKeyword::User,
ContextualKeyword::Share,
ContextualKeyword::Database,
];
const GRANT_TAIL_CONTEXTUAL_WORDS: &[ContextualKeyword] = &[
ContextualKeyword::Option,
ContextualKeyword::Cascade,
ContextualKeyword::Restrict,
];
const GRANT_TAIL_START_CONTEXTUAL_WORDS: &[ContextualKeyword] =
&[ContextualKeyword::Cascade, ContextualKeyword::Restrict];
pub(super) fn grant_stmt(p: &mut Parser) {
let m = p.start();
p.bump(GRANT_KW);
priv_list(p, |p| p.at(ON_KW) || at_to(p));
if p.at(ON_KW) {
grant_target(p);
}
if at_to(p) {
grantee(p, GranteeIntro::To);
}
grant_tail(p);
m.complete(p, GRANT_STMT);
}
fn at_to(p: &Parser) -> bool {
p.nth_contextual(0, ContextualKeyword::To)
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum GranteeIntro {
To,
From,
}
pub(super) fn revoke_stmt(p: &mut Parser) {
let m = p.start();
p.bump(REVOKE_KW);
if p.at(GRANT_KW) && p.nth_contextual(1, ContextualKeyword::Option) {
p.bump(GRANT_KW);
p.bump_as(CONTEXTUAL_KEYWORD); p.eat(FOR_KW);
}
priv_list(p, |p| p.at(ON_KW) || p.at(FROM_KW));
if p.at(ON_KW) {
grant_target(p);
}
if p.at(FROM_KW) {
grantee(p, GranteeIntro::From);
}
grant_tail(p);
m.complete(p, REVOKE_STMT);
}
fn priv_list(p: &mut Parser, stop: impl Fn(&Parser) -> bool) {
let m = p.start();
while !stop(p) && !at_to(p) && !at_stmt_terminator(p) {
if p.at(COMMA) {
p.bump(COMMA);
} else if p.nth_contextual(0, ContextualKeyword::Privileges) {
p.bump_as(CONTEXTUAL_KEYWORD); } else {
p.bump_any();
}
}
m.complete(p, PRIV_LIST);
}
fn grant_target(p: &mut Parser) {
let m = p.start();
p.bump(ON_KW);
while !at_to(p) && !p.at(FROM_KW) && !at_grant_tail(p) && !at_stmt_terminator(p) {
if at_object_type_word(p) {
p.bump_as(CONTEXTUAL_KEYWORD); } else {
p.bump_any();
}
}
m.complete(p, GRANT_TARGET);
}
fn at_object_type_word(p: &Parser) -> bool {
p.nth_any_contextual(0, OBJECT_TYPE_CONTEXTUAL_WORDS)
}
fn grantee(p: &mut Parser, intro: GranteeIntro) {
let m = p.start();
match intro {
GranteeIntro::To => {
if at_to(p) {
p.bump_as(CONTEXTUAL_KEYWORD);
}
}
GranteeIntro::From => p.expect(FROM_KW),
}
while at_grantee_kind(p) {
p.bump_as(CONTEXTUAL_KEYWORD);
}
if p.at_name() {
name_ref(p);
}
m.complete(p, GRANTEE);
}
fn at_grantee_kind(p: &Parser) -> bool {
p.nth_any_contextual(0, GRANTEE_KIND_CONTEXTUAL_WORDS)
}
fn grant_tail(p: &mut Parser) {
while !at_stmt_terminator(p) {
if p.nth_any_contextual(0, GRANT_TAIL_CONTEXTUAL_WORDS) {
p.bump_as(CONTEXTUAL_KEYWORD);
} else {
p.bump_any();
}
}
}
fn at_grant_tail(p: &Parser) -> bool {
p.at(WITH_KW) || p.nth_any_contextual(0, GRANT_TAIL_START_CONTEXTUAL_WORDS)
}