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/10/14 17:11:25 UTC

(datafusion-sqlparser-rs) 01/01: Postgres: support OVERRIDING { SYSTEM | USER } VALUE in INSERT (#2626)

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

github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-2626-dacdbc99cd16c335fccd353ba41f6c18a7cd73b8
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit 159c667f5808d02e4212896463bdb7d69eb62575
Author: Zvonimir <zv...@datadoghq.com>
AuthorDate: Wed Oct 14 17:11:03 2026 +0000

    Postgres: support OVERRIDING { SYSTEM | USER } VALUE in INSERT (#2626)
    
    Co-authored-by: Claude Opus 5.5 <no...@anthropic.com>
---
 src/ast/dml.rs              | 32 +++++++++++++++++++++
 src/ast/mod.rs              |  4 +--
 src/ast/spans.rs            |  1 +
 src/dialect/snowflake.rs    |  1 +
 src/keywords.rs             |  1 +
 src/parser/mod.rs           | 25 +++++++++++++++-
 tests/sqlparser_postgres.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 130 insertions(+), 3 deletions(-)

diff --git a/src/ast/dml.rs b/src/ast/dml.rs
index 291fc9fa..c8d55ee6 100644
--- a/src/ast/dml.rs
+++ b/src/ast/dml.rs
@@ -37,6 +37,29 @@ use super::{
     Values,
 };
 
+/// The `OVERRIDING` clause of a PostgreSQL `INSERT`: whether values supplied for an identity
+/// column are used instead of the values its sequence would generate.
+///
+/// See <https://www.postgresql.org/docs/current/sql-insert.html>
+#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum InsertOverriding {
+    /// `OVERRIDING SYSTEM VALUE`
+    System,
+    /// `OVERRIDING USER VALUE`
+    User,
+}
+
+impl Display for InsertOverriding {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            InsertOverriding::System => write!(f, "OVERRIDING SYSTEM VALUE"),
+            InsertOverriding::User => write!(f, "OVERRIDING USER VALUE"),
+        }
+    }
+}
+
 /// INSERT statement.
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -87,6 +110,10 @@ pub struct Insert {
     pub partitioned: Option<Vec<Expr>>,
     /// Columns defined after PARTITION
     pub after_columns: Vec<Ident>,
+    /// PostgreSQL `OVERRIDING { SYSTEM | USER } VALUE`, between the column list and the source.
+    ///
+    /// See <https://www.postgresql.org/docs/current/sql-insert.html>
+    pub overriding: Option<InsertOverriding>,
     /// whether the insert has the table keyword (Hive)
     pub has_table_keyword: bool,
     /// ON INSERT
@@ -225,6 +252,11 @@ impl Display for Insert {
             SpaceOrNewline.fmt(f)?;
         }
 
+        if let Some(overriding) = &self.overriding {
+            write!(f, "{overriding}")?;
+            SpaceOrNewline.fmt(f)?;
+        }
+
         if let Some(output) = &self.output {
             write!(f, "{output}")?;
             SpaceOrNewline.fmt(f)?;
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 9d3ee77e..fcd013aa 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -88,8 +88,8 @@ pub use self::ddl::{
     UserDefinedTypeStorage, ViewColumnDef, WithData,
 };
 pub use self::dml::{
-    Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr,
-    MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, MultiTableInsertIntoClause,
+    Delete, Insert, InsertOverriding, Merge, MergeAction, MergeClause, MergeClauseKind,
+    MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, MultiTableInsertIntoClause,
     MultiTableInsertType, MultiTableInsertValue, MultiTableInsertValues,
     MultiTableInsertWhenClause, OutputClause, Update,
 };
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index cae24f95..6c4a11c9 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -1363,6 +1363,7 @@ impl Spanned for Insert {
             source,
             partitioned,
             after_columns,
+            overriding: _,        // enum, postgres specific
             has_table_keyword: _, // bool
             on,
             returning,
diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs
index 3ebc1686..a25f2863 100644
--- a/src/dialect/snowflake.rs
+++ b/src/dialect/snowflake.rs
@@ -1906,6 +1906,7 @@ fn parse_multi_table_insert(
         assignments: vec![],
         partitioned: None,
         after_columns: vec![],
+        overriding: None,
         has_table_keyword: false,
         on: None,
         returning: None,
diff --git a/src/keywords.rs b/src/keywords.rs
index deae7bbd..e25cb34c 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -762,6 +762,7 @@ define_keywords!(
     OVERLAPS,
     OVERLAY,
     OVERRIDE,
+    OVERRIDING,
     OVERWRITE,
     OWNED,
     OWNER,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 6daad0f6..e7f26c1a 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -18672,9 +18672,12 @@ impl<'a> Parser<'a> {
             let table = self.parse_keyword(Keyword::TABLE);
             let table_object = self.parse_table_object()?;
 
-            // `BY NAME` is an INSERT clause, not a table alias.
+            // `BY NAME` and `OVERRIDING { SYSTEM | USER } VALUE` are INSERT clauses, not table
+            // aliases.
             let table_alias = if self.dialect.supports_insert_table_alias()
                 && !self.peek_keywords(&[Keyword::BY, Keyword::NAME])
+                && !self.peek_keywords(&[Keyword::OVERRIDING, Keyword::SYSTEM])
+                && !self.peek_keywords(&[Keyword::OVERRIDING, Keyword::USER])
                 && !self.peek_sub_query()
                 && self
                     .peek_one_of_keywords(&[Keyword::DEFAULT, Keyword::VALUES])
@@ -18699,6 +18702,7 @@ impl<'a> Parser<'a> {
             let is_mysql = dialect_of!(self is MySqlDialect);
 
             let mut by_name = false;
+            let mut overriding = None;
             let (columns, partitioned, after_columns, output, source, assignments) = if self
                 .parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES])
             {
@@ -18721,6 +18725,8 @@ impl<'a> Parser<'a> {
                     Default::default()
                 };
 
+                overriding = self.parse_insert_overriding()?;
+
                 let output = self.maybe_parse_output_clause()?;
 
                 let (source, assignments) = if self.peek_keyword(Keyword::FORMAT)
@@ -18838,6 +18844,7 @@ impl<'a> Parser<'a> {
                 partitioned,
                 columns,
                 after_columns,
+                overriding,
                 source,
                 assignments,
                 has_table_keyword: table,
@@ -18926,6 +18933,22 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Parse PostgreSQL's `OVERRIDING { SYSTEM | USER } VALUE` in an `INSERT`, if present.
+    fn parse_insert_overriding(&mut self) -> Result<Option<InsertOverriding>, ParserError> {
+        if !self.parse_keyword(Keyword::OVERRIDING) {
+            return Ok(None);
+        }
+        let overriding = if self.parse_keyword(Keyword::SYSTEM) {
+            InsertOverriding::System
+        } else if self.parse_keyword(Keyword::USER) {
+            InsertOverriding::User
+        } else {
+            return self.expected_ref("SYSTEM or USER", self.peek_token_ref());
+        };
+        self.expect_keyword_is(Keyword::VALUE)?;
+        Ok(Some(overriding))
+    }
+
     /// Parse an optional `PARTITION (...)` clause for INSERT statements.
     pub fn parse_insert_partition(&mut self) -> Result<Option<Vec<Expr>>, ParserError> {
         if self.parse_keyword(Keyword::PARTITION) {
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index fb998510..dbc61db8 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -6146,6 +6146,7 @@ fn test_simple_postgres_insert_with_alias() {
             assignments: vec![],
             partitioned: None,
             after_columns: vec![],
+            overriding: None,
             has_table_keyword: false,
             on: None,
             returning: None,
@@ -6230,6 +6231,7 @@ fn test_simple_postgres_insert_with_alias() {
             assignments: vec![],
             partitioned: None,
             after_columns: vec![],
+            overriding: None,
             has_table_keyword: false,
             on: None,
             returning: None,
@@ -6312,6 +6314,7 @@ fn test_simple_insert_with_quoted_alias() {
             assignments: vec![],
             partitioned: None,
             after_columns: vec![],
+            overriding: None,
             has_table_keyword: false,
             on: None,
             returning: None,
@@ -10037,6 +10040,72 @@ fn parse_trim_from_without_characters() {
     pg().one_statement_parses_to("SELECT TRIM(FROM ' x ')", "SELECT TRIM(' x ')");
 }
 
+#[test]
+fn parse_insert_overriding() {
+    for (sql, expected) in [
+        (
+            "INSERT INTO t (id, a) OVERRIDING SYSTEM VALUE VALUES (1, 'x')",
+            InsertOverriding::System,
+        ),
+        (
+            "INSERT INTO t (id, a) OVERRIDING USER VALUE VALUES (1, 'x')",
+            InsertOverriding::User,
+        ),
+        (
+            "INSERT INTO t (id, a) OVERRIDING SYSTEM VALUE SELECT id, a FROM u",
+            InsertOverriding::System,
+        ),
+        (
+            "INSERT INTO t (id) OVERRIDING USER VALUE VALUES (1) ON CONFLICT DO NOTHING RETURNING id",
+            InsertOverriding::User,
+        ),
+    ] {
+        match pg_and_generic().verified_stmt(sql) {
+            Statement::Insert(Insert { overriding, .. }) => assert_eq!(overriding, Some(expected)),
+            statement => panic!("Expected INSERT statement, got: {statement:?}"),
+        }
+    }
+
+    // Without a column list, PostgreSQL's implicit table alias must not swallow `OVERRIDING`.
+    for sql in [
+        "INSERT INTO t OVERRIDING SYSTEM VALUE VALUES (1, 'x')",
+        "INSERT INTO t OVERRIDING USER VALUE SELECT * FROM u",
+    ] {
+        match pg().verified_stmt(sql) {
+            Statement::Insert(Insert {
+                table_alias,
+                overriding,
+                ..
+            }) => {
+                assert_eq!(table_alias, None);
+                assert!(overriding.is_some());
+            }
+            statement => panic!("Expected INSERT statement, got: {statement:?}"),
+        }
+    }
+    pg().verified_stmt("INSERT INTO t AS x (id) OVERRIDING SYSTEM VALUE VALUES (1)");
+
+    // An INSERT without the clause has none.
+    match pg_and_generic().verified_stmt("INSERT INTO t (id) VALUES (1)") {
+        Statement::Insert(Insert { overriding, .. }) => assert_eq!(overriding, None),
+        statement => panic!("Expected INSERT statement, got: {statement:?}"),
+    }
+
+    // `OVERRIDING` takes `SYSTEM` or `USER`, then `VALUE`, and is followed by a query: PostgreSQL's
+    // grammar does not allow it before `DEFAULT VALUES`.
+    for sql in [
+        "INSERT INTO t (id) OVERRIDING VALUE VALUES (1)",
+        "INSERT INTO t (id) OVERRIDING SYSTEM VALUES (1)",
+        "INSERT INTO t (id) OVERRIDING DEFAULT VALUE VALUES (1)",
+        "INSERT INTO t OVERRIDING SYSTEM VALUE DEFAULT VALUES",
+    ] {
+        assert!(
+            pg_and_generic().parse_sql_statements(sql).is_err(),
+            "{sql} should not parse"
+        );
+    }
+}
+
 #[test]
 fn parse_insert_by_name_keywords_as_table_and_alias() {
     // Without a table name, `BY NAME` is not an INSERT BY NAME clause. PostgreSQL


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