agent_patch
runs/2026-02-28__18-27-22__gpt-5-1-codex-mini/flux-pr-1500/flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/agent-logs/agent.patch
7117 bytes
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/mod.rs b/app/src/ast/mod.rs
index 505386f..c8ad0f8 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -561,6 +561,12 @@ pub enum Expr {
expr: Box<Expr>,
key: Ident,
},
+ /// Method call on an expression, e.g. `(expr).value('x', 'y')`.
+ MethodCall {
+ expr: Box<Expr>,
+ method: Ident,
+ args: FunctionArgumentList,
+ },
/// `IS FALSE` operator
IsFalse(Box<Expr>),
/// `IS NOT FALSE` operator
@@ -1630,6 +1636,9 @@ impl fmt::Display for Expr {
Expr::CompositeAccess { expr, key } => {
write!(f, "{expr}.{key}")
}
+ Expr::MethodCall { expr, method, args } => {
+ write!(f, "{expr}.{method}{args}")
+ }
Expr::AtTimeZone {
timestamp,
time_zone,
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/mod.rs b/app/src/dialect/mod.rs
index a732aa5..bc15e6c 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/mod.rs
+++ b/app/src/dialect/mod.rs
@@ -270,6 +270,11 @@ pub trait Dialect: Debug + Any {
false
}
+ /// Returns true if the dialect allows calling methods on expressions via `.method(...)`.
+ fn supports_expression_method_calls(&self) -> bool {
+ false
+ }
+
/// Returns true if the dialect supports lambda functions, for example:
///
/// ```sql
@@ -464,6 +469,15 @@ pub trait Dialect: Debug + Any {
| Token::QuestionAnd
| Token::QuestionPipe
| Token::CustomBinaryOperator(_) => Ok(p!(PgOther)),
+ Token::Period if self.supports_expression_method_calls() => {
+ match (
+ parser.peek_nth_token(1).token,
+ parser.peek_nth_token(2).token,
+ ) {
+ (Token::Word(_), Token::LParen) => Ok(p!(MemberAccess)),
+ _ => Ok(self.prec_unknown()),
+ }
+ }
_ => Ok(self.prec_unknown()),
}
}
@@ -496,6 +510,7 @@ pub trait Dialect: Debug + Any {
/// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference
fn prec_value(&self, prec: Precedence) -> u8 {
match prec {
+ Precedence::MemberAccess => 60,
Precedence::DoubleColon => 50,
Precedence::AtTz => 41,
Precedence::MulDivModOp => 40,
@@ -645,6 +660,7 @@ pub trait Dialect: Debug + Any {
/// higher number -> higher precedence
#[derive(Debug, Clone, Copy)]
pub enum Precedence {
+ MemberAccess,
DoubleColon,
AtTz,
MulDivModOp,
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/mssql.rs b/app/src/dialect/mssql.rs
index 8aab0bc..d42648e 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/mssql.rs
+++ b/app/src/dialect/mssql.rs
@@ -62,4 +62,8 @@ impl Dialect for MsSqlDialect {
fn supports_boolean_literals(&self) -> bool {
false
}
+
+ fn supports_expression_method_calls(&self) -> bool {
+ true
+ }
}
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 4115bbc..2dbd946 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -2881,6 +2881,8 @@ impl<'a> Parser<'a> {
op: UnaryOperator::PGPostfixFactorial,
expr: Box::new(expr),
})
+ } else if Token::Period == tok && self.dialect.supports_expression_method_calls() {
+ self.parse_method_call(expr)
} else if Token::LBracket == tok {
if dialect_of!(self is PostgreSqlDialect | DuckDbDialect | GenericDialect) {
self.parse_subscript(expr)
@@ -2902,6 +2904,17 @@ impl<'a> Parser<'a> {
}
}
+ fn parse_method_call(&mut self, expr: Expr) -> Result<Expr, ParserError> {
+ let method = self.parse_identifier(false)?;
+ self.expect_token(&Token::LParen)?;
+ let args = self.parse_function_argument_list()?;
+ Ok(Expr::MethodCall {
+ expr: Box::new(expr),
+ method,
+ args,
+ })
+ }
+
/// Parse the `ESCAPE CHAR` portion of `LIKE`, `ILIKE`, and `SIMILAR TO`
pub fn parse_escape_char(&mut self) -> Result<Option<String>, ParserError> {
if self.parse_keyword(Keyword::ESCAPE) {
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index c28f89e..8bad602 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -1445,6 +1445,51 @@ fn parse_true_false_as_identifiers() {
);
}
+#[test]
+fn parse_mssql_expression_method_calls() {
+ let expr = ms().verified_expr("CONVERT(XML, xmlcol).value('XQuery', 'SQL').query('/root')");
+
+ assert_eq!(
+ expr,
+ Expr::MethodCall {
+ expr: Box::new(Expr::MethodCall {
+ expr: Box::new(Expr::Convert {
+ is_try: false,
+ expr: Box::new(Expr::Identifier(Ident::new("xmlcol"))),
+ data_type: Some(DataType::Custom(
+ ObjectName(vec![Ident::new("XML")]),
+ vec![]
+ )),
+ charset: None,
+ target_before_value: true,
+ styles: vec![],
+ }),
+ method: Ident::new("value"),
+ args: FunctionArgumentList {
+ duplicate_treatment: None,
+ args: vec![
+ FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
+ Value::SingleQuotedString("XQuery".to_string()),
+ ))),
+ FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
+ Value::SingleQuotedString("SQL".to_string()),
+ ))),
+ ],
+ clauses: vec![],
+ },
+ }),
+ method: Ident::new("query"),
+ args: FunctionArgumentList {
+ duplicate_treatment: None,
+ args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
+ Value::SingleQuotedString("/root".to_string()),
+ )))],
+ clauses: vec![],
+ },
+ }
+ );
+}
+
fn ms() -> TestedDialects {
TestedDialects::new(vec![Box::new(MsSqlDialect {})])
}