STET

agent_patch

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

20050 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1414.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 480442b..bff9448 100644
--- a/tmp/agent-patch-flux-pr-1414.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
@@ -5728,14 +5728,82 @@ pub struct HiveFormat {
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
-pub struct SqlOption {
-    pub name: Ident,
-    pub value: Expr,
+pub enum SqlOption {
+    KeyValue { name: Ident, value: Expr },
+    MssqlTableOption(MssqlTableOption),
 }
 
 impl fmt::Display for SqlOption {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{} = {}", self.name, self.value)
+        match self {
+            SqlOption::KeyValue { name, value } => write!(f, "{name} = {value}"),
+            SqlOption::MssqlTableOption(option) => write!(f, "{option}"),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum MssqlPartitionRange {
+    Left,
+    Right,
+}
+
+impl fmt::Display for MssqlPartitionRange {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            MssqlPartitionRange::Left => write!(f, "LEFT"),
+            MssqlPartitionRange::Right => write!(f, "RIGHT"),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum MssqlTableOption {
+    ClusteredIndex {
+        columns: Vec<OrderByExpr>,
+    },
+    ClusteredColumnstoreIndex {
+        order_by: Option<Vec<OrderByExpr>>,
+    },
+    Heap,
+    Partition {
+        column: Ident,
+        range: Option<MssqlPartitionRange>,
+        values: Vec<Expr>,
+    },
+}
+
+impl fmt::Display for MssqlTableOption {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            MssqlTableOption::ClusteredIndex { columns } => {
+                write!(f, "CLUSTERED INDEX ({})", display_comma_separated(columns))
+            }
+            MssqlTableOption::ClusteredColumnstoreIndex { order_by } => {
+                write!(f, "CLUSTERED COLUMNSTORE INDEX")?;
+                if let Some(order_by) = order_by {
+                    write!(f, " ORDER ({})", display_comma_separated(order_by))?;
+                }
+                Ok(())
+            }
+            MssqlTableOption::Heap => write!(f, "HEAP"),
+            MssqlTableOption::Partition {
+                column,
+                range,
+                values,
+            } => {
+                write!(f, "PARTITION ({column} RANGE")?;
+                if let Some(range) = range {
+                    write!(f, " {range}")?;
+                }
+                write!(f, " FOR VALUES ({})", display_comma_separated(values))?;
+                write!(f, ")")
+            }
+        }
     }
 }
 
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/keywords.rs b/app/src/keywords.rs
index 3ee447c..c4bed7d 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/keywords.rs
+++ b/app/src/keywords.rs
@@ -165,6 +165,7 @@ define_keywords!(
     COLLECT,
     COLLECTION,
     COLUMN,
+    COLUMNSTORE,
     COLUMNS,
     COMMENT,
     COMMIT,
@@ -357,6 +358,7 @@ define_keywords!(
     HEADER,
     HIGH_PRIORITY,
     HISTORY,
+    HEAP,
     HIVEVAR,
     HOLD,
     HOSTS,
diff --git a/tmp/agent-patch-flux-pr-1414.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 b39685c..45a8f2b 100644
--- a/tmp/agent-patch-flux-pr-1414.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
@@ -6480,10 +6480,96 @@ impl<'a> Parser<'a> {
     }
 
     pub fn parse_sql_option(&mut self) -> Result<SqlOption, ParserError> {
+        if let [
+            Token::Word(Word { keyword: Keyword::CLUSTERED, .. }),
+            Token::Word(Word {
+                keyword: next_keyword,
+                ..
+            }),
+        ] = self.peek_tokens::<2>()
+        {
+            if matches!(next_keyword, Keyword::INDEX | Keyword::COLUMNSTORE) {
+                self.expect_keyword(Keyword::CLUSTERED)?;
+                return Ok(SqlOption::MssqlTableOption(
+                    self.parse_mssql_clustered_option()?,
+                ));
+            }
+        }
+
+        if let [
+            Token::Word(Word {
+                keyword: Keyword::PARTITION,
+                ..
+            }),
+            Token::LParen,
+        ] = self.peek_tokens::<2>()
+        {
+            self.expect_keyword(Keyword::PARTITION)?;
+            return Ok(SqlOption::MssqlTableOption(self.parse_mssql_partition_option()?));
+        }
+
+        if let [
+            Token::Word(Word {
+                keyword: Keyword::HEAP,
+                ..
+            }),
+            next_token,
+        ] = self.peek_tokens::<2>()
+        {
+            if matches!(next_token, Token::Comma | Token::RParen) {
+                self.expect_keyword(Keyword::HEAP)?;
+                return Ok(SqlOption::MssqlTableOption(MssqlTableOption::Heap));
+            }
+        }
+
         let name = self.parse_identifier(false)?;
         self.expect_token(&Token::Eq)?;
         let value = self.parse_expr()?;
-        Ok(SqlOption { name, value })
+        Ok(SqlOption::KeyValue { name, value })
+    }
+
+    fn parse_mssql_clustered_option(&mut self) -> Result<MssqlTableOption, ParserError> {
+        if self.parse_keyword(Keyword::COLUMNSTORE) {
+            self.expect_keyword(Keyword::INDEX)?;
+            let order_by = if self.parse_keyword(Keyword::ORDER) {
+                self.expect_token(&Token::LParen)?;
+                let columns = self.parse_comma_separated(Parser::parse_order_by_expr)?;
+                self.expect_token(&Token::RParen)?;
+                Some(columns)
+            } else {
+                None
+            };
+            Ok(MssqlTableOption::ClusteredColumnstoreIndex { order_by })
+        } else {
+            self.expect_keyword(Keyword::INDEX)?;
+            self.expect_token(&Token::LParen)?;
+            let columns = self.parse_comma_separated(Parser::parse_order_by_expr)?;
+            self.expect_token(&Token::RParen)?;
+            Ok(MssqlTableOption::ClusteredIndex { columns })
+        }
+    }
+
+    fn parse_mssql_partition_option(&mut self) -> Result<MssqlTableOption, ParserError> {
+        self.expect_token(&Token::LParen)?;
+        let column = self.parse_identifier(false)?;
+        self.expect_keyword(Keyword::RANGE)?;
+        let range = if self.parse_keyword(Keyword::LEFT) {
+            Some(MssqlPartitionRange::Left)
+        } else if self.parse_keyword(Keyword::RIGHT) {
+            Some(MssqlPartitionRange::Right)
+        } else {
+            None
+        };
+        self.expect_keywords(&[Keyword::FOR, Keyword::VALUES])?;
+        self.expect_token(&Token::LParen)?;
+        let values = self.parse_comma_separated(Parser::parse_expr)?;
+        self.expect_token(&Token::RParen)?;
+        self.expect_token(&Token::RParen)?;
+        Ok(MssqlTableOption::Partition {
+            column,
+            range,
+            values,
+        })
     }
 
     pub fn parse_partition(&mut self) -> Result<Partition, ParserError> {
diff --git a/tmp/agent-patch-flux-pr-1414.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 4f84b37..53bcaaf 100644
--- a/tmp/agent-patch-flux-pr-1414.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
@@ -267,11 +267,11 @@ fn parse_create_view_with_options() {
                     ViewColumnDef {
                         name: Ident::new("age"),
                         data_type: None,
-                        options: Some(vec![SqlOption {
-                            name: Ident::new("description"),
-                            value: Expr::Value(Value::DoubleQuotedString("field age".to_string())),
-                        }])
-                    },
+                    options: Some(vec![SqlOption::KeyValue {
+                        name: Ident::new("description"),
+                        value: Expr::Value(Value::DoubleQuotedString("field age".to_string())),
+                    }])
+                },
                 ],
                 columns
             );
@@ -287,7 +287,7 @@ fn parse_create_view_with_options() {
                 unreachable!()
             };
             assert_eq!(
-                &SqlOption {
+                &SqlOption::KeyValue {
                     name: Ident::new("description"),
                     value: Expr::Value(Value::DoubleQuotedString(
                         "a view that expires in 2 days".to_string()
@@ -414,7 +414,7 @@ fn parse_create_table_with_options() {
                             },
                             ColumnOptionDef {
                                 name: None,
-                                option: ColumnOption::Options(vec![SqlOption {
+                                option: ColumnOption::Options(vec![SqlOption::KeyValue {
                                     name: Ident::new("description"),
                                     value: Expr::Value(Value::DoubleQuotedString(
                                         "field x".to_string()
@@ -429,7 +429,7 @@ fn parse_create_table_with_options() {
                         collation: None,
                         options: vec![ColumnOptionDef {
                             name: None,
-                            option: ColumnOption::Options(vec![SqlOption {
+                            option: ColumnOption::Options(vec![SqlOption::KeyValue {
                                 name: Ident::new("description"),
                                 value: Expr::Value(Value::DoubleQuotedString(
                                     "field y".to_string()
@@ -448,11 +448,11 @@ fn parse_create_table_with_options() {
                         Ident::new("age"),
                     ])),
                     Some(vec![
-                        SqlOption {
+                        SqlOption::KeyValue {
                             name: Ident::new("partition_expiration_days"),
                             value: Expr::Value(number("1")),
                         },
-                        SqlOption {
+                        SqlOption::KeyValue {
                             name: Ident::new("description"),
                             value: Expr::Value(Value::DoubleQuotedString(
                                 "table option description".to_string()
@@ -2005,7 +2005,7 @@ fn test_bigquery_create_function() {
             function_body: Some(CreateFunctionBody::AsAfterOptions(Expr::Value(number(
                 "42"
             )))),
-            options: Some(vec![SqlOption {
+            options: Some(vec![SqlOption::KeyValue {
                 name: Ident::new("x"),
                 value: Expr::Value(Value::SingleQuotedString("y".into())),
             }]),
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index bebd33d..ea85d9c 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -3637,11 +3637,11 @@ fn parse_create_table_with_options() {
         Statement::CreateTable(CreateTable { with_options, .. }) => {
             assert_eq!(
                 vec![
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "foo".into(),
                         value: Expr::Value(Value::SingleQuotedString("bar".into())),
                     },
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "a".into(),
                         value: Expr::Value(number("123")),
                     },
@@ -3870,7 +3870,7 @@ fn parse_alter_table() {
         AlterTableOperation::SetTblProperties { table_properties } => {
             assert_eq!(
                 table_properties,
-                [SqlOption {
+                [SqlOption::KeyValue {
                     name: Ident {
                         value: "classification".to_string(),
                         quote_style: Some('\'')
@@ -3958,11 +3958,11 @@ fn parse_alter_view_with_options() {
         Statement::AlterView { with_options, .. } => {
             assert_eq!(
                 vec![
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "foo".into(),
                         value: Expr::Value(Value::SingleQuotedString("bar".into())),
                     },
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "a".into(),
                         value: Expr::Value(number("123")),
                     },
@@ -6729,11 +6729,11 @@ fn parse_create_view_with_options() {
         Statement::CreateView { options, .. } => {
             assert_eq!(
                 CreateTableOptions::With(vec![
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "foo".into(),
                         value: Expr::Value(Value::SingleQuotedString("bar".into())),
                     },
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "a".into(),
                         value: Expr::Value(number("123")),
                     },
@@ -8827,11 +8827,11 @@ fn parse_cache_table() {
             table_name: ObjectName(vec![Ident::with_quote('\'', cache_table_name)]),
             has_as: false,
             options: vec![
-                SqlOption {
+                SqlOption::KeyValue {
                     name: Ident::with_quote('\'', "K1"),
                     value: Expr::Value(Value::SingleQuotedString("V1".into())),
                 },
-                SqlOption {
+                SqlOption::KeyValue {
                     name: Ident::with_quote('\'', "K2"),
                     value: Expr::Value(number("0.88")),
                 },
@@ -8852,11 +8852,11 @@ fn parse_cache_table() {
             table_name: ObjectName(vec![Ident::with_quote('\'', cache_table_name)]),
             has_as: false,
             options: vec![
-                SqlOption {
+                SqlOption::KeyValue {
                     name: Ident::with_quote('\'', "K1"),
                     value: Expr::Value(Value::SingleQuotedString("V1".into())),
                 },
-                SqlOption {
+                SqlOption::KeyValue {
                     name: Ident::with_quote('\'', "K2"),
                     value: Expr::Value(number("0.88")),
                 },
@@ -8877,11 +8877,11 @@ fn parse_cache_table() {
             table_name: ObjectName(vec![Ident::with_quote('\'', cache_table_name)]),
             has_as: true,
             options: vec![
-                SqlOption {
+                SqlOption::KeyValue {
                     name: Ident::with_quote('\'', "K1"),
                     value: Expr::Value(Value::SingleQuotedString("V1".into())),
                 },
-                SqlOption {
+                SqlOption::KeyValue {
                     name: Ident::with_quote('\'', "K2"),
                     value: Expr::Value(number("0.88")),
                 },
@@ -9695,7 +9695,7 @@ fn parse_unload() {
                 value: "s3://...".to_string(),
                 quote_style: Some('\'')
             },
-            with: vec![SqlOption {
+            with: vec![SqlOption::KeyValue {
                 name: Ident {
                     value: "format".to_string(),
                     quote_style: None
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index 5c2ec87..845dcf1 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -385,6 +385,41 @@ fn parse_delimited_identifiers() {
     //TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
 }
 
+#[test]
+fn parse_mssql_create_table_with_mssql_options() {
+    let sql = "CREATE TABLE dbo.orders (order_id INT) WITH (CLUSTERED COLUMNSTORE INDEX, CLUSTERED INDEX (order_id), HEAP, PARTITION (OrderDate RANGE RIGHT FOR VALUES (1, 2, 3)))";
+    let statements = ms().parse_sql_statements(sql).unwrap();
+    assert_eq!(statements.len(), 1);
+    let stmt = statements.into_iter().next().unwrap();
+    let expected = vec![
+        SqlOption::MssqlTableOption(MssqlTableOption::ClusteredColumnstoreIndex { order_by: None }),
+        SqlOption::MssqlTableOption(MssqlTableOption::ClusteredIndex {
+            columns: vec![OrderByExpr {
+                expr: Expr::Identifier(Ident::new("order_id")),
+                asc: None,
+                nulls_first: None,
+                with_fill: None,
+            }],
+        }),
+        SqlOption::MssqlTableOption(MssqlTableOption::Heap),
+        SqlOption::MssqlTableOption(MssqlTableOption::Partition {
+            column: Ident::new("OrderDate"),
+            range: Some(MssqlPartitionRange::Right),
+            values: vec![
+                Expr::Value(number("1")),
+                Expr::Value(number("2")),
+                Expr::Value(number("3")),
+            ],
+        }),
+    ];
+    match stmt {
+        Statement::CreateTable(CreateTable { with_options, .. }) => {
+            assert_eq!(expected, with_options);
+        }
+        _ => unreachable!(),
+    }
+}
+
 #[test]
 fn parse_table_name_in_square_brackets() {
     let select = ms().verified_only_select(r#"SELECT [a column] FROM [a schema].[a table]"#);
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index 1ebb5d5..bd39ddf 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -461,15 +461,15 @@ fn parse_create_table_with_defaults() {
             assert_eq!(
                 with_options,
                 vec![
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "fillfactor".into(),
                         value: Expr::Value(number("20"))
                     },
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "user_catalog_table".into(),
                         value: Expr::Value(Value::Boolean(true))
                     },
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "autovacuum_vacuum_threshold".into(),
                         value: Expr::Value(number("100"))
                     },
@@ -4482,11 +4482,11 @@ fn parse_create_table_with_options() {
         Statement::CreateTable(CreateTable { with_options, .. }) => {
             assert_eq!(
                 vec![
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "foo".into(),
                         value: Expr::Value(Value::SingleQuotedString("bar".into())),
                     },
-                    SqlOption {
+                    SqlOption::KeyValue {
                         name: "a".into(),
                         value: Expr::Value(number("123")),
                     },