Skip to content

Commit 014b82f

Browse files
authored
Add logical xor (apache#357)
1 parent a8901be commit 014b82f

File tree

5 files changed

+83
-0
lines changed

5 files changed

+83
-0
lines changed

src/ast/operator.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub enum BinaryOperator {
7171
NotEq,
7272
And,
7373
Or,
74+
Xor,
7475
Like,
7576
NotLike,
7677
ILike,
@@ -105,6 +106,7 @@ impl fmt::Display for BinaryOperator {
105106
BinaryOperator::NotEq => "<>",
106107
BinaryOperator::And => "AND",
107108
BinaryOperator::Or => "OR",
109+
BinaryOperator::Xor => "XOR",
108110
BinaryOperator::Like => "LIKE",
109111
BinaryOperator::NotLike => "NOT LIKE",
110112
BinaryOperator::ILike => "ILIKE",

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ define_keywords!(
494494
WITHOUT,
495495
WORK,
496496
WRITE,
497+
XOR,
497498
YEAR,
498499
ZONE
499500
);

src/parser.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,7 @@ impl<'a> Parser<'a> {
898898
None
899899
}
900900
}
901+
Keyword::XOR => Some(BinaryOperator::Xor),
901902
_ => None,
902903
},
903904
_ => None,
@@ -1018,6 +1019,7 @@ impl<'a> Parser<'a> {
10181019
match token {
10191020
Token::Word(w) if w.keyword == Keyword::OR => Ok(5),
10201021
Token::Word(w) if w.keyword == Keyword::AND => Ok(10),
1022+
Token::Word(w) if w.keyword == Keyword::XOR => Ok(24),
10211023
Token::Word(w) if w.keyword == Keyword::NOT => match self.peek_nth_token(1) {
10221024
// The precedence of NOT varies depending on keyword that
10231025
// follows it. If it is followed by IN, BETWEEN, or LIKE,

src/tokenizer.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,47 @@ mod tests {
824824
Token::Whitespace(Whitespace::Space),
825825
Token::make_word("three", None),
826826
];
827+
compare(expected, tokens);
828+
}
829+
830+
#[test]
831+
fn tokenize_logical_xor() {
832+
let sql =
833+
String::from("SELECT true XOR true, false XOR false, true XOR false, false XOR true");
834+
let dialect = GenericDialect {};
835+
let mut tokenizer = Tokenizer::new(&dialect, &sql);
836+
let tokens = tokenizer.tokenize().unwrap();
827837

838+
let expected = vec![
839+
Token::make_keyword("SELECT"),
840+
Token::Whitespace(Whitespace::Space),
841+
Token::make_keyword("true"),
842+
Token::Whitespace(Whitespace::Space),
843+
Token::make_keyword("XOR"),
844+
Token::Whitespace(Whitespace::Space),
845+
Token::make_keyword("true"),
846+
Token::Comma,
847+
Token::Whitespace(Whitespace::Space),
848+
Token::make_keyword("false"),
849+
Token::Whitespace(Whitespace::Space),
850+
Token::make_keyword("XOR"),
851+
Token::Whitespace(Whitespace::Space),
852+
Token::make_keyword("false"),
853+
Token::Comma,
854+
Token::Whitespace(Whitespace::Space),
855+
Token::make_keyword("true"),
856+
Token::Whitespace(Whitespace::Space),
857+
Token::make_keyword("XOR"),
858+
Token::Whitespace(Whitespace::Space),
859+
Token::make_keyword("false"),
860+
Token::Comma,
861+
Token::Whitespace(Whitespace::Space),
862+
Token::make_keyword("false"),
863+
Token::Whitespace(Whitespace::Space),
864+
Token::make_keyword("XOR"),
865+
Token::Whitespace(Whitespace::Space),
866+
Token::make_keyword("true"),
867+
];
828868
compare(expected, tokens);
829869
}
830870

tests/sqlparser_common.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,44 @@ fn parse_bitwise_ops() {
815815
}
816816
}
817817

818+
#[test]
819+
fn parse_logical_xor() {
820+
let sql = "SELECT true XOR true, false XOR false, true XOR false, false XOR true";
821+
let select = verified_only_select(sql);
822+
assert_eq!(
823+
SelectItem::UnnamedExpr(Expr::BinaryOp {
824+
left: Box::new(Expr::Value(Value::Boolean(true))),
825+
op: BinaryOperator::Xor,
826+
right: Box::new(Expr::Value(Value::Boolean(true))),
827+
}),
828+
select.projection[0]
829+
);
830+
assert_eq!(
831+
SelectItem::UnnamedExpr(Expr::BinaryOp {
832+
left: Box::new(Expr::Value(Value::Boolean(false))),
833+
op: BinaryOperator::Xor,
834+
right: Box::new(Expr::Value(Value::Boolean(false))),
835+
}),
836+
select.projection[1]
837+
);
838+
assert_eq!(
839+
SelectItem::UnnamedExpr(Expr::BinaryOp {
840+
left: Box::new(Expr::Value(Value::Boolean(true))),
841+
op: BinaryOperator::Xor,
842+
right: Box::new(Expr::Value(Value::Boolean(false))),
843+
}),
844+
select.projection[2]
845+
);
846+
assert_eq!(
847+
SelectItem::UnnamedExpr(Expr::BinaryOp {
848+
left: Box::new(Expr::Value(Value::Boolean(false))),
849+
op: BinaryOperator::Xor,
850+
right: Box::new(Expr::Value(Value::Boolean(true))),
851+
}),
852+
select.projection[3]
853+
);
854+
}
855+
818856
#[test]
819857
fn parse_between() {
820858
fn chk(negated: bool) {

0 commit comments

Comments
 (0)