This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch 
gh-readonly-queue/main/pr-2113-367aa6e8d0fe696c16dbeb9a89f7305860e2e7c2
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit defe9396e52b889bb1f0b0606d9df3dfae351e18
Author: xitep <[email protected]>
AuthorDate: Tue Dec 2 11:13:24 2025 +0100

    Introduce Oracle dialect (#2113)
---
 examples/cli.rs           |  1 +
 src/dialect/mod.rs        |  3 ++
 src/dialect/oracle.rs     | 81 +++++++++++++++++++++++++++++++++++++++++++++++
 src/keywords.rs           |  9 ++++++
 src/parser/mod.rs         |  2 +-
 src/test_utils.rs         |  1 +
 tests/sqlparser_common.rs | 14 +++++---
 7 files changed, 105 insertions(+), 6 deletions(-)

diff --git a/examples/cli.rs b/examples/cli.rs
index 08a40a6d..3c4299b2 100644
--- a/examples/cli.rs
+++ b/examples/cli.rs
@@ -58,6 +58,7 @@ $ cargo run --example cli - [--dialectname]
         "--clickhouse" => Box::new(ClickHouseDialect {}),
         "--duckdb" => Box::new(DuckDbDialect {}),
         "--sqlite" => Box::new(SQLiteDialect {}),
+        "--oracle" => Box::new(OracleDialect {}),
         "--generic" | "" => Box::new(GenericDialect {}),
         s => panic!("Unexpected parameter: {s}"),
     };
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index ef4e1cdd..83c6da48 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -24,6 +24,7 @@ mod generic;
 mod hive;
 mod mssql;
 mod mysql;
+mod oracle;
 mod postgresql;
 mod redshift;
 mod snowflake;
@@ -45,6 +46,7 @@ pub use self::generic::GenericDialect;
 pub use self::hive::HiveDialect;
 pub use self::mssql::MsSqlDialect;
 pub use self::mysql::MySqlDialect;
+pub use self::oracle::OracleDialect;
 pub use self::postgresql::PostgreSqlDialect;
 pub use self::redshift::RedshiftSqlDialect;
 pub use self::snowflake::SnowflakeDialect;
@@ -1260,6 +1262,7 @@ pub fn dialect_from_str(dialect_name: impl AsRef<str>) -> 
Option<Box<dyn Dialect
         "ansi" => Some(Box::new(AnsiDialect {})),
         "duckdb" => Some(Box::new(DuckDbDialect {})),
         "databricks" => Some(Box::new(DatabricksDialect {})),
+        "oracle" => Some(Box::new(OracleDialect {})),
         _ => None,
     }
 }
