WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 07b074b

Browse files
committed
OracleDialect: Initial draft
1 parent 982f766 commit 07b074b

File tree

9 files changed

+272
-36
lines changed

9 files changed

+272
-36
lines changed

examples/cli.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ $ cargo run --example cli - [--dialectname]
5858
"--clickhouse" => Box::new(ClickHouseDialect {}),
5959
"--duckdb" => Box::new(DuckDbDialect {}),
6060
"--sqlite" => Box::new(SQLiteDialect {}),
61+
"--oracle" => Box::new(OracleDialect {}),
6162
"--generic" | "" => Box::new(GenericDialect {}),
6263
s => panic!("Unexpected parameter: {s}"),
6364
};

src/ast/query.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,15 @@ impl SetExpr {
174174
None
175175
}
176176
}
177+
178+
/// If this `SetExpr` is a `SELECT`, returns a mutable [`Select`].
179+
pub fn as_select_mut(&mut self) -> Option<&mut Select> {
180+
if let Self::Select(select) = self {
181+
Some(&mut **select)
182+
} else {
183+
None
184+
}
185+
}
177186
}
178187

179188
impl fmt::Display for SetExpr {

src/dialect/mod.rs

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod generic;
2424
mod hive;
2525
mod mssql;
2626
mod mysql;
27+
mod oracle;
2728
mod postgresql;
2829
mod redshift;
2930
mod snowflake;
@@ -45,6 +46,7 @@ pub use self::generic::GenericDialect;
4546
pub use self::hive::HiveDialect;
4647
pub use self::mssql::MsSqlDialect;
4748
pub use self::mysql::MySqlDialect;
49+
pub use self::oracle::OracleDialect;
4850
pub use self::postgresql::PostgreSqlDialect;
4951
pub use self::redshift::RedshiftSqlDialect;
5052
pub use self::snowflake::SnowflakeDialect;
@@ -84,6 +86,26 @@ macro_rules! dialect_is {
8486
}
8587
}
8688

89+
const DEFAULT_PREC_VALUE_PERIOD: u8 = 100;
90+
const DEFAULT_PREC_VALUE_DOUBLE_COLON: u8 = 50;
91+
const DEFAULT_PREC_VALUE_AT_TZ: u8 = 41;
92+
const DEFAULT_PREC_VALUE_MUL_DIV_MOD_OP: u8 = 40;
93+
const DEFAULT_PREC_VALUE_PLUS_MINUS: u8 = 30;
94+
const DEFAULT_PREC_VALUE_XOR: u8 = 24;
95+
const DEFAULT_PREC_VALUE_AMPERSAND: u8 = 23;
96+
const DEFAULT_PREC_VALUE_CARET: u8 = 22;
97+
const DEFAULT_PREC_VALUE_PIPE: u8 = 21;
98+
const DEFAULT_PREC_VALUE_BETWEEN: u8 = 20;
99+
const DEFAULT_PREC_VALUE_EQ: u8 = 20;
100+
const DEFAULT_PREC_VALUE_LIKE: u8 = 19;
101+
const DEFAULT_PREC_VALUE_IS: u8 = 17;
102+
const DEFAULT_PREC_VALUE_PG_OTHER: u8 = 16;
103+
const DEFAULT_PREC_VALUE_UNARY_NOT: u8 = 15;
104+
const DEFAULT_PREC_VALUE_AND: u8 = 10;
105+
const DEFAULT_PREC_VALUE_OR: u8 = 5;
106+
107+
const DEFAULT_PREC_VALUE_UNKNOWN: u8 = 0;
108+
87109
/// Encapsulates the differences between SQL implementations.
88110
///
89111
/// # SQL Dialects
@@ -773,6 +795,36 @@ pub trait Dialect: Debug + Any {
773795
}
774796
}
775797

