STET

agent_patch

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

10097 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1839.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/data_type.rs b/app/src/ast/data_type.rs
index 52919de..0780f21 100644
--- a/tmp/agent-patch-flux-pr-1839.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/data_type.rs
+++ b/app/src/ast/data_type.rs
@@ -25,7 +25,7 @@ use serde::{Deserialize, Serialize};
 #[cfg(feature = "visitor")]
 use sqlparser_derive::{Visit, VisitMut};
 
-use crate::ast::{display_comma_separated, Expr, ObjectName, StructField, UnionField};
+use crate::ast::{display_comma_separated, Expr, Ident, ObjectName, StructField, UnionField};
 
 use super::{value::escape_single_quote_string, ColumnDef};
 
@@ -40,6 +40,15 @@ pub enum EnumMember {
     NamedValue(String, Expr),
 }
 
+/// Representation of a `TABLE` type, optionally bound to a table variable.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct TableDataType {
+    pub table_variable: Option<Ident>,
+    pub columns: Vec<ColumnDef>,
+}
+
 /// SQL data types
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -48,7 +57,7 @@ pub enum DataType {
     /// Table type in [PostgreSQL], e.g. CREATE FUNCTION RETURNS TABLE(...).
     ///
     /// [PostgreSQL]: https://www.postgresql.org/docs/15/sql-createfunction.html
-    Table(Vec<ColumnDef>),
+    Table(TableDataType),
     /// Fixed-length character type, e.g. CHARACTER(10).
     Character(Option<CharacterLength>),
     /// Fixed-length char type, e.g. CHAR(10).
@@ -716,7 +725,20 @@ impl fmt::Display for DataType {
             DataType::Unspecified => Ok(()),
             DataType::Trigger => write!(f, "TRIGGER"),
             DataType::AnyType => write!(f, "ANY TYPE"),
-            DataType::Table(fields) => write!(f, "TABLE({})", display_comma_separated(fields)),
+            DataType::Table(table_data) => {
+                if let Some(table_variable) = &table_data.table_variable {
+                    write!(f, "{table_variable} ")?;
+                }
+                write!(f, "TABLE")?;
+                if !table_data.columns.is_empty() {
+                    write!(
+                        f,
+                        "({})",
+                        display_comma_separated(table_data.columns.as_slice())
+                    )?;
+                }
+                Ok(())
+            }
             DataType::GeometricType(kind) => write!(f, "{}", kind),
         }
     }
