agent_patch
runs/2026-02-28__18-27-22__gpt-5-4/flux-pr-1628/flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/agent-logs/agent.patch
11359 bytes
diff --git a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/dml.rs b/app/src/ast/dml.rs
index d68a227..a159d9a 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/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,12 @@ pub struct Insert {
pub overwrite: bool,
/// A SQL query that specifies what to insert
pub source: Option<Box<Query>>,
+ /// ClickHouse `SETTINGS` clause on `INSERT`
+ pub settings: Option<Vec<Setting>>,
+ /// ClickHouse `FORMAT <name>` clause on `INSERT`
+ pub format_clause: Option<FormatClause>,
+ /// Raw payload that follows a ClickHouse `FORMAT` clause
+ pub format_data: Option<String>,
/// MySQL `INSERT INTO ... SET`
/// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
pub assignments: Vec<Assignment>,
@@ -544,9 +550,17 @@ 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}")?;
+ } else if let Some(format_clause) = &self.format_clause {
+ write!(f, "{format_clause}")?;
+ if let Some(format_data) = &self.format_data {
+ write!(f, "{format_data}")?;
+ }
} else if !self.assignments.is_empty() {
write!(f, "SET ")?;
write!(f, "{}", display_comma_separated(&self.assignments))?;
diff --git a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 8a27c4a..d226eeb 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -1147,6 +1147,9 @@ impl Spanned for Insert {
columns,
overwrite: _, // bool
source,
+ settings: _,
+ format_clause: _,
+ format_data: _,
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-4/app/src/parser/mod.rs b/app/src/parser/mod.rs
index b6e3fd1..7508852 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -12033,9 +12033,18 @@ 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,
+ format_data,
+ assignments,
+ ) =
if self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) {
- (vec![], None, vec![], None, vec![])
+ (vec![], None, vec![], None, 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 +12061,34 @@ 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 settings = self.parse_settings()?;
+
+ let (source, format_clause, format_data, assignments) = if dialect_of!(self is ClickHouseDialect | GenericDialect)
+ && self.parse_keyword(Keyword::FORMAT)
+ {
+ let format_clause = if self.parse_keyword(Keyword::NULL) {
+ FormatClause::Null
} else {
- (Some(self.parse_query()?), vec![])
+ FormatClause::Identifier(self.parse_identifier()?)
};
+ let format_data = self.parse_statement_tail_as_string();
+ (None, Some(format_clause), format_data, vec![])
+ } else if self.dialect.supports_insert_set() && self.parse_keyword(Keyword::SET) {
+ (None, None, None, self.parse_comma_separated(Parser::parse_assignment)?)
+ } else {
+ (Some(self.parse_query()?), None, None, vec![])
+ };
- (columns, partitioned, after_columns, source, assignments)
+ (
+ columns,
+ partitioned,
+ after_columns,
+ source,
+ settings,
+ format_clause,
+ format_data,
+ assignments,
+ )
};
let insert_alias = if dialect_of!(self is MySqlDialect | GenericDialect)
@@ -12139,6 +12168,9 @@ impl<'a> Parser<'a> {
columns,
after_columns,
source,
+ settings,
+ format_clause,
+ format_data,
assignments,
has_table_keyword: table,
on,
@@ -12187,6 +12219,17 @@ impl<'a> Parser<'a> {
}
}
+ fn parse_statement_tail_as_string(&mut self) -> Option<String> {
+ let mut tail = String::new();
+ while !matches!(self.peek_token_no_skip().token, Token::EOF | Token::SemiColon) {
+ let Some(token) = self.next_token_no_skip() else {
+ break;
+ };
+ tail.push_str(&token.to_string());
+ }
+ if tail.is_empty() { None } else { Some(tail) }
+ }
+
pub fn parse_load_data_table_format(
&mut self,
) -> Result<Option<HiveLoadDataFormat>, ParserError> {
diff --git a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_clickhouse.rs b/app/tests/sqlparser_clickhouse.rs
index 4fa657b..c1af847 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_clickhouse.rs
+++ b/app/tests/sqlparser_clickhouse.rs
@@ -228,6 +228,69 @@ 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() {
+ let sql =
+ r#"INSERT INTO tbl SETTINGS input_format_skip_unknown_fields = 1 FORMAT JSONEachRow {"id":1}"#;
+
+ match clickhouse_and_generic().verified_stmt(sql) {
+ Statement::Insert(Insert {
+ table,
+ settings,
+ format_clause,
+ format_data,
+ source,
+ ..
+ }) => {
+ assert_eq!(table, TableObject::TableName(ObjectName(vec![Ident::new("tbl")])));
+ assert_eq!(
+ settings,
+ Some(vec![Setting {
+ key: Ident::new("input_format_skip_unknown_fields"),
+ value: Value::Number("1".to_string(), false),
+ }])
+ );
+ assert_eq!(
+ format_clause,
+ Some(FormatClause::Identifier(Ident::new("JSONEachRow")))
+ );
+ assert_eq!(format_data, Some(r#" {"id":1}"#.to_string()));
+ assert_eq!(source, None);
+ }
+ _ => unreachable!(),
+ }
+}
+
+#[test]
+fn parse_insert_with_settings_and_values() {
+ let sql = "INSERT INTO tbl SETTINGS async_insert = 1 VALUES (1)";
+
+ match clickhouse_and_generic().verified_stmt(sql) {
+ Statement::Insert(Insert {
+ settings,
+ source,
+ format_clause,
+ format_data,
+ ..
+ }) => {
+ assert_eq!(
+ settings,
+ Some(vec![Setting {
+ key: Ident::new("async_insert"),
+ value: Value::Number("1".to_string(), false),
+ }])
+ );
+ let source = source.expect("expected VALUES source");
+ assert!(matches!(source.body.as_ref(), SetExpr::Values(_)));
+ assert_eq!(source.settings, None);
+ assert_eq!(source.format_clause, None);
+ assert_eq!(format_clause, None);
+ assert_eq!(format_data, None);
+ }
+ _ => unreachable!(),
+ }
+}
+
#[test]
fn parse_alter_table_attach_and_detach_partition() {
for operation in &["ATTACH", "DETACH"] {
diff --git a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index ce31a06..6f8650f 100644
--- a/tmp/agent-patch-flux-pr-1628.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -4423,6 +4423,9 @@ fn test_simple_postgres_insert_with_alias() {
settings: None,
format_clause: None,
})),
+ settings: None,
+ format_clause: None,
+ format_data: None,
assignments: vec![],
partitioned: None,
after_columns: vec![],
@@ -4494,6 +4497,9 @@ fn test_simple_postgres_insert_with_alias() {
settings: None,
format_clause: None,
})),
+ settings: None,
+ format_clause: None,
+ format_data: None,
assignments: vec![],
partitioned: None,
after_columns: vec![],
@@ -4561,6 +4567,9 @@ fn test_simple_insert_with_quoted_alias() {
settings: None,
format_clause: None,
})),
+ settings: None,
+ format_clause: None,
+ format_data: None,
assignments: vec![],
partitioned: None,
after_columns: vec![],