diff --git a/src/dialect/oracle.rs b/src/dialect/oracle.rs
new file mode 100644
index 00000000..0d6aee5e
--- /dev/null
+++ b/src/dialect/oracle.rs
@@ -0,0 +1,81 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use super::Dialect;
+
+/// A [`Dialect`] for [Oracle 
Databases](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/index.html)
+#[derive(Debug)]
+pub struct OracleDialect;
+
+impl Dialect for OracleDialect {
+    // ~ appears not to be called anywhere
+    fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
+        Some('"')
+    }
+
+    fn is_delimited_identifier_start(&self, ch: char) -> bool {
+        ch == '"'
+    }
+
+    fn is_identifier_start(&self, ch: char) -> bool {
+        ch.is_alphabetic()
+    }
+
+    fn is_identifier_part(&self, ch: char) -> bool {
+        ch.is_alphanumeric() || ch == '_' || ch == '$' || ch == '#' || ch == 
'@'
+    }
+
+    fn supports_outer_join_operator(&self) -> bool {
+        true
+    }
+
+    fn supports_connect_by(&self) -> bool {
+        true
+    }
+
+    fn supports_execute_immediate(&self) -> bool {
+        true
+    }
+
+    fn supports_match_recognize(&self) -> bool {
+        true
+    }
+
+    fn supports_window_function_null_treatment_arg(&self) -> bool {
+        true
+    }
+
+    fn supports_boolean_literals(&self) -> bool {
+        false
+    }
+
+    fn supports_comment_on(&self) -> bool {
+        true
+    }
+
+    fn supports_create_table_select(&self) -> bool {
+        true
+    }
+
+    fn supports_set_stmt_without_operator(&self) -> bool {
+        true
+    }
+
+    fn supports_group_by_expr(&self) -> bool {
+        true
+    }
+}
diff --git a/src/keywords.rs b/src/keywords.rs
index 834d3495..827df1ce 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -220,6 +220,7 @@ define_keywords!(
     COMMITTED,
     COMMUTATOR,
     COMPATIBLE,
+    COMPRESS,
     COMPRESSION,
     COMPUPDATE,
     COMPUTE,
@@ -464,6 +465,7 @@ define_keywords!(
     IAM_ROLE,
     ICEBERG,
     ID,
+    IDENTIFIED,
     IDENTITY,
     IDENTITY_INSERT,
     IF,
@@ -567,6 +569,7 @@ define_keywords!(
     LOG,
     LOGIN,
     LOGS,
+    LONG,
     LONGBLOB,
     LONGTEXT,
     LOWCARDINALITY,
@@ -652,6 +655,7 @@ define_keywords!(
     NFKD,
     NO,
     NOBYPASSRLS,
+    NOCOMPRESS,
     NOCREATEDB,
     NOCREATEROLE,
     NOINHERIT,
@@ -675,6 +679,7 @@ define_keywords!(
     NULLABLE,
     NULLIF,
     NULLS,
+    NUMBER,
     NUMERIC,
     NVARCHAR,
     OBJECT,
@@ -741,6 +746,7 @@ define_keywords!(
     PAST,
     PATH,
     PATTERN,
+    PCTFREE,
     PER,
     PERCENT,
     PERCENTILE_CONT,
@@ -913,6 +919,7 @@ define_keywords!(
     SIGNED,
     SIMILAR,
     SIMPLE,
+    SIZE,
     SKIP,
     SLOW,
     SMALLINT,
@@ -974,6 +981,7 @@ define_keywords!(
     SWAP,
     SYMMETRIC,
     SYNC,
+    SYNONYM,
     SYSTEM,
     SYSTEM_TIME,
     SYSTEM_USER,
@@ -1085,6 +1093,7 @@ define_keywords!(
     VARBINARY,
     VARBIT,
     VARCHAR,
+    VARCHAR2,
     VARIABLE,
     VARIABLES,
     VARYING,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 13c3c5b3..b2fa3b16 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -12247,7 +12247,7 @@ impl<'a> Parser<'a> {
         let (tables, with_from_keyword) = if 
!self.parse_keyword(Keyword::FROM) {
             // `FROM` keyword is optional in BigQuery SQL.
             // 
https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#delete_statement
-            if dialect_of!(self is BigQueryDialect | GenericDialect) {
+            if dialect_of!(self is BigQueryDialect | OracleDialect | 
GenericDialect) {
                 (vec![], false)
             } else {
                 let tables = self.parse_comma_separated(|p| 
p.parse_object_name(false))?;
diff --git a/src/test_utils.rs b/src/test_utils.rs
index 73d29312..9ba5960e 100644
--- a/src/test_utils.rs
+++ b/src/test_utils.rs
@@ -291,6 +291,7 @@ pub fn all_dialects() -> TestedDialects {
         Box::new(DuckDbDialect {}),
         Box::new(DatabricksDialect {}),
         Box::new(ClickHouseDialect {}),
+        Box::new(OracleDialect {}),
     ])
 }
 
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 91952b8c..ccad67e3 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -34,8 +34,8 @@ use sqlparser::ast::TableFactor::{Pivot, Unpivot};
 use sqlparser::ast::*;
 use sqlparser::dialect::{
     AnsiDialect, BigQueryDialect, ClickHouseDialect, DatabricksDialect, 
Dialect, DuckDbDialect,
-    GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, 
PostgreSqlDialect, RedshiftSqlDialect,
-    SQLiteDialect, SnowflakeDialect,
+    GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, OracleDialect, 
PostgreSqlDialect,
+    RedshiftSqlDialect, SQLiteDialect, SnowflakeDialect,
 };
 use sqlparser::keywords::{Keyword, ALL_KEYWORDS};
 use sqlparser::parser::{Parser, ParserError, ParserOptions};
@@ -712,7 +712,9 @@ fn parse_delete_statement() {
 fn parse_delete_without_from_error() {
     let sql = "DELETE \"table\" WHERE 1";
 
-    let dialects = all_dialects_except(|d| d.is::<BigQueryDialect>() || 
d.is::<GenericDialect>());
+    let dialects = all_dialects_except(|d| {
+        d.is::<BigQueryDialect>() || d.is::<OracleDialect>() || 
d.is::<GenericDialect>()
+    });
     let res = dialects.parse_sql_statements(sql);
     assert_eq!(
         ParserError::ParserError("Expected: FROM, found: WHERE".to_string()),
@@ -723,7 +725,9 @@ fn parse_delete_without_from_error() {
 #[test]
 fn parse_delete_statement_for_multi_tables() {
     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";
-    let dialects = all_dialects_except(|d| d.is::<BigQueryDialect>() || 
d.is::<GenericDialect>());
+    let dialects = all_dialects_except(|d| {
+        d.is::<BigQueryDialect>() || d.is::<OracleDialect>() || 
d.is::<GenericDialect>()
+    });
     match dialects.verified_stmt(sql) {
         Statement::Delete(Delete {
             tables,
@@ -12943,7 +12947,7 @@ fn test_match_recognize_patterns() {
     fn check(pattern: &str, expect: MatchRecognizePattern) {
         let select =
             all_dialects_where(|d| 
d.supports_match_recognize()).verified_only_select(&format!(
-                "SELECT * FROM my_table MATCH_RECOGNIZE(PATTERN ({pattern}) 
DEFINE DUMMY AS true)" // "select * from my_table match_recognize ("
+                "SELECT * FROM my_table MATCH_RECOGNIZE(PATTERN ({pattern}) 
DEFINE DUMMY AS 1 = 1)" // "select * from my_table match_recognize ("
             ));
         let TableFactor::MatchRecognize {
             pattern: actual, ..


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to