STET

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

Back to adjudication
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 {})])
 }