Skip to content

Commit b4699bd

Browse files
authored
Support bitwise and, or, xor (apache#181)
Operator precedence is coming from: https://cloud.google.com/bigquery/docs/reference/standard-sql/operators
1 parent 00dc490 commit b4699bd

File tree

4 files changed

+66
-5
lines changed

4 files changed

+66
-5
lines changed

src/ast/operator.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ pub enum BinaryOperator {
4949
Or,
5050
Like,
5151
NotLike,
52+
BitwiseOr,
53+
BitwiseAnd,
54+
BitwiseXor,
5255
}
5356

5457
impl fmt::Display for BinaryOperator {
@@ -70,6 +73,9 @@ impl fmt::Display for BinaryOperator {
7073
BinaryOperator::Or => "OR",
7174
BinaryOperator::Like => "LIKE",
7275
BinaryOperator::NotLike => "NOT LIKE",
76+
BinaryOperator::BitwiseOr => "|",
77+
BinaryOperator::BitwiseAnd => "&",
78+
BinaryOperator::BitwiseXor => "^",
7379
})
7480
}
7581
}

src/parser.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,9 @@ impl Parser {
578578
Token::Mult => Some(BinaryOperator::Multiply),
579579
Token::Mod => Some(BinaryOperator::Modulus),
580580
Token::StringConcat => Some(BinaryOperator::StringConcat),
581+
Token::Pipe => Some(BinaryOperator::BitwiseOr),
582+
Token::Caret => Some(BinaryOperator::BitwiseXor),
583+
Token::Ampersand => Some(BinaryOperator::BitwiseAnd),
581584
Token::Div => Some(BinaryOperator::Divide),
582585
Token::Word(ref k) => match k.keyword.as_ref() {
583586
"AND" => Some(BinaryOperator::And),
@@ -708,6 +711,9 @@ impl Parser {
708711
Token::Eq | Token::Lt | Token::LtEq | Token::Neq | Token::Gt | Token::GtEq => {
709712
Ok(20)
710713
}
714+
Token::Pipe => Ok(21),
715+
Token::Caret => Ok(22),
716+
Token::Ampersand => Ok(23),
711717
Token::Plus | Token::Minus => Ok(Self::PLUS_MINUS_PREC),
712718
Token::Mult | Token::Div | Token::Mod | Token::StringConcat => Ok(40),
713719
Token::DoubleColon => Ok(50),

src/tokenizer.rs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,12 @@ pub enum Token {
8484
LBracket,
8585
/// Right bracket `]`
8686
RBracket,
87-
/// Ampersand &
87+
/// Ampersand `&`
8888
Ampersand,
89+
/// Pipe `|`
90+
Pipe,
91+
/// Caret `^`
92+
Caret,
8993
/// Left brace `{`
9094
LBrace,
9195
/// Right brace `}`
@@ -125,6 +129,8 @@ impl fmt::Display for Token {
125129
Token::LBracket => f.write_str("["),
126130
Token::RBracket => f.write_str("]"),
127131
Token::Ampersand => f.write_str("&"),
132+
Token::Caret => f.write_str("^"),
133+
Token::Pipe => f.write_str("|"),
128134
Token::LBrace => f.write_str("{"),
129135
Token::RBrace => f.write_str("}"),
130136
}
@@ -381,10 +387,8 @@ impl<'a> Tokenizer<'a> {
381387
chars.next(); // consume the '|'
382388
match chars.peek() {
383389
Some('|') => self.consume_and_return(chars, Token::StringConcat),
384-
_ => Err(TokenizerError(format!(
385-
"Expecting to see `||`. Bitwise or operator `|` is not supported. \nError at Line: {}, Col: {}",
386-
self.line, self.col
387-
))),
390+
// Bitshift '|' operator
391+
_ => Ok(Some(Token::Pipe)),
388392
}
389393
}
390394
'=' => self.consume_and_return(chars, Token::Eq),
@@ -426,6 +430,7 @@ impl<'a> Tokenizer<'a> {
426430
'[' => self.consume_and_return(chars, Token::LBracket),
427431
']' => self.consume_and_return(chars, Token::RBracket),
428432
'&' => self.consume_and_return(chars, Token::Ampersand),
433+
'^' => self.consume_and_return(chars, Token::Caret),
429434
'{' => self.consume_and_return(chars, Token::LBrace),
430435
'}' => self.consume_and_return(chars, Token::RBrace),
431436
other => self.consume_and_return(chars, Token::Char(other)),
@@ -594,6 +599,29 @@ mod tests {
594599

595600
compare(expected, tokens);
596601
}
602+
#[test]
603+
fn tokenize_bitwise_op() {
604+
let sql = String::from("SELECT one | two ^ three");
605+
let dialect = GenericDialect {};
606+
let mut tokenizer = Tokenizer::new(&dialect, &sql);
607+
let tokens = tokenizer.tokenize().unwrap();
608+
609+
let expected = vec![
610+
Token::make_keyword("SELECT"),
611+
Token::Whitespace(Whitespace::Space),
612+
Token::make_word("one", None),
613+
Token::Whitespace(Whitespace::Space),
614+
Token::Pipe,
615+
Token::Whitespace(Whitespace::Space),
616+
Token::make_word("two", None),
617+
Token::Whitespace(Whitespace::Space),
618+
Token::Caret,
619+
Token::Whitespace(Whitespace::Space),
620+
Token::make_word("three", None),
621+
];
622+
623+
compare(expected, tokens);
624+
}
597625

598626
#[test]
599627
fn tokenize_simple_select() {

tests/sqlparser_common.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,27 @@ fn parse_string_agg() {
680680
);
681681
}
682682

683+
#[test]
684+
fn parse_bitwise_ops() {
685+
let bitwise_ops = &[
686+
("^", BinaryOperator::BitwiseXor),
687+
("|", BinaryOperator::BitwiseOr),
688+
("&", BinaryOperator::BitwiseAnd),
689+
];
690+
691+
for (str_op, op) in bitwise_ops {
692+
let select = verified_only_select(&format!("SELECT a {} b", &str_op));
693+
assert_eq!(
694+
SelectItem::UnnamedExpr(Expr::BinaryOp {
695+
left: Box::new(Expr::Identifier(Ident::new("a"))),
696+
op: op.clone(),
697+
right: Box::new(Expr::Identifier(Ident::new("b"))),
698+
}),
699+
select.projection[0]
700+
);
701+
}
702+
}
703+
683704
#[test]
684705
fn parse_between() {
685706
fn chk(negated: bool) {

0 commit comments

Comments
 (0)