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
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")),
},