STET

agent_patch

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

10750 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/ddl.rs b/app/src/ast/ddl.rs
index f81c6fc..f60dee0 100644
--- a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/ddl.rs
+++ b/app/src/ast/ddl.rs
@@ -1420,13 +1420,32 @@ impl fmt::Display for ColumnDef {
 /// amount COMMENT 'The total amount for the order line'
 /// created_at DateTime64
 /// ```
+#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum ViewColumnOptionsSeparator {
+    /// Join options with commas (used by `OPTIONS(...)`).
+    CommaSeparated,
+    /// Join options with spaces (used by Snowflake view column options).
+    SpaceSeparated,
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct ViewColumnOptions {
+    /// Column options defined on the view column.
+    pub options: Vec<ColumnOption>,
+    /// How to join the column options when serializing back to SQL.
+    pub separator: ViewColumnOptionsSeparator,
+}
+
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub struct ViewColumnDef {
     pub name: Ident,
     pub data_type: Option<DataType>,
-    pub options: Option<Vec<ColumnOption>>,
+    pub options: Option<ViewColumnOptions>,
 }
 
 impl fmt::Display for ViewColumnDef {
@@ -1436,7 +1455,16 @@ impl fmt::Display for ViewColumnDef {
             write!(f, " {}", data_type)?;
         }
         if let Some(options) = self.options.as_ref() {
-            write!(f, " {}", display_comma_separated(options.as_slice()))?;
+            match options.separator {
+                ViewColumnOptionsSeparator::CommaSeparated => {
+                    write!(f, " {}", display_comma_separated(options.options.as_slice()))?
+                }
+                ViewColumnOptionsSeparator::SpaceSeparated => {
+                    for option in &options.options {
+                        write!(f, " {option}")?;
+                    }
+                }
+            }
         }
         Ok(())
     }
diff --git a/tmp/agent-patch-flux-pr-1891.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 ef0c4dc..88a4a75 100644
--- a/tmp/agent-patch-flux-pr-1891.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
@@ -68,6 +68,7 @@ pub use self::ddl::{
     IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition,
     ProcedureParam, ReferentialAction, ReplicaIdentity, TableConstraint, TagsColumnOption,
     UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
+    ViewColumnOptions, ViewColumnOptionsSeparator,
 };
 pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
 pub use self::operator::{BinaryOperator, UnaryOperator};
diff --git a/tmp/agent-patch-flux-pr-1891.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 ca321cc..fac8e3b 100644
--- a/tmp/agent-patch-flux-pr-1891.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
@@ -993,7 +993,11 @@ impl Spanned for ViewColumnDef {
 
         union_spans(
             core::iter::once(name.span)
-                .chain(options.iter().flat_map(|i| i.iter().map(|k| k.span()))),
+                .chain(
+                    options
+                        .iter()
+                        .flat_map(|opts| opts.options.iter().map(|k| k.span())),
+                ),
         )
     }
 }
