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
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]