STET

agent_patch

runs/2026-02-28__18-27-22__gpt-5-4/flux-pr-1526/flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/agent-logs/agent.patch

11103 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/mod.rs b/app/src/ast/mod.rs
index ad59f07..635f4df 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -60,8 +60,9 @@ pub use self::query::{
     OrderBy, OrderByExpr, PivotValueSource, ProjectionSelect, Query, RenameSelectItem,
     RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
     SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table,
-    TableAlias, TableFactor, TableFunctionArgs, TableVersion, TableWithJoins, Top, TopQuantity,
-    ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
+    TableAlias, TableAliasColumnDef, TableFactor, TableFunctionArgs, TableVersion,
+    TableWithJoins, Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With,
+    WithFill,
 };
 
 pub use self::trigger::{
diff --git a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/query.rs b/app/src/ast/query.rs
index 2160da0..7e7081e 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/query.rs
+++ b/app/src/ast/query.rs
@@ -544,7 +544,7 @@ 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.
@@ -1592,12 +1592,39 @@ impl fmt::Display for TableFactor {
     }
 }
 
+#[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 From<Ident> for TableAliasColumnDef {
+    fn from(name: Ident) -> Self {
+        Self {
+            name,
+            data_type: None,
+        }
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub struct TableAlias {
     pub name: Ident,
-    pub columns: Vec<Ident>,
+    pub columns: Vec<TableAliasColumnDef>,
 }
 
 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-4/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 0c82378..8a7e307 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -8261,8 +8261,9 @@ 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
-    /// the alias is allowed to optionally name the columns in the table, in
+    /// like in `... FROM generate_series(1, 10) AS t (col)` or
+    /// `... 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(
         &mut self,
@@ -8270,7 +8271,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, false)?;
                 Ok(Some(TableAlias { name, columns }))
             }
             None => Ok(None),
@@ -8607,6 +8608,41 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Parse a parenthesized comma-separated list of table alias columns, where each
+    /// item may optionally specify a data type.
+    pub fn parse_parenthesized_table_alias_columns(
+        &mut self,
+        optional: IsOptional,
+        allow_empty: bool,
+    ) -> Result<Vec<TableAliasColumnDef>, ParserError> {
+        if self.consume_token(&Token::LParen) {
+            if allow_empty && self.peek_token().token == Token::RParen {
+                self.next_token();
+                Ok(vec![])
+            } else {
+                let cols = self.parse_comma_separated(Parser::parse_table_alias_column_def)?;
+                self.expect_token(&Token::RParen)?;
+                Ok(cols)
+            }
+        } else if optional == Optional {
+            Ok(vec![])
+        } else {
+            self.expected(
+                "a list of table alias 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 +9183,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 +9210,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, false)?;
             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-4/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index 283071e..161708a 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -553,7 +553,11 @@ 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::from(Ident::new("A")),
+                        TableAliasColumnDef::from(Ident::new("B")),
+                        TableAliasColumnDef::from(Ident::new("C")),
+                    ],
                 }),
                 args: None,
                 with_hints: vec![],
@@ -6372,7 +6376,10 @@ 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::from(Ident::new("col1")),
+            TableAliasColumnDef::from(Ident::new("col2")),
+        ],
         query
             .with
             .unwrap()
@@ -6401,10 +6408,10 @@ fn parse_recursive_cte() {
                 value: "nums".to_string(),
                 quote_style: None,
             },
-            columns: vec![Ident {
+            columns: vec![TableAliasColumnDef::from(Ident {
                 value: "val".to_string(),
                 quote_style: None,
-            }],
+            })],
         },
         query: Box::new(cte_query),
         from: None,
@@ -9347,7 +9354,10 @@ fn parse_pivot_table() {
                     value: "p".to_string(),
                     quote_style: None
                 },
-                columns: vec![Ident::new("c"), Ident::new("d")],
+                columns: vec![
+                    TableAliasColumnDef::from(Ident::new("c")),
+                    TableAliasColumnDef::from(Ident::new("d")),
+                ],
             }),
         }
     );
diff --git a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index a6c480c..1bce604 100644
--- a/tmp/agent-patch-flux-pr-1526.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -4500,6 +4500,57 @@ fn test_table_function_with_ordinality() {
     }
 }
 
+#[test]
+fn parse_typed_table_alias_columns() {
+    let sql = "SELECT * FROM generate_series(1, 10) AS t(val INT, label TEXT)";
+    let from = pg().verified_only_select(sql).from;
+    assert_eq!(1, from.len());
+    match &from[0].relation {
+        TableFactor::Table {
+            alias: Some(alias), ..
+        } => {
+            assert_eq!(alias.name, Ident::new("t"));
+            assert_eq!(
+                alias.columns,
+                vec![
+                    TableAliasColumnDef {
+                        name: Ident::new("val"),
+                        data_type: Some(DataType::Int(None)),
+                    },
+                    TableAliasColumnDef {
+                        name: Ident::new("label"),
+                        data_type: Some(DataType::Text),
+                    },
+                ]
+            );
+        }
+        _ => panic!("Expecting TableFactor::Table with alias"),
+    }
+    assert_eq!(pg().verified_stmt(sql).to_string(), sql);
+}
+
+#[test]
+fn parse_typed_cte_alias_columns() {
+    let sql = "WITH cte(id INT, name TEXT) AS (SELECT 1, 'a') SELECT * FROM cte";
+    let query = pg().verified_query(sql);
+    let alias = &query.with.as_ref().unwrap().cte_tables[0].alias;
+    assert_eq!(alias.name, Ident::new("cte"));
+    assert_eq!(
+        alias.columns,
+        vec![
+            TableAliasColumnDef {
+                name: Ident::new("id"),
+                data_type: Some(DataType::Int(None)),
+            },
+            TableAliasColumnDef {
+                name: Ident::new("name"),
+                data_type: Some(DataType::Text),
+            },
+        ]
+    );
+    assert_eq!(query.to_string(), sql);
+}
+
 #[test]
 fn test_table_unnest_with_ordinality() {
     let from = pg_and_generic()