diff --git a/tmp/agent-patch-flux-pr-1891.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 ca658d7..564ec41 100644
--- a/tmp/agent-patch-flux-pr-1891.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
@@ -10579,17 +10579,7 @@ impl<'a> Parser<'a> {
     /// Parses a column definition within a view.
     fn parse_view_column(&mut self) -> Result<ViewColumnDef, ParserError> {
         let name = self.parse_identifier()?;
-        let options = if (dialect_of!(self is BigQueryDialect | GenericDialect)
-            && self.parse_keyword(Keyword::OPTIONS))
-            || (dialect_of!(self is SnowflakeDialect | GenericDialect)
-                && self.parse_keyword(Keyword::COMMENT))
-        {
-            self.prev_token();
-            self.parse_optional_column_option()?
-                .map(|option| vec![option])
-        } else {
-            None
-        };
+        let options = self.parse_view_column_options()?;
         let data_type = if dialect_of!(self is ClickHouseDialect) {
             Some(self.parse_data_type()?)
         } else {
@@ -10602,6 +10592,45 @@ impl<'a> Parser<'a> {
         })
     }
 
+    fn parse_view_column_options(&mut self) -> Result<Option<ViewColumnOptions>, ParserError> {
+        if dialect_of!(self is BigQueryDialect | GenericDialect)
+            && self.parse_keyword(Keyword::OPTIONS)
+        {
+            self.prev_token();
+            let mut options = vec![];
+            if let Some(option) = self.parse_optional_column_option()? {
+                options.push(option);
+            }
+            if options.is_empty() {
+                return Ok(None);
+            }
+            return Ok(Some(ViewColumnOptions {
+                options,
+                separator: ViewColumnOptionsSeparator::CommaSeparated,
+            }));
+        }
+
+        if dialect_of!(self is SnowflakeDialect | GenericDialect) {
+            let mut options = vec![];
+            loop {
+                if let Some(option) = self.parse_optional_column_option()? {
+                    options.push(option);
+                } else {
+                    break;
+                }
+            }
+            if options.is_empty() {
+                return Ok(None);
+            }
+            return Ok(Some(ViewColumnOptions {
+                options,
+                separator: ViewColumnOptionsSeparator::SpaceSeparated,
+            }));
+        }
+
+        Ok(None)
+    }
+
     /// Parses a parenthesized comma-separated list of unqualified, possibly quoted identifiers.
     /// For example: `(col1, "col 2", ...)`
     pub fn parse_parenthesized_column_list(
diff --git a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_bigquery.rs b/app/tests/sqlparser_bigquery.rs
index 0de0b12..5a8d5c9 100644
--- a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_bigquery.rs
+++ b/app/tests/sqlparser_bigquery.rs
@@ -355,14 +355,17 @@ fn parse_create_view_with_options() {
                     ViewColumnDef {
                         name: Ident::new("age"),
                         data_type: None,
-                        options: Some(vec![ColumnOption::Options(vec![SqlOption::KeyValue {
-                            key: Ident::new("description"),
-                            value: Expr::Value(
-                                Value::DoubleQuotedString("field age".to_string()).with_span(
-                                    Span::new(Location::new(1, 42), Location::new(1, 52))
-                                )
-                            ),
-                        }])]),
+                        options: Some(ViewColumnOptions {
+                            options: vec![ColumnOption::Options(vec![SqlOption::KeyValue {
+                                key: Ident::new("description"),
+                                value: Expr::Value(
+                                    Value::DoubleQuotedString("field age".to_string()).with_span(
+                                        Span::new(Location::new(1, 42), Location::new(1, 52))
+                                    )
+                                ),
+                            }])],
+                            separator: ViewColumnOptionsSeparator::CommaSeparated,
+                        }),
                     },
                 ],
                 columns
diff --git a/tmp/agent-patch-flux-pr-1891.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 7dc00f9..235f2f3 100644
--- a/tmp/agent-patch-flux-pr-1891.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
@@ -3142,7 +3142,10 @@ fn parse_view_column_descriptions() {
                     ViewColumnDef {
                         name: Ident::new("a"),
                         data_type: None,
-                        options: Some(vec![ColumnOption::Comment("Comment".to_string())]),
+                        options: Some(ViewColumnOptions {
+                            options: vec![ColumnOption::Comment("Comment".to_string())],
+                            separator: ViewColumnOptionsSeparator::SpaceSeparated,
+                        }),
                     },
                     ViewColumnDef {
                         name: Ident::new("b"),
@@ -3156,6 +3159,37 @@ fn parse_view_column_descriptions() {
     };
 }
 
+#[test]
+fn parse_snowflake_view_column_multiple_options() {
+    let sql = "CREATE OR REPLACE VIEW v (a COMMENT 'Comment' MASKING POLICY mask_p) AS SELECT a FROM t";
+
+    match snowflake_and_generic().verified_stmt(sql) {
+        Statement::CreateView { columns, .. } => {
+            assert_eq!(
+                columns,
+                vec![ViewColumnDef {
+                    name: Ident::new("a"),
+                    data_type: None,
+                    options: Some(ViewColumnOptions {
+                        options: vec![
+                            ColumnOption::Comment("Comment".to_string()),
+                            ColumnOption::Policy(ColumnPolicy::MaskingPolicy(
+                                ColumnPolicyProperty {
+                                    with: false,
+                                    policy_name: Ident::new("mask_p"),
+                                    using_columns: None,
+                                },
+                            )),
+                        ],
+                        separator: ViewColumnOptionsSeparator::SpaceSeparated,
+                    }),
+                }]
+            );
+        }
+        _ => unreachable!(),
+    };
+}
+
 #[test]
 fn test_parentheses_overflow() {
     // TODO: increase / improve after we fix the recursion limit