798+
/// Decide the lexical Precedence of operators.
799+
///
800+
/// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference
801+
fn prec_value(&self, prec: Precedence) -> u8 {
802+
match prec {
803+
Precedence::Period => DEFAULT_PREC_VALUE_PERIOD,
804+
Precedence::DoubleColon => DEFAULT_PREC_VALUE_DOUBLE_COLON,
805+
Precedence::AtTz => DEFAULT_PREC_VALUE_AT_TZ,
806+
Precedence::MulDivModOp => DEFAULT_PREC_VALUE_MUL_DIV_MOD_OP,
807+
Precedence::PlusMinus => DEFAULT_PREC_VALUE_PLUS_MINUS,
808+
Precedence::Xor => DEFAULT_PREC_VALUE_XOR,
809+
Precedence::Ampersand => DEFAULT_PREC_VALUE_AMPERSAND,
810+
Precedence::Caret => DEFAULT_PREC_VALUE_CARET,
811+
Precedence::Pipe => DEFAULT_PREC_VALUE_PIPE,
812+
Precedence::Between => DEFAULT_PREC_VALUE_BETWEEN,
813+
Precedence::Eq => DEFAULT_PREC_VALUE_EQ,
814+
Precedence::Like => DEFAULT_PREC_VALUE_LIKE,
815+
Precedence::Is => DEFAULT_PREC_VALUE_IS,
816+
Precedence::PgOther => DEFAULT_PREC_VALUE_PG_OTHER,
817+
Precedence::UnaryNot => DEFAULT_PREC_VALUE_UNARY_NOT,
818+
Precedence::And => DEFAULT_PREC_VALUE_AND,
819+
Precedence::Or => DEFAULT_PREC_VALUE_OR,
820+
}
821+
}
822+
823+
/// Returns the precedence when the precedence is otherwise unknown
824+
fn prec_unknown(&self) -> u8 {
825+
DEFAULT_PREC_VALUE_UNKNOWN
826+
}
827+
776828
/// Dialect-specific statement parser override
777829
///
778830
/// This method is called to parse the next statement.
@@ -796,36 +848,6 @@ pub trait Dialect: Debug + Any {
796848
Ok(None)
797849
}
798850

799-
/// Decide the lexical Precedence of operators.
800-
///
801-
/// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference
802-
fn prec_value(&self, prec: Precedence) -> u8 {
803-
match prec {
804-
Precedence::Period => 100,
805-
Precedence::DoubleColon => 50,
806-
Precedence::AtTz => 41,
807-
Precedence::MulDivModOp => 40,
808-
Precedence::PlusMinus => 30,
809-
Precedence::Xor => 24,
810-
Precedence::Ampersand => 23,
811-
Precedence::Caret => 22,
812-
Precedence::Pipe => 21,
813-
Precedence::Between => 20,
814-
Precedence::Eq => 20,
815-
Precedence::Like => 19,
816-
Precedence::Is => 17,
817-
Precedence::PgOther => 16,
818-
Precedence::UnaryNot => 15,
819-
Precedence::And => 10,
820-
Precedence::Or => 5,
821-
}
822-
}
823-
824-
/// Returns the precedence when the precedence is otherwise unknown
825-
fn prec_unknown(&self) -> u8 {
826-
0
827-
}
828-
829851
/// Returns true if this dialect requires the `TABLE` keyword after `DESCRIBE`
830852
///
831853
/// Defaults to false.
@@ -1260,6 +1282,7 @@ pub fn dialect_from_str(dialect_name: impl AsRef<str>) -> Option<Box<dyn Dialect
12601282
"ansi" => Some(Box::new(AnsiDialect {})),
12611283
"duckdb" => Some(Box::new(DuckDbDialect {})),
12621284
"databricks" => Some(Box::new(DatabricksDialect {})),
1285+
"oracle" => Some(Box::new(OracleDialect {})),
12631286
_ => None,
12641287
}
12651288
}

