agent_patch
runs/2026-02-28__18-27-22__gpt-5-4/flux-pr-1759/flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/agent-logs/agent.patch
20936 bytes
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/query.rs b/app/src/ast/query.rs
index 982985e..921798f 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/query.rs
+++ b/app/src/ast/query.rs
@@ -62,6 +62,8 @@ pub struct Query {
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/format)
/// (ClickHouse-specific)
pub format_clause: Option<FormatClause>,
+ /// BigQuery pipe operators appended to the query body.
+ pub pipe_operators: Vec<PipeOperator>,
}
impl fmt::Display for Query {
@@ -92,10 +94,84 @@ impl fmt::Display for Query {
if let Some(ref format) = self.format_clause {
write!(f, " {}", format)?;
}
+ for pipe_operator in &self.pipe_operators {
+ write!(f, " |> {pipe_operator}")?;
+ }
Ok(())
}
}
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum PipeOperator {
+ Where(Expr),
+ Aggregate {
+ projection: Vec<SelectItem>,
+ group_by: Vec<Expr>,
+ },
+ Select {
+ distinct: Option<Distinct>,
+ projection: Vec<SelectItem>,
+ },
+ OrderBy(OrderBy),
+ Limit(LimitClause),
+}
+
+impl fmt::Display for PipeOperator {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ PipeOperator::Where(expr) => write!(f, "WHERE {expr}"),
+ PipeOperator::Aggregate {
+ projection,
+ group_by,
+ } => {
+ write!(f, "AGGREGATE {}", display_comma_separated(projection))?;
+ if !group_by.is_empty() {
+ write!(f, " GROUP BY {}", display_comma_separated(group_by))?;
+ }
+ Ok(())
+ }
+ PipeOperator::Select {
+ distinct,
+ projection,
+ } => {
+ write!(f, "SELECT")?;
+ if let Some(distinct) = distinct {
+ write!(f, " {distinct}")?;
+ }
+ if !projection.is_empty() {
+ write!(f, " {}", display_comma_separated(projection))?;
+ }
+ Ok(())
+ }
+ PipeOperator::OrderBy(order_by) => write!(f, "{order_by}"),
+ PipeOperator::Limit(LimitClause::LimitOffset {
+ limit,
+ offset,
+ limit_by,
+ }) => {
+ write!(f, "LIMIT")?;
+ if let Some(limit) = limit {
+ write!(f, " {limit}")?;
+ } else {
+ write!(f, " ALL")?;
+ }
+ if let Some(offset) = offset {
+ write!(f, " {offset}")?;
+ }
+ if !limit_by.is_empty() {
+ write!(f, " BY {}", display_separated(limit_by, ", "))?;
+ }
+ Ok(())
+ }
+ PipeOperator::Limit(LimitClause::OffsetCommaLimit { offset, limit }) => {
+ write!(f, "LIMIT {offset}, {limit}")
+ }
+ }
+ }
+}
+
/// Query syntax for ClickHouse ADD PROJECTION statement.
/// Its syntax is similar to SELECT statement, but it is used to add a new projection to a table.
/// Syntax is `SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]`
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 28d479f..bb201f7 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -102,6 +102,7 @@ impl Spanned for Query {
for_clause: _, // todo, mssql specific
settings: _, // todo, clickhouse specific
format_clause: _, // todo, clickhouse specific
+ pipe_operators: _, // todo, bigquery specific
} = self;
union_spans(
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/keywords.rs b/app/src/keywords.rs
index 15a6f91..392878b 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/keywords.rs
+++ b/app/src/keywords.rs
@@ -83,6 +83,7 @@ define_keywords!(
ADMIN,
AFTER,
AGAINST,
+ AGGREGATE,
AGGREGATION,
ALERT,
ALGORITHM,
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 03ea91f..762b478 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -10571,6 +10571,7 @@ impl<'a> Parser<'a> {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}
.into())
} else if self.parse_keyword(Keyword::UPDATE) {
@@ -10584,6 +10585,7 @@ impl<'a> Parser<'a> {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}
.into())
} else if self.parse_keyword(Keyword::DELETE) {
@@ -10597,6 +10599,7 @@ impl<'a> Parser<'a> {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}
.into())
} else {
@@ -10636,6 +10639,7 @@ impl<'a> Parser<'a> {
} else {
None
};
+ let pipe_operators = self.parse_pipe_operators()?;
Ok(Query {
with,
@@ -10647,11 +10651,63 @@ impl<'a> Parser<'a> {
for_clause,
settings,
format_clause,
+ pipe_operators,
}
.into())
}
}
+ fn parse_pipe_operators(&mut self) -> Result<Vec<PipeOperator>, ParserError> {
+ let mut pipe_operators = vec![];
+ while dialect_of!(self is BigQueryDialect) && self.consume_token(&Token::PipeRight) {
+ pipe_operators.push(self.parse_pipe_operator()?);
+ }
+ Ok(pipe_operators)
+ }
+
+ fn parse_pipe_operator(&mut self) -> Result<PipeOperator, ParserError> {
+ if self.parse_keyword(Keyword::WHERE) {
+ Ok(PipeOperator::Where(self.parse_expr()?))
+ } else if self.parse_keyword(Keyword::AGGREGATE) {
+ let projection = self.parse_projection()?;
+ let group_by = if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) {
+ self.parse_comma_separated(Parser::parse_expr)?
+ } else {
+ vec![]
+ };
+ Ok(PipeOperator::Aggregate {
+ projection,
+ group_by,
+ })
+ } else if self.parse_keyword(Keyword::SELECT) {
+ let distinct = self.parse_all_or_distinct()?;
+ let projection = self.parse_projection()?;
+ Ok(PipeOperator::Select {
+ distinct,
+ projection,
+ })
+ } else if let Some(order_by) = self.parse_optional_order_by()? {
+ Ok(PipeOperator::OrderBy(order_by))
+ } else if self.parse_keyword(Keyword::LIMIT) {
+ let limit = self.parse_limit()?;
+ let offset = if self.parse_keyword(Keyword::OFFSET) {
+ Some(self.parse_offset()?)
+ } else {
+ None
+ };
+ Ok(PipeOperator::Limit(LimitClause::LimitOffset {
+ limit,
+ offset,
+ limit_by: vec![],
+ }))
+ } else {
+ self.expected(
+ "WHERE, AGGREGATE, SELECT, ORDER BY, or LIMIT after |>",
+ self.peek_token(),
+ )
+ }
+ }
+
fn parse_settings(&mut self) -> Result<Option<Vec<Setting>>, ParserError> {
let settings = if dialect_of!(self is ClickHouseDialect|GenericDialect)
&& self.parse_keyword(Keyword::SETTINGS)
@@ -12122,6 +12178,7 @@ impl<'a> Parser<'a> {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}),
alias,
})
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/tokenizer.rs b/app/src/tokenizer.rs
index 13bce0c..10175da 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/tokenizer.rs
+++ b/app/src/tokenizer.rs
@@ -162,6 +162,8 @@ pub enum Token {
Ampersand,
/// Pipe `|`
Pipe,
+ /// Pipe right `|>`
+ PipeRight,
/// Caret `^`
Caret,
/// Left brace `{`
@@ -331,6 +333,7 @@ impl fmt::Display for Token {
Token::Ampersand => f.write_str("&"),
Token::Caret => f.write_str("^"),
Token::Pipe => f.write_str("|"),
+ Token::PipeRight => f.write_str("|>"),
Token::LBrace => f.write_str("{"),
Token::RBrace => f.write_str("}"),
Token::RArrow => f.write_str("=>"),
@@ -1400,7 +1403,7 @@ impl<'a> Tokenizer<'a> {
"|>>",
Token::VerticalBarShiftRight,
),
- _ => self.start_binop_opt(chars, "|>", None),
+ _ => self.consume_and_return(chars, Token::PipeRight),
}
}
// Bitshift '|' operator
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_bigquery.rs b/app/tests/sqlparser_bigquery.rs
index 416d2e4..f548379 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_bigquery.rs
+++ b/app/tests/sqlparser_bigquery.rs
@@ -2327,6 +2327,13 @@ fn test_select_as_value() {
assert_eq!(Some(ValueTableMode::AsValue), select.value_table_mode);
}
+#[test]
+fn parse_bigquery_pipe_operators() {
+ let sql = "FROM t |> WHERE x > 0 |> AGGREGATE SUM(y) AS total GROUP BY z |> ORDER BY total DESC |> LIMIT 5 |> SELECT z, total";
+ let query = bigquery().verified_query(sql);
+ assert_eq!(5, query.pipe_operators.len());
+}
+
#[test]
fn test_triple_quote_typed_strings() {
bigquery().verified_expr(r#"JSON '''{"foo":"bar's"}'''"#);
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index fa2346c..77473cb 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -489,6 +489,7 @@ fn parse_update_set_from() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}),
alias: Some(TableAlias {
name: Ident::new("t2"),
@@ -4310,6 +4311,7 @@ fn parse_create_table_as_table() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
});
match verified_stmt(sql1) {
@@ -4335,6 +4337,7 @@ fn parse_create_table_as_table() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
});
match verified_stmt(sql2) {
@@ -6332,6 +6335,7 @@ fn parse_interval_and_or_xor() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}))];
assert_eq!(actual_ast, expected_ast);
@@ -9467,6 +9471,7 @@ fn parse_merge() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}),
alias: Some(TableAlias {
name: Ident {
@@ -11344,6 +11349,7 @@ fn parse_unload() {
order_by: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}),
to: Ident {
value: "s3://...".to_string(),
@@ -12564,6 +12570,7 @@ fn test_extract_seconds_ok() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}))];
assert_eq!(actual_ast, expected_ast);
@@ -14641,6 +14648,7 @@ fn test_select_from_first() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
};
assert_eq!(expected, ast);
assert_eq!(ast.to_string(), q);
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index ef61034..48ceda9 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -114,6 +114,7 @@ fn parse_create_procedure() {
order_by: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
body: Box::new(SetExpr::Select(Box::new(Select {
select_token: AttachedToken::empty(),
distinct: None,
@@ -1252,6 +1253,7 @@ fn parse_substring_in_select() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}),
query
);
@@ -1354,6 +1356,7 @@ fn parse_mssql_declare() {
order_by: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
body: Box::new(SetExpr::Select(Box::new(Select {
select_token: AttachedToken::empty(),
distinct: None,
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mysql.rs b/app/tests/sqlparser_mysql.rs
index f74248b..583106d 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mysql.rs
+++ b/app/tests/sqlparser_mysql.rs
@@ -1113,6 +1113,7 @@ fn parse_escaped_quote_identifiers_with_escape() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}))
);
}
@@ -1165,6 +1166,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}))
);
}
@@ -1211,6 +1213,7 @@ fn parse_escaped_backticks_with_escape() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}))
);
}
@@ -1261,6 +1264,7 @@ fn parse_escaped_backticks_with_no_escape() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}))
);
}
@@ -1436,6 +1440,7 @@ fn parse_simple_insert() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -1484,6 +1489,7 @@ fn parse_ignore_insert() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -1532,6 +1538,7 @@ fn parse_priority_insert() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -1577,6 +1584,7 @@ fn parse_priority_insert() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -1624,6 +1632,7 @@ fn parse_insert_as() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -1686,6 +1695,7 @@ fn parse_insert_as() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -1735,6 +1745,7 @@ fn parse_replace_insert() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -1775,6 +1786,7 @@ fn parse_empty_row_insert() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -1839,6 +1851,7 @@ fn parse_insert_with_on_duplicate_update() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
source
);
@@ -2745,6 +2758,7 @@ fn parse_substring_in_select() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}),
query
);
@@ -3051,6 +3065,7 @@ fn parse_hex_string_introducer() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
}))
)
}
diff --git a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index 27fc7fa..116e56b 100644
--- a/tmp/agent-patch-flux-pr-1759.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -1326,6 +1326,7 @@ fn parse_copy_to() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
to: true,
target: CopyTarget::File {
@@ -2994,6 +2995,7 @@ fn parse_array_subquery_expr() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
filter: None,
null_treatment: None,
@@ -4785,6 +4787,7 @@ fn test_simple_postgres_insert_with_alias() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
assignments: vec![],
partitioned: None,
@@ -4797,6 +4800,7 @@ fn test_simple_postgres_insert_with_alias() {
insert_alias: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})
)
}
@@ -4856,6 +4860,7 @@ fn test_simple_postgres_insert_with_alias() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
assignments: vec![],
partitioned: None,
@@ -4868,6 +4873,7 @@ fn test_simple_postgres_insert_with_alias() {
insert_alias: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})
)
}
@@ -4925,6 +4931,7 @@ fn test_simple_insert_with_quoted_alias() {
for_clause: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})),
assignments: vec![],
partitioned: None,
@@ -4937,6 +4944,7 @@ fn test_simple_insert_with_quoted_alias() {
insert_alias: None,
settings: None,
format_clause: None,
+ pipe_operators: vec![],
})
)
}