mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-22 15:04:04 +00:00
More tests and some small bugfixes
This commit is contained in:
parent
7624095738
commit
72024661a9
5 changed files with 202 additions and 27 deletions
|
@ -13,8 +13,8 @@ impl Dialect for PostgreSqlDialect {
|
|||
CHAR, CHARACTER, VARYING, LARGE, VARCHAR, CLOB, BINARY, VARBINARY, BLOB, FLOAT, REAL,
|
||||
DOUBLE, PRECISION, INT, INTEGER, SMALLINT, BIGINT, NUMERIC, DECIMAL, DEC, BOOLEAN,
|
||||
DATE, TIME, TIMESTAMP, VALUES, DEFAULT, ZONE, REGCLASS, TEXT, BYTEA, TRUE, FALSE, COPY,
|
||||
STDIN, PRIMARY, KEY, UNIQUE, UUID, ADD, CONSTRAINT, FOREIGN, REFERENCES,
|
||||
CASE, WHEN, THEN, ELSE, END,
|
||||
STDIN, PRIMARY, KEY, UNIQUE, UUID, ADD, CONSTRAINT, FOREIGN, REFERENCES, CASE, WHEN,
|
||||
THEN, ELSE, END, JOIN, LEFT, RIGHT, FULL, CROSS, OUTER, INNER, NATURAL, ON, USING,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -431,34 +431,34 @@ impl ToString for Join {
|
|||
}
|
||||
fn suffix(constraint: &JoinConstraint) -> String {
|
||||
match constraint {
|
||||
JoinConstraint::On(expr) => format!(" ON({})", expr.to_string()),
|
||||
JoinConstraint::Using(attrs) => format!(" USING({})", attrs.join(", ")),
|
||||
JoinConstraint::On(expr) => format!("ON {}", expr.to_string()),
|
||||
JoinConstraint::Using(attrs) => format!("USING({})", attrs.join(", ")),
|
||||
_ => "".to_string(),
|
||||
}
|
||||
}
|
||||
match &self.join_operator {
|
||||
JoinOperator::Inner(constraint) => format!(
|
||||
"{}INNER JOIN {}{}",
|
||||
" {}JOIN {} {}",
|
||||
prefix(constraint),
|
||||
self.relation.to_string(),
|
||||
prefix(constraint)
|
||||
suffix(constraint)
|
||||
),
|
||||
JoinOperator::Cross => format!("CROSS JOIN {}", self.relation.to_string()),
|
||||
JoinOperator::Cross => format!(" CROSS JOIN {}", self.relation.to_string()),
|
||||
JoinOperator::Implicit => format!(", {}", self.relation.to_string()),
|
||||
JoinOperator::LeftOuter(constraint) => format!(
|
||||
"{}LEFT OUTER JOIN {}{}",
|
||||
" {}LEFT JOIN {} {}",
|
||||
prefix(constraint),
|
||||
self.relation.to_string(),
|
||||
suffix(constraint)
|
||||
),
|
||||
JoinOperator::RightOuter(constraint) => format!(
|
||||
"{}RIGHT OUTER JOIN {}{}",
|
||||
" {}RIGHT JOIN {} {}",
|
||||
prefix(constraint),
|
||||
self.relation.to_string(),
|
||||
suffix(constraint)
|
||||
),
|
||||
JoinOperator::FullOuter(constraint) => format!(
|
||||
"{}FULL OUTER JOIN {}{}",
|
||||
" {}FULL JOIN {} {}",
|
||||
prefix(constraint),
|
||||
self.relation.to_string(),
|
||||
suffix(constraint)
|
||||
|
|
|
@ -11,22 +11,18 @@ fn parse_simple_select() {
|
|||
let sql = String::from("SELECT id, fname, lname FROM customer WHERE id = 1");
|
||||
let ast = parse_sql(&sql);
|
||||
match ast {
|
||||
ASTNode::SQLSelect {
|
||||
projection, ..
|
||||
} => {
|
||||
ASTNode::SQLSelect { projection, .. } => {
|
||||
assert_eq!(3, projection.len());
|
||||
}
|
||||
_ => assert!(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn parse_sql(sql: &str) -> ASTNode {
|
||||
let dialect = AnsiSqlDialect {};
|
||||
let mut tokenizer = Tokenizer::new(&dialect,&sql, );
|
||||
let mut tokenizer = Tokenizer::new(&dialect, &sql);
|
||||
let tokens = tokenizer.tokenize().unwrap();
|
||||
let mut parser = Parser::new(tokens);
|
||||
let ast = parser.parse().unwrap();
|
||||
ast
|
||||
}
|
||||
|
||||
|
|
|
@ -529,21 +529,20 @@ fn parse_joins_on() {
|
|||
})),
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
joins_from("SELECT * FROM t1 JOIN t2 ON c1 = c2"),
|
||||
joins_from(verified("SELECT * FROM t1 JOIN t2 ON c1 = c2")),
|
||||
vec![join_with_constraint("t2", JoinOperator::Inner)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from("SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2"),
|
||||
joins_from(verified("SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2")),
|
||||
vec![join_with_constraint("t2", JoinOperator::LeftOuter)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2"),
|
||||
joins_from(verified("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2")),
|
||||
vec![join_with_constraint("t2", JoinOperator::RightOuter)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from("SELECT * FROM t1 FULL OUTER JOIN t2 ON c1 = c2"),
|
||||
joins_from(verified("SELECT * FROM t1 FULL JOIN t2 ON c1 = c2")),
|
||||
vec![join_with_constraint("t2", JoinOperator::FullOuter)]
|
||||
);
|
||||
}
|
||||
|
@ -561,25 +560,61 @@ fn parse_joins_using() {
|
|||
}
|
||||
|
||||
assert_eq!(
|
||||
joins_from("SELECT * FROM t1 JOIN t2 USING(c1)"),
|
||||
joins_from(verified("SELECT * FROM t1 JOIN t2 USING(c1)")),
|
||||
vec![join_with_constraint("t2", JoinOperator::Inner)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from("SELECT * FROM t1 LEFT JOIN t2 USING(c1)"),
|
||||
joins_from(verified("SELECT * FROM t1 LEFT JOIN t2 USING(c1)")),
|
||||
vec![join_with_constraint("t2", JoinOperator::LeftOuter)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from("SELECT * FROM t1 RIGHT JOIN t2 USING(c1)"),
|
||||
joins_from(verified("SELECT * FROM t1 RIGHT JOIN t2 USING(c1)")),
|
||||
vec![join_with_constraint("t2", JoinOperator::RightOuter)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from("SELECT * FROM t1 FULL OUTER JOIN t2 USING(c1)"),
|
||||
joins_from(verified("SELECT * FROM t1 FULL JOIN t2 USING(c1)")),
|
||||
vec![join_with_constraint("t2", JoinOperator::FullOuter)]
|
||||
);
|
||||
}
|
||||
|
||||
fn joins_from(sql: &str) -> Vec<Join> {
|
||||
match parse_sql(sql) {
|
||||
#[test]
|
||||
fn parse_complex_join() {
|
||||
let sql = "SELECT c1, c2 FROM t1, t4 JOIN t2 ON t2.c = t1.c LEFT JOIN t3 USING(q, c) WHERE t4.c = t1.c";
|
||||
assert_eq!(sql, parse_sql(sql).to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_join_syntax_variants() {
|
||||
fn parses_to(from: &str, to: &str) {
|
||||
assert_eq!(to, &parse_sql(from).to_string())
|
||||
}
|
||||
|
||||
parses_to(
|
||||
"SELECT c1 FROM t1 INNER JOIN t2 USING(c1)",
|
||||
"SELECT c1 FROM t1 JOIN t2 USING(c1)",
|
||||
);
|
||||
parses_to(
|
||||
"SELECT c1 FROM t1 LEFT OUTER JOIN t2 USING(c1)",
|
||||
"SELECT c1 FROM t1 LEFT JOIN t2 USING(c1)",
|
||||
);
|
||||
parses_to(
|
||||
"SELECT c1 FROM t1 RIGHT OUTER JOIN t2 USING(c1)",
|
||||
"SELECT c1 FROM t1 RIGHT JOIN t2 USING(c1)",
|
||||
);
|
||||
parses_to(
|
||||
"SELECT c1 FROM t1 FULL OUTER JOIN t2 USING(c1)",
|
||||
"SELECT c1 FROM t1 FULL JOIN t2 USING(c1)",
|
||||
);
|
||||
}
|
||||
|
||||
fn verified(query: &str) -> ASTNode {
|
||||
let ast = parse_sql(query);
|
||||
assert_eq!(query, &ast.to_string());
|
||||
ast
|
||||
}
|
||||
|
||||
fn joins_from(ast: ASTNode) -> Vec<Join> {
|
||||
match ast {
|
||||
ASTNode::SQLSelect { joins, .. } => joins,
|
||||
_ => panic!("Expected SELECT"),
|
||||
}
|
||||
|
|
|
@ -716,6 +716,150 @@ fn parse_function_now() {
|
|||
assert_eq!(sql, ast.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_implicit_join() {
|
||||
let sql = "SELECT * FROM t1, t2";
|
||||
|
||||
match verified(sql) {
|
||||
ASTNode::SQLSelect { joins, .. } => {
|
||||
assert_eq!(joins.len(), 1);
|
||||
assert_eq!(
|
||||
joins[0],
|
||||
Join {
|
||||
relation: ASTNode::SQLIdentifier("t2".to_string()),
|
||||
join_operator: JoinOperator::Implicit
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => assert!(false),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_cross_join() {
|
||||
let sql = "SELECT * FROM t1 CROSS JOIN t2";
|
||||
|
||||
match verified(sql) {
|
||||
ASTNode::SQLSelect { joins, .. } => {
|
||||
assert_eq!(joins.len(), 1);
|
||||
assert_eq!(
|
||||
joins[0],
|
||||
Join {
|
||||
relation: ASTNode::SQLIdentifier("t2".to_string()),
|
||||
join_operator: JoinOperator::Cross
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => assert!(false),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_joins_on() {
|
||||
fn join_with_constraint(
|
||||
relation: impl Into<String>,
|
||||
f: impl Fn(JoinConstraint) -> JoinOperator,
|
||||
) -> Join {
|
||||
Join {
|
||||
relation: ASTNode::SQLIdentifier(relation.into()),
|
||||
join_operator: f(JoinConstraint::On(ASTNode::SQLBinaryExpr {
|
||||
left: Box::new(ASTNode::SQLIdentifier("c1".into())),
|
||||
op: SQLOperator::Eq,
|
||||
right: Box::new(ASTNode::SQLIdentifier("c2".into())),
|
||||
})),
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
joins_from(verified("SELECT * FROM t1 JOIN t2 ON c1 = c2")),
|
||||
vec![join_with_constraint("t2", JoinOperator::Inner)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from(verified("SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2")),
|
||||
vec![join_with_constraint("t2", JoinOperator::LeftOuter)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from(verified("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2")),
|
||||
vec![join_with_constraint("t2", JoinOperator::RightOuter)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from(verified("SELECT * FROM t1 FULL JOIN t2 ON c1 = c2")),
|
||||
vec![join_with_constraint("t2", JoinOperator::FullOuter)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_joins_using() {
|
||||
fn join_with_constraint(
|
||||
relation: impl Into<String>,
|
||||
f: impl Fn(JoinConstraint) -> JoinOperator,
|
||||
) -> Join {
|
||||
Join {
|
||||
relation: ASTNode::SQLIdentifier(relation.into()),
|
||||
join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
joins_from(verified("SELECT * FROM t1 JOIN t2 USING(c1)")),
|
||||
vec![join_with_constraint("t2", JoinOperator::Inner)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from(verified("SELECT * FROM t1 LEFT JOIN t2 USING(c1)")),
|
||||
vec![join_with_constraint("t2", JoinOperator::LeftOuter)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from(verified("SELECT * FROM t1 RIGHT JOIN t2 USING(c1)")),
|
||||
vec![join_with_constraint("t2", JoinOperator::RightOuter)]
|
||||
);
|
||||
assert_eq!(
|
||||
joins_from(verified("SELECT * FROM t1 FULL JOIN t2 USING(c1)")),
|
||||
vec![join_with_constraint("t2", JoinOperator::FullOuter)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_join_syntax_variants() {
|
||||
fn parses_to(from: &str, to: &str) {
|
||||
assert_eq!(to, &parse_sql(from).to_string())
|
||||
}
|
||||
|
||||
parses_to(
|
||||
"SELECT c1 FROM t1 INNER JOIN t2 USING(c1)",
|
||||
"SELECT c1 FROM t1 JOIN t2 USING(c1)",
|
||||
);
|
||||
parses_to(
|
||||
"SELECT c1 FROM t1 LEFT OUTER JOIN t2 USING(c1)",
|
||||
"SELECT c1 FROM t1 LEFT JOIN t2 USING(c1)",
|
||||
);
|
||||
parses_to(
|
||||
"SELECT c1 FROM t1 RIGHT OUTER JOIN t2 USING(c1)",
|
||||
"SELECT c1 FROM t1 RIGHT JOIN t2 USING(c1)",
|
||||
);
|
||||
parses_to(
|
||||
"SELECT c1 FROM t1 FULL OUTER JOIN t2 USING(c1)",
|
||||
"SELECT c1 FROM t1 FULL JOIN t2 USING(c1)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_complex_join() {
|
||||
let sql = "SELECT c1, c2 FROM t1, t4 JOIN t2 ON t2.c = t1.c LEFT JOIN t3 USING(q, c) WHERE t4.c = t1.c";
|
||||
assert_eq!(sql, parse_sql(sql).to_string());
|
||||
}
|
||||
|
||||
fn verified(query: &str) -> ASTNode {
|
||||
let ast = parse_sql(query);
|
||||
assert_eq!(query, &ast.to_string());
|
||||
ast
|
||||
}
|
||||
|
||||
fn joins_from(ast: ASTNode) -> Vec<Join> {
|
||||
match ast {
|
||||
ASTNode::SQLSelect { joins, .. } => joins,
|
||||
_ => panic!("Expected SELECT"),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_sql(sql: &str) -> ASTNode {
|
||||
debug!("sql: {}", sql);
|
||||
let mut parser = parser(sql);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue