agent_patch
runs/2026-02-28__18-27-22__gpt-5-3-codex/flux-pr-1628/flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/agent-logs/agent.patch
10320 bytes
diff --git a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/dml.rs b/app/src/ast/dml.rs
index d68a227..1da8121 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/dml.rs
+++ b/app/src/ast/dml.rs
@@ -33,10 +33,10 @@ pub use super::ddl::{ColumnDef, TableConstraint};
use super::{
display_comma_separated, display_separated, Assignment, ClusteredBy, CommentDef, Expr,
- FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident,
- InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert, OneOrManyWithParens,
- OrderByExpr, Query, RowAccessPolicy, SelectItem, SqlOption, SqliteOnConflict, TableEngine,
- TableObject, TableWithJoins, Tag, WrappedCollection,
+ FileFormat, FormatClause, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
+ HiveRowFormat, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert,
+ OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting, SqlOption,
+ SqliteOnConflict, TableEngine, TableObject, TableWithJoins, Tag, WrappedCollection,
};
/// CREATE INDEX statement.
@@ -479,6 +479,10 @@ pub struct Insert {
pub overwrite: bool,
/// A SQL query that specifies what to insert
pub source: Option<Box<Query>>,
+ /// ClickHouse `SETTINGS` clause.
+ pub settings: Option<Vec<Setting>>,
+ /// ClickHouse `FORMAT` clause.
+ pub format_clause: Option<FormatClause>,
/// MySQL `INSERT INTO ... SET`
/// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
pub assignments: Vec<Assignment>,
@@ -544,6 +548,9 @@ impl Display for Insert {
if !self.after_columns.is_empty() {
write!(f, "({}) ", display_comma_separated(&self.after_columns))?;
}
+ if let Some(ref settings) = self.settings {
+ write!(f, "SETTINGS {} ", display_comma_separated(settings))?;
+ }
if let Some(source) = &self.source {
write!(f, "{source}")?;
@@ -553,6 +560,9 @@ impl Display for Insert {
} else if self.source.is_none() && self.columns.is_empty() {
write!(f, "DEFAULT VALUES")?;
}
+ if let Some(ref format) = self.format_clause {
+ write!(f, " {}", format)?;
+ }
if let Some(insert_alias) = &self.insert_alias {
write!(f, " AS {0}", insert_alias.row_alias)?;
diff --git a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 8a27c4a..452fecd 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -1136,6 +1136,8 @@ impl Spanned for AlterIndexOperation {
///
/// Missing spans:ever
/// - [Insert::insert_alias]
+/// - [Insert::settings]
+/// - [Insert::format_clause]
impl Spanned for Insert {
fn span(&self) -> Span {
let Insert {
@@ -1147,6 +1149,8 @@ impl Spanned for Insert {
columns,
overwrite: _, // bool
source,
+ settings: _,
+ format_clause: _,
partitioned,
after_columns,
has_table_keyword: _, // bool
diff --git a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs b/app/src/parser/mod.rs
index b6e3fd1..206636e 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -9699,18 +9699,7 @@ impl<'a> Parser<'a> {
locks.push(self.parse_lock()?);
}
}
- let format_clause = if dialect_of!(self is ClickHouseDialect | GenericDialect)
- && self.parse_keyword(Keyword::FORMAT)
- {
- if self.parse_keyword(Keyword::NULL) {
- Some(FormatClause::Null)
- } else {
- let ident = self.parse_identifier()?;
- Some(FormatClause::Identifier(ident))
- }
- } else {
- None
- };
+ let format_clause = self.parse_format_clause()?;
Ok(Query {
with,
@@ -9746,6 +9735,22 @@ impl<'a> Parser<'a> {
Ok(settings)
}
+ fn parse_format_clause(&mut self) -> Result<Option<FormatClause>, ParserError> {
+ let format_clause = if dialect_of!(self is ClickHouseDialect | GenericDialect)
+ && self.parse_keyword(Keyword::FORMAT)
+ {
+ if self.parse_keyword(Keyword::NULL) {
+ Some(FormatClause::Null)
+ } else {
+ let ident = self.parse_identifier()?;
+ Some(FormatClause::Identifier(ident))
+ }
+ } else {
+ None
+ };
+ Ok(format_clause)
+ }
+
/// Parse a mssql `FOR [XML | JSON | BROWSE]` clause
pub fn parse_for_clause(&mut self) -> Result<Option<ForClause>, ParserError> {
if self.parse_keyword(Keyword::XML) {
@@ -12033,9 +12038,9 @@ impl<'a> Parser<'a> {
let is_mysql = dialect_of!(self is MySqlDialect);
- let (columns, partitioned, after_columns, source, assignments) =
+ let (columns, partitioned, after_columns, source, settings, format_clause, assignments) =
if self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) {
- (vec![], None, vec![], None, vec![])
+ (vec![], None, vec![], None, None, None, vec![])
} else {
let (columns, partitioned, after_columns) = if !self.peek_subquery_start() {
let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?;
@@ -12052,14 +12057,30 @@ impl<'a> Parser<'a> {
Default::default()
};
- let (source, assignments) =
- if self.dialect.supports_insert_set() && self.parse_keyword(Keyword::SET) {
- (None, self.parse_comma_separated(Parser::parse_assignment)?)
+ let (source, settings, format_clause, assignments) = if self
+ .dialect
+ .supports_insert_set()
+ && self.parse_keyword(Keyword::SET)
+ {
+ (None, None, None, self.parse_comma_separated(Parser::parse_assignment)?)
+ } else {
+ let settings = self.parse_settings()?;
+ if let Some(format_clause) = self.parse_format_clause()? {
+ (None, settings, Some(format_clause), vec![])
} else {
- (Some(self.parse_query()?), vec![])
- };
+ (Some(self.parse_query()?), settings, None, vec![])
+ }
+ };
- (columns, partitioned, after_columns, source, assignments)
+ (
+ columns,
+ partitioned,
+ after_columns,
+ source,
+ settings,
+ format_clause,
+ assignments,
+ )
};
let insert_alias = if dialect_of!(self is MySqlDialect | GenericDialect)
@@ -12139,6 +12160,8 @@ impl<'a> Parser<'a> {
columns,
after_columns,
source,
+ settings,
+ format_clause,
assignments,
has_table_keyword: table,
on,
diff --git a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_clickhouse.rs b/app/tests/sqlparser_clickhouse.rs
index 4fa657b..bcea6bd 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_clickhouse.rs
+++ b/app/tests/sqlparser_clickhouse.rs
@@ -228,6 +228,61 @@ fn parse_insert_into_function() {
clickhouse().verified_stmt(r#"INSERT INTO FUNCTION remote('localhost', default.simple_table) VALUES (100, 'inserted via remote()')"#);
}
+#[test]
+fn parse_insert_with_settings_and_format_clause() {
+ let sql = "INSERT INTO tbl SETTINGS max_threads = 4, input_format_skip_unknown_fields = 1 FORMAT JSONEachRow";
+ match clickhouse_and_generic().verified_stmt(sql) {
+ Statement::Insert(Insert {
+ table,
+ source,
+ settings,
+ format_clause,
+ ..
+ }) => {
+ assert_eq!(table.to_string(), "tbl");
+ assert_eq!(source, None);
+ assert_eq!(
+ settings,
+ Some(vec![
+ Setting {
+ key: Ident::new("max_threads"),
+ value: Value::Number("4".parse().unwrap(), false),
+ },
+ Setting {
+ key: Ident::new("input_format_skip_unknown_fields"),
+ value: Value::Number("1".parse().unwrap(), false),
+ }
+ ])
+ );
+ assert_eq!(
+ format_clause,
+ Some(FormatClause::Identifier(Ident::new("JSONEachRow")))
+ );
+ }
+ _ => unreachable!(),
+ }
+}
+
+#[test]
+fn parse_insert_with_settings_before_values() {
+ let sql = "INSERT INTO tbl SETTINGS max_threads = 4 VALUES (1)";
+ match clickhouse_and_generic().verified_stmt(sql) {
+ Statement::Insert(Insert {
+ source, settings, ..
+ }) => {
+ assert!(source.is_some());
+ assert_eq!(
+ settings,
+ Some(vec![Setting {
+ key: Ident::new("max_threads"),
+ value: Value::Number("4".parse().unwrap(), false),
+ }])
+ );
+ }
+ _ => unreachable!(),
+ }
+}
+
#[test]
fn parse_alter_table_attach_and_detach_partition() {
for operation in &["ATTACH", "DETACH"] {