Support for ClickHouse array types (e.g. [1,2,3]) (#429)

This commit is contained in:
Simon Liu 2022-03-09 00:44:32 +08:00 committed by GitHub
parent 994b86a45c
commit 3f5619446f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 14 deletions

View file

@ -157,6 +157,24 @@ impl fmt::Display for ObjectName {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Array {
pub elem: Vec<Expr>,
pub named: bool,
}
impl fmt::Display for Array {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}[{}]",
if self.named { "ARRAY" } else { "" },
display_comma_separated(&self.elem)
)
}
}
/// An SQL expression of any type.
///
/// The parser does not distinguish between expressions of different types
@ -298,7 +316,7 @@ pub enum Expr {
indexs: Vec<Expr>,
},
/// An array expression e.g. `ARRAY[1, 2]`
Array(Vec<Expr>),
Array(Array),
}
impl fmt::Display for Expr {
@ -482,7 +500,7 @@ impl fmt::Display for Expr {
Ok(())
}
Expr::Array(set) => {
write!(f, "ARRAY[{}]", display_comma_separated(set))
write!(f, "{}", set)
}
}
}

View file

@ -420,7 +420,7 @@ impl<'a> Parser<'a> {
Keyword::TRIM => self.parse_trim_expr(),
Keyword::INTERVAL => self.parse_literal_interval(),
Keyword::LISTAGG => self.parse_listagg_expr(),
Keyword::ARRAY => self.parse_array_expr(),
Keyword::ARRAY => self.parse_array_expr(true),
Keyword::NOT => Ok(Expr::UnaryOp {
op: UnaryOperator::Not,
expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?),
@ -450,6 +450,7 @@ impl<'a> Parser<'a> {
_ => Ok(Expr::Identifier(w.to_ident())),
},
}, // End of Token::Word
Token::LBracket => self.parse_array_expr(false),
tok @ Token::Minus | tok @ Token::Plus => {
let op = if tok == Token::Plus {
UnaryOperator::Plus
@ -825,11 +826,13 @@ impl<'a> Parser<'a> {
}
}
pub fn parse_array_expr(&mut self) -> Result<Expr, ParserError> {
self.expect_token(&Token::LBracket)?;
pub fn parse_array_expr(&mut self, named: bool) -> Result<Expr, ParserError> {
if named {
self.expect_token(&Token::LBracket)?;
}
let exprs = self.parse_comma_separated(Parser::parse_expr)?;
self.expect_token(&Token::RBracket)?;
Ok(Expr::Array(exprs))
Ok(Expr::Array(Array { elem: exprs, named }))
}
/// Parse a SQL LISTAGG expression, e.g. `LISTAGG(...) WITHIN GROUP (ORDER BY ...)`.

View file

@ -828,10 +828,13 @@ fn parse_array_index_expr() {
assert_eq!(
&Expr::ArrayIndex {
obj: Box::new(Expr::Nested(Box::new(Expr::Cast {
expr: Box::new(Expr::Array(vec![Expr::Array(vec![
num[2].clone(),
num[3].clone(),
])])),
expr: Box::new(Expr::Array(Array {
elem: vec![Expr::Array(Array {
elem: vec![num[2].clone(), num[3].clone(),],
named: true,
})],
named: true,
})),
data_type: DataType::Array(Box::new(DataType::Array(Box::new(DataType::Int(
None
)))))

View file

@ -15,6 +15,7 @@
#[macro_use]
mod test_utils;
use test_utils::*;
use sqlparser::ast::Expr::{Identifier, MapAccess};
@ -30,7 +31,7 @@ fn parse_map_access_expr() {
&MapAccess {
column: Box::new(Identifier(Ident {
value: "string_values".to_string(),
quote_style: None
quote_style: None,
})),
keys: vec![Expr::Function(Function {
name: ObjectName(vec!["indexOf".into()]),
@ -40,16 +41,32 @@ fn parse_map_access_expr() {
)))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
Value::SingleQuotedString("endpoint".to_string())
)))
))),
],
over: None,
distinct: false
})]
distinct: false,
})],
},
expr_from_projection(only(&select.projection)),
);
}
#[test]
fn parse_array_expr() {
let sql = "SELECT ['1', '2'] FROM test";
let select = clickhouse().verified_only_select(sql);
assert_eq!(
&Expr::Array(Array {
elem: vec![
Expr::Value(Value::SingleQuotedString("1".to_string())),
Expr::Value(Value::SingleQuotedString("2".to_string()))
],
named: false,
}),
expr_from_projection(only(&select.projection))
)
}
fn clickhouse() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(ClickHouseDialect {})],