You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/02/23 14:06:52 UTC

(datafusion-sqlparser-rs) branch gh-readonly-queue/main/pr-2214-36e8ce602d75ebe607ec45f065cfb529f9e1ec5b created (now 0b589b25)

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

github-bot pushed a change to branch gh-readonly-queue/main/pr-2214-36e8ce602d75ebe607ec45f065cfb529f9e1ec5b
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


      at 0b589b25 [Oracle] Table alias for INSERTed table (#2214)

This branch includes the following new commits:

     new 0b589b25 [Oracle] Table alias for INSERTed table (#2214)

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org


(datafusion-sqlparser-rs) 01/01: [Oracle] Table alias for INSERTed table (#2214)

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0b589b2555be7a29879220744124ec7b350e6bbf
Author: xitep <xi...@users.noreply.github.com>
AuthorDate: Mon Feb 23 15:06:31 2026 +0100

    [Oracle] Table alias for INSERTed table (#2214)
    
    Co-authored-by: Ifeanyi Ubah <if...@yahoo.com>
---
 src/ast/dml.rs              |  17 +++++--
 src/ast/mod.rs              |  11 +++++
 src/ast/spans.rs            |   2 +-
 src/dialect/mod.rs          |   5 ++
 src/dialect/oracle.rs       |   4 ++
 src/dialect/postgresql.rs   |   4 ++
 src/parser/mod.rs           |  33 +++++++++-----
 tests/sqlparser_oracle.rs   | 108 +++++++++++++++++++++++++++++++++++++++++++-
 tests/sqlparser_postgres.rs |  33 +++++++++-----
 9 files changed, 186 insertions(+), 31 deletions(-)

diff --git a/src/ast/dml.rs b/src/ast/dml.rs
index a0be916d..06f731c5 100644
--- a/src/ast/dml.rs
+++ b/src/ast/dml.rs
@@ -33,7 +33,8 @@ use super::{
     display_comma_separated, helpers::attached_token::AttachedToken, query::InputFormatClause,
     Assignment, Expr, FromTable, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnInsert,
     OptimizerHint, OrderByExpr, Query, SelectInto, SelectItem, Setting, SqliteOnConflict,
-    TableFactor, TableObject, TableWithJoins, UpdateTableFromKind, Values,
+    TableAliasWithoutColumns, TableFactor, TableObject, TableWithJoins, UpdateTableFromKind,
+    Values,
 };
 
 /// INSERT statement.
@@ -56,8 +57,9 @@ pub struct Insert {
     pub into: bool,
     /// TABLE
     pub table: TableObject,
-    /// table_name as foo (for PostgreSQL)
-    pub table_alias: Option<Ident>,
+    /// `table_name as foo` (for PostgreSQL)
+    /// `table_name foo` (for Oracle)
+    pub table_alias: Option<TableAliasWithoutColumns>,
     /// COLUMNS
     pub columns: Vec<Ident>,
     /// Overwrite (Hive)
@@ -125,8 +127,13 @@ pub struct Insert {
 impl Display for Insert {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // SQLite OR conflict has a special format: INSERT OR ... INTO table_name
-        let table_name = if let Some(alias) = &self.table_alias {
-            format!("{0} AS {alias}", self.table)
+        let table_name = if let Some(table_alias) = &self.table_alias {
+            format!(
+                "{table} {as_keyword}{alias}",
+                table = self.table,
+                as_keyword = if table_alias.explicit { "AS " } else { "" },
+                alias = table_alias.alias
+            )
         } else {
             self.table.to_string()
         };
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 61b0f65b..7b1e9447 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -6480,6 +6480,17 @@ pub struct InsertAliases {
     pub col_aliases: Option<Vec<Ident>>,
 }
 
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+/// Optional alias for an `INSERT` table; i.e. the table to be inserted into
+pub struct TableAliasWithoutColumns {
+    /// `true` if the aliases was explicitly introduced with the "AS" keyword
+    pub explicit: bool,
+    /// the alias name itself
+    pub alias: Ident,
+}
+
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 128fe01b..b29a134b 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -1327,7 +1327,7 @@ impl Spanned for Insert {
         union_spans(
             core::iter::once(insert_token.0.span)
                 .chain(core::iter::once(table.span()))
-                .chain(table_alias.as_ref().map(|i| i.span))
+                .chain(table_alias.iter().map(|k| k.alias.span))
                 .chain(columns.iter().map(|i| i.span))
                 .chain(source.as_ref().map(|q| q.span()))
                 .chain(assignments.iter().map(|i| i.span()))
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index b1be1590..bcca455e 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -1240,6 +1240,11 @@ pub trait Dialect: Debug + Any {
         false
     }
 
+    /// Returns true if this dialect supports `INSERT INTO t [[AS] alias] ...`.
+    fn supports_insert_table_alias(&self) -> bool {
+        false
+    }
+
     /// Returns true if this dialect supports `SET` statements without an explicit
     /// assignment operator such as `=`. For example: `SET SHOWPLAN_XML ON`.
     fn supports_set_stmt_without_operator(&self) -> bool {
diff --git a/src/dialect/oracle.rs b/src/dialect/oracle.rs
index c2147eae..dce0493d 100644
--- a/src/dialect/oracle.rs
+++ b/src/dialect/oracle.rs
@@ -110,4 +110,8 @@ impl Dialect for OracleDialect {
     fn supports_comment_optimizer_hint(&self) -> bool {
         true
     }
+
+    fn supports_insert_table_alias(&self) -> bool {
+        true
+    }
 }
diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs
index b0511f6d..13bd82bf 100644
--- a/src/dialect/postgresql.rs
+++ b/src/dialect/postgresql.rs
@@ -288,4 +288,8 @@ impl Dialect for PostgreSqlDialect {
     fn supports_interval_options(&self) -> bool {
         true
     }
+
+    fn supports_insert_table_alias(&self) -> bool {
+        true
+    }
 }
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 0767e432..bb11d79c 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -17231,12 +17231,27 @@ impl<'a> Parser<'a> {
             let table = self.parse_keyword(Keyword::TABLE);
             let table_object = self.parse_table_object()?;
 
-            let table_alias =
-                if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) {
-                    Some(self.parse_identifier()?)
+            let table_alias = if self.dialect.supports_insert_table_alias()
+                && !self.peek_sub_query()
+                && self
+                    .peek_one_of_keywords(&[Keyword::DEFAULT, Keyword::VALUES])
+                    .is_none()
+            {
+                if self.parse_keyword(Keyword::AS) {
+                    Some(TableAliasWithoutColumns {
+                        explicit: true,
+                        alias: self.parse_identifier()?,
+                    })
                 } else {
-                    None
-                };
+                    self.maybe_parse(|parser| parser.parse_identifier())?
+                        .map(|alias| TableAliasWithoutColumns {
+                            explicit: false,
+                            alias,
+                        })
+                }
+            } else {
+                None
+            };
 
             let is_mysql = dialect_of!(self is MySqlDialect);
 
@@ -19477,14 +19492,8 @@ impl<'a> Parser<'a> {
 
     /// Returns true if the next keyword indicates a sub query, i.e. SELECT or WITH
     fn peek_sub_query(&mut self) -> bool {
-        if self
-            .parse_one_of_keywords(&[Keyword::SELECT, Keyword::WITH])
+        self.peek_one_of_keywords(&[Keyword::SELECT, Keyword::WITH])
             .is_some()
-        {
-            self.prev_token();
-            return true;
-        }
-        false
     }
 
     pub(crate) fn parse_show_stmt_options(&mut self) -> Result<ShowStatementOptions, ParserError> {
diff --git a/tests/sqlparser_oracle.rs b/tests/sqlparser_oracle.rs
index 8f7bb867..b34a9308 100644
--- a/tests/sqlparser_oracle.rs
+++ b/tests/sqlparser_oracle.rs
@@ -21,7 +21,10 @@
 use pretty_assertions::assert_eq;
 
 use sqlparser::{
-    ast::{BinaryOperator, Expr, Ident, QuoteDelimitedString, Value, ValueWithSpan},
+    ast::{
+        BinaryOperator, Expr, Ident, Insert, ObjectName, Query, QuoteDelimitedString, SetExpr,
+        Statement, TableAliasWithoutColumns, TableObject, Value, ValueWithSpan,
+    },
     dialect::OracleDialect,
     parser::ParserError,
     tokenizer::Span,
@@ -421,3 +424,106 @@ fn test_connect_by() {
           ORDER BY \"Employee\", \"Manager\", \"Pathlen\", \"Path\"",
     );
 }
+
+#[test]
+fn test_insert_with_table_alias() {
+    let oracle_dialect = oracle();
+
+    fn verify_table_name_with_alias(stmt: &Statement, exp_table_name: &str, exp_table_alias: &str) {
+        assert!(matches!(stmt,
+            Statement::Insert(Insert {
+                table: TableObject::TableName(table_name),
+                table_alias: Some(TableAliasWithoutColumns {
+                    explicit: false,
+                    alias: Ident {
+                        value: table_alias,
+                        quote_style: None,
+                        span: _
+                    }
+                }),
+                ..
+            })
+            if table_alias == exp_table_alias
+            && table_name == &ObjectName::from(vec![Ident {
+                value: exp_table_name.into(),
+                quote_style: None,
+                span: Span::empty(),
+            }])
+        ));
+    }
+
+    let stmt = oracle_dialect.verified_stmt(
+        "INSERT INTO foo_t t \
+         SELECT 1, 2, 3 FROM dual",
+    );
+    verify_table_name_with_alias(&stmt, "foo_t", "t");
+
+    let stmt = oracle_dialect.verified_stmt(
+        "INSERT INTO foo_t asdf (a, b, c) \
+         SELECT 1, 2, 3 FROM dual",
+    );
+    verify_table_name_with_alias(&stmt, "foo_t", "asdf");
+
+    let stmt = oracle_dialect.verified_stmt(
+        "INSERT INTO foo_t t (a, b, c) \
+         VALUES (1, 2, 3)",
+    );
+    verify_table_name_with_alias(&stmt, "foo_t", "t");
+
+    let stmt = oracle_dialect.verified_stmt(
+        "INSERT INTO foo_t t \
+         VALUES (1, 2, 3)",
+    );
+    verify_table_name_with_alias(&stmt, "foo_t", "t");
+}
+
+#[test]
+fn test_insert_without_alias() {
+    let oracle_dialect = oracle();
+
+    // check DEFAULT
+    let sql = "INSERT INTO t default SELECT 'a' FROM dual";
+    assert_eq!(
+        oracle_dialect.parse_sql_statements(sql),
+        Err(ParserError::ParserError(
+            "Expected: SELECT, VALUES, or a subquery in the query body, found: default".into()
+        ))
+    );
+
+    // check SELECT
+    let sql = "INSERT INTO t SELECT 'a' FROM dual";
+    let stmt = oracle_dialect.verified_stmt(sql);
+    assert!(matches!(
+        &stmt,
+        Statement::Insert(Insert {
+            table_alias: None,
+            source: Some(source),
+            ..
+        })
+        if matches!(&**source, Query { body, .. } if matches!(&**body, SetExpr::Select(_)))));
+
+    // check WITH
+    let sql = "INSERT INTO dual WITH w AS (SELECT 1 AS y FROM dual) SELECT y FROM w";
+    let stmt = oracle_dialect.verified_stmt(sql);
+    assert!(matches!(
+        &stmt,
+        Statement::Insert(Insert {
+            table_alias: None,
+            source: Some(source),
+            ..
+        })
+        if matches!(&**source, Query { body, .. } if matches!(&**body, SetExpr::Select(_)))));
+
+    // check VALUES
+    let sql = "INSERT INTO t VALUES (1)";
+    let stmt = oracle_dialect.verified_stmt(sql);
+    assert!(matches!(
+        stmt,
+        Statement::Insert(Insert {
+            table_alias: None,
+            source: Some(source),
+            ..
+        })
+        if matches!(&*source, Query { body, .. } if matches!(&**body, SetExpr::Values(_)))
+    ));
+}
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index 03517876..510f6ccc 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -5445,10 +5445,13 @@ fn test_simple_postgres_insert_with_alias() {
                 quote_style: None,
                 span: Span::empty(),
             }])),
-            table_alias: Some(Ident {
-                value: "test_table".to_string(),
-                quote_style: None,
-                span: Span::empty(),
+            table_alias: Some(TableAliasWithoutColumns {
+                explicit: true,
+                alias: Ident {
+                    value: "test_table".to_string(),
+                    quote_style: None,
+                    span: Span::empty(),
+                }
             }),
             columns: vec![
                 Ident {
@@ -5521,10 +5524,13 @@ fn test_simple_postgres_insert_with_alias() {
                 quote_style: None,
                 span: Span::empty(),
             }])),
-            table_alias: Some(Ident {
-                value: "test_table".to_string(),
-                quote_style: None,
-                span: Span::empty(),
+            table_alias: Some(TableAliasWithoutColumns {
+                explicit: true,
+                alias: Ident {
+                    value: "test_table".to_string(),
+                    quote_style: None,
+                    span: Span::empty(),
+                }
             }),
             columns: vec![
                 Ident {
@@ -5599,10 +5605,13 @@ fn test_simple_insert_with_quoted_alias() {
                 quote_style: None,
                 span: Span::empty(),
             }])),
-            table_alias: Some(Ident {
-                value: "Test_Table".to_string(),
-                quote_style: Some('"'),
-                span: Span::empty(),
+            table_alias: Some(TableAliasWithoutColumns {
+                explicit: true,
+                alias: Ident {
+                    value: "Test_Table".to_string(),
+                    quote_style: Some('"'),
+                    span: Span::empty(),
+                }
             }),
             columns: vec![
                 Ident {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org