diff --git a/tmp/agent-patch-flux-pr-1839.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 47b321d..dc86fbb 100644
--- a/tmp/agent-patch-flux-pr-1839.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
@@ -5204,19 +5204,26 @@ impl<'a> Parser<'a> {
         let (name, args) = self.parse_create_function_name_and_params()?;
 
         self.expect_keyword(Keyword::RETURNS)?;
-        let return_type = Some(self.parse_data_type()?);
+        let return_type = Some(self.parse_mssql_return_type()?);
 
-        self.expect_keyword_is(Keyword::AS)?;
-
-        let begin_token = self.expect_keyword(Keyword::BEGIN)?;
-        let statements = self.parse_statement_list(&[Keyword::END])?;
-        let end_token = self.expect_keyword(Keyword::END)?;
+        let _ = self.parse_keyword(Keyword::AS);
 
-        let function_body = Some(CreateFunctionBody::AsBeginEnd(BeginEndStatements {
-            begin_token: AttachedToken(begin_token),
-            statements,
-            end_token: AttachedToken(end_token),
-        }));
+        let function_body = if self.peek_keyword(Keyword::BEGIN) {
+            let begin_token = self.expect_keyword(Keyword::BEGIN)?;
+            let statements = self.parse_statement_list(&[Keyword::END])?;
+            let end_token = self.expect_keyword(Keyword::END)?;
+            Some(CreateFunctionBody::AsBeginEnd(BeginEndStatements {
+                begin_token: AttachedToken(begin_token),
+                statements,
+                end_token: AttachedToken(end_token),
+            }))
+        } else if self.peek_keyword(Keyword::RETURN) {
+            self.expect_keyword(Keyword::RETURN)?;
+            let return_expr = self.parse_mssql_return_expr()?;
+            Some(CreateFunctionBody::Return(return_expr))
+        } else {
+            return self.expected("BEGIN or RETURN after CREATE FUNCTION body", self.peek_token());
+        };
 
         Ok(Statement::CreateFunction(CreateFunction {
             or_alter,
@@ -5238,6 +5245,45 @@ impl<'a> Parser<'a> {
         }))
     }
 
+    fn parse_mssql_return_type(&mut self) -> Result<DataType, ParserError> {
+        if self.peek_keyword(Keyword::TABLE) {
+            self.expect_keyword(Keyword::TABLE)?;
+            let columns = self.parse_optional_returns_table_columns()?;
+            return Ok(DataType::Table(TableDataType {
+                table_variable: None,
+                columns,
+            }));
+        }
+
+        if matches!(self.peek_token().token, Token::Word(_))
+            && matches!(
+                self.peek_nth_token_ref(1).token,
+                Token::Word(Word {
+                    keyword: Keyword::TABLE,
+                    ..
+                })
+            )
+        {
+            let table_variable = self.parse_identifier()?;
+            self.expect_keyword(Keyword::TABLE)?;
+            let columns = self.parse_returns_table_columns()?;
+            return Ok(DataType::Table(TableDataType {
+                table_variable: Some(table_variable),
+                columns,
+            }));
+        }
+
+        self.parse_data_type()
+    }
+
+    fn parse_mssql_return_expr(&mut self) -> Result<Expr, ParserError> {
+        if self.peek_keyword(Keyword::SELECT) || self.peek_keyword(Keyword::WITH) {
+            Ok(Expr::Subquery(self.parse_query()?))
+        } else {
+            self.parse_expr()
+        }
+    }
+
     fn parse_create_function_name_and_params(
         &mut self,
     ) -> Result<(ObjectName, Vec<OperateFunctionArg>), ParserError> {
@@ -9839,13 +9885,7 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_returns_table_column(&mut self) -> Result<ColumnDef, ParserError> {
-        let name = self.parse_identifier()?;
-        let data_type = self.parse_data_type()?;
-        Ok(ColumnDef {
-            name,
-            data_type,
-            options: Vec::new(), // No constraints expected here
-        })
+        self.parse_column_def()
     }
 
     fn parse_returns_table_columns(&mut self) -> Result<Vec<ColumnDef>, ParserError> {
@@ -9855,6 +9895,15 @@ impl<'a> Parser<'a> {
         Ok(columns)
     }
 
+    fn parse_optional_returns_table_columns(&mut self) -> Result<Vec<ColumnDef>, ParserError> {
+        if !self.consume_token(&Token::LParen) {
+            return Ok(Vec::new());
+        }
+        let columns = self.parse_comma_separated(Parser::parse_column_def)?;
+        self.expect_token(&Token::RParen)?;
+        Ok(columns)
+    }
+
     pub fn parse_string_values(&mut self) -> Result<Vec<String>, ParserError> {
         self.expect_token(&Token::LParen)?;
         let mut values = Vec::new();
diff --git a/tmp/agent-patch-flux-pr-1839.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 7b3769e..c9dfccc 100644
--- a/tmp/agent-patch-flux-pr-1839.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
@@ -288,6 +288,73 @@ fn parse_create_function() {
         END\
     ";
     let _ = ms().verified_stmt(create_function_with_return_expression);
+
+    let returns_without_as = "\
+        CREATE FUNCTION some_scalar_udf(@foo INT) \
+        RETURNS INT \
+        RETURN @foo; \
+    ";
+    let _ = ms().verified_stmt(returns_without_as);
+
+    let begin_without_as = "\
+        CREATE FUNCTION some_scalar_udf(@foo INT) \
+        RETURNS INT \
+        BEGIN \
+            RETURN @foo; \
+        END \
+    ";
+    let _ = ms().verified_stmt(begin_without_as);
+}
+
+#[test]
+fn parse_mssql_inline_table_valued_function() {
+    let sql = "\
+        CREATE FUNCTION dbo.inline_values() \
+        RETURNS TABLE \
+        RETURN SELECT Id, Name FROM dbo.Users;
+    ";
+    match ms().verified_stmt(sql) {
+        Statement::CreateFunction(CreateFunction {
+            return_type: Some(DataType::Table(table)),
+            function_body: Some(CreateFunctionBody::Return(Expr::Subquery(_))),
+            ..
+        }) => {
+            assert!(table.table_variable.is_none());
+            assert!(table.columns.is_empty());
+        }
+        other => panic!("expected CREATE FUNCTION, got {other:?}"),
+    }
+}
+
+#[test]
+fn parse_mssql_multi_statement_table_valued_function() {
+    let sql = "\
+        CREATE FUNCTION dbo.multi_table(@value INT) \
+        RETURNS @result TABLE (id INT NOT NULL, name VARCHAR(50) PRIMARY KEY) \
+        BEGIN \
+            INSERT INTO @result (id, name) VALUES (@value, 'name'); \
+            RETURN; \
+        END \
+    ";
+    match ms().verified_stmt(sql) {
+        Statement::CreateFunction(CreateFunction {
+            return_type: Some(DataType::Table(table)),
+            function_body: Some(CreateFunctionBody::AsBeginEnd(_)),
+            ..
+        }) => {
+            assert_eq!(table.table_variable, Some(Ident::new("@result")));
+            assert_eq!(table.columns.len(), 2);
+            assert!(table.columns[0]
+                .options
+                .iter()
+                .any(|option| matches!(option.option, ColumnOption::NotNull)));
+            assert!(table.columns[1]
+                .options
+                .iter()
+                .any(|option| matches!(option.option, ColumnOption::Unique { is_primary: true, .. })));
+        }
+        other => panic!("expected CREATE FUNCTION, got {other:?}"),
+    }
 }
 
 #[test]