STET

agent_patch

runs/2026-02-28__18-27-22__gpt-5-4/flux-pr-1747/flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/agent-logs/agent.patch

49948 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/dml.rs b/app/src/ast/dml.rs
index 9cdb1ca..0c9b942 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/dml.rs
+++ b/app/src/ast/dml.rs
@@ -33,11 +33,11 @@ pub use super::ddl::{ColumnDef, TableConstraint};
 
 use super::{
     display_comma_separated, display_separated, query::InputFormatClause, Assignment, ClusteredBy,
-    CommentDef, Expr, FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
-    HiveRowFormat, Ident, IndexType, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit,
-    OnInsert, OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting,
-    SqlOption, SqliteOnConflict, StorageSerializationPolicy, TableEngine, TableObject,
-    TableWithJoins, Tag, WrappedCollection,
+    CommentDef, CreateTableOptions, Expr, FileFormat, FromTable, HiveDistributionStyle,
+    HiveFormat, HiveIOFormat, HiveRowFormat, Ident, IndexType, InsertAliases,
+    MysqlInsertPriority, ObjectName, OnCommit, OnInsert, OneOrManyWithParens, OrderByExpr, Query,
+    RowAccessPolicy, SelectItem, Setting, SqlOption, SqliteOnConflict,
+    StorageSerializationPolicy, TableObject, TableWithJoins, Tag, WrappedCollection,
 };
 
 /// Index column type.