src/dialect/oracle.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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+
use log::debug;
19+
20+
use crate::{dialect::DEFAULT_PREC_VALUE_MUL_DIV_MOD_OP, tokenizer::Token};
21+
22+
use super::Dialect;
23+
24+
/// A [`Dialect`] for [Oracle Databases](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/index.html)
25+
#[derive(Debug)]
26+
pub struct OracleDialect;
27+
28+
impl Dialect for OracleDialect {
29+
// ~ appears not to be called anywhere
30+
fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
31+
Some('"')
32+
}
33+
34+
fn is_delimited_identifier_start(&self, ch: char) -> bool {
35+
ch == '"'
36+
}
37+
38+
fn is_identifier_start(&self, ch: char) -> bool {
39+
ch.is_alphabetic()
40+
}
41+
42+
fn is_identifier_part(&self, ch: char) -> bool {
43+
ch.is_alphanumeric() || ch == '_' || ch == '$' || ch == '#' || ch == '@'
44+
}
45+
46+
fn supports_outer_join_operator(&self) -> bool {
47+
true
48+
}
49+
50+
fn supports_connect_by(&self) -> bool {
51+
true
52+
}
53+
54+
fn supports_execute_immediate(&self) -> bool {
55+
true
56+
}
57+
58+
fn supports_match_recognize(&self) -> bool {
59+
true
60+
}
61+
62+
fn supports_window_function_null_treatment_arg(&self) -> bool {
63+
true
64+
}
65+
66+
fn supports_boolean_literals(&self) -> bool {
67+
false
68+
}
69+
70+
fn supports_comment_on(&self) -> bool {
71+
true
72+
}
73+
74+
fn supports_create_table_select(&self) -> bool {
75+
true
76+
}
77+
78+
fn supports_set_stmt_without_operator(&self) -> bool {
79+
true
80+
}
81+
82+
fn get_next_precedence(
83+
&self,
84+
_parser: &crate::parser::Parser,
85+
) -> Option<Result<u8, crate::parser::ParserError>> {
86+
let t = _parser.peek_token();
87+
debug!("get_next_precedence() {t:?}");
88+
89+
match t.token {
90+
Token::StringConcat => {
91+
// ~ overriding the default precedence to the same level as mul-div-mod
92+
// ~ see: https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/About-SQL-Operators.html
93+
Some(Ok(DEFAULT_PREC_VALUE_MUL_DIV_MOD_OP))
94+
}
95+
_ => None,
96+
}
97+
}
98+
99+
fn supports_group_by_expr(&self) -> bool {
100+
true
101+
}
102+
}

