// Port of sqlglot/parser.py: TRUNCATE, COPY, GRANT/REVOKE, pipe syntax, DECLARE and
// other miscellaneous parsers.
///|
pub fn Parser::parse_truncate_table(self : Parser) -> Expr? raise SqlglotError {
let start = self.prev
if self.match_(L_PAREN) {
self.retreat(self.index - 2)
return self.parse_function()
}
let is_database = self.match_(DATABASE)
self.match_(TABLE) |> ignore
let exists = self.parse_exists(not_=false)
let expressions = self.parse_csv(() => {
self.parse_table(schema=true, is_db_reference=is_database)
})
let cluster = if self.match_(ON) { self.parse_on_property() } else { None }
let identity = if self.match_text_seq(["RESTART", "IDENTITY"]) {
Some("RESTART")
} else if self.match_text_seq(["CONTINUE", "IDENTITY"]) {
Some("CONTINUE")
} else {
None
}
let option = if self.match_text("CASCADE") || self.match_text("RESTRICT") {
Some(self.prev.text)
} else {
None
}
let partition = self.parse_partition()
if self.curr.ok() {
return Some(self.parse_as_command(start))
}
Some(
self.expression(
mk(TruncateTable, [
("expressions", expressions),
("is_database", is_database),
("exists", exists),
("cluster", cluster),
("identity", identity),
("option", option),
("partition", partition),
]),
),
)
}
///|
pub fn Parser::parse_indexed_column(self : Parser) -> Expr? raise SqlglotError {
self.parse_ordered(parse_method=() => self.parse_opclass())
}
///|
pub fn Parser::parse_with_operator(self : Parser) -> Expr? raise SqlglotError {
let this = self.parse_indexed_column()
if !self.match_(WITH) {
return this
}
let op = self.parse_var(any_token=true, tokens=self.cfg.reserved_tokens)
Some(self.expression(mk(WithOperator, [("this", this), ("op", op)])))
}
///|
pub fn Parser::parse_wrapped_options(
self : Parser,
) -> Array[Expr] raise SqlglotError {
self.match_(EQ) |> ignore
self.match_(L_PAREN) |> ignore
let opts = []
while self.curr.ok() && !self.match_(R_PAREN) {
let option : Value? = if self.match_text_seq(["FORMAT_NAME", "="]) {
Some(Node(self.parse_format_name()))
} else {
self.parse_property()
}
match option {
None => {
self.raise_error("Unable to parse option")
break
}
Some(Node(o)) => opts.push(o)
Some(List(l)) =>
for x in l {
match x {
Node(o) => opts.push(o)
_ => ()
}
}
_ => ()
}
}
opts
}
///|
pub fn Parser::parse_copy_parameters(
self : Parser,
) -> Array[Expr] raise SqlglotError {
let sep : TokenType? = if self.dialect.cfg.copy_params_are_csv {
Some(COMMA)
} else {
None
}
let options = []
while self.curr.ok() && !self.match_(R_PAREN, advance=false) {
let option = self.parse_var(any_token=true)
let prev = self.prev_upper()
self.match_(EQ) |> ignore
self.match_(ALIAS) |> ignore
let param = self.expression(mk1(CopyParameter, option))
if self.cfg.copy_into_varlen_options.contains(prev) &&
self.match_(L_PAREN, advance=false) {
param.set("expressions", self.parse_wrapped_options())
} else if prev == "FILE_FORMAT" {
param.set("expression", self.parse_field())
} else if prev == "FORMAT" &&
self.prev.token_type == ALIAS &&
self.match_texts(["AVRO", "JSON"]) {
param.set("this", var_("FORMAT AS \{self.prev_upper()}"))
param.set("expression", self.parse_field())
} else {
param.set(
"expression",
expr_or(self.parse_unquoted_field(), () => self.parse_bracket(None)),
)
}
options.push(param)
match sep {
Some(s) => self.match_(s) |> ignore
None => ()
}
}
options
}
///|
pub fn Parser::parse_credentials(self : Parser) -> Expr? raise SqlglotError {
let expr = self.expression(mk0(Credentials))
if self.match_text_seq(["STORAGE_INTEGRATION", "="]) {
expr.set("storage", self.parse_field())
}
if self.match_text("CREDENTIALS") {
if self.match_(EQ) {
expr.set("credentials", self.parse_wrapped_options())
} else {
expr.set("credentials", self.parse_field())
}
}
if self.match_text("ENCRYPTION") {
expr.set("encryption", self.parse_wrapped_options())
}
if self.match_text("IAM_ROLE") {
let role = if self.match_(DEFAULT) {
Some(var_(self.prev.text))
} else {
self.parse_field()
}
expr.set("iam_role", role)
}
if self.match_text("REGION") {
expr.set("region", self.parse_field())
}
Some(expr)
}
///|
pub fn Parser::parse_file_location(self : Parser) -> Expr? raise SqlglotError {
match self.fns.hooks.parse_file_location {
Some(f) => f(self)
None => self.parse_field()
}
}
///|
pub fn Parser::parse_copy(self : Parser) -> Expr? raise SqlglotError {
let start = self.prev
self.match_(INTO) |> ignore
let this = if self.match_(L_PAREN, advance=false) {
self.parse_select(nested=true, parse_subquery_alias=false)
} else {
self.parse_table(schema=true)
}
let kind = self.match_(FROM) || !self.match_text("TO")
let mut files = self.parse_csv(() => self.parse_file_location())
if self.match_(EQ, advance=false) {
self.advance(times=-1)
files = []
}
let credentials = self.parse_credentials()
self.match_text("WITH") |> ignore
let params = self.parse_wrapped(
() => self.parse_copy_parameters(),
optional=true,
)
if self.curr.ok() {
return Some(self.parse_as_command(start))
}
Some(
self.expression(
mk(Copy, [
("this", this),
("kind", kind),
("credentials", credentials),
("files", files),
("params", params),
]),
),
)
}
///|
pub fn Parser::parse_normalize(self : Parser) -> Expr? raise SqlglotError {
let this = self.parse_bitwise()
let form = self.andv(self.match_(COMMA), () => self.parse_var())
Some(self.expression(mk(Normalize, [("this", this), ("form", form)])))
}
///|
pub fn Parser::parse_ceil_floor(
self : Parser,
kind : Kind,
) -> Expr? raise SqlglotError {
let args = self.parse_csv(() => self.parse_lambda())
let this = args.get(0)
let decimals = args.get(1)
let to = if self.match_text("TO") { self.parse_var() } else { None }
Some(mk(kind, [("this", this), ("decimals", decimals), ("to", to)]))
}
///|
pub fn Parser::parse_star_ops(self : Parser) -> Expr? raise SqlglotError {
let star_token = self.prev
if self.match_text_seq(["COLUMNS", "("], advance=false) {
let this = self.parse_function()
match this {
Some(t) if t.kind == Columns => t.set("unpack", true)
_ => ()
}
return this
}
let index = self.index
let ilike = if self.match_(ILIKE) { self.parse_string() } else { None }
if ilike is None {
self.retreat(index)
}
let except_ = self.parse_star_op(["EXCEPT", "EXCLUDE"])
let replace = self.parse_star_op(["REPLACE"])
let rename = self.parse_star_op(["RENAME"])
Some(
self
.expression(
mk(Star, [
("ilike", ilike),
("except_", except_),
("replace", replace),
("rename", rename),
]),
)
.update_positions_from_token(star_token),
)
}
///|
pub fn Parser::parse_grant_privilege(self : Parser) -> Expr? raise SqlglotError {
let privilege_parts = []
while self.curr.ok() &&
!self.match_set(self.cfg.privilege_follow_tokens, advance=false) {
privilege_parts.push(py_upper(self.curr.text))
self.advance()
}
if privilege_parts.is_empty() {
self.raise_error("Expected privilege")
return None
}
let this = var_(privilege_parts.join(" "))
let expressions = if self.match_(L_PAREN, advance=false) {
Some(self.parse_wrapped_csv(() => self.parse_column()))
} else {
None
}
Some(
self.expression(
mk(GrantPrivilege, [("this", this), ("expressions", expressions)]),
),
)
}
///|
pub fn Parser::parse_grant_principal(self : Parser) -> Expr? raise SqlglotError {
let kind : Value = if self.match_texts(["ROLE", "GROUP"]) {
Str(self.prev_upper())
} else {
Bool(false)
}
let principal = match self.parse_id_var() {
Some(p) => p
None => return None
}
Some(
self.expression(mk(GrantPrincipal, [("this", principal), ("kind", kind)])),
)
}
///|
pub fn Parser::parse_grant_revoke_common(
self : Parser,
) -> (Array[Expr], String?, Expr?) raise SqlglotError {
let privileges = self.parse_csv(() => self.parse_grant_privilege())
self.match_(ON) |> ignore
let kind = if self.match_set(self.cfg.creatables) {
Some(self.prev_upper())
} else {
None
}
let securable = self.try_parse(() => self.parse_table_parts())
(privileges, kind, securable)
}
///|
pub fn Parser::parse_grant(self : Parser) -> Expr? raise SqlglotError {
let start = self.prev
let (privileges, kind, securable) = self.parse_grant_revoke_common()
if securable is None || !self.match_text("TO") {
return Some(self.parse_as_command(start))
}
let principals = self.parse_csv(() => self.parse_grant_principal())
let grant_option = self.match_text_seq(["WITH", "GRANT", "OPTION"])
if self.curr.ok() {
return Some(self.parse_as_command(start))
}
Some(
self.expression(
mk(Grant, [
("privileges", privileges),
("kind", kind),
("securable", securable),
("principals", principals),
("grant_option", grant_option),
]),
),
)
}
///|
pub fn Parser::parse_revoke(self : Parser) -> Expr? raise SqlglotError {
let start = self.prev
let grant_option = self.match_text_seq(["GRANT", "OPTION", "FOR"])
let (privileges, kind, securable) = self.parse_grant_revoke_common()
if securable is None || !self.match_text("FROM") {
return Some(self.parse_as_command(start))
}
let principals = self.parse_csv(() => self.parse_grant_principal())
let cascade = if self.match_texts(["CASCADE", "RESTRICT"]) {
Some(self.prev_upper())
} else {
None
}
if self.curr.ok() {
return Some(self.parse_as_command(start))
}
Some(
self.expression(
mk(Revoke, [
("privileges", privileges),
("kind", kind),
("securable", securable),
("principals", principals),
("grant_option", grant_option),
("cascade", cascade),
]),
),
)
}
///|
pub fn Parser::parse_overlay(self : Parser) -> Expr? raise SqlglotError {
fn parse_overlay_arg(text : String) -> Expr? raise SqlglotError {
if self.match_(COMMA) || self.match_text(text) {
self.parse_bitwise()
} else {
None
}
}
let this = self.parse_bitwise()
let expression = parse_overlay_arg("PLACING")
let from_ = parse_overlay_arg("FROM")
let for_ = parse_overlay_arg("FOR")
Some(
self.expression(
mk(Overlay, [
("this", this),
("expression", expression),
("from_", from_),
("for_", for_),
]),
),
)
}
///|
pub fn Parser::parse_format_name(self : Parser) -> Expr raise SqlglotError {
let value = expr_or(self.parse_string(), () => self.parse_table_parts())
self.expression(
mk(Property, [("this", var_("FORMAT_NAME")), ("value", value)]),
)
}
// ---------------------------------------------------------------------------
// Pipe syntax
///|
pub fn Parser::build_pipe_cte(
self : Parser,
query : Expr,
expressions : Array[Expr],
alias_cte? : Expr,
) -> Expr {
// Python passes either the TableAlias (used as-is by `from_`) or a name string
// (parsed into a Table by `from_`).
let new_cte : Expr = match alias_cte {
Some(a) => a
None => {
self.pipe_cte_counter += 1
mk1(TableAlias, to_identifier("__tmp\{self.pipe_cte_counter}"))
}
}
let ctes = match query.arg("with_") {
Some(w) => Some(w.pop())
None => None
}
let new_select = mk(Select, [("expressions", expressions)])
let table_name = match new_cte.this() {
Some(i) => i.copy()
None => to_identifier("")
}
new_select.set("from_", match alias_cte {
Some(a) => mk1(From, a.copy())
None => mk1(From, mk1(Table, table_name))
})
match ctes {
Some(c) => new_select.set("with_", c)
None => ()
}
let cte = mk(CTE, [("this", query), ("alias", new_cte)])
match new_select.arg("with_") {
Some(w) => w.append("expressions", cte)
None => new_select.set("with_", mk(With, [("expressions", [cte])]))
}
new_select
}
///|
pub fn Parser::parse_pipe_syntax_select(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
match self.parse_select(consume_pipe=false) {
None => Some(query)
Some(select) => {
query.set("expressions", select.expressions().map(e => e))
Some(self.build_pipe_cte(query, [mk0(Star)]))
}
}
}
///|
pub fn Parser::parse_pipe_syntax_limit(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
let limit = self.parse_limit()
let offset = self.parse_offset()
match limit {
Some(l) => {
let curr_limit = match query.arg("limit") {
Some(c) => c
None => l
}
let cur = curr_limit.expression().bind(x => x.to_py_float())
let new = l.expression().bind(x => x.to_py_float())
match (cur, new) {
(Some(c), Some(n)) if c >= n => {
let lim = if l.kind == Limit {
l
} else {
mk(Limit, [("expression", l)])
}
query.set("limit", lim)
}
_ => ()
}
}
None => ()
}
match offset {
Some(o) => {
let curr = match query.arg("offset") {
Some(c) => c.expression().bind(x => x.to_py_int()).unwrap_or(0L)
None => 0L
}
let add = o.expression().bind(x => x.to_py_int()).unwrap_or(0L)
query.set(
"offset",
mk(Offset, [("expression", literal_number((curr + add).to_string()))]),
)
}
None => ()
}
Some(query)
}
///|
pub fn Parser::parse_pipe_syntax_aggregate_fields(
self : Parser,
) -> Expr? raise SqlglotError {
let this = self.parse_disjunction()
if self.match_text_seq(["GROUP", "AND"], advance=false) {
return this
}
let this = self.parse_alias(this)
if self.match_any([ASC, DESC], advance=false) {
return self.parse_ordered(parse_method=() => this)
}
this
}
///|
pub fn Parser::parse_pipe_syntax_aggregate_group_order_by(
self : Parser,
query : Expr,
group_by_exists? : Bool = true,
) -> Expr raise SqlglotError {
let expr = self.parse_csv(() => self.parse_pipe_syntax_aggregate_fields())
let aggregates_or_groups = []
let orders = []
for element in expr {
let this = if element.kind == Ordered {
let t = element.this().unwrap()
if t.kind.is_a(Alias) {
element.set("this", t.arg("alias"))
}
orders.push(element)
t
} else {
element
}
aggregates_or_groups.push(this)
}
if group_by_exists {
let projections = aggregates_or_groups + query.expressions()
query.set("expressions", projections)
let groups = aggregates_or_groups.map(p => {
match p.arg("alias") {
Some(a) => a
None => p
}
})
let existing = match query.arg("group") {
Some(g) => g.expressions()
None => []
}
query.set(
"group",
mk(Group, [("expressions", existing + groups.map(g => g.copy()))]),
)
} else {
query.set("expressions", aggregates_or_groups)
}
if !orders.is_empty() {
query.set("order", mk(Order, [("expressions", orders)]))
}
query
}
///|
pub fn Parser::parse_pipe_syntax_aggregate(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
self.match_text("AGGREGATE") |> ignore
let mut query = self.parse_pipe_syntax_aggregate_group_order_by(
query,
group_by_exists=false,
)
if self.match_(GROUP_BY) ||
(self.match_text_seq(["GROUP", "AND"]) && self.match_(ORDER_BY)) {
query = self.parse_pipe_syntax_aggregate_group_order_by(query)
}
Some(self.build_pipe_cte(query, [mk0(Star)]))
}
///|
pub fn Parser::parse_pipe_syntax_set_operator(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
let first_setop = match self.parse_set_operation(Some(query)) {
Some(s) => s
None => return None
}
fn parse_and_unwrap_query() -> Expr? raise SqlglotError {
match self.parse_paren() {
Some(e) => Some(e.unnest())
None => None
}
}
match first_setop.this() {
Some(t) => t.pop() |> ignore
None => ()
}
let setops = [first_setop.expression().unwrap().pop().unnest()]
setops.append(self.parse_csv(parse_and_unwrap_query))
let mut query = self.build_pipe_cte(query, [mk0(Star)])
let ctes = match query.arg("with_") {
Some(w) => Some(w.pop())
None => None
}
for s in setops {
let op = mk(first_setop.kind, [("this", query), ("expression", s)])
for k, v in first_setop.args {
if k != "this" && k != "expression" {
op.set(k, v)
}
}
query = op
}
query.set("with_", ctes)
Some(self.build_pipe_cte(query, [mk0(Star)]))
}
///|
pub fn Parser::parse_pipe_syntax_join(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
let join = match self.parse_join() {
Some(j) => j
None => return None
}
if query.kind == Select {
query.append("joins", join)
}
Some(query)
}
///|
pub fn Parser::parse_pipe_syntax_pivot(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
let pivots = match self.parse_pivots() {
Some(p) => p
None => return Some(query)
}
match query.arg("from_") {
Some(f) =>
match f.this() {
Some(t) => t.set("pivots", pivots)
None => ()
}
None => ()
}
Some(self.build_pipe_cte(query, [mk0(Star)]))
}
///|
pub fn Parser::parse_pipe_syntax_extend(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
self.match_text("EXTEND") |> ignore
query.set("expressions", [mk0(Star)] + self.parse_expressions())
Some(self.build_pipe_cte(query, [mk0(Star)]))
}
///|
pub fn Parser::parse_pipe_syntax_tablesample(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
let sample = self.parse_table_sample()
match query.arg("with_") {
Some(w) => {
let exprs = w.expressions()
match exprs[exprs.length() - 1].this() {
Some(t) => t.set("sample", sample)
None => ()
}
}
None => query.set("sample", sample)
}
Some(query)
}
///|
pub fn Parser::parse_pipe_syntax_query(
self : Parser,
query : Expr,
) -> Expr? raise SqlglotError {
let mut query = query
if query.kind.is_a(Subquery) {
query = select_star_from(Some(query))
}
if !query.has("from_") {
query = select_star_from(Some(query.subquery(copy=false)))
}
while self.match_(PIPE_GT) {
let start_index = self.index
let start_text = py_upper(self.curr.text)
match self.fns.pipe_syntax_transform_parsers.get(start_text) {
None => {
let parsed_query = match self.parse_pipe_syntax_set_operator(query) {
Some(q) => Some(q)
None => self.parse_pipe_syntax_join(query)
}
match parsed_query {
Some(q) => query = q
None => {
self.retreat(start_index)
self.raise_error(
"Unsupported pipe syntax operator: '\{start_text}'.",
)
break
}
}
}
Some(parser) =>
match parser(self, query) {
Some(q) => query = q
None => ()
}
}
}
Some(query)
}
// ---------------------------------------------------------------------------
///|
pub fn Parser::parse_declareitem(self : Parser) -> Expr? raise SqlglotError {
self.match_texts(["VAR", "VARIABLE"]) |> ignore
let vars = self.parse_csv(() => self.parse_id_var())
if vars.is_empty() {
return None
}
self.match_(ALIAS) |> ignore
let kind = if self.match_(TABLE) {
self.parse_schema()
} else {
self.parse_types()
}
let default = self.andv(self.match_(DEFAULT) || self.match_(EQ), () => {
self.parse_bitwise()
})
Some(
self.expression(
mk(DeclareItem, [("this", vars), ("kind", kind), ("default", default)]),
),
)
}
///|
pub fn Parser::parse_declare(self : Parser) -> Expr? raise SqlglotError {
let start = self.prev
let replace = self.match_text_seq(["OR", "REPLACE"])
let expressions = self.try_parse(fn() raise SqlglotError {
let r = self.parse_csv(() => self.parse_declareitem())
if r.is_empty() {
None
} else {
Some(r)
}
})
match expressions {
Some(e) if !self.curr.ok() =>
Some(
self.expression(mk(Declare, [("expressions", e), ("replace", replace)])),
)
_ => Some(self.parse_as_command(start))
}
}
///|
pub fn Parser::parse_json_value(self : Parser) -> Expr? raise SqlglotError {
let this = self.parse_bitwise()
self.match_(COMMA) |> ignore
let path = self.parse_bitwise()
let returning = self.andv(self.match_(RETURNING), () => self.parse_type())
let on_condition = self.parse_on_condition()
Some(
self.expression(
mk(JSONValue, [
("this", this),
("path", self.dialect.to_json_path(path)),
("returning", returning),
("on_condition", on_condition),
]),
),
)
}
///|
pub fn Parser::parse_group_concat(self : Parser) -> Expr? raise SqlglotError {
match self.fns.hooks.parse_group_concat {
Some(f) => f(self)
None => self.parse_group_concat_base()
}
}
///|
pub fn Parser::parse_group_concat_base(
self : Parser,
) -> Expr? raise SqlglotError {
let args = self.parse_csv(() => self.parse_lambda())
fn concat_exprs(
node : Expr?,
exprs : Array[Expr],
) -> Expr raise SqlglotError {
match node {
Some(n) if n.kind == Distinct && n.expressions().length() > 1 => {
let c = self.expression(
mk(Concat, [
("expressions", n.expressions()),
("safe", true),
("coalesce", self.dialect.cfg.concat_coalesce),
]),
)
n.set("expressions", [c])
return n
}
_ => ()
}
if exprs.length() == 1 {
return exprs[0]
}
self.expression(
mk(Concat, [
("expressions", args),
("safe", true),
("coalesce", self.dialect.cfg.concat_coalesce),
]),
)
}
let this = if !args.is_empty() {
let last = args[args.length() - 1]
let order = if last.kind.is_a(Order) { Some(last) } else { None }
match order {
Some(o) => {
args[args.length() - 1] = o.this().unwrap()
o.set("this", concat_exprs(o.this(), args))
Some(o)
}
None => Some(concat_exprs(Some(args[0]), args))
}
} else {
None
}
let separator = if self.match_(SEPARATOR) { self.parse_field() } else { None }
Some(
self.expression(mk(GroupConcat, [("this", this), ("separator", separator)])),
)
}
///|
pub fn Parser::parse_initcap(self : Parser) -> Expr? raise SqlglotError {
let expr = from_arg_list(Initcap, self.parse_function_args())
if !expr.args.contains("expression") {
expr.set(
"expression",
literal_string(self.dialect.cfg.initcap_default_delimiter_chars),
)
}
Some(expr)
}
///|
pub fn Parser::parse_operator(
self : Parser,
this : Expr?,
) -> Expr? raise SqlglotError {
if !self.match_(L_PAREN) {
self.retreat(self.index - 1)
return None
}
let op = StringBuilder()
while self.curr.ok() && !self.match_(R_PAREN) {
op.write_string(self.curr.text)
self.advance()
}
let comments = self.prev_comments
Some(
self.expression(
mk(Operator, [
("this", this),
("operator", op.to_string()),
("expression", self.parse_bitwise()),
]),
comments~,
),
)
}