@@ -146,19 +146,13 @@ pub struct CreateTable {
     pub constraints: Vec<TableConstraint>,
     pub hive_distribution: HiveDistributionStyle,
     pub hive_formats: Option<HiveFormat>,
-    pub table_properties: Vec<SqlOption>,
-    pub with_options: Vec<SqlOption>,
+    pub table_options: Vec<CreateTableOptions>,
     pub file_format: Option<FileFormat>,
     pub location: Option<String>,
     pub query: Option<Box<Query>>,
     pub without_rowid: bool,
     pub like: Option<ObjectName>,
     pub clone: Option<ObjectName>,
-    pub engine: Option<TableEngine>,
-    pub comment: Option<CommentDef>,
-    pub auto_increment_offset: Option<u32>,
-    pub default_charset: Option<String>,
-    pub collation: Option<String>,
     pub on_commit: Option<OnCommit>,
     /// ClickHouse "ON CLUSTER" clause:
     /// <https://clickhouse.com/docs/en/sql-reference/distributed-ddl/>
@@ -282,8 +276,14 @@ impl Display for CreateTable {
 
         // Hive table comment should be after column definitions, please refer to:
         // [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
-        if let Some(CommentDef::AfterColumnDefsWithoutEq(comment)) = &self.comment {
-            write!(f, " COMMENT '{comment}'")?;
+        for option_group in &self.table_options {
+            if let CreateTableOptions::Plain(options) = option_group {
+                for option in options {
+                    if let SqlOption::Comment(CommentDef::AfterColumnDefsWithoutEq(_)) = option {
+                        write!(f, " {option}")?;
+                    }
+                }
+            }
         }
 
         // Only for SQLite
@@ -375,35 +375,19 @@ impl Display for CreateTable {
             }
             write!(f, " LOCATION '{}'", self.location.as_ref().unwrap())?;
         }
-        if !self.table_properties.is_empty() {
-            write!(
-                f,
-                " TBLPROPERTIES ({})",
-                display_comma_separated(&self.table_properties)
-            )?;
-        }
-        if !self.with_options.is_empty() {
-            write!(f, " WITH ({})", display_comma_separated(&self.with_options))?;
-        }
-        if let Some(engine) = &self.engine {
-            write!(f, " ENGINE={engine}")?;
-        }
-        if let Some(comment_def) = &self.comment {
-            match comment_def {
-                CommentDef::WithEq(comment) => {
-                    write!(f, " COMMENT = '{comment}'")?;
-                }
-                CommentDef::WithoutEq(comment) => {
-                    write!(f, " COMMENT '{comment}'")?;
-                }
-                // For CommentDef::AfterColumnDefsWithoutEq will be displayed after column definition
-                CommentDef::AfterColumnDefsWithoutEq(_) => (),
+        for option_group in &self.table_options {
+            let is_after_columns_comment = matches!(
+                option_group,
+                CreateTableOptions::Plain(options)
+                    if options.iter().any(|option| matches!(
+                        option,
+                        SqlOption::Comment(CommentDef::AfterColumnDefsWithoutEq(_))
+                    ))
+            );
+            if !is_after_columns_comment {
+                write!(f, " {option_group}")?;
             }
         }
-
-        if let Some(auto_increment_offset) = self.auto_increment_offset {
-            write!(f, " AUTO_INCREMENT {auto_increment_offset}")?;
-        }
         if let Some(primary_key) = &self.primary_key {
             write!(f, " PRIMARY KEY {}", primary_key)?;
         }
@@ -503,13 +487,6 @@ impl Display for CreateTable {
             write!(f, " WITH TAG ({})", display_comma_separated(tag.as_slice()))?;
         }
 
-        if let Some(default_charset) = &self.default_charset {
-            write!(f, " DEFAULT CHARSET={default_charset}")?;
-        }
-        if let Some(collation) = &self.collation {
-            write!(f, " COLLATE={collation}")?;
-        }
-
         if self.on_commit.is_some() {
             let on_commit = match self.on_commit {
                 Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS",
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/helpers/stmt_create_table.rs b/app/src/ast/helpers/stmt_create_table.rs
index 1c50cb8..baf0410 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/helpers/stmt_create_table.rs
+++ b/app/src/ast/helpers/stmt_create_table.rs
@@ -26,9 +26,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,
-    StorageSerializationPolicy, TableConstraint, TableEngine, Tag, WrappedCollection,
+    ClusteredBy, ColumnDef, CommentDef, CreateTableOptions, Expr, FileFormat,
+    HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, OneOrManyWithParens, Query,
+    RowAccessPolicy, SqlOption, Statement, StorageSerializationPolicy, TableConstraint, Tag,
+    WrappedCollection,
 };
 use crate::parser::ParserError;
 
@@ -76,19 +77,13 @@ pub struct CreateTableBuilder {
     pub constraints: Vec<TableConstraint>,
     pub hive_distribution: HiveDistributionStyle,
     pub hive_formats: Option<HiveFormat>,
-    pub table_properties: Vec<SqlOption>,
-    pub with_options: Vec<SqlOption>,
+    pub table_options: Vec<CreateTableOptions>,
     pub file_format: Option<FileFormat>,
     pub location: Option<String>,
     pub query: Option<Box<Query>>,
     pub without_rowid: bool,
     pub like: Option<ObjectName>,
     pub clone: Option<ObjectName>,
-    pub engine: Option<TableEngine>,
-    pub comment: Option<CommentDef>,
-    pub auto_increment_offset: Option<u32>,
-    pub default_charset: Option<String>,
-    pub collation: Option<String>,
     pub on_commit: Option<OnCommit>,
     pub on_cluster: Option<Ident>,
     pub primary_key: Option<Box<Expr>>,
@@ -131,19 +126,13 @@ impl CreateTableBuilder {
             constraints: vec![],
             hive_distribution: HiveDistributionStyle::NONE,
             hive_formats: None,
-            table_properties: vec![],
-            with_options: vec![],
+            table_options: vec![],
             file_format: None,
             location: None,
             query: None,
             without_rowid: false,
             like: None,
             clone: None,
-            engine: None,
-            comment: None,
-            auto_increment_offset: None,
-            default_charset: None,
-            collation: None,
             on_commit: None,
             on_cluster: None,
             primary_key: None,
@@ -230,13 +219,23 @@ impl CreateTableBuilder {
         self
     }
 
+    pub fn table_options(mut self, table_options: Vec<CreateTableOptions>) -> Self {
+        self.table_options = table_options;
+        self
+    }
+
     pub fn table_properties(mut self, table_properties: Vec<SqlOption>) -> Self {
-        self.table_properties = table_properties;
+        if !table_properties.is_empty() {
+            self.table_options
+                .push(CreateTableOptions::TableProperties(table_properties));
+        }
         self
     }
 
     pub fn with_options(mut self, with_options: Vec<SqlOption>) -> Self {
-        self.with_options = with_options;
+        if !with_options.is_empty() {
+            self.table_options.push(CreateTableOptions::With(with_options));
+        }
         self
     }
     pub fn file_format(mut self, file_format: Option<FileFormat>) -> Self {
@@ -268,28 +267,11 @@ impl CreateTableBuilder {
         self
     }
 
-    pub fn engine(mut self, engine: Option<TableEngine>) -> Self {
-        self.engine = engine;
-        self
-    }
-
     pub fn comment(mut self, comment: Option<CommentDef>) -> Self {
-        self.comment = comment;
-        self
-    }
-
-    pub fn auto_increment_offset(mut self, offset: Option<u32>) -> Self {
-        self.auto_increment_offset = offset;
-        self
-    }
-
-    pub fn default_charset(mut self, default_charset: Option<String>) -> Self {
-        self.default_charset = default_charset;
-        self
-    }
-
-    pub fn collation(mut self, collation: Option<String>) -> Self {
-        self.collation = collation;
+        if let Some(comment) = comment {
+            self.table_options
+                .push(CreateTableOptions::Plain(vec![SqlOption::Comment(comment)]));
+        }
         self
     }
 
@@ -437,19 +419,13 @@ impl CreateTableBuilder {
             constraints: self.constraints,
             hive_distribution: self.hive_distribution,
             hive_formats: self.hive_formats,
-            table_properties: self.table_properties,
-            with_options: self.with_options,
+            table_options: self.table_options,
             file_format: self.file_format,
             location: self.location,
             query: self.query,
             without_rowid: self.without_rowid,
             like: self.like,
             clone: self.clone,
-            engine: self.engine,
-            comment: self.comment,
-            auto_increment_offset: self.auto_increment_offset,
-            default_charset: self.default_charset,
-            collation: self.collation,
             on_commit: self.on_commit,
             on_cluster: self.on_cluster,
             primary_key: self.primary_key,
@@ -499,19 +475,13 @@ impl TryFrom<Statement> for CreateTableBuilder {
                 constraints,
                 hive_distribution,
                 hive_formats,
-                table_properties,
-                with_options,
+                table_options,
                 file_format,
                 location,
                 query,
                 without_rowid,
                 like,
                 clone,
-                engine,
-                comment,
-                auto_increment_offset,
-                default_charset,
-                collation,
                 on_commit,
                 on_cluster,
                 primary_key,
@@ -548,19 +518,13 @@ impl TryFrom<Statement> for CreateTableBuilder {
                 constraints,
                 hive_distribution,
                 hive_formats,
-                table_properties,
-                with_options,
+                table_options,
                 file_format,
                 location,
                 query,
                 without_rowid,
                 like,
                 clone,
-                engine,
-                comment,
-                auto_increment_offset,
-                default_charset,
-                collation,
                 on_commit,
                 on_cluster,
                 primary_key,
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/mod.rs b/app/src/ast/mod.rs
index 582922a..0c43adf 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -2662,6 +2662,12 @@ impl fmt::Display for Declare {
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub enum CreateTableOptions {
     None,
+    /// Plain table options specified as space-separated clauses.
+    /// e.g. `ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`
+    Plain(Vec<SqlOption>),
+    /// Hive table properties.
+    /// e.g. `TBLPROPERTIES ("prop" = "value")`
+    TableProperties(Vec<SqlOption>),
     /// Options specified using the `WITH` keyword.
     /// e.g. `WITH (description = "123")`
     ///
@@ -2686,6 +2692,12 @@ pub enum CreateTableOptions {
 impl fmt::Display for CreateTableOptions {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
+            CreateTableOptions::Plain(options) => {
+                write!(f, "{}", display_separated(options, " "))
+            }
+            CreateTableOptions::TableProperties(options) => {
+                write!(f, "TBLPROPERTIES ({})", display_comma_separated(options))
+            }
             CreateTableOptions::With(with_options) => {
                 write!(f, "WITH ({})", display_comma_separated(with_options))
             }
@@ -7560,6 +7572,18 @@ pub enum SqlOption {
         range_direction: Option<PartitionRangeDirection>,
         for_values: Vec<Expr>,
     },
+    /// `ENGINE=<engine_name>(...)`
+    Engine(TableEngine),
+    /// `AUTO_INCREMENT <value>`
+    AutoIncrement(u32),
+    /// `DEFAULT CHARSET=<charset>`
+    DefaultCharset(ObjectName),
+    /// `COLLATE=<collation>`
+    Collate(ObjectName),
+    /// `TABLESPACE[=]<name>`
+    TableSpace(ObjectName),
+    /// `COMMENT ['='] '<value>'`
+    Comment(CommentDef),
 }
 
 impl fmt::Display for SqlOption {
@@ -7591,6 +7615,19 @@ impl fmt::Display for SqlOption {
                     display_comma_separated(for_values)
                 )
             }
+            SqlOption::Engine(engine) => write!(f, "ENGINE={engine}"),
+            SqlOption::AutoIncrement(value) => write!(f, "AUTO_INCREMENT {value}"),
+            SqlOption::DefaultCharset(charset) => write!(f, "DEFAULT CHARSET={charset}"),
+            SqlOption::Collate(collation) => write!(f, "COLLATE={collation}"),
+            SqlOption::TableSpace(tablespace) => write!(f, "TABLESPACE={tablespace}"),
+            SqlOption::Comment(comment) => match comment {
+                CommentDef::WithEq(value) => {
+                    write!(f, "COMMENT = '{}'", value::escape_single_quote_string(value))
+                }
+                CommentDef::WithoutEq(value) | CommentDef::AfterColumnDefsWithoutEq(value) => {
+                    write!(f, "COMMENT '{}'", value::escape_single_quote_string(value))
+                }
+            },
         }
     }
 }
@@ -8868,7 +8905,7 @@ impl Display for CreateViewParams {
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub struct TableEngine {
     pub name: String,
-    pub parameters: Option<Vec<Ident>>,
+    pub parameters: Option<Vec<Expr>>,
 }
 
 impl Display for TableEngine {
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 836f229..cf9bcce 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -567,19 +567,13 @@ impl Spanned for CreateTable {
             constraints,
             hive_distribution: _, // hive specific
             hive_formats: _,      // hive specific
-            table_properties,
-            with_options,
+            table_options,
             file_format: _, // enum
             location: _,    // string, no span
             query,
             without_rowid: _, // bool
             like,
             clone,
-            engine: _,                          // todo
-            comment: _,                         // todo, no span
-            auto_increment_offset: _,           // u32, no span
-            default_charset: _,                 // string, no span
-            collation: _,                       // string, no span
             on_commit: _,                       // enum
             on_cluster: _,                      // todo, clickhouse specific
             primary_key: _,                     // todo, clickhouse specific
@@ -610,8 +604,7 @@ impl Spanned for CreateTable {
             core::iter::once(name.span())
                 .chain(columns.iter().map(|i| i.span()))
                 .chain(constraints.iter().map(|i| i.span()))
-                .chain(table_properties.iter().map(|i| i.span()))
-                .chain(with_options.iter().map(|i| i.span()))
+                .chain(table_options.iter().map(|i| i.span()))
                 .chain(query.iter().map(|i| i.span()))
                 .chain(like.iter().map(|i| i.span()))
                 .chain(clone.iter().map(|i| i.span())),
@@ -1004,6 +997,17 @@ impl Spanned for SqlOption {
             } => union_spans(
                 core::iter::once(column_name.span).chain(for_values.iter().map(|i| i.span())),
             ),
+            SqlOption::Engine(table_engine) => union_spans(
+                table_engine
+                    .parameters
+                    .iter()
+                    .flat_map(|parameters| parameters.iter().map(|expr| expr.span())),
+            ),
+            SqlOption::AutoIncrement(_) => Span::empty(),
+            SqlOption::DefaultCharset(object_name)
+            | SqlOption::Collate(object_name)
+            | SqlOption::TableSpace(object_name) => object_name.span(),
+            SqlOption::Comment(_) => Span::empty(),
         }
     }
 }
@@ -1039,6 +1043,8 @@ impl Spanned for CreateTableOptions {
     fn span(&self) -> Span {
         match self {
             CreateTableOptions::None => Span::empty(),
+            CreateTableOptions::Plain(vec) => union_spans(vec.iter().map(|i| i.span())),
+            CreateTableOptions::TableProperties(vec) => union_spans(vec.iter().map(|i| i.span())),
             CreateTableOptions::With(vec) => union_spans(vec.iter().map(|i| i.span())),
             CreateTableOptions::Options(vec) => union_spans(vec.iter().map(|i| i.span())),
         }
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/keywords.rs b/app/src/keywords.rs
index bf8a191..8688552 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/keywords.rs
+++ b/app/src/keywords.rs
@@ -870,6 +870,7 @@ define_keywords!(
     TABLE,
     TABLES,
     TABLESAMPLE,
+    TABLESPACE,
     TAG,
     TARGET,
     TASK,
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs b/app/src/parser/mod.rs
index cbd464c..5651a9c 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -7041,17 +7041,19 @@ impl<'a> Parser<'a> {
 
         // parse optional column list (schema)
         let (columns, constraints) = self.parse_columns()?;
-        let mut comment = if dialect_of!(self is HiveDialect)
-            && self.parse_keyword(Keyword::COMMENT)
-        {
+        let mut table_options = vec![];
+        if dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT) {
             let next_token = self.next_token();
             match next_token.token {
-                Token::SingleQuotedString(str) => Some(CommentDef::AfterColumnDefsWithoutEq(str)),
+                Token::SingleQuotedString(str) => Self::push_create_table_option(
+                    &mut table_options,
+                    CreateTableOptions::Plain(vec![SqlOption::Comment(
+                        CommentDef::AfterColumnDefsWithoutEq(str),
+                    )]),
+                ),
                 _ => self.expected("comment", next_token)?,
             }
-        } else {
-            None
-        };
+        }
 
         // SQLite supports `WITHOUT ROWID` at the end of `CREATE TABLE`
         let without_rowid = self.parse_keywords(&[Keyword::WITHOUT, Keyword::ROWID]);
@@ -7059,39 +7061,7 @@ impl<'a> Parser<'a> {
         let hive_distribution = self.parse_hive_distribution()?;
         let clustered_by = self.parse_optional_clustered_by()?;
         let hive_formats = self.parse_hive_formats()?;
-        // PostgreSQL supports `WITH ( options )`, before `AS`
-        let with_options = self.parse_options(Keyword::WITH)?;
-        let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?;
-
-        let engine = if self.parse_keyword(Keyword::ENGINE) {
-            self.expect_token(&Token::Eq)?;
-            let next_token = self.next_token();
-            match next_token.token {
-                Token::Word(w) => {
-                    let name = w.value;
-                    let parameters = if self.peek_token() == Token::LParen {
-                        Some(self.parse_parenthesized_identifiers()?)
-                    } else {
-                        None
-                    };
-                    Some(TableEngine { name, parameters })
-                }
-                _ => self.expected("identifier", next_token)?,
-            }
-        } else {
-            None
-        };
-
-        let auto_increment_offset = if self.parse_keyword(Keyword::AUTO_INCREMENT) {
-            let _ = self.consume_token(&Token::Eq);
-            let next_token = self.next_token();
-            match next_token.token {
-                Token::Number(s, _) => Some(Self::parse::<u32>(s, next_token.span.start)?),
-                _ => self.expected("literal int", next_token)?,
-            }
-        } else {
-            None
-        };
+        self.parse_create_table_option_groups(&mut table_options)?;
 
         // ClickHouse supports `PRIMARY KEY`, before `ORDER BY`
         // https://clickhouse.com/docs/en/sql-reference/statements/create/table#primary-key
@@ -7119,29 +7089,11 @@ impl<'a> Parser<'a> {
             None
         };
 
-        let create_table_config = self.parse_optional_create_table_config()?;
+        self.parse_create_table_option_groups(&mut table_options)?;
 
-        let default_charset = if self.parse_keywords(&[Keyword::DEFAULT, Keyword::CHARSET]) {
-            self.expect_token(&Token::Eq)?;
-            let next_token = self.next_token();
-            match next_token.token {
-                Token::Word(w) => Some(w.value),
-                _ => self.expected("identifier", next_token)?,
-            }
-        } else {
-            None
-        };
+        let create_table_config = self.parse_optional_create_table_config()?;
 
-        let collation = if self.parse_keywords(&[Keyword::COLLATE]) {
-            self.expect_token(&Token::Eq)?;
-            let next_token = self.next_token();
-            match next_token.token {
-                Token::Word(w) => Some(w.value),
-                _ => self.expected("identifier", next_token)?,
-            }
-        } else {
-            None
-        };
+        self.parse_create_table_option_groups(&mut table_options)?;
 
         let on_commit = if self.parse_keywords(&[Keyword::ON, Keyword::COMMIT]) {
             Some(self.parse_create_table_on_commit()?)
@@ -7151,13 +7103,6 @@ impl<'a> Parser<'a> {
 
         let strict = self.parse_keyword(Keyword::STRICT);
 
-        // Excludes Hive dialect here since it has been handled after table column definitions.
-        if !dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT) {
-            // rewind the COMMENT keyword
-            self.prev_token();
-            comment = self.parse_optional_inline_comment()?
-        };
-
         // Parse optional `AS ( query )`
         let query = if self.parse_keyword(Keyword::AS) {
             Some(self.parse_query()?)
@@ -7174,8 +7119,7 @@ impl<'a> Parser<'a> {
             .temporary(temporary)
             .columns(columns)
             .constraints(constraints)
-            .with_options(with_options)
-            .table_properties(table_properties)
+            .table_options(table_options)
             .or_replace(or_replace)
             .if_not_exists(if_not_exists)
             .transient(transient)
@@ -7186,12 +7130,7 @@ impl<'a> Parser<'a> {
             .without_rowid(without_rowid)
             .like(like)
             .clone_clause(clone)
-            .engine(engine)
-            .comment(comment)
-            .auto_increment_offset(auto_increment_offset)
             .order_by(order_by)
-            .default_charset(default_charset)
-            .collation(collation)
             .on_commit(on_commit)
             .on_cluster(on_cluster)
             .clustered_by(clustered_by)
@@ -7219,6 +7158,101 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn push_create_table_option(
+        table_options: &mut Vec<CreateTableOptions>,
+        option_group: CreateTableOptions,
+    ) {
+        table_options.push(option_group);
+    }
+
+    fn parse_create_table_option_groups(
+        &mut self,
+        table_options: &mut Vec<CreateTableOptions>,
+    ) -> Result<(), ParserError> {
+        while let Some(option_group) = self.maybe_parse_create_table_option_group()? {
+            Self::push_create_table_option(table_options, option_group);
+        }
+        Ok(())
+    }
+
+    fn maybe_parse_create_table_option_group(
+        &mut self,
+    ) -> Result<Option<CreateTableOptions>, ParserError> {
+        let with_options = self.parse_options(Keyword::WITH)?;
+        if !with_options.is_empty() {
+            return Ok(Some(CreateTableOptions::With(with_options)));
+        }
+
+        let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?;
+        if !table_properties.is_empty() {
+            return Ok(Some(CreateTableOptions::TableProperties(table_properties)));
+        }
+
+        let mut options = vec![];
+        loop {
+            let option = if self.parse_keyword(Keyword::ENGINE) {
+                self.expect_token(&Token::Eq)?;
+                let next_token = self.next_token();
+                match next_token.token {
+                    Token::Word(w) => {
+                        let name = w.value;
+                        let parameters = if self.consume_token(&Token::LParen) {
+                            let parameters = if self.peek_token() != Token::RParen {
+                                self.parse_comma_separated(|p| p.parse_expr())?
+                            } else {
+                                vec![]
+                            };
+                            self.expect_token(&Token::RParen)?;
+                            Some(parameters)
+                        } else {
+                            None
+                        };
+                        Some(SqlOption::Engine(TableEngine { name, parameters }))
+                    }
+                    _ => self.expected("identifier", next_token)?,
+                }
+            } else if self.parse_keyword(Keyword::AUTO_INCREMENT) {
+                let _ = self.consume_token(&Token::Eq);
+                let next_token = self.next_token();
+                match next_token.token {
+                    Token::Number(s, _) => Some(SqlOption::AutoIncrement(Self::parse::<u32>(
+                        s,
+                        next_token.span.start,
+                    )?)),
+                    _ => self.expected("literal int", next_token)?,
+                }
+            } else if self.parse_keywords(&[Keyword::DEFAULT, Keyword::CHARSET])
+                || self.parse_keyword(Keyword::CHARSET)
+            {
+                self.expect_token(&Token::Eq)?;
+                Some(SqlOption::DefaultCharset(self.parse_object_name(false)?))
+            } else if self.parse_keyword(Keyword::COLLATE) {
+                self.expect_token(&Token::Eq)?;
+                Some(SqlOption::Collate(self.parse_object_name(false)?))
+            } else if self.parse_keyword(Keyword::TABLESPACE) {
+                let _ = self.consume_token(&Token::Eq);
+                Some(SqlOption::TableSpace(self.parse_object_name(false)?))
+            } else if !dialect_of!(self is HiveDialect) {
+                self.parse_optional_inline_comment()?
+                    .map(SqlOption::Comment)
+            } else {
+                None
+            };
+
+            if let Some(option) = option {
+                options.push(option);
+            } else {
+                break;
+            }
+        }
+
+        if options.is_empty() {
+            Ok(None)
+        } else {
+            Ok(Some(CreateTableOptions::Plain(options)))
+        }
+    }
+
     /// Parse configuration like inheritance, partitioning, clustering information during the table creation.
     ///
     /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_2)
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_clickhouse.rs b/app/tests/sqlparser_clickhouse.rs
index c56f988..1bbac2b 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_clickhouse.rs
+++ b/app/tests/sqlparser_clickhouse.rs
@@ -721,7 +721,7 @@ fn parse_create_table_with_primary_key() {
         Statement::CreateTable(CreateTable {
             name,
             columns,
-            engine,
+            table_options,
             primary_key,
             order_by,
             ..
@@ -743,14 +743,18 @@ fn parse_create_table_with_primary_key() {
                 columns
             );
             assert_eq!(
-                engine,
-                Some(TableEngine {
-                    name: "SharedMergeTree".to_string(),
-                    parameters: Some(vec![
-                        Ident::with_quote('\'', "/clickhouse/tables/{uuid}/{shard}"),
-                        Ident::with_quote('\'', "{replica}"),
-                    ]),
-                })
+                table_options,
+                vec![CreateTableOptions::Plain(vec![SqlOption::Engine(
+                    TableEngine {
+                        name: "SharedMergeTree".to_string(),
+                        parameters: Some(vec![
+                            Expr::Value(Value::SingleQuotedString(
+                                "/clickhouse/tables/{uuid}/{shard}".to_string()
+                            )),
+                            Expr::Value(Value::SingleQuotedString("{replica}".to_string())),
+                        ]),
+                    }
+                )])]
             );
             fn assert_function(actual: &Function, name: &str, arg: &str) -> bool {
                 assert_eq!(actual.name, ObjectName::from(vec![Ident::new(name)]));
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index 1ddf3f9..3b5132e 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -3657,13 +3657,16 @@ fn parse_create_table() {
             name,
             columns,
             constraints,
-            with_options,
+            table_options,
             if_not_exists: false,
             external: false,
             file_format: None,
             location: None,
             ..
         }) => {
+            let [CreateTableOptions::With(with_options)] = table_options.as_slice() else {
+                panic!("expected WITH table options");
+            };
             assert_eq!("uk_cities", name.to_string());
             assert_eq!(
                 columns,
@@ -3840,13 +3843,16 @@ fn parse_create_table_with_constraint_characteristics() {
             name,
             columns,
             constraints,
-            with_options,
+            table_options,
             if_not_exists: false,
             external: false,
             file_format: None,
             location: None,
             ..
         }) => {
+            let [CreateTableOptions::With(with_options)] = table_options.as_slice() else {
+                panic!("expected WITH table options");
+            };
             assert_eq!("uk_cities", name.to_string());
             assert_eq!(
                 columns,
@@ -4421,7 +4427,10 @@ fn parse_create_table_with_options() {
 
     let sql = "CREATE TABLE t (c INT) WITH (foo = 'bar', a = 123)";
     match generic.verified_stmt(sql) {
-        Statement::CreateTable(CreateTable { with_options, .. }) => {
+        Statement::CreateTable(CreateTable { table_options, .. }) => {
+            let [CreateTableOptions::With(with_options)] = table_options.as_slice() else {
+                panic!("expected WITH table options");
+            };
             assert_eq!(
                 vec![
                     SqlOption::KeyValue {
@@ -4482,13 +4491,14 @@ fn parse_create_external_table() {
             name,
             columns,
             constraints,
-            with_options,
+            table_options,
             if_not_exists,
             external,
             file_format,
             location,
             ..
         }) => {
+            assert!(table_options.is_empty());
             assert_eq!("uk_cities", name.to_string());
             assert_eq!(
                 columns,
@@ -4550,7 +4560,7 @@ fn parse_create_or_replace_external_table() {
             name,
             columns,
             constraints,
-            with_options,
+            table_options,
             if_not_exists,
             external,
             file_format,
@@ -4558,6 +4568,7 @@ fn parse_create_or_replace_external_table() {
             or_replace,
             ..
         }) => {
+            assert!(table_options.is_empty());
             assert_eq!("uk_cities", name.to_string());
             assert_eq!(
                 columns,
@@ -11420,7 +11431,9 @@ fn test_parse_inline_comment() {
     // [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
     match all_dialects_except(|d| d.is::<HiveDialect>()).verified_stmt(sql) {
         Statement::CreateTable(CreateTable {
-            columns, comment, ..
+            columns,
+            table_options,
+            ..
         }) => {
             assert_eq!(
                 columns,
@@ -11434,8 +11447,10 @@ fn test_parse_inline_comment() {
                 }]
             );
             assert_eq!(
-                comment.unwrap(),
-                CommentDef::WithEq("comment with equal".to_string())
+                table_options,
+                vec![CreateTableOptions::Plain(vec![SqlOption::Comment(
+                    CommentDef::WithEq("comment with equal".to_string())
+                )])]
             );
         }
         _ => unreachable!(),
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_duckdb.rs b/app/tests/sqlparser_duckdb.rs
index 3205832..48eb0ef 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_duckdb.rs
+++ b/app/tests/sqlparser_duckdb.rs
@@ -735,19 +735,13 @@ fn test_duckdb_union_datatype() {
                 storage: Default::default(),
                 location: Default::default()
             }),
-            table_properties: Default::default(),
-            with_options: Default::default(),
+            table_options: Default::default(),
             file_format: Default::default(),
             location: Default::default(),
             query: Default::default(),
             without_rowid: Default::default(),
             like: Default::default(),
             clone: Default::default(),
-            engine: Default::default(),
-            comment: Default::default(),
-            auto_increment_offset: Default::default(),
-            default_charset: Default::default(),
-            collation: Default::default(),
             on_commit: Default::default(),
             on_cluster: Default::default(),
             primary_key: Default::default(),
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_hive.rs b/app/tests/sqlparser_hive.rs
index 9b04309..8b01152 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_hive.rs
+++ b/app/tests/sqlparser_hive.rs
@@ -130,12 +130,12 @@ fn create_table_with_comment() {
         " INTO 4 BUCKETS"
     );
     match hive().verified_stmt(sql) {
-        Statement::CreateTable(CreateTable { comment, .. }) => {
+        Statement::CreateTable(CreateTable { table_options, .. }) => {
             assert_eq!(
-                comment,
-                Some(CommentDef::AfterColumnDefsWithoutEq(
-                    "table comment".to_string()
-                ))
+                table_options,
+                vec![CreateTableOptions::Plain(vec![SqlOption::Comment(
+                    CommentDef::AfterColumnDefsWithoutEq("table comment".to_string())
+                )])]
             )
         }
         _ => unreachable!(),
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index 88e7a1f..96eaecd 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -1747,19 +1747,13 @@ fn parse_create_table_with_valid_options() {
                     storage: None,
                     location: None,
                 },),
-                table_properties: vec![],
-                with_options,
+                table_options: vec![CreateTableOptions::With(with_options)],
                 file_format: None,
                 location: None,
                 query: None,
                 without_rowid: false,
                 like: None,
                 clone: None,
-                engine: None,
-                comment: None,
-                auto_increment_offset: None,
-                default_charset: None,
-                collation: None,
                 on_commit: None,
                 on_cluster: None,
                 primary_key: None,
@@ -1918,19 +1912,13 @@ fn parse_create_table_with_identity_column() {
                     storage: None,
                     location: None,
                 },),
-                table_properties: vec![],
-                with_options: vec![],
+                table_options: vec![],
                 file_format: None,
                 location: None,
                 query: None,
                 without_rowid: false,
                 like: None,
                 clone: None,
-                engine: None,
-                comment: None,
-                auto_increment_offset: None,
-                default_charset: None,
-                collation: None,
                 on_commit: None,
                 on_cluster: None,
                 primary_key: None,
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mysql.rs b/app/tests/sqlparser_mysql.rs
index 990107b..07d60c9 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mysql.rs
+++ b/app/tests/sqlparser_mysql.rs
@@ -848,9 +848,22 @@ fn parse_create_table_comment() {
 
     for sql in [without_equal, with_equal] {
         match mysql().verified_stmt(sql) {
-            Statement::CreateTable(CreateTable { name, comment, .. }) => {
+            Statement::CreateTable(CreateTable {
+                name,
+                table_options,
+                ..
+            }) => {
                 assert_eq!(name.to_string(), "foo");
-                assert_eq!(comment.expect("Should exist").to_string(), "baz");
+                assert!(table_options.iter().any(|group| matches!(
+                    group,
+                    CreateTableOptions::Plain(options)
+                        if options.iter().any(|option| matches!(
+                            option,
+                            SqlOption::Comment(CommentDef::WithoutEq(comment))
+                                | SqlOption::Comment(CommentDef::WithEq(comment))
+                                if comment == "baz"
+                        ))
+                )));
             }
             _ => unreachable!(),
         }
@@ -868,14 +881,18 @@ fn parse_create_table_auto_increment_offset() {
         match mysql().one_statement_parses_to(sql, canonical) {
             Statement::CreateTable(CreateTable {
                 name,
-                auto_increment_offset,
+                table_options,
                 ..
             }) => {
                 assert_eq!(name.to_string(), "foo");
-                assert_eq!(
-                    auto_increment_offset.expect("Should exist").to_string(),
-                    "123"
-                );
+                assert!(table_options.iter().any(|group| matches!(
+                    group,
+                    CreateTableOptions::Plain(options)
+                        if options.iter().any(|option| matches!(
+                            option,
+                            SqlOption::AutoIncrement(value) if *value == 123
+                        ))
+                )));
             }
             _ => unreachable!(),
         }
@@ -921,8 +938,7 @@ fn parse_create_table_engine_default_charset() {
         Statement::CreateTable(CreateTable {
             name,
             columns,
-            engine,
-            default_charset,
+            table_options,
             ..
         }) => {
             assert_eq!(name.to_string(), "foo");
@@ -935,13 +951,15 @@ fn parse_create_table_engine_default_charset() {
                 columns
             );
             assert_eq!(
-                engine,
-                Some(TableEngine {
-                    name: "InnoDB".to_string(),
-                    parameters: None
-                })
+                table_options,
+                vec![CreateTableOptions::Plain(vec![
+                    SqlOption::Engine(TableEngine {
+                        name: "InnoDB".to_string(),
+                        parameters: None
+                    }),
+                    SqlOption::DefaultCharset(ObjectName::from(vec![Ident::new("utf8mb3")])),
+                ])]
             );
-            assert_eq!(default_charset, Some("utf8mb3".to_string()));
         }
         _ => unreachable!(),
     }
@@ -954,7 +972,7 @@ fn parse_create_table_collate() {
         Statement::CreateTable(CreateTable {
             name,
             columns,
-            collation,
+            table_options,
             ..
         }) => {
             assert_eq!(name.to_string(), "foo");
@@ -966,7 +984,12 @@ fn parse_create_table_collate() {
                 },],
                 columns
             );
-            assert_eq!(collation, Some("utf8mb4_0900_ai_ci".to_string()));
+            assert_eq!(
+                table_options,
+                vec![CreateTableOptions::Plain(vec![SqlOption::Collate(
+                    ObjectName::from(vec![Ident::new("utf8mb4_0900_ai_ci")])
+                )])]
+            );
         }
         _ => unreachable!(),
     }
@@ -978,12 +1001,24 @@ fn parse_create_table_both_options_and_as_query() {
     match mysql_and_generic().verified_stmt(sql) {
         Statement::CreateTable(CreateTable {
             name,
-            collation,
+            table_options,
             query,
             ..
         }) => {
             assert_eq!(name.to_string(), "foo");
-            assert_eq!(collation, Some("utf8mb4_0900_ai_ci".to_string()));
+            assert_eq!(
+                table_options,
+                vec![CreateTableOptions::Plain(vec![
+                    SqlOption::Engine(TableEngine {
+                        name: "InnoDB".to_string(),
+                        parameters: None
+                    }),
+                    SqlOption::DefaultCharset(ObjectName::from(vec![Ident::new("utf8mb3")])),
+                    SqlOption::Collate(ObjectName::from(vec![Ident::new(
+                        "utf8mb4_0900_ai_ci"
+                    )])),
+                ])]
+            );
             assert_eq!(
                 query.unwrap().body.as_select().unwrap().projection,
                 vec![SelectItem::UnnamedExpr(Expr::Value(
@@ -1001,6 +1036,29 @@ fn parse_create_table_both_options_and_as_query() {
     ));
 }
 
+#[test]
+fn parse_create_table_plain_options_in_any_order() {
+    let sql = "CREATE TABLE foo (id INT(11)) DEFAULT CHARSET=utf8mb3 ENGINE=InnoDB COLLATE=utf8mb4_0900_ai_ci";
+    match mysql().one_statement_parses_to(sql, sql) {
+        Statement::CreateTable(CreateTable { table_options, .. }) => {
+            assert_eq!(
+                table_options,
+                vec![CreateTableOptions::Plain(vec![
+                    SqlOption::DefaultCharset(ObjectName::from(vec![Ident::new("utf8mb3")])),
+                    SqlOption::Engine(TableEngine {
+                        name: "InnoDB".to_string(),
+                        parameters: None
+                    }),
+                    SqlOption::Collate(ObjectName::from(vec![Ident::new(
+                        "utf8mb4_0900_ai_ci"
+                    )])),
+                ])]
+            );
+        }
+        _ => unreachable!(),
+    }
+}
+
 #[test]
 fn parse_create_table_comment_character_set() {
     let sql = "CREATE TABLE foo (s TEXT CHARACTER SET utf8mb4 COMMENT 'comment')";
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index 6c008c8..b385c1c 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -348,7 +348,7 @@ fn parse_create_table_with_defaults() {
             name,
             columns,
             constraints,
-            with_options,
+            table_options,
             if_not_exists: false,
             external: false,
             file_format: None,
@@ -356,6 +356,9 @@ fn parse_create_table_with_defaults() {
             ..
         }) => {
             use pretty_assertions::assert_eq;
+            let [CreateTableOptions::With(with_options)] = table_options.as_slice() else {
+                panic!("expected WITH table options");
+            };
             assert_eq!("public.customer", name.to_string());
             assert_eq!(
                 columns,
@@ -4668,13 +4671,14 @@ fn parse_create_table_with_alias() {
             name,
             columns,
             constraints,
-            with_options: _with_options,
+            table_options,
             if_not_exists: false,
             external: false,
             file_format: None,
             location: None,
             ..
         }) => {
+            assert_eq!(table_options, vec![CreateTableOptions::With(_with_options.clone())]);
             assert_eq!("public.datatype_aliases", name.to_string());
             assert_eq!(
                 columns,
@@ -5078,7 +5082,10 @@ fn parse_at_time_zone() {
 fn parse_create_table_with_options() {
     let sql = "CREATE TABLE t (c INT) WITH (foo = 'bar', a = 123)";
     match pg().verified_stmt(sql) {
-        Statement::CreateTable(CreateTable { with_options, .. }) => {
+        Statement::CreateTable(CreateTable { table_options, .. }) => {
+            let [CreateTableOptions::With(with_options)] = table_options.as_slice() else {
+                panic!("expected WITH table options");
+            };
             assert_eq!(
                 vec![
                     SqlOption::KeyValue {
@@ -5506,19 +5513,13 @@ fn parse_trigger_related_functions() {
                 storage: None,
                 location: None
             }),
-            table_properties: vec![],
-            with_options: vec![],
+            table_options: vec![],
             file_format: None,
             location: None,
             query: None,
             without_rowid: false,
             like: None,
             clone: None,
-            engine: None,
-            comment: None,
-            auto_increment_offset: None,
-            default_charset: None,
-            collation: None,
             on_commit: None,
             on_cluster: None,
             primary_key: None,
diff --git a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_snowflake.rs b/app/tests/sqlparser_snowflake.rs
index aa97411..cd9f18a 100644
--- a/tmp/agent-patch-flux-pr-1747.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_snowflake.rs
+++ b/app/tests/sqlparser_snowflake.rs
@@ -470,9 +470,18 @@ fn test_snowflake_create_table_cluster_by() {
 #[test]
 fn test_snowflake_create_table_comment() {
     match snowflake().verified_stmt("CREATE TABLE my_table (a INT) COMMENT = 'some comment'") {
-        Statement::CreateTable(CreateTable { name, comment, .. }) => {
+        Statement::CreateTable(CreateTable {
+            name,
+            table_options,
+            ..
+        }) => {
             assert_eq!("my_table", name.to_string());
-            assert_eq!("some comment", comment.unwrap().to_string());
+            assert_eq!(
+                table_options,
+                vec![CreateTableOptions::Plain(vec![SqlOption::Comment(
+                    CommentDef::WithEq("some comment".to_string())
+                )])]
+            );
         }
         _ => unreachable!(),
     }