STET

agent_patch

runs/2026-02-28__18-27-22__gpt-5-1-codex-mini/flux-pr-2148/flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/agent-logs/agent.patch

14599 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/dml.rs b/app/src/ast/dml.rs
index 4c36f70..21353ef 100644
--- a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/dml.rs
+++ b/app/src/ast/dml.rs
@@ -193,6 +193,127 @@ impl Display for Insert {
     }
 }
 
+/// Snowflake multi-table `INSERT` target clause with optional column list and values.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct SnowflakeMultiInsertTarget {
+    /// Target table for the `INTO` clause.
+    pub table: TableObject,
+    /// Optional list of columns after the table name.
+    pub columns: Vec<Ident>,
+    /// Optional `VALUES` clause.
+    pub values: Option<Values>,
+}
+
+impl fmt::Display for SnowflakeMultiInsertTarget {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "INTO {}", self.table)?;
+        if !self.columns.is_empty() {
+            write!(f, " ({})", display_comma_separated(&self.columns))?;
+        }
+        if let Some(values) = &self.values {
+            write!(f, " {values}")?;
+        }
+        Ok(())
+    }
+}
+
+/// Conditional clause for Snowflake multi-table `INSERT` statements.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct SnowflakeMultiInsertWhen {
+    /// Expression evaluated for the `WHEN` clause.
+    pub condition: Expr,
+    /// Targets executed when the condition is met.
+    pub targets: Vec<SnowflakeMultiInsertTarget>,
+}
+
+/// Indicates whether a Snowflake multi-table `INSERT` executes `ALL` targets
+/// or stops after the first match.
+#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum SnowflakeMultiInsertKind {
+    /// `INSERT ALL` executes all matching `INTO` clauses.
+    All,
+    /// `INSERT FIRST` stops once the first matching `INTO` clause runs.
+    First,
+}
+
+impl fmt::Display for SnowflakeMultiInsertKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(match self {
+            SnowflakeMultiInsertKind::All => "ALL",
+            SnowflakeMultiInsertKind::First => "FIRST",
+        })
+    }
+}
+
+/// Snowflake multi-table `INSERT` statement.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct SnowflakeMultiInsert {
+    /// Token for the `INSERT` keyword.
+    pub insert_token: AttachedToken,
+    /// Optional optimizer hint following `INSERT`.
+    pub optimizer_hint: Option<OptimizerHint>,
+    /// Whether `OVERWRITE` was specified.
+    pub overwrite: bool,
+    /// Which mode (`ALL` or `FIRST`) the statement is using.
+    pub kind: SnowflakeMultiInsertKind,
+    /// Targets executed without conditions (unconditional `INTO` clauses).
+    pub unconditional_targets: Vec<SnowflakeMultiInsertTarget>,
+    /// Conditional `WHEN ... THEN` clauses.
+    pub when_clauses: Vec<SnowflakeMultiInsertWhen>,
+    /// Optional `ELSE` clause targets.
+    pub else_targets: Option<Vec<SnowflakeMultiInsertTarget>>,
+    /// Source query whose rows are distributed into the targets.
+    pub source: Box<Query>,
+}
+
+impl fmt::Display for SnowflakeMultiInsert {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "INSERT")?;
+        if let Some(hint) = &self.optimizer_hint {
+            write!(f, " {hint}")?;
+        }
+        if self.overwrite {
+            write!(f, " OVERWRITE")?;
+        }
+        write!(f, " {}", self.kind)?;
+
+        if !self.unconditional_targets.is_empty() {
+            for target in &self.unconditional_targets {
+                SpaceOrNewline.fmt(f)?;
+                target.fmt(f)?;
+            }
+        } else {
+            for when in &self.when_clauses {
+                SpaceOrNewline.fmt(f)?;
+                write!(f, "WHEN {} THEN", when.condition)?;
+                for target in &when.targets {
+                    SpaceOrNewline.fmt(f)?;
+                    target.fmt(f)?;
+                }
+            }
+            if let Some(targets) = &self.else_targets {
+                SpaceOrNewline.fmt(f)?;
+                write!(f, "ELSE")?;
+                for target in targets {
+                    SpaceOrNewline.fmt(f)?;
+                    target.fmt(f)?;
+                }
+            }
+        }
+
+        SpaceOrNewline.fmt(f)?;
+        write!(f, "{}", self.source)
+    }
+}
+
 /// DELETE statement.
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
diff --git a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/mod.rs b/app/src/ast/mod.rs
index cc34ec7..ac46b05 100644
--- a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -83,7 +83,8 @@ pub use self::ddl::{
 };
 pub use self::dml::{
     Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr,
-    MergeInsertKind, MergeUpdateExpr, OutputClause, Update,
+    MergeInsertKind, MergeUpdateExpr, OutputClause, SnowflakeMultiInsert,
+    SnowflakeMultiInsertKind, SnowflakeMultiInsertTarget, SnowflakeMultiInsertWhen, Update,
 };
 pub use self::operator::{BinaryOperator, UnaryOperator};
 pub use self::query::{
@@ -3426,6 +3427,8 @@ pub enum Statement {
     /// INSERT
     /// ```
     Insert(Insert),
+    /// Snowflake multi-table `INSERT`
+    SnowflakeMultiInsert(SnowflakeMultiInsert),
     /// ```sql
     /// INSTALL
     /// ```
@@ -5029,6 +5032,7 @@ impl fmt::Display for Statement {
             }
             Statement::Analyze(analyze) => analyze.fmt(f),
             Statement::Insert(insert) => insert.fmt(f),
+            Statement::SnowflakeMultiInsert(insert) => insert.fmt(f),
             Statement::Install {
                 extension_name: name,
             } => write!(f, "INSTALL {name}"),
diff --git a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/spans.rs b/app/src/ast/spans.rs
index bdd430e..2b72fa6 100644
--- a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -250,6 +250,7 @@ impl Spanned for Values {
 ///
 /// Missing spans:
 /// - [Statement::CopyIntoSnowflake]
+/// - [Statement::SnowflakeMultiInsert]
 /// - [Statement::CreateSecret]
 /// - [Statement::CreateRole]
 /// - [Statement::AlterType]
@@ -354,6 +355,7 @@ impl Spanned for Statement {
                 from_query: _,
                 partition: _,
             } => Span::empty(),
+            Statement::SnowflakeMultiInsert { .. } => Span::empty(),
             Statement::Open(open) => open.span(),
             Statement::Close { cursor } => match cursor {
                 CloseCursor::All => Span::empty(),
diff --git a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 8f3ae38..487e6f5 100644
--- a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -17020,6 +17020,10 @@ impl<'a> Parser<'a> {
                 file_format,
                 source,
             })
+        } else if dialect_of!(self is SnowflakeDialect)
+            && (self.peek_keyword(Keyword::ALL) || self.peek_keyword(Keyword::FIRST))
+        {
+            self.parse_snowflake_multi_insert(insert_token, optimizer_hint, overwrite)
         } else {
             // Hive lets you put table here regardless
             let table = self.parse_keyword(Keyword::TABLE);
@@ -17176,6 +17180,103 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn parse_snowflake_multi_insert(
+        &mut self,
+        insert_token: TokenWithSpan,
+        optimizer_hint: Option<OptimizerHint>,
+        overwrite: bool,
+    ) -> Result<Statement, ParserError> {
+        let kind = if self.parse_keyword(Keyword::ALL) {
+            SnowflakeMultiInsertKind::All
+        } else if self.parse_keyword(Keyword::FIRST) {
+            SnowflakeMultiInsertKind::First
+        } else {
+            return parser_err!(
+                "Snowflake INSERT must start with ALL or FIRST",
+                self.peek_token().span.start,
+            );
+        };
+
+        if self.peek_keyword(Keyword::WHEN) {
+            let when_clauses = self.parse_snowflake_multi_insert_when_clauses()?;
+            let else_targets = if self.parse_keyword(Keyword::ELSE) {
+                Some(self.parse_snowflake_multi_insert_targets()?)
+            } else {
+                None
+            };
+            let source = Box::new(self.parse_query()?);
+            Ok(Statement::SnowflakeMultiInsert(SnowflakeMultiInsert {
+                insert_token: insert_token.into(),
+                optimizer_hint,
+                overwrite,
+                kind,
+                unconditional_targets: vec![],
+                when_clauses,
+                else_targets,
+                source,
+            }))
+        } else if kind == SnowflakeMultiInsertKind::First {
+            parser_err!("INSERT FIRST requires WHEN clauses", self.peek_token().span.start)
+        } else {
+            let unconditional_targets = self.parse_snowflake_multi_insert_targets()?;
+            let source = Box::new(self.parse_query()?);
+            Ok(Statement::SnowflakeMultiInsert(SnowflakeMultiInsert {
+                insert_token: insert_token.into(),
+                optimizer_hint,
+                overwrite,
+                kind,
+                unconditional_targets,
+                when_clauses: vec![],
+                else_targets: None,
+                source,
+            }))
+        }
+    }
+
+    fn parse_snowflake_multi_insert_when_clauses(
+        &mut self,
+    ) -> Result<Vec<SnowflakeMultiInsertWhen>, ParserError> {
+        let mut when_clauses = Vec::new();
+        while self.parse_keyword(Keyword::WHEN) {
+            let condition = self.parse_expr()?;
+            self.expect_keyword(Keyword::THEN)?;
+            let targets = self.parse_snowflake_multi_insert_targets()?;
+            when_clauses.push(SnowflakeMultiInsertWhen { condition, targets });
+        }
+        if when_clauses.is_empty() {
+            parser_err!(
+                "Snowflake INSERT requires WHEN clauses",
+                self.peek_token().span.start,
+            )
+        } else {
+            Ok(when_clauses)
+        }
+    }
+
+    fn parse_snowflake_multi_insert_targets(
+        &mut self,
+    ) -> Result<Vec<SnowflakeMultiInsertTarget>, ParserError> {
+        let mut targets = vec![self.parse_snowflake_multi_insert_target()?];
+        while self.peek_keyword(Keyword::INTO) {
+            targets.push(self.parse_snowflake_multi_insert_target()?);
+        }
+        Ok(targets)
+    }
+
+    fn parse_snowflake_multi_insert_target(
+        &mut self,
+    ) -> Result<SnowflakeMultiInsertTarget, ParserError> {
+        self.expect_keyword(Keyword::INTO)?;
+        let table = self.parse_table_object()?;
+        let columns = self.parse_parenthesized_column_list(Optional, false)?;
+        let values = if self.parse_keyword(Keyword::VALUES) {
+            Some(self.parse_values(false, false)?)
+        } else {
+            None
+        };
+        Ok(SnowflakeMultiInsertTarget { table, columns, values })
+    }
+
     /// Parses input format clause used for ClickHouse.
     ///
     /// <https://clickhouse.com/docs/en/interfaces/formats>
diff --git a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_snowflake.rs b/app/tests/sqlparser_snowflake.rs
index 014a241..34b9cd0 100644
--- a/tmp/agent-patch-flux-pr-2148.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_snowflake.rs
+++ b/app/tests/sqlparser_snowflake.rs
@@ -3388,6 +3388,45 @@ fn parse_insert_overwrite() {
     snowflake().verified_stmt(insert_overwrite_into);
 }
 
+#[test]
+fn parse_snowflake_multi_insert_all() {
+    let sql = "INSERT ALL INTO t1 (a) VALUES (a) INTO t2 (b) VALUES (b) SELECT a, b FROM src";
+    match snowflake().verified_stmt(sql) {
+        Statement::SnowflakeMultiInsert(insert) => {
+            assert_eq!(insert.kind, SnowflakeMultiInsertKind::All);
+            assert!(insert.when_clauses.is_empty());
+            assert!(insert.else_targets.is_none());
+            assert_eq!(insert.unconditional_targets.len(), 2);
+            assert_eq!(insert.unconditional_targets[0].columns, vec![Ident::new("a")]);
+            assert!(insert.unconditional_targets[0].values.is_some());
+            assert_eq!(insert.unconditional_targets[1].columns, vec![Ident::new("b")]);
+            assert!(insert.unconditional_targets[1].values.is_some());
+        }
+        _ => unreachable!(),
+    }
+}
+
+#[test]
+fn parse_snowflake_multi_insert_when_else() {
+    let sql = "INSERT FIRST WHEN a = 1 THEN INTO t1 (a) VALUES (a) WHEN a = 2 THEN INTO t2 (b) ELSE INTO t3 (c) INTO t4 SELECT a, b, c FROM src";
+    match snowflake().verified_stmt(sql) {
+        Statement::SnowflakeMultiInsert(insert) => {
+            assert_eq!(insert.kind, SnowflakeMultiInsertKind::First);
+            assert_eq!(insert.when_clauses.len(), 2);
+            assert_eq!(insert.when_clauses[0].condition.to_string(), "a = 1");
+            assert_eq!(insert.when_clauses[1].condition.to_string(), "a = 2");
+            assert!(insert.when_clauses[0].targets[0].values.is_some());
+            assert!(insert.when_clauses[1].targets[0].values.is_none());
+            let else_targets = insert.else_targets.as_ref().unwrap();
+            assert_eq!(else_targets.len(), 2);
+            assert_eq!(else_targets[0].columns, vec![Ident::new("c")]);
+            assert!(else_targets[1].columns.is_empty());
+            assert_eq!(insert.source.to_string(), "SELECT a, b, c FROM src");
+        }
+        _ => unreachable!(),
+    }
+}
+
 #[test]
 fn test_table_sample() {
     snowflake_and_generic().verified_stmt("SELECT * FROM testtable SAMPLE (10)");