agent_patch
runs/2026-02-21__03-36-16__gpt-5-1-codex-mini/flux-pr-1414/flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/agent-logs/agent.patch
14854 bytes
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/dml.rs b/app/src/ast/dml.rs
index c0e58e2..792ce15 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/dml.rs
+++ b/app/src/ast/dml.rs
@@ -22,11 +22,11 @@ use sqlparser_derive::{Visit, VisitMut};
pub use super::ddl::{ColumnDef, TableConstraint};
use super::{
- display_comma_separated, display_separated, ClusteredBy, CommentDef, Expr, FileFormat,
- FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident,
- InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert, OneOrManyWithParens,
- OrderByExpr, Query, RowAccessPolicy, SelectItem, SqlOption, SqliteOnConflict, TableEngine,
- TableWithJoins, Tag, WrappedCollection,
+ display_comma_separated, display_separated, ClusteredBy, CommentDef, CreateTableWithOption,
+ Expr, FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat,
+ Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert,
+ OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, SqlOption,
+ SqliteOnConflict, TableEngine, TableWithJoins, Tag, WrappedCollection,
};
/// CREATE INDEX statement.
@@ -116,7 +116,7 @@ pub struct CreateTable {
pub hive_distribution: HiveDistributionStyle,
pub hive_formats: Option<HiveFormat>,
pub table_properties: Vec<SqlOption>,
- pub with_options: Vec<SqlOption>,
+ pub with_options: Vec<CreateTableWithOption>,
pub file_format: Option<FileFormat>,
pub location: Option<String>,
pub query: Option<Box<Query>>,
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/helpers/stmt_create_table.rs b/app/src/ast/helpers/stmt_create_table.rs
index 82532b2..8b26c50 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/helpers/stmt_create_table.rs
+++ b/app/src/ast/helpers/stmt_create_table.rs
@@ -9,9 +9,10 @@ use sqlparser_derive::{Visit, VisitMut};
use super::super::dml::CreateTable;
use crate::ast::{
- ClusteredBy, ColumnDef, CommentDef, Expr, FileFormat, HiveDistributionStyle, HiveFormat, Ident,
- ObjectName, OnCommit, OneOrManyWithParens, Query, RowAccessPolicy, SqlOption, Statement,
- TableConstraint, TableEngine, Tag, WrappedCollection,
+ ClusteredBy, ColumnDef, CommentDef, CreateTableWithOption, Expr, FileFormat,
+ HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, OneOrManyWithParens,
+ Query, RowAccessPolicy, SqlOption, Statement, TableConstraint, TableEngine, Tag,
+ WrappedCollection,
};
use crate::parser::ParserError;
@@ -60,7 +61,7 @@ pub struct CreateTableBuilder {
pub hive_distribution: HiveDistributionStyle,
pub hive_formats: Option<HiveFormat>,
pub table_properties: Vec<SqlOption>,
- pub with_options: Vec<SqlOption>,
+ pub with_options: Vec<CreateTableWithOption>,
pub file_format: Option<FileFormat>,
pub location: Option<String>,
pub query: Option<Box<Query>>,
@@ -200,7 +201,7 @@ impl CreateTableBuilder {
self
}
- pub fn with_options(mut self, with_options: Vec<SqlOption>) -> Self {
+ pub fn with_options(mut self, with_options: Vec<CreateTableWithOption>) -> Self {
self.with_options = with_options;
self
}
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/mod.rs b/app/src/ast/mod.rs
index 480442b..cb2985e 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -69,6 +69,7 @@ pub use self::value::{
use crate::ast::helpers::stmt_data_loading::{
DataLoadingOptions, StageLoadSelectItem, StageParamsObject,
};
+use crate::tokenizer::Token;
#[cfg(feature = "visitor")]
pub use visitor::*;
@@ -5739,6 +5740,114 @@ impl fmt::Display for SqlOption {
}
}
+/// CREATE TABLE `WITH` options, including MSSQL-specific clauses (e.g. `CLUSTERED INDEX`).
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum CreateTableWithOption {
+ /// Generic `WITH` option that uses `name = value` syntax.
+ SqlOption(SqlOption),
+ /// MSSQL `CLUSTERED COLUMNSTORE INDEX` option.
+ ClusteredColumnstoreIndex,
+ /// MSSQL `CLUSTERED INDEX (...)` option.
+ ClusteredIndex { columns: Vec<Expr> },
+ /// MSSQL `HEAP` option.
+ Heap,
+ /// MSSQL `PARTITION (...)` option (captures the token stream inside the parentheses).
+ Partition(Vec<Token>),
+}
+
+impl fmt::Display for CreateTableWithOption {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ CreateTableWithOption::SqlOption(option) => write!(f, "{option}"),
+ CreateTableWithOption::ClusteredColumnstoreIndex => {
+ f.write_str("CLUSTERED COLUMNSTORE INDEX")
+ }
+ CreateTableWithOption::ClusteredIndex { columns } => {
+ write!(
+ f,
+ "CLUSTERED INDEX ({})",
+ display_comma_separated(columns)
+ )
+ }
+ CreateTableWithOption::Heap => f.write_str("HEAP"),
+ CreateTableWithOption::Partition(tokens) => {
+ f.write_str("PARTITION (")?;
+ write_token_sequence(tokens, f)?;
+ f.write_str(")")
+ }
+ }
+ }
+}
+
+impl From<SqlOption> for CreateTableWithOption {
+ fn from(sql_option: SqlOption) -> Self {
+ CreateTableWithOption::SqlOption(sql_option)
+ }
+}
+
+fn write_token_sequence(tokens: &[Token], f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut prev: Option<&Token> = None;
+ for token in tokens {
+ if should_insert_space(prev, token) {
+ f.write_str(" ")?;
+ }
+ write!(f, "{token}")?;
+ prev = Some(token);
+ }
+ Ok(())
+}
+
+fn should_insert_space(prev: Option<&Token>, current: &Token) -> bool {
+ let prev = match prev {
+ Some(prev) => prev,
+ None => return false,
+ };
+
+ if matches!(
+ current,
+ Token::Comma
+ | Token::Period
+ | Token::Colon
+ | Token::DoubleColon
+ | Token::Eq
+ | Token::Neq
+ | Token::Lt
+ | Token::Gt
+ | Token::LtEq
+ | Token::GtEq
+ | Token::DoubleEq
+ | Token::Spaceship
+ | Token::Plus
+ | Token::Minus
+ | Token::Mul
+ | Token::Div
+ | Token::DuckIntDiv
+ | Token::StringConcat
+ | Token::Mod
+ | Token::RParen
+ | Token::RBracket
+ | Token::RBrace
+ | Token::SemiColon
+ | Token::Backslash
+ | Token::Arrow
+ | Token::LongArrow
+ | Token::HashArrow
+ ) {
+ return false;
+ }
+
+ if matches!(
+ prev,
+ Token::LParen | Token::LBracket | Token::LBrace | Token::Period | Token::Colon | Token::DoubleColon
+ ) {
+ return false;
+ }
+
+ true
+}
+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/parser/mod.rs b/app/src/parser/mod.rs
index b39685c..30d179f 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -4443,7 +4443,7 @@ impl<'a> Parser<'a> {
let name = self.parse_object_name(allow_unquoted_hyphen)?;
let columns = self.parse_view_columns()?;
let mut options = CreateTableOptions::None;
- let with_options = self.parse_options(Keyword::WITH)?;
+ let with_options = self.parse_create_table_with_options()?;
if !with_options.is_empty() {
options = CreateTableOptions::With(with_options);
}
@@ -6419,6 +6419,77 @@ impl<'a> Parser<'a> {
}
}
+ pub fn parse_create_table_with_options(
+ &mut self,
+ ) -> Result<Vec<CreateTableWithOption>, ParserError> {
+ if self.parse_keyword(Keyword::WITH) {
+ self.expect_token(&Token::LParen)?;
+ let options = self
+ .parse_comma_separated(Parser::parse_create_table_with_option)?;
+ self.expect_token(&Token::RParen)?;
+ Ok(options)
+ } else {
+ Ok(vec![])
+ }
+ }
+
+ pub fn parse_create_table_with_option(&mut self) -> Result<CreateTableWithOption, ParserError> {
+ if dialect_of!(self is MsSqlDialect) {
+ if self.parse_keywords(&[
+ Keyword::CLUSTERED,
+ Keyword::COLUMNSTORE,
+ Keyword::INDEX,
+ ]) {
+ return Ok(CreateTableWithOption::ClusteredColumnstoreIndex);
+ }
+ if self.parse_keywords(&[Keyword::CLUSTERED, Keyword::INDEX]) {
+ self.expect_token(&Token::LParen)?;
+ let columns = if self.peek_token().token == Token::RParen {
+ vec![]
+ } else {
+ self.parse_comma_separated(Parser::parse_expr)?
+ };
+ self.expect_token(&Token::RParen)?;
+ return Ok(CreateTableWithOption::ClusteredIndex { columns });
+ }
+ if self.parse_keyword(Keyword::HEAP) {
+ return Ok(CreateTableWithOption::Heap);
+ }
+ if self.parse_keyword(Keyword::PARTITION) {
+ let tokens = self.parse_parenthesized_token_sequence()?;
+ return Ok(CreateTableWithOption::Partition(tokens));
+ }
+ }
+
+ let option = self.parse_sql_option()?;
+ Ok(option.into())
+ }
+
+ fn parse_parenthesized_token_sequence(&mut self) -> Result<Vec<Token>, ParserError> {
+ self.expect_token(&Token::LParen)?;
+ let mut tokens = vec![];
+ let mut depth = 1;
+ while depth > 0 {
+ let next = self.next_token();
+ match next.token {
+ Token::EOF => return self.expected("')'", next),
+ Token::LParen => {
+ depth += 1;
+ tokens.push(Token::LParen);
+ }
+ Token::RParen => {
+ depth -= 1;
+ if depth == 0 {
+ break;
+ }
+ tokens.push(Token::RParen);
+ }
+ token => tokens.push(token),
+ }
+ }
+ Ok(tokens)
+ }
+
pub fn parse_index_type(&mut self) -> Result<IndexType, ParserError> {
if self.parse_keyword(Keyword::BTREE) {
Ok(IndexType::BTree)
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index bebd33d..fa71598 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -3640,11 +3640,13 @@ fn parse_create_table_with_options() {
SqlOption {
name: "foo".into(),
value: Expr::Value(Value::SingleQuotedString("bar".into())),
- },
+ }
+ .into(),
SqlOption {
name: "a".into(),
value: Expr::Value(number("123")),
- },
+ }
+ .into(),
],
with_options
);
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index 5c2ec87..75400d5 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -199,6 +199,29 @@ fn parse_mssql_top_paren() {
assert!(!top.percent);
}
+#[test]
+fn parse_mssql_create_table_with_table_opts() {
+ let sql = "CREATE TABLE dbo.test (id INT) WITH (HEAP, CLUSTERED INDEX (id), CLUSTERED COLUMNSTORE INDEX, PARTITION ([OrderDateKey] RANGE RIGHT FOR VALUES (20000101,20010101)))";
+ let canonical = "CREATE TABLE dbo.test (id INT) WITH (HEAP, CLUSTERED INDEX (id), CLUSTERED COLUMNSTORE INDEX, PARTITION ([OrderDateKey] RANGE RIGHT FOR VALUES (20000101, 20010101)))";
+ let stmt = ms().one_statement_parses_to(sql, canonical);
+ match stmt {
+ Statement::CreateTable(CreateTable { with_options, .. }) => {
+ assert_eq!(with_options.len(), 4);
+ assert!(matches!(with_options[0], CreateTableWithOption::Heap));
+ match &with_options[1] {
+ CreateTableWithOption::ClusteredIndex { columns } => assert_eq!(columns, &vec![Expr::Identifier(Ident::new("id"))]),
+ other => panic!("expected clustered index, got {other:?}"),
+ }
+ assert!(matches!(with_options[2], CreateTableWithOption::ClusteredColumnstoreIndex));
+ assert_eq!(
+ with_options[3].to_string(),
+ "PARTITION ([OrderDateKey] RANGE RIGHT FOR VALUES (20000101, 20010101))"
+ );
+ }
+ _ => unreachable!(),
+ }
+}
+
#[test]
fn parse_mssql_top_percent() {
let sql = "SELECT TOP (5) PERCENT * FROM foo";
diff --git a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index 1ebb5d5..5673f4e 100644
--- a/tmp/agent-patch-flux-pr-1414.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -4485,11 +4485,13 @@ fn parse_create_table_with_options() {
SqlOption {
name: "foo".into(),
value: Expr::Value(Value::SingleQuotedString("bar".into())),
- },
+ }
+ .into(),
SqlOption {
name: "a".into(),
value: Expr::Value(number("123")),
- },
+ }
+ .into(),
],
with_options
);