mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-21 14:40:18 +00:00
Support the string concat operator (#178)
The selected precedence is based on BigQuery documentation, where it is equal to `*` and `/`: https://cloud.google.com/bigquery/docs/reference/standard-sql/operators
This commit is contained in:
parent
5f3c1bda01
commit
00dc490f72
4 changed files with 52 additions and 1 deletions
|
@ -38,6 +38,7 @@ pub enum BinaryOperator {
|
||||||
Multiply,
|
Multiply,
|
||||||
Divide,
|
Divide,
|
||||||
Modulus,
|
Modulus,
|
||||||
|
StringConcat,
|
||||||
Gt,
|
Gt,
|
||||||
Lt,
|
Lt,
|
||||||
GtEq,
|
GtEq,
|
||||||
|
@ -58,6 +59,7 @@ impl fmt::Display for BinaryOperator {
|
||||||
BinaryOperator::Multiply => "*",
|
BinaryOperator::Multiply => "*",
|
||||||
BinaryOperator::Divide => "/",
|
BinaryOperator::Divide => "/",
|
||||||
BinaryOperator::Modulus => "%",
|
BinaryOperator::Modulus => "%",
|
||||||
|
BinaryOperator::StringConcat => "||",
|
||||||
BinaryOperator::Gt => ">",
|
BinaryOperator::Gt => ">",
|
||||||
BinaryOperator::Lt => "<",
|
BinaryOperator::Lt => "<",
|
||||||
BinaryOperator::GtEq => ">=",
|
BinaryOperator::GtEq => ">=",
|
||||||
|
|
|
@ -577,6 +577,7 @@ impl Parser {
|
||||||
Token::Minus => Some(BinaryOperator::Minus),
|
Token::Minus => Some(BinaryOperator::Minus),
|
||||||
Token::Mult => Some(BinaryOperator::Multiply),
|
Token::Mult => Some(BinaryOperator::Multiply),
|
||||||
Token::Mod => Some(BinaryOperator::Modulus),
|
Token::Mod => Some(BinaryOperator::Modulus),
|
||||||
|
Token::StringConcat => Some(BinaryOperator::StringConcat),
|
||||||
Token::Div => Some(BinaryOperator::Divide),
|
Token::Div => Some(BinaryOperator::Divide),
|
||||||
Token::Word(ref k) => match k.keyword.as_ref() {
|
Token::Word(ref k) => match k.keyword.as_ref() {
|
||||||
"AND" => Some(BinaryOperator::And),
|
"AND" => Some(BinaryOperator::And),
|
||||||
|
@ -708,7 +709,7 @@ impl Parser {
|
||||||
Ok(20)
|
Ok(20)
|
||||||
}
|
}
|
||||||
Token::Plus | Token::Minus => Ok(Self::PLUS_MINUS_PREC),
|
Token::Plus | Token::Minus => Ok(Self::PLUS_MINUS_PREC),
|
||||||
Token::Mult | Token::Div | Token::Mod => Ok(40),
|
Token::Mult | Token::Div | Token::Mod | Token::StringConcat => Ok(40),
|
||||||
Token::DoubleColon => Ok(50),
|
Token::DoubleColon => Ok(50),
|
||||||
_ => Ok(0),
|
_ => Ok(0),
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,8 @@ pub enum Token {
|
||||||
Div,
|
Div,
|
||||||
/// Modulo Operator `%`
|
/// Modulo Operator `%`
|
||||||
Mod,
|
Mod,
|
||||||
|
/// String concatenation `||`
|
||||||
|
StringConcat,
|
||||||
/// Left parenthesis `(`
|
/// Left parenthesis `(`
|
||||||
LParen,
|
LParen,
|
||||||
/// Right parenthesis `)`
|
/// Right parenthesis `)`
|
||||||
|
@ -111,6 +113,7 @@ impl fmt::Display for Token {
|
||||||
Token::Minus => f.write_str("-"),
|
Token::Minus => f.write_str("-"),
|
||||||
Token::Mult => f.write_str("*"),
|
Token::Mult => f.write_str("*"),
|
||||||
Token::Div => f.write_str("/"),
|
Token::Div => f.write_str("/"),
|
||||||
|
Token::StringConcat => f.write_str("||"),
|
||||||
Token::Mod => f.write_str("%"),
|
Token::Mod => f.write_str("%"),
|
||||||
Token::LParen => f.write_str("("),
|
Token::LParen => f.write_str("("),
|
||||||
Token::RParen => f.write_str(")"),
|
Token::RParen => f.write_str(")"),
|
||||||
|
@ -374,6 +377,16 @@ impl<'a> Tokenizer<'a> {
|
||||||
'+' => self.consume_and_return(chars, Token::Plus),
|
'+' => self.consume_and_return(chars, Token::Plus),
|
||||||
'*' => self.consume_and_return(chars, Token::Mult),
|
'*' => self.consume_and_return(chars, Token::Mult),
|
||||||
'%' => self.consume_and_return(chars, Token::Mod),
|
'%' => self.consume_and_return(chars, Token::Mod),
|
||||||
|
'|' => {
|
||||||
|
chars.next(); // consume the '|'
|
||||||
|
match chars.peek() {
|
||||||
|
Some('|') => self.consume_and_return(chars, Token::StringConcat),
|
||||||
|
_ => Err(TokenizerError(format!(
|
||||||
|
"Expecting to see `||`. Bitwise or operator `|` is not supported. \nError at Line: {}, Col: {}",
|
||||||
|
self.line, self.col
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
'=' => self.consume_and_return(chars, Token::Eq),
|
'=' => self.consume_and_return(chars, Token::Eq),
|
||||||
'.' => self.consume_and_return(chars, Token::Period),
|
'.' => self.consume_and_return(chars, Token::Period),
|
||||||
'!' => {
|
'!' => {
|
||||||
|
@ -562,6 +575,26 @@ mod tests {
|
||||||
compare(expected, tokens);
|
compare(expected, tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tokenize_string_string_concat() {
|
||||||
|
let sql = String::from("SELECT 'a' || 'b'");
|
||||||
|
let dialect = GenericDialect {};
|
||||||
|
let mut tokenizer = Tokenizer::new(&dialect, &sql);
|
||||||
|
let tokens = tokenizer.tokenize().unwrap();
|
||||||
|
|
||||||
|
let expected = vec![
|
||||||
|
Token::make_keyword("SELECT"),
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::SingleQuotedString(String::from("a")),
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::StringConcat,
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::SingleQuotedString(String::from("b")),
|
||||||
|
];
|
||||||
|
|
||||||
|
compare(expected, tokens);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tokenize_simple_select() {
|
fn tokenize_simple_select() {
|
||||||
let sql = String::from("SELECT * FROM customer WHERE id = 1 LIMIT 5");
|
let sql = String::from("SELECT * FROM customer WHERE id = 1 LIMIT 5");
|
||||||
|
|
|
@ -665,6 +665,21 @@ fn parse_in_subquery() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_agg() {
|
||||||
|
let sql = "SELECT a || b";
|
||||||
|
|
||||||
|
let select = verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
SelectItem::UnnamedExpr(Expr::BinaryOp {
|
||||||
|
left: Box::new(Expr::Identifier(Ident::new("a"))),
|
||||||
|
op: BinaryOperator::StringConcat,
|
||||||
|
right: Box::new(Expr::Identifier(Ident::new("b"))),
|
||||||
|
}),
|
||||||
|
select.projection[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_between() {
|
fn parse_between() {
|
||||||
fn chk(negated: bool) {
|
fn chk(negated: bool) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue