agent_patch
runs/2026-02-28__18-27-22__gpt-5-3-codex/flux-pr-1500/flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/agent-logs/agent.patch
6875 bytes
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/mod.rs b/app/src/ast/mod.rs
index 505386f..0665ebd 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -561,6 +561,12 @@ pub enum Expr {
expr: Box<Expr>,
key: Ident,
},
+ /// Dot-notation method call on an expression, e.g. `xml_col.value('(/a)[1]', 'int')`.
+ Method {
+ 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::Method { 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-3-codex/app/src/dialect/mod.rs b/app/src/dialect/mod.rs
index a732aa5..53bf416 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/dialect/mod.rs
+++ b/app/src/dialect/mod.rs
@@ -451,6 +451,7 @@ pub trait Dialect: Debug + Any {
| Token::LBracket
| Token::Overlap
| Token::CaretAt => Ok(p!(DoubleColon)),
+ Token::Period if self.supports_expression_dot_method() => Ok(p!(DoubleColon)),
Token::Arrow
| Token::LongArrow
| Token::HashArrow
@@ -596,6 +597,12 @@ pub trait Dialect: Debug + Any {
false
}
+ /// Returns true if this dialect supports calling methods on arbitrary
+ /// expressions using dot notation.
+ fn supports_expression_dot_method(&self) -> bool {
+ false
+ }
+
/// Returns true if the dialect supports `!a` syntax for boolean `NOT` expressions.
fn supports_bang_not_operator(&self) -> bool {
false
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/dialect/mssql.rs b/app/src/dialect/mssql.rs
index 8aab0bc..4d81b38 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/dialect/mssql.rs
+++ b/app/src/dialect/mssql.rs
@@ -58,6 +58,10 @@ impl Dialect for MsSqlDialect {
true
}
+ fn supports_expression_dot_method(&self) -> bool {
+ true
+ }
+
/// In MSSQL, there is no boolean type, and `true` and `false` are valid column names
fn supports_boolean_literals(&self) -> bool {
false
diff --git a/tmp/agent-patch-flux-pr-1500.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 4115bbc..b39a889 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -1319,6 +1319,11 @@ impl<'a> Parser<'a> {
self.expect_token(&Token::RParen)?;
if !self.consume_token(&Token::Period) {
Ok(expr)
+ } else if self.dialect.supports_expression_dot_method() {
+ // Let infix parsing handle method calls such as
+ // `(SELECT ...).value(...)`.
+ self.prev_token();
+ Ok(expr)
} else {
let tok = self.next_token();
let key = match tok.token {
@@ -2876,6 +2881,15 @@ impl<'a> Parser<'a> {
data_type: self.parse_data_type()?,
format: None,
})
+ } else if tok == Token::Period && self.dialect.supports_expression_dot_method() {
+ let method = self.parse_identifier(false)?;
+ self.expect_token(&Token::LParen)?;
+ let args = self.parse_function_argument_list()?;
+ Ok(Expr::Method {
+ expr: Box::new(expr),
+ method,
+ args,
+ })
} else if Token::ExclamationMark == tok && self.dialect.supports_factorial_operator() {
Ok(Expr::UnaryOp {
op: UnaryOperator::PGPostfixFactorial,
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index c28f89e..760463a 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -29,7 +29,7 @@ use sqlparser::ast::DeclareAssignment::MsSqlAssignment;
use sqlparser::ast::Value::SingleQuotedString;
use sqlparser::ast::*;
use sqlparser::dialect::{GenericDialect, MsSqlDialect};
-use sqlparser::parser::ParserError;
+use sqlparser::parser::{Parser, ParserError};
#[test]
fn parse_mssql_identifiers() {
@@ -1445,6 +1445,54 @@ fn parse_true_false_as_identifiers() {
);
}
+#[test]
+fn parse_mssql_expression_dot_methods() {
+ ms().one_statement_parses_to(
+ "SELECT CONVERT(XML, '<root><a>1</a></root>').value('(/root/a/text())[1]', 'int')",
+ "SELECT CONVERT(XML, '<root><a>1</a></root>').value('(/root/a/text())[1]', 'int')",
+ );
+
+ ms().one_statement_parses_to(
+ "SELECT CONVERT(XML, '<root><a>1</a></root>').query('/root').value('(/root/a/text())[1]', 'int')",
+ "SELECT CONVERT(XML, '<root><a>1</a></root>').query('/root').value('(/root/a/text())[1]', 'int')",
+ );
+
+ ms().one_statement_parses_to(
+ "SELECT (CONVERT(XML, '<root><a>1</a></root>')).value('(/root/a/text())[1]', 'int')",
+ "SELECT (CONVERT(XML, '<root><a>1</a></root>')).value('(/root/a/text())[1]', 'int')",
+ );
+
+ let select = ms().verified_only_select(
+ "SELECT CONVERT(XML, '<root><a>1</a></root>').query('/root').value('(/root/a/text())[1]', 'int')",
+ );
+ let expr = expr_from_projection(&select.projection[0]);
+ match expr {
+ Expr::Method {
+ expr,
+ method,
+ args: _,
+ } => {
+ assert_eq!(method.value, "value");
+ match expr.as_ref() {
+ Expr::Method {
+ method,
+ args: _,
+ expr: _,
+ } => assert_eq!(method.value, "query"),
+ _ => panic!("expected nested method call for query"),
+ }
+ }
+ _ => panic!("expected method call"),
+ }
+}
+
+#[test]
+fn parse_mssql_expression_dot_methods_are_dialect_gated() {
+ let sql = "SELECT (1 + 2).value(1)";
+ ms().verified_only_select(sql);
+ assert!(Parser::parse_sql(&GenericDialect {}, sql).is_err());
+}
+
fn ms() -> TestedDialects {
TestedDialects::new(vec![Box::new(MsSqlDialect {})])
}