Skip to content

Commit

Permalink
Support mysql partition to table selection (apache#959)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
  • Loading branch information
2 people authored and serprex committed Nov 6, 2023
1 parent 6d0abbd commit 8e8358b
Show file tree
Hide file tree
Showing 14 changed files with 108 additions and 7 deletions.
6 changes: 6 additions & 0 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,8 @@ pub enum TableFactor {
/// Optional version qualifier to facilitate table time-travel, as
/// supported by BigQuery and MSSQL.
version: Option<TableVersion>,
/// [Partition selection](https://dev.mysql.com/doc/refman/8.0/en/partitioning-selection.html), supported by MySQL.
partitions: Vec<Ident>,
},
Derived {
lateral: bool,
Expand Down Expand Up @@ -734,8 +736,12 @@ impl fmt::Display for TableFactor {
args,
with_hints,
version,
partitions,
} => {
write!(f, "{name}")?;
if !partitions.is_empty() {
write!(f, "PARTITION ({})", display_comma_separated(partitions))?;
}
if let Some(args) = args {
write!(f, "({})", display_comma_separated(args))?;
}
Expand Down
2 changes: 2 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,8 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
Keyword::QUALIFY,
Keyword::WINDOW,
Keyword::END,
// for MYSQL PARTITION SELECTION
Keyword::PARTITION,
];

/// Can't be used as a column alias, so that `SELECT <expr> alias`
Expand Down
41 changes: 41 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6324,6 +6324,14 @@ impl<'a> Parser<'a> {
} else {
let name = self.parse_object_name()?;

let partitions: Vec<Ident> = if dialect_of!(self is MySqlDialect | GenericDialect)
&& self.parse_keyword(Keyword::PARTITION)
{
self.parse_partitions()?
} else {
vec![]
};

// Parse potential version qualifier
let version = self.parse_table_version()?;

Expand Down Expand Up @@ -6358,6 +6366,7 @@ impl<'a> Parser<'a> {
args,
with_hints,
version,
partitions,
})
}
}
Expand Down Expand Up @@ -7746,6 +7755,13 @@ impl<'a> Parser<'a> {
Err(ParserError::ParserError("Expected FROM/TO".to_string()))
}
}

fn parse_partitions(&mut self) -> Result<Vec<Ident>, ParserError> {
self.expect_token(&Token::LParen)?;
let partitions = self.parse_comma_separated(Parser::parse_identifier)?;
self.expect_token(&Token::RParen)?;
Ok(partitions)
}
}

impl Word {
Expand Down Expand Up @@ -8363,4 +8379,29 @@ mod tests {
"sql parser error: Unexpected token following period in identifier: *",
);
}

#[test]
fn test_mysql_partition_selection() {
let sql = "SELECT * FROM employees PARTITION (p0, p2)";
let expected = vec!["p0", "p2"];

let ast: Vec<Statement> = Parser::parse_sql(&MySqlDialect {}, sql).unwrap();
assert_eq!(ast.len(), 1);
if let Statement::Query(v) = &ast[0] {
if let SetExpr::Select(select) = &*v.body {
assert_eq!(select.from.len(), 1);
let from: &TableWithJoins = &select.from[0];
let table_factor = &from.relation;
if let TableFactor::Table { partitions, .. } = table_factor {
let actual: Vec<&str> = partitions
.iter()
.map(|ident| ident.value.as_str())
.collect();
assert_eq!(expected, actual);
}
}
} else {
panic!("fail to parse mysql partition selection");
}
}
}
1 change: 1 addition & 0 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub fn table(name: impl Into<String>) -> TableFactor {
args: None,
with_hints: vec![],
version: None,
partitions: vec![],
}
}

Expand Down
2 changes: 2 additions & 0 deletions tests/sqlparser_bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ fn parse_table_identifiers() {
args: None,
with_hints: vec![],
version: None,
partitions: vec![],
},
joins: vec![]
},]
Expand Down Expand Up @@ -160,6 +161,7 @@ fn parse_table_time_travel() {
version: Some(TableVersion::ForSystemTimeAsOf(Expr::Value(
Value::SingleQuotedString(version)
))),
partitions: vec![],
},
joins: vec![]
},]
Expand Down
16 changes: 9 additions & 7 deletions tests/sqlparser_clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,16 @@ fn parse_map_access_expr() {
args: None,
with_hints: vec![],
version: None,
partitions: vec![],
},
joins: vec![]
joins: vec![],
}],
lateral_views: vec![],
selection: Some(BinaryOp {
left: Box::new(BinaryOp {
left: Box::new(Identifier(Ident::new("id"))),
op: BinaryOperator::Eq,
right: Box::new(Expr::Value(Value::SingleQuotedString("test".to_string())))
right: Box::new(Expr::Value(Value::SingleQuotedString("test".to_string()))),
}),
op: BinaryOperator::And,
right: Box::new(BinaryOp {
Expand All @@ -91,19 +92,19 @@ fn parse_map_access_expr() {
distinct: false,
special: false,
order_by: vec![],
})]
})],
}),
op: BinaryOperator::NotEq,
right: Box::new(Expr::Value(Value::SingleQuotedString("foo".to_string())))
})
right: Box::new(Expr::Value(Value::SingleQuotedString("foo".to_string()))),
}),
}),
group_by: GroupByExpr::Expressions(vec![]),
cluster_by: vec![],
distribute_by: vec![],
sort_by: vec![],
having: None,
named_window: vec![],
qualify: None
qualify: None,
},
select
);
Expand All @@ -117,7 +118,7 @@ fn parse_array_expr() {
&Expr::Array(Array {
elem: vec![
Expr::Value(Value::SingleQuotedString("1".to_string())),
Expr::Value(Value::SingleQuotedString("2".to_string()))
Expr::Value(Value::SingleQuotedString("2".to_string())),
],
named: false,
}),
Expand Down Expand Up @@ -171,6 +172,7 @@ fn parse_delimited_identifiers() {
args,
with_hints,
version,
partitions: _,
} => {
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
Expand Down
Loading

0 comments on commit 8e8358b

Please sign in to comment.