STET

agent_patch

runs/2026-02-21__03-36-16__gpt-5-1-codex-mini/flux-pr-1500/flux-pr-1500.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/agent-logs/agent.patch

6085 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/mod.rs b/app/src/ast/mod.rs
index 505386f..d26cf04 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-03-05__17-48-34__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,
     },
+    /// Dialect-specific method invocation accessed through dot notation, e.g. `expr.value(...)`.
+    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-03-05__17-48-34__gpt-5-1-codex-mini/app/src/dialect/mod.rs b/app/src/dialect/mod.rs
index a732aa5..ddacb66 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/dialect/mod.rs
+++ b/app/src/dialect/mod.rs
@@ -279,6 +279,12 @@ pub trait Dialect: Debug + Any {
         false
     }
 
+    /// Returns true if the dialect supports calling methods on expressions via dot notation,
+    /// for example `expr.value('(/foo)[1]', 'varchar(100)')`.
+    fn supports_expression_method_call(&self) -> bool {
+        false
+    }
+
     /// Returns true if the dialect supports multiple variable assignment
     /// using parentheses in a `SET` variable declaration.
     ///
@@ -451,6 +457,7 @@ pub trait Dialect: Debug + Any {
             | Token::LBracket
             | Token::Overlap
             | Token::CaretAt => Ok(p!(DoubleColon)),
+            Token::Period if self.supports_expression_method_call() => Ok(p!(DoubleColon)),
             Token::Arrow
             | Token::LongArrow
             | Token::HashArrow
diff --git a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/dialect/mssql.rs b/app/src/dialect/mssql.rs
index 8aab0bc..7876893 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/dialect/mssql.rs
+++ b/app/src/dialect/mssql.rs
@@ -58,6 +58,10 @@ impl Dialect for MsSqlDialect {
         true
     }
 
+    fn supports_expression_method_call(&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-03-05__17-48-34__gpt-5-1-codex-mini/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 4115bbc..06617a2 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -1319,6 +1319,8 @@ impl<'a> Parser<'a> {
                 self.expect_token(&Token::RParen)?;
                 if !self.consume_token(&Token::Period) {
                     Ok(expr)
+                } else if self.dialect.supports_expression_method_call() && self.peek_method_call() {
+                    self.parse_expression_method_call(expr)
                 } else {
                     let tok = self.next_token();
                     let key = match tok.token {
@@ -2881,6 +2883,11 @@ impl<'a> Parser<'a> {
                 op: UnaryOperator::PGPostfixFactorial,
                 expr: Box::new(expr),
             })
+        } else if Token::Period == tok
+            && self.dialect.supports_expression_method_call()
+            && self.peek_method_call()
+        {
+            self.parse_expression_method_call(expr)
         } else if Token::LBracket == tok {
             if dialect_of!(self is PostgreSqlDialect | DuckDbDialect | GenericDialect) {
                 self.parse_subscript(expr)
@@ -2902,6 +2909,24 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn peek_method_call(&self) -> bool {
+        matches!(
+            self.peek_token().token,
+            Token::Word(_) | Token::SingleQuotedString(_) | Token::DoubleQuotedString(_)
+        ) && self.peek_nth_token(1).token == Token::LParen
+    }
+
+    fn parse_expression_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-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index c28f89e..8ee0f98 100644
--- a/tmp/agent-patch-flux-pr-1500.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -1445,6 +1445,24 @@ fn parse_true_false_as_identifiers() {
     );
 }
 
+#[test]
+fn parse_mssql_dot_method_call() {
+    let sql = "CONVERT(XML, '<root/>').value('(/root)[1]', 'varchar(10)')";
+    let expr = ms().verified_expr(sql);
+    let Expr::MethodCall {
+        expr: target,
+        method,
+        args,
+    } = expr else {
+        panic!("expected method call");
+    };
+    assert_eq!(Ident::new("value"), method);
+    assert_eq!(2, args.args.len());
+    assert_eq!("'(/root)[1]'", args.args[0].to_string());
+    assert_eq!("'varchar(10)'", args.args[1].to_string());
+    assert!(matches!(*target, Expr::Convert { .. }));
+}
+
 fn ms() -> TestedDialects {
     TestedDialects::new(vec![Box::new(MsSqlDialect {})])
 }