Skip to content
This repository was archived by the owner on Sep 23, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 95 additions & 24 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,79 @@ impl Parser {
}
Ok(nodes)
}
fn parse_set_operator(&mut self, left: Node) -> BQ2CSTResult<Node> {
let mut operator: Node;
if self
.get_token(0)?
.in_(&vec!["INNER", "FULL", "LEFT", "OUTER"])
{
let mut method;
if self.get_token(0)?.in_(&vec!["INNER", "OUTER"]) {
method = self.construct_node(NodeType::Keyword)?;
} else {
method = self.construct_node(NodeType::KeywordSequence)?;
self.next_token()?; // -> OUTER
let outer = self.construct_node(NodeType::Keyword)?;
method.push_node("next_keyword", outer);
}

self.next_token()?; // -> UNION
operator = self.construct_node(NodeType::SetOperator)?;
operator.push_node("method", method);
} else {
operator = self.construct_node(NodeType::SetOperator)?;
}
self.next_token()?; // ALL | DISTINCT
operator.push_node("distinct_or_all", self.construct_node(NodeType::Keyword)?);

if self.get_token(1)?.is("BY") {
self.next_token()?; // -> BY
let mut by = self.construct_node(NodeType::KeywordSequence)?;
self.next_token()?; // -> NAME
let mut name: Node;
if self.get_token(1)?.is("ON") {
name = self.construct_node(NodeType::KeywordSequence)?;
self.next_token()?; // -> ON
let mut on = self.construct_node(NodeType::KeywordWithExpr)?;
self.next_token()?; // -> (
let columns = self.parse_grouped_exprs(false)?;

on.push_node("expr", columns);
name.push_node("next_keyword", on);
} else {
name = self.construct_node(NodeType::Keyword)?;
}
by.push_node("next_keyword", name);
operator.push_node("by", by);
} else if self.get_token(1)?.in_(&vec!["STRICT", "CORRESPONDING"]) {
self.next_token()?; // -> STRICT | CORRESPONDING
let mut strict_exists = false;
let mut strict = Node::empty(NodeType::Unknown);
if self.get_token(0)?.is("STRICT") {
strict_exists = true;
strict = self.construct_node(NodeType::KeywordSequence)?;
self.next_token()?; // -> CORRESPONDING
}
let mut corresponding = self.construct_node(NodeType::Keyword)?;
if self.get_token(1)?.is("BY") {
corresponding.node_type = NodeType::KeywordSequence;
self.next_token()?; // -> BY
let mut by = self.construct_node(NodeType::KeywordWithExpr)?;
self.next_token()?; // -> (
by.push_node("expr", self.parse_grouped_exprs(false)?);
corresponding.push_node("next_keyword", by);
}
if strict_exists {
strict.push_node("next_keyword", corresponding);
corresponding = strict;
}
operator.push_node("corresponding", corresponding);
}
operator.push_node("left", left);
self.next_token()?; // DISTINCT -> stmt
operator.push_node("right", self.parse_select_statement(false, false)?);
Ok(operator)
}
fn parse_statement(&mut self, semicolon: bool) -> BQ2CSTResult<Node> {
let node = match self.get_token(0)?.literal.to_uppercase().as_str() {
// SELECT
Expand Down Expand Up @@ -1588,19 +1661,18 @@ impl Parser {
));
}
node.push_node("rparen", self.construct_node(NodeType::Symbol)?);
while self
.get_token(1)?
.in_(&vec!["UNION", "INTERSECT", "EXCEPT"])
&& root
while self.get_token(1)?.in_(&vec![
"UNION",
"INTERSECT",
"EXCEPT",
"INNER",
"FULL",
"LEFT",
"OUTER",
]) && root
{
self.next_token()?; // stmt -> UNION
let mut operator = self.construct_node(NodeType::SetOperator)?;
self.next_token()?; // UNION -> DISTINCT
operator.push_node("distinct_or_all", self.construct_node(NodeType::Keyword)?);
operator.push_node("left", node);
self.next_token()?; // DISTINCT -> stmt
operator.push_node("right", self.parse_select_statement(false, false)?);
node = operator;
self.next_token()?;
node = self.parse_set_operator(node)?;
}
// ORDER BY
if self.get_token(1)?.is("ORDER") && root {
Expand Down Expand Up @@ -1811,19 +1883,18 @@ impl Parser {
node.push_node("limit", limit);
}
// UNION
while self
.get_token(1)?
.in_(&vec!["UNION", "INTERSECT", "EXCEPT"])
&& root
while self.get_token(1)?.in_(&vec![
"UNION",
"INTERSECT",
"EXCEPT",
"INNER",
"FULL",
"LEFT",
"OUTER",
]) && root
{
self.next_token()?; // stmt -> UNION
let mut operator = self.construct_node(NodeType::SetOperator)?;
self.next_token()?; // UNION -> DISTINCT
operator.push_node("distinct_or_all", self.construct_node(NodeType::Keyword)?);
operator.push_node("left", node);
self.next_token()?; // DISTINCT -> stmt
operator.push_node("right", self.parse_select_statement(false, false)?);
node = operator;
self.next_token()?;
node = self.parse_set_operator(node)?;
}
// ;
if self.get_token(1)?.is(";") && semicolon {
Expand Down
167 changes: 167 additions & 0 deletions src/parser/tests/tests_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,173 @@ right:
self: SELECT (SelectStatement)
exprs:
- self: 3 (NumericLiteral)
",
0,
)),
Box::new(SuccessTestCase::new(
"\
SELECT 1 INNER UNION ALL BY NAME SELECT 2
",
"\
self: UNION (SetOperator)
by:
self: BY (KeywordSequence)
next_keyword:
self: NAME (Keyword)
distinct_or_all:
self: ALL (Keyword)
left:
self: SELECT (SelectStatement)
exprs:
- self: 1 (NumericLiteral)
method:
self: INNER (Keyword)
right:
self: SELECT (SelectStatement)
exprs:
- self: 2 (NumericLiteral)
",
0,
)),
Box::new(SuccessTestCase::new(
"\
SELECT 1 FULL OUTER UNION ALL BY NAME ON (foo) SELECT 2
",
"\
self: UNION (SetOperator)
by:
self: BY (KeywordSequence)
next_keyword:
self: NAME (KeywordSequence)
next_keyword:
self: ON (KeywordWithExpr)
expr:
self: ( (GroupedExprs)
exprs:
- self: foo (Identifier)
rparen:
self: ) (Symbol)
distinct_or_all:
self: ALL (Keyword)
left:
self: SELECT (SelectStatement)
exprs:
- self: 1 (NumericLiteral)
method:
self: FULL (KeywordSequence)
next_keyword:
self: OUTER (Keyword)
right:
self: SELECT (SelectStatement)
exprs:
- self: 2 (NumericLiteral)
",
0,
)),
Box::new(SuccessTestCase::new(
"\
SELECT 1 UNION ALL CORRESPONDING SELECT 2
",
"\
self: UNION (SetOperator)
corresponding:
self: CORRESPONDING (Keyword)
distinct_or_all:
self: ALL (Keyword)
left:
self: SELECT (SelectStatement)
exprs:
- self: 1 (NumericLiteral)
right:
self: SELECT (SelectStatement)
exprs:
- self: 2 (NumericLiteral)
",
0,
)),
Box::new(SuccessTestCase::new(
"\
SELECT 1 UNION ALL STRICT CORRESPONDING SELECT 2
",
"\
self: UNION (SetOperator)
corresponding:
self: STRICT (KeywordSequence)
next_keyword:
self: CORRESPONDING (Keyword)
distinct_or_all:
self: ALL (Keyword)
left:
self: SELECT (SelectStatement)
exprs:
- self: 1 (NumericLiteral)
right:
self: SELECT (SelectStatement)
exprs:
- self: 2 (NumericLiteral)
",
0,
)),
Box::new(SuccessTestCase::new(
"\
SELECT 1 UNION ALL STRICT CORRESPONDING BY (foo, bar) SELECT 2
",
"\
self: UNION (SetOperator)
corresponding:
self: STRICT (KeywordSequence)
next_keyword:
self: CORRESPONDING (KeywordSequence)
next_keyword:
self: BY (KeywordWithExpr)
expr:
self: ( (GroupedExprs)
exprs:
- self: foo (Identifier)
comma:
self: , (Symbol)
- self: bar (Identifier)
rparen:
self: ) (Symbol)
distinct_or_all:
self: ALL (Keyword)
left:
self: SELECT (SelectStatement)
exprs:
- self: 1 (NumericLiteral)
right:
self: SELECT (SelectStatement)
exprs:
- self: 2 (NumericLiteral)
",
0,
)),
Box::new(SuccessTestCase::new(
"\
SELECT 1 UNION ALL CORRESPONDING BY (foo) SELECT 2
",
"\
self: UNION (SetOperator)
corresponding:
self: CORRESPONDING (KeywordSequence)
next_keyword:
self: BY (KeywordWithExpr)
expr:
self: ( (GroupedExprs)
exprs:
- self: foo (Identifier)
rparen:
self: ) (Symbol)
distinct_or_all:
self: ALL (Keyword)
left:
self: SELECT (SelectStatement)
exprs:
- self: 1 (NumericLiteral)
right:
self: SELECT (SelectStatement)
exprs:
- self: 2 (NumericLiteral)
",
0,
)),
Expand Down
2 changes: 2 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,8 @@ export type SetOperator = XXXStatement & {
node_type: "SetOperator";
children: {
with?: { Node: WithClause };
by?: NodeChild;
corresponding?: NodeChild;
distinct_or_all: NodeChild;
left: { Node: SetOperator | SelectStatement | GroupedStatement };
right: { Node: SetOperator | SelectStatement | GroupedStatement };
Expand Down