Skip to content

Commit adbfc46

Browse files
authored
[Oracle] Lower StringConcat precedence (#2115)
1 parent ca2d333 commit adbfc46

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

src/dialect/oracle.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use super::Dialect;
18+
use log::debug;
19+
20+
use crate::{
21+
parser::{Parser, ParserError},
22+
tokenizer::Token,
23+
};
24+
25+
use super::{Dialect, Precedence};
1926

2027
/// A [`Dialect`] for [Oracle Databases](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/index.html)
2128
#[derive(Debug)]
@@ -75,6 +82,16 @@ impl Dialect for OracleDialect {
7582
true
7683
}
7784

85+
fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
86+
let t = parser.peek_token();
87+
debug!("get_next_precedence() {t:?}");
88+
89+
match t.token {
90+
Token::StringConcat => Some(Ok(self.prec_value(Precedence::PlusMinus))),
91+
_ => None,
92+
}
93+
}
94+
7895
fn supports_group_by_expr(&self) -> bool {
7996
true
8097
}

tests/sqlparser_oracle.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
//! Test SQL syntax, specific to [sqlparser::dialect::OracleDialect].
19+
20+
#[cfg(test)]
21+
use pretty_assertions::assert_eq;
22+
23+
use sqlparser::{
24+
ast::{BinaryOperator, Expr, Value, ValueWithSpan},
25+
dialect::OracleDialect,
26+
tokenizer::Span,
27+
};
28+
use test_utils::{expr_from_projection, number, TestedDialects};
29+
30+
mod test_utils;
31+
32+
fn oracle() -> TestedDialects {
33+
TestedDialects::new(vec![Box::new(OracleDialect)])
34+
}
35+
36+
/// Oracle: `||` has a lower precedence than `*` and `/`
37+
#[test]
38+
fn muldiv_have_higher_precedence_than_strconcat() {
39+
// ............... A .. B ...... C .. D ...........
40+
let sql = "SELECT 3 / 5 || 'asdf' || 7 * 9 FROM dual";
41+
let select = oracle().verified_only_select(sql);
42+
assert_eq!(1, select.projection.len());
43+
assert_eq!(
44+
expr_from_projection(&select.projection[0]),
45+
// (C || D)
46+
&Expr::BinaryOp {
47+
// (A || B)
48+
left: Box::new(Expr::BinaryOp {
49+
// A
50+
left: Box::new(Expr::BinaryOp {
51+
left: Box::new(Expr::Value(number("3").into())),
52+
op: BinaryOperator::Divide,
53+
right: Box::new(Expr::Value(number("5").into())),
54+
}),
55+
op: BinaryOperator::StringConcat,
56+
right: Box::new(Expr::Value(ValueWithSpan {
57+
value: Value::SingleQuotedString("asdf".into()),
58+
span: Span::empty(),
59+
})),
60+
}),
61+
op: BinaryOperator::StringConcat,
62+
// D
63+
right: Box::new(Expr::BinaryOp {
64+
left: Box::new(Expr::Value(number("7").into())),
65+
op: BinaryOperator::Multiply,
66+
right: Box::new(Expr::Value(number("9").into())),
67+
}),
68+
}
69+
);
70+
}
71+
72+
/// Oracle: `+`, `-`, and `||` have the same precedence and parse from left-to-right
73+
#[test]
74+
fn plusminus_have_same_precedence_as_strconcat() {
75+
// ................ A .. B .... C .. D ............
76+
let sql = "SELECT 3 + 5 || '.3' || 7 - 9 FROM dual";
77+
let select = oracle().verified_only_select(sql);
78+
assert_eq!(1, select.projection.len());
79+
assert_eq!(
80+
expr_from_projection(&select.projection[0]),
81+
// D
82+
&Expr::BinaryOp {
83+
left: Box::new(Expr::BinaryOp {
84+
// B
85+
left: Box::new(Expr::BinaryOp {
86+
// A
87+
left: Box::new(Expr::BinaryOp {
88+
left: Box::new(Expr::Value(number("3").into())),
89+
op: BinaryOperator::Plus,
90+
right: Box::new(Expr::Value(number("5").into())),
91+
}),
92+
op: BinaryOperator::StringConcat,
93+
right: Box::new(Expr::Value(ValueWithSpan {
94+
value: Value::SingleQuotedString(".3".into()),
95+
span: Span::empty(),
96+
})),
97+
}),
98+
op: BinaryOperator::StringConcat,
99+
right: Box::new(Expr::Value(number("7").into())),
100+
}),
101+
op: BinaryOperator::Minus,
102+
right: Box::new(Expr::Value(number("9").into()))
103+
}
104+
);
105+
}

0 commit comments

Comments
 (0)