agent_patch
runs/2026-02-21__03-36-16__gpt-5-1-codex-mini/flux-pr-1891/flux-pr-1891.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/agent-logs/agent.patch
8441 bytes
diff --git a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/src/ast/ddl.rs b/app/src/ast/ddl.rs
index f81c6fc..be0437e 100644
--- a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/src/ast/ddl.rs
+++ b/app/src/ast/ddl.rs
@@ -1426,7 +1426,7 @@ impl fmt::Display for ColumnDef {
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,12 +1436,59 @@ 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()))?;
+ write!(f, " {}", options)?;
}
Ok(())
}
}
+/// Formatting strategy for a list of view column options.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum ViewColumnOptionSeparator {
+ /// Options should be joined using commas.
+ Comma,
+ /// Options should be joined using spaces.
+ Space,
+}
+
+impl fmt::Display for ViewColumnOptionSeparator {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ViewColumnOptionSeparator::Comma => write!(f, ","),
+ ViewColumnOptionSeparator::Space => write!(f, ""),
+ }
+ }
+}
+
+/// Represents a block of column options attached to a view column definition.
+#[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 {
+ pub options: Vec<ColumnOption>,
+ pub separator: ViewColumnOptionSeparator,
+}
+
+impl fmt::Display for ViewColumnOptions {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.separator {
+ ViewColumnOptionSeparator::Comma =>
+ write!(f, "{}", display_comma_separated(self.options.as_slice())),
+ ViewColumnOptionSeparator::Space => {
+ for (idx, option) in self.options.iter().enumerate() {
+ if idx > 0 {
+ write!(f, " ")?;
+ }
+ write!(f, "{option}")?;
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
/// An optionally-named `ColumnOption`: `[ CONSTRAINT <name> ] <column-option>`.
///
/// Note that implementations are substantially more permissive than the ANSI
diff --git a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/src/parser/mod.rs b/app/src/parser/mod.rs
index ca658d7..9be7421 100644
--- a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -10579,22 +10579,26 @@ 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 data_type = if dialect_of!(self is ClickHouseDialect) {
Some(self.parse_data_type()?)
} else {
None
};
+ let mut options = Vec::new();
+ while let Some(option) = self.parse_optional_column_option()? {
+ options.push(option);
+ }
+ let options = if options.is_empty() {
+ None
+ } else {
+ let separator = if options.len() > 1 && dialect_of!(self is SnowflakeDialect) {
+ ViewColumnOptionSeparator::Space
+ } else {
+ ViewColumnOptionSeparator::Comma
+ };
+ Some(ViewColumnOptions { options, separator })
+ };
+
Ok(ViewColumnDef {
name,
data_type,
diff --git a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/tests/sqlparser_bigquery.rs b/app/tests/sqlparser_bigquery.rs
index 0de0b12..b79f83e 100644
--- a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-21__03-36-16__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: ViewColumnOptionSeparator::Comma,
+ }),
},
],
columns
diff --git a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/tests/sqlparser_snowflake.rs b/app/tests/sqlparser_snowflake.rs
index 7dc00f9..7578fec 100644
--- a/tmp/agent-patch-flux-pr-1891.1-of-1.2026-02-21__03-36-16__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: ViewColumnOptionSeparator::Comma,
+ }),
},
ViewColumnDef {
name: Ident::new("b"),
@@ -3156,6 +3159,31 @@ fn parse_view_column_descriptions() {
};
}
+#[test]
+fn parse_view_column_multiple_options_for_snowflake() {
+ let sql = "CREATE OR REPLACE VIEW v (a COMMENT 'Comment' MASKING POLICY policy_name) AS SELECT a FROM table1";
+
+ match snowflake().verified_stmt(sql) {
+ Statement::CreateView { columns, .. } => {
+ assert_eq!(columns.len(), 1);
+ let options = columns[0].options.as_ref().expect("a column option is present");
+ assert_eq!(options.separator, ViewColumnOptionSeparator::Space);
+ assert_eq!(
+ options.options,
+ vec![
+ ColumnOption::Comment("Comment".to_string()),
+ ColumnOption::Policy(ColumnPolicy::MaskingPolicy(ColumnPolicyProperty {
+ with: false,
+ policy_name: Ident::new("policy_name"),
+ using_columns: None,
+ }))
+ ]
+ );
+ }
+ _ => unreachable!(),
+ };
+}
+
#[test]
fn test_parentheses_overflow() {
// TODO: increase / improve after we fix the recursion limit