mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-22 21:52:28 +00:00
Enable map access for function, add ClickHouse dialect (#382)
* 1 Add ClickHouse dialects. 2 Enable map access for function. * 1 Fixed compilation errors. 2 Modify the code according to @alamb's comments. * Fixed compilation errors.
This commit is contained in:
parent
9569d1b215
commit
60ad78dafc
6 changed files with 105 additions and 16 deletions
|
@ -245,7 +245,7 @@ pub enum Expr {
|
||||||
},
|
},
|
||||||
MapAccess {
|
MapAccess {
|
||||||
column: Box<Expr>,
|
column: Box<Expr>,
|
||||||
keys: Vec<Value>,
|
keys: Vec<Expr>,
|
||||||
},
|
},
|
||||||
/// Scalar function call e.g. `LEFT(foo, 5)`
|
/// Scalar function call e.g. `LEFT(foo, 5)`
|
||||||
Function(Function),
|
Function(Function),
|
||||||
|
@ -284,8 +284,8 @@ impl fmt::Display for Expr {
|
||||||
write!(f, "{}", column)?;
|
write!(f, "{}", column)?;
|
||||||
for k in keys {
|
for k in keys {
|
||||||
match k {
|
match k {
|
||||||
k @ Value::Number(_, _) => write!(f, "[{}]", k)?,
|
k @ Expr::Value(Value::Number(_, _)) => write!(f, "[{}]", k)?,
|
||||||
Value::SingleQuotedString(s) => write!(f, "[\"{}\"]", s)?,
|
Expr::Value(Value::SingleQuotedString(s)) => write!(f, "[\"{}\"]", s)?,
|
||||||
_ => write!(f, "[{}]", k)?,
|
_ => write!(f, "[{}]", k)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
src/dialect/clickhouse.rs
Normal file
27
src/dialect/clickhouse.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use crate::dialect::Dialect;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ClickHouseDialect {}
|
||||||
|
|
||||||
|
impl Dialect for ClickHouseDialect {
|
||||||
|
fn is_identifier_start(&self, ch: char) -> bool {
|
||||||
|
// See https://clickhouse.com/docs/en/sql-reference/syntax/#syntax-identifiers
|
||||||
|
('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) || ch == '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_identifier_part(&self, ch: char) -> bool {
|
||||||
|
self.is_identifier_start(ch) || ('0'..='9').contains(&ch)
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
mod ansi;
|
mod ansi;
|
||||||
|
mod clickhouse;
|
||||||
mod generic;
|
mod generic;
|
||||||
mod hive;
|
mod hive;
|
||||||
mod mssql;
|
mod mssql;
|
||||||
|
@ -23,6 +24,7 @@ use core::any::{Any, TypeId};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
pub use self::ansi::AnsiDialect;
|
pub use self::ansi::AnsiDialect;
|
||||||
|
pub use self::clickhouse::ClickHouseDialect;
|
||||||
pub use self::generic::GenericDialect;
|
pub use self::generic::GenericDialect;
|
||||||
pub use self::hive::HiveDialect;
|
pub use self::hive::HiveDialect;
|
||||||
pub use self::mssql::MsSqlDialect;
|
pub use self::mssql::MsSqlDialect;
|
||||||
|
|
|
@ -1072,7 +1072,7 @@ impl<'a> Parser<'a> {
|
||||||
let key = self.parse_map_key()?;
|
let key = self.parse_map_key()?;
|
||||||
let tok = self.consume_token(&Token::RBracket);
|
let tok = self.consume_token(&Token::RBracket);
|
||||||
debug!("Tok: {}", tok);
|
debug!("Tok: {}", tok);
|
||||||
let mut key_parts: Vec<Value> = vec![key];
|
let mut key_parts: Vec<Expr> = vec![key];
|
||||||
while self.consume_token(&Token::LBracket) {
|
while self.consume_token(&Token::LBracket) {
|
||||||
let key = self.parse_map_key()?;
|
let key = self.parse_map_key()?;
|
||||||
let tok = self.consume_token(&Token::RBracket);
|
let tok = self.consume_token(&Token::RBracket);
|
||||||
|
@ -2175,17 +2175,20 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a map key string
|
/// Parse a map key string
|
||||||
pub fn parse_map_key(&mut self) -> Result<Value, ParserError> {
|
pub fn parse_map_key(&mut self) -> Result<Expr, ParserError> {
|
||||||
match self.next_token() {
|
match self.next_token() {
|
||||||
Token::Word(Word { value, keyword, .. }) if keyword == Keyword::NoKeyword => {
|
Token::Word(Word { value, keyword, .. }) if keyword == Keyword::NoKeyword => {
|
||||||
Ok(Value::SingleQuotedString(value))
|
if self.peek_token() == Token::LParen {
|
||||||
|
return self.parse_function(ObjectName(vec![Ident::new(value)]));
|
||||||
}
|
}
|
||||||
Token::SingleQuotedString(s) => Ok(Value::SingleQuotedString(s)),
|
Ok(Expr::Value(Value::SingleQuotedString(value)))
|
||||||
|
}
|
||||||
|
Token::SingleQuotedString(s) => Ok(Expr::Value(Value::SingleQuotedString(s))),
|
||||||
#[cfg(not(feature = "bigdecimal"))]
|
#[cfg(not(feature = "bigdecimal"))]
|
||||||
Token::Number(s, _) => Ok(Value::Number(s, false)),
|
Token::Number(s, _) => Ok(Expr::Value(Value::Number(s, false))),
|
||||||
#[cfg(feature = "bigdecimal")]
|
#[cfg(feature = "bigdecimal")]
|
||||||
Token::Number(s, _) => Ok(Value::Number(s.parse().unwrap(), false)),
|
Token::Number(s, _) => Ok(Expr::Value(Value::Number(s.parse().unwrap(), false))),
|
||||||
unexpected => self.expected("literal string or number", unexpected),
|
unexpected => self.expected("literal string, number or function", unexpected),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -730,7 +730,7 @@ fn parse_map_access_expr() {
|
||||||
value: "foo".to_string(),
|
value: "foo".to_string(),
|
||||||
quote_style: None
|
quote_style: None
|
||||||
})),
|
})),
|
||||||
keys: vec![Value::Number(zero.clone(), false)]
|
keys: vec![Expr::Value(Value::Number(zero.clone(), false))]
|
||||||
},
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
@ -743,8 +743,8 @@ fn parse_map_access_expr() {
|
||||||
quote_style: None
|
quote_style: None
|
||||||
})),
|
})),
|
||||||
keys: vec![
|
keys: vec![
|
||||||
Value::Number(zero.clone(), false),
|
Expr::Value(Value::Number(zero.clone(), false)),
|
||||||
Value::Number(zero.clone(), false)
|
Expr::Value(Value::Number(zero.clone(), false))
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
|
@ -758,9 +758,9 @@ fn parse_map_access_expr() {
|
||||||
quote_style: None
|
quote_style: None
|
||||||
})),
|
})),
|
||||||
keys: vec![
|
keys: vec![
|
||||||
Value::Number(zero, false),
|
Expr::Value(Value::Number(zero, false)),
|
||||||
Value::SingleQuotedString("baz".to_string()),
|
Expr::Value(Value::SingleQuotedString("baz".to_string())),
|
||||||
Value::SingleQuotedString("fooz".to_string())
|
Expr::Value(Value::SingleQuotedString("fooz".to_string()))
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
|
|
57
tests/sqpparser_clickhouse.rs
Normal file
57
tests/sqpparser_clickhouse.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
//! Test SQL syntax specific to ClickHouse.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod test_utils;
|
||||||
|
use test_utils::*;
|
||||||
|
|
||||||
|
use sqlparser::ast::Expr::{Identifier, MapAccess};
|
||||||
|
use sqlparser::ast::*;
|
||||||
|
|
||||||
|
use sqlparser::dialect::ClickHouseDialect;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_map_access_expr() {
|
||||||
|
let sql = r#"SELECT string_values[indexOf(string_names, 'endpoint')] FROM foos"#;
|
||||||
|
let select = clickhouse().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
&MapAccess {
|
||||||
|
column: Box::new(Identifier(Ident {
|
||||||
|
value: "string_values".to_string(),
|
||||||
|
quote_style: None
|
||||||
|
})),
|
||||||
|
keys: vec![Expr::Function(Function {
|
||||||
|
name: ObjectName(vec!["indexOf".into()]),
|
||||||
|
args: vec![
|
||||||
|
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(Ident::new(
|
||||||
|
"string_names"
|
||||||
|
)))),
|
||||||
|
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
|
||||||
|
Value::SingleQuotedString("endpoint".to_string())
|
||||||
|
)))
|
||||||
|
],
|
||||||
|
over: None,
|
||||||
|
distinct: false
|
||||||
|
})]
|
||||||
|
},
|
||||||
|
expr_from_projection(only(&select.projection)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clickhouse() -> TestedDialects {
|
||||||
|
TestedDialects {
|
||||||
|
dialects: vec![Box::new(ClickHouseDialect {})],
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue