mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-04 06:18:17 +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 {
|
||||
column: Box<Expr>,
|
||||
keys: Vec<Value>,
|
||||
keys: Vec<Expr>,
|
||||
},
|
||||
/// Scalar function call e.g. `LEFT(foo, 5)`
|
||||
Function(Function),
|
||||
|
@ -284,8 +284,8 @@ impl fmt::Display for Expr {
|
|||
write!(f, "{}", column)?;
|
||||
for k in keys {
|
||||
match k {
|
||||
k @ Value::Number(_, _) => write!(f, "[{}]", k)?,
|
||||
Value::SingleQuotedString(s) => write!(f, "[\"{}\"]", s)?,
|
||||
k @ Expr::Value(Value::Number(_, _)) => write!(f, "[{}]", k)?,
|
||||
Expr::Value(Value::SingleQuotedString(s)) => write!(f, "[\"{}\"]", s)?,
|
||||
_ => 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.
|
||||
|
||||
mod ansi;
|
||||
mod clickhouse;
|
||||
mod generic;
|
||||
mod hive;
|
||||
mod mssql;
|
||||
|
@ -23,6 +24,7 @@ use core::any::{Any, TypeId};
|
|||
use core::fmt::Debug;
|
||||
|
||||
pub use self::ansi::AnsiDialect;
|
||||
pub use self::clickhouse::ClickHouseDialect;
|
||||
pub use self::generic::GenericDialect;
|
||||
pub use self::hive::HiveDialect;
|
||||
pub use self::mssql::MsSqlDialect;
|
||||
|
|
|
@ -1072,7 +1072,7 @@ impl<'a> Parser<'a> {
|
|||
let key = self.parse_map_key()?;
|
||||
let tok = self.consume_token(&Token::RBracket);
|
||||
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) {
|
||||
let key = self.parse_map_key()?;
|
||||
let tok = self.consume_token(&Token::RBracket);
|
||||
|
@ -2175,17 +2175,20 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
/// 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() {
|
||||
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)]));
|
||||
}
|
||||
Ok(Expr::Value(Value::SingleQuotedString(value)))
|
||||
}
|
||||
Token::SingleQuotedString(s) => Ok(Value::SingleQuotedString(s)),
|
||||
Token::SingleQuotedString(s) => Ok(Expr::Value(Value::SingleQuotedString(s))),
|
||||
#[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")]
|
||||
Token::Number(s, _) => Ok(Value::Number(s.parse().unwrap(), false)),
|
||||
unexpected => self.expected("literal string or number", unexpected),
|
||||
Token::Number(s, _) => Ok(Expr::Value(Value::Number(s.parse().unwrap(), false))),
|
||||
unexpected => self.expected("literal string, number or function", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -730,7 +730,7 @@ fn parse_map_access_expr() {
|
|||
value: "foo".to_string(),
|
||||
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)),
|
||||
);
|
||||
|
@ -743,8 +743,8 @@ fn parse_map_access_expr() {
|
|||
quote_style: None
|
||||
})),
|
||||
keys: vec![
|
||||
Value::Number(zero.clone(), false),
|
||||
Value::Number(zero.clone(), false)
|
||||
Expr::Value(Value::Number(zero.clone(), false)),
|
||||
Expr::Value(Value::Number(zero.clone(), false))
|
||||
]
|
||||
},
|
||||
expr_from_projection(only(&select.projection)),
|
||||
|
@ -758,9 +758,9 @@ fn parse_map_access_expr() {
|
|||
quote_style: None
|
||||
})),
|
||||
keys: vec![
|
||||
Value::Number(zero, false),
|
||||
Value::SingleQuotedString("baz".to_string()),
|
||||
Value::SingleQuotedString("fooz".to_string())
|
||||
Expr::Value(Value::Number(zero, false)),
|
||||
Expr::Value(Value::SingleQuotedString("baz".to_string())),
|
||||
Expr::Value(Value::SingleQuotedString("fooz".to_string()))
|
||||
]
|
||||
},
|
||||
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