agent_patch
runs/2026-02-28__18-27-22__gpt-5-3-codex/flux-pr-1526/flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/agent-logs/agent.patch
10091 bytes
diff --git a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/query.rs b/app/src/ast/query.rs
index 2160da0..50bb045 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/query.rs
+++ b/app/src/ast/query.rs
@@ -544,7 +544,8 @@ impl fmt::Display for CteAsMaterialized {
}
}
-/// A single CTE (used after `WITH`): `<alias> [(col1, col2, ...)] AS <materialized> ( <query> )`
+/// A single CTE (used after `WITH`):
+/// `<alias> [(col1 [type], col2 [type], ...)] AS <materialized> ( <query> )`
/// The names in the column list before `AS`, when specified, replace the names
/// of the columns returned by the query. The parser does not validate that the
/// number of columns in the query matches the number of columns in the query.
@@ -1597,7 +1598,25 @@ impl fmt::Display for TableFactor {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct TableAlias {
pub name: Ident,
- pub columns: Vec<Ident>,
+ pub columns: Vec<TableAliasColumnDef>,
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct TableAliasColumnDef {
+ pub name: Ident,
+ pub data_type: Option<DataType>,
+}
+
+impl fmt::Display for TableAliasColumnDef {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.name)?;
+ if let Some(data_type) = &self.data_type {
+ write!(f, " {data_type}")?;
+ }
+ Ok(())
+ }
}
impl fmt::Display for TableAlias {
diff --git a/tmp/agent-patch-flux-pr-1526.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 0c82378..c770c0f 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -8261,7 +8261,7 @@ impl<'a> Parser<'a> {
}
/// Parse `AS identifier` when the AS is describing a table-valued object,
- /// like in `... FROM generate_series(1, 10) AS t (col)`. In this case
+ /// like in `... FROM generate_series(1, 10) AS t (col int)`. In this case
/// the alias is allowed to optionally name the columns in the table, in
/// addition to the table itself.
pub fn parse_optional_table_alias(
@@ -8270,7 +8270,7 @@ impl<'a> Parser<'a> {
) -> Result<Option<TableAlias>, ParserError> {
match self.parse_optional_alias(reserved_kwds)? {
Some(name) => {
- let columns = self.parse_parenthesized_column_list(Optional, false)?;
+ let columns = self.parse_parenthesized_table_alias_columns(Optional)?;
Ok(Some(TableAlias { name, columns }))
}
None => Ok(None),
@@ -8607,6 +8607,32 @@ impl<'a> Parser<'a> {
}
}
+ /// Parse a parenthesized comma-separated list of table alias columns:
+ /// `<col_name> [data_type]`
+ fn parse_parenthesized_table_alias_columns(
+ &mut self,
+ optional: IsOptional,
+ ) -> Result<Vec<TableAliasColumnDef>, ParserError> {
+ if self.consume_token(&Token::LParen) {
+ let columns = self.parse_comma_separated(Parser::parse_table_alias_column_def)?;
+ self.expect_token(&Token::RParen)?;
+ Ok(columns)
+ } else if optional == Optional {
+ Ok(vec![])
+ } else {
+ self.expected("a list of columns in parentheses", self.peek_token())
+ }
+ }
+
+ fn parse_table_alias_column_def(&mut self) -> Result<TableAliasColumnDef, ParserError> {
+ let name = self.parse_identifier(false)?;
+ let data_type = match self.peek_token().token {
+ Token::Comma | Token::RParen => None,
+ _ => Some(self.parse_data_type()?),
+ };
+ Ok(TableAliasColumnDef { name, data_type })
+ }
+
pub fn parse_precision(&mut self) -> Result<u64, ParserError> {
self.expect_token(&Token::LParen)?;
let n = self.parse_literal_uint()?;
@@ -9147,7 +9173,7 @@ impl<'a> Parser<'a> {
})
}
- /// Parse a CTE (`alias [( col1, col2, ... )] AS (subquery)`)
+ /// Parse a CTE (`alias [( col1 [type], col2 [type], ... )] AS (subquery)`)
pub fn parse_cte(&mut self) -> Result<Cte, ParserError> {
let name = self.parse_identifier(false)?;
@@ -9174,7 +9200,7 @@ impl<'a> Parser<'a> {
materialized: is_materialized,
}
} else {
- let columns = self.parse_parenthesized_column_list(Optional, false)?;
+ let columns = self.parse_parenthesized_table_alias_columns(Optional)?;
self.expect_keyword(Keyword::AS)?;
let mut is_materialized = None;
if dialect_of!(self is PostgreSqlDialect) {
diff --git a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index 283071e..7bcc561 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -553,7 +553,20 @@ fn parse_select_with_table_alias() {
name: ObjectName(vec![Ident::new("lineitem")]),
alias: Some(TableAlias {
name: Ident::new("l"),
- columns: vec![Ident::new("A"), Ident::new("B"), Ident::new("C"),],
+ columns: vec![
+ TableAliasColumnDef {
+ name: Ident::new("A"),
+ data_type: None,
+ },
+ TableAliasColumnDef {
+ name: Ident::new("B"),
+ data_type: None,
+ },
+ TableAliasColumnDef {
+ name: Ident::new("C"),
+ data_type: None,
+ },
+ ],
}),
args: None,
with_hints: vec![],
@@ -6372,7 +6385,16 @@ fn parse_cte_renamed_columns() {
let sql = "WITH cte (col1, col2) AS (SELECT foo, bar FROM baz) SELECT * FROM cte";
let query = all_dialects().verified_query(sql);
assert_eq!(
- vec![Ident::new("col1"), Ident::new("col2")],
+ vec![
+ TableAliasColumnDef {
+ name: Ident::new("col1"),
+ data_type: None,
+ },
+ TableAliasColumnDef {
+ name: Ident::new("col2"),
+ data_type: None,
+ },
+ ],
query
.with
.unwrap()
@@ -6401,9 +6423,12 @@ fn parse_recursive_cte() {
value: "nums".to_string(),
quote_style: None,
},
- columns: vec![Ident {
- value: "val".to_string(),
- quote_style: None,
+ columns: vec![TableAliasColumnDef {
+ name: Ident {
+ value: "val".to_string(),
+ quote_style: None,
+ },
+ data_type: None,
}],
},
query: Box::new(cte_query),
@@ -9347,7 +9372,16 @@ fn parse_pivot_table() {
value: "p".to_string(),
quote_style: None
},
- columns: vec![Ident::new("c"), Ident::new("d")],
+ columns: vec![
+ TableAliasColumnDef {
+ name: Ident::new("c"),
+ data_type: None,
+ },
+ TableAliasColumnDef {
+ name: Ident::new("d"),
+ data_type: None,
+ },
+ ],
}),
}
);
diff --git a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index a6c480c..3c4cf50 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -4184,6 +4184,50 @@ fn parse_join_constraint_unnest_alias() {
);
}
+#[test]
+fn parse_table_alias_with_typed_columns() {
+ let select = pg().verified_only_select(
+ "SELECT * FROM generate_series(1, 10) AS t(id int, label text)",
+ );
+ let alias = match &only(select.from).relation {
+ TableFactor::TableFunction { alias, .. } => alias.as_ref().unwrap(),
+ _ => panic!("Expected table function"),
+ };
+ assert_eq!(
+ alias.columns,
+ vec![
+ TableAliasColumnDef {
+ name: Ident::new("id"),
+ data_type: Some(DataType::Int(None)),
+ },
+ TableAliasColumnDef {
+ name: Ident::new("label"),
+ data_type: Some(DataType::Text),
+ },
+ ]
+ );
+}
+
+#[test]
+fn parse_cte_with_typed_columns() {
+ let query = pg().verified_query("WITH t(id int, label text) AS (SELECT 1, 'a') SELECT * FROM t");
+ let with = query.with.unwrap();
+ let cte = with.cte_tables.first().unwrap();
+ assert_eq!(
+ cte.alias.columns,
+ vec![
+ TableAliasColumnDef {
+ name: Ident::new("id"),
+ data_type: Some(DataType::Int(None)),
+ },
+ TableAliasColumnDef {
+ name: Ident::new("label"),
+ data_type: Some(DataType::Text),
+ },
+ ]
+ );
+}
+
#[test]
fn test_complex_postgres_insert_with_alias() {
let sql1 = "WITH existing AS (SELECT test_table.id FROM test_tables AS test_table WHERE (a = 12) AND (b = 34)), inserted AS (INSERT INTO test_tables AS test_table (id, a, b, c) VALUES (DEFAULT, 56, 78, 90) ON CONFLICT(a, b) DO UPDATE SET c = EXCLUDED.c WHERE (test_table.c <> EXCLUDED.c)) SELECT c FROM existing";