src/keywords.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ define_keywords!(
220220
COMMITTED,
221221
COMMUTATOR,
222222
COMPATIBLE,
223+
COMPRESS,
223224
COMPRESSION,
224225
COMPUPDATE,
225226
COMPUTE,
@@ -464,6 +465,7 @@ define_keywords!(
464465
IAM_ROLE,
465466
ICEBERG,
466467
ID,
468+
IDENTIFIED,
467469
IDENTITY,
468470
IDENTITY_INSERT,
469471
IF,
@@ -567,6 +569,7 @@ define_keywords!(
567569
LOG,
568570
LOGIN,
569571
LOGS,
572+
LONG,
570573
LONGBLOB,
571574
LONGTEXT,
572575
LOWCARDINALITY,
@@ -652,6 +655,7 @@ define_keywords!(
652655
NFKD,
653656
NO,
654657
NOBYPASSRLS,
658+
NOCOMPRESS,
655659
NOCREATEDB,
656660
NOCREATEROLE,
657661
NOINHERIT,
@@ -675,6 +679,7 @@ define_keywords!(
675679
NULLABLE,
676680
NULLIF,
677681
NULLS,
682+
NUMBER,
678683
NUMERIC,
679684
NVARCHAR,
680685
OBJECT,
@@ -741,6 +746,7 @@ define_keywords!(
741746
PAST,
742747
PATH,
743748
PATTERN,
749+
PCTFREE,
744750
PER,
745751
PERCENT,
746752
PERCENTILE_CONT,
@@ -913,6 +919,7 @@ define_keywords!(
913919
SIGNED,
914920
SIMILAR,
915921
SIMPLE,
922+
SIZE,
916923
SKIP,
917924
SLOW,
918925
SMALLINT,
@@ -974,6 +981,7 @@ define_keywords!(
974981
SWAP,
975982
SYMMETRIC,
976983
SYNC,
984+
SYNONYM,
977985
SYSTEM,
978986
SYSTEM_TIME,
979987
SYSTEM_USER,
@@ -1085,6 +1093,7 @@ define_keywords!(
10851093
VARBINARY,
10861094
VARBIT,
10871095
VARCHAR,
1096+
VARCHAR2,
10881097
VARIABLE,
10891098
VARIABLES,
10901099
VARYING,

src/parser/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12228,7 +12228,7 @@ impl<'a> Parser<'a> {
1222812228
let (tables, with_from_keyword) = if !self.parse_keyword(Keyword::FROM) {
1222912229
// `FROM` keyword is optional in BigQuery SQL.
1223012230
// https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#delete_statement
12231-
if dialect_of!(self is BigQueryDialect | GenericDialect) {
12231+
if dialect_of!(self is BigQueryDialect | OracleDialect | GenericDialect) {
1223212232
(vec![], false)
1223312233
} else {
1223412234
let tables = self.parse_comma_separated(|p| p.parse_object_name(false))?;

src/test_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ pub fn all_dialects() -> TestedDialects {
291291
Box::new(DuckDbDialect {}),
292292
Box::new(DatabricksDialect {}),
293293
Box::new(ClickHouseDialect {}),
294+
Box::new(OracleDialect {}),
294295
])
295296
}
296297

tests/sqlparser_common.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ use sqlparser::ast::TableFactor::{Pivot, Unpivot};
3434
use sqlparser::ast::*;
3535
use sqlparser::dialect::{
3636
AnsiDialect, BigQueryDialect, ClickHouseDialect, DatabricksDialect, Dialect, DuckDbDialect,
37-
GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, PostgreSqlDialect, RedshiftSqlDialect,
38-
SQLiteDialect, SnowflakeDialect,
37+
GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, OracleDialect, PostgreSqlDialect,
38+
RedshiftSqlDialect, SQLiteDialect, SnowflakeDialect,
3939
};
4040
use sqlparser::keywords::{Keyword, ALL_KEYWORDS};
4141
use sqlparser::parser::{Parser, ParserError, ParserOptions};
@@ -712,7 +712,9 @@ fn parse_delete_statement() {
712712
fn parse_delete_without_from_error() {
713713
let sql = "DELETE \"table\" WHERE 1";
714714

715-
let dialects = all_dialects_except(|d| d.is::<BigQueryDialect>() || d.is::<GenericDialect>());
715+
let dialects = all_dialects_except(|d| {
716+
d.is::<BigQueryDialect>() || d.is::<OracleDialect>() || d.is::<GenericDialect>()
717+
});
716718
let res = dialects.parse_sql_statements(sql);
717719
assert_eq!(
718720
ParserError::ParserError("Expected: FROM, found: WHERE".to_string()),
@@ -723,7 +725,9 @@ fn parse_delete_without_from_error() {
723725
#[test]
724726
fn parse_delete_statement_for_multi_tables() {
725727
let sql = "DELETE schema1.table1, schema2.table2 FROM schema1.table1 JOIN schema2.table2 ON schema2.table2.col1 = schema1.table1.col1 WHERE schema2.table2.col2 = 1";
726-
let dialects = all_dialects_except(|d| d.is::<BigQueryDialect>() || d.is::<GenericDialect>());
728+
let dialects = all_dialects_except(|d| {
729+
d.is::<BigQueryDialect>() || d.is::<OracleDialect>() || d.is::<GenericDialect>()
730+
});
727731
match dialects.verified_stmt(sql) {
728732
Statement::Delete(Delete {
729733
tables,
@@ -12943,7 +12947,7 @@ fn test_match_recognize_patterns() {
1294312947
fn check(pattern: &str, expect: MatchRecognizePattern) {
1294412948
let select =
1294512949
all_dialects_where(|d| d.supports_match_recognize()).verified_only_select(&format!(
12946-
"SELECT * FROM my_table MATCH_RECOGNIZE(PATTERN ({pattern}) DEFINE DUMMY AS true)" // "select * from my_table match_recognize ("
12950+
"SELECT * FROM my_table MATCH_RECOGNIZE(PATTERN ({pattern}) DEFINE DUMMY AS 1 = 1)" // "select * from my_table match_recognize ("
1294712951
));
1294812952
let TableFactor::MatchRecognize {
1294912953
pattern: actual, ..

0 commit comments

Comments
 (0)