Skip to content

Commit f961efc

Browse files
authored
support column type definitions in table aliases (#1526)
1 parent 4c629e8 commit f961efc

File tree

4 files changed

+105
-14
lines changed

4 files changed

+105
-14
lines changed

src/ast/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ pub use self::query::{
6060
OrderBy, OrderByExpr, PivotValueSource, ProjectionSelect, Query, RenameSelectItem,
6161
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
6262
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table,
63-
TableAlias, TableFactor, TableFunctionArgs, TableVersion, TableWithJoins, Top, TopQuantity,
64-
ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
63+
TableAlias, TableAliasColumnDef, TableFactor, TableFunctionArgs, TableVersion, TableWithJoins,
64+
Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
6565
};
6666

6767
pub use self::trigger::{

src/ast/query.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1597,7 +1597,7 @@ impl fmt::Display for TableFactor {
15971597
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
15981598
pub struct TableAlias {
15991599
pub name: Ident,
1600-
pub columns: Vec<Ident>,
1600+
pub columns: Vec<TableAliasColumnDef>,
16011601
}
16021602

16031603
impl fmt::Display for TableAlias {
@@ -1610,6 +1610,41 @@ impl fmt::Display for TableAlias {
16101610
}
16111611
}
16121612

1613+
/// SQL column definition in a table expression alias.
1614+
/// Most of the time, the data type is not specified.
1615+
/// But some table-valued functions do require specifying the data type.
1616+
///
1617+
/// See <https://www.postgresql.org/docs/17/queries-table-expressions.html#QUERIES-TABLEFUNCTIONS>
1618+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1619+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1620+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1621+
pub struct TableAliasColumnDef {
1622+
/// Column name alias
1623+
pub name: Ident,
1624+
/// Some table-valued functions require specifying the data type in the alias.
1625+
pub data_type: Option<DataType>,
1626+
}
1627+
1628+
impl TableAliasColumnDef {
1629+
/// Create a new table alias column definition with only a name and no type
1630+
pub fn from_name<S: Into<String>>(name: S) -> Self {
1631+
TableAliasColumnDef {
1632+
name: Ident::new(name),
1633+
data_type: None,
1634+
}
1635+
}
1636+
}
1637+
1638+
impl fmt::Display for TableAliasColumnDef {
1639+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1640+
write!(f, "{}", self.name)?;
1641+
if let Some(ref data_type) = self.data_type {
1642+
write!(f, " {}", data_type)?;
1643+
}
1644+
Ok(())
1645+
}
1646+
}
1647+
16131648
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
16141649
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16151650
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/parser/mod.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8270,7 +8270,7 @@ impl<'a> Parser<'a> {
82708270
) -> Result<Option<TableAlias>, ParserError> {
82718271
match self.parse_optional_alias(reserved_kwds)? {
82728272
Some(name) => {
8273-
let columns = self.parse_parenthesized_column_list(Optional, false)?;
8273+
let columns = self.parse_table_alias_column_defs()?;
82748274
Ok(Some(TableAlias { name, columns }))
82758275
}
82768276
None => Ok(None),
@@ -8607,6 +8607,21 @@ impl<'a> Parser<'a> {
86078607
}
86088608
}
86098609

8610+
/// Parse a parenthesized comma-separated list of table alias column definitions.
8611+
fn parse_table_alias_column_defs(&mut self) -> Result<Vec<TableAliasColumnDef>, ParserError> {
8612+
if self.consume_token(&Token::LParen) {
8613+
let cols = self.parse_comma_separated(|p| {
8614+
let name = p.parse_identifier(false)?;
8615+
let data_type = p.maybe_parse(|p| p.parse_data_type())?;
8616+
Ok(TableAliasColumnDef { name, data_type })
8617+
})?;
8618+
self.expect_token(&Token::RParen)?;
8619+
Ok(cols)
8620+
} else {
8621+
Ok(vec![])
8622+
}
8623+
}
8624+
86108625
pub fn parse_precision(&mut self) -> Result<u64, ParserError> {
86118626
self.expect_token(&Token::LParen)?;
86128627
let n = self.parse_literal_uint()?;
@@ -9174,7 +9189,7 @@ impl<'a> Parser<'a> {
91749189
materialized: is_materialized,
91759190
}
91769191
} else {
9177-
let columns = self.parse_parenthesized_column_list(Optional, false)?;
9192+
let columns = self.parse_table_alias_column_defs()?;
91789193
self.expect_keyword(Keyword::AS)?;
91799194
let mut is_materialized = None;
91809195
if dialect_of!(self is PostgreSqlDialect) {

tests/sqlparser_common.rs

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,11 @@ fn parse_select_with_table_alias() {
553553
name: ObjectName(vec![Ident::new("lineitem")]),
554554
alias: Some(TableAlias {
555555
name: Ident::new("l"),
556-
columns: vec![Ident::new("A"), Ident::new("B"), Ident::new("C"),],
556+
columns: vec![
557+
TableAliasColumnDef::from_name("A"),
558+
TableAliasColumnDef::from_name("B"),
559+
TableAliasColumnDef::from_name("C"),
560+
],
557561
}),
558562
args: None,
559563
with_hints: vec![],
@@ -5597,6 +5601,40 @@ fn parse_table_function() {
55975601
);
55985602
}
55995603

5604+
#[test]
5605+
fn parse_select_with_alias_and_column_defs() {
5606+
let sql = r#"SELECT * FROM jsonb_to_record('{"a": "x", "b": 2}'::JSONB) AS x (a TEXT, b INT)"#;
5607+
let select = verified_only_select(sql);
5608+
5609+
match only(&select.from) {
5610+
TableWithJoins {
5611+
relation: TableFactor::Table {
5612+
alias: Some(alias), ..
5613+
},
5614+
..
5615+
} => {
5616+
assert_eq!(alias.name.value, "x");
5617+
assert_eq!(
5618+
alias.columns,
5619+
vec![
5620+
TableAliasColumnDef {
5621+
name: Ident::new("a"),
5622+
data_type: Some(DataType::Text),
5623+
},
5624+
TableAliasColumnDef {
5625+
name: Ident::new("b"),
5626+
data_type: Some(DataType::Int(None)),
5627+
},
5628+
]
5629+
);
5630+
}
5631+
_ => unreachable!(
5632+
"Expecting only TableWithJoins with TableFactor::Table, got {:#?}",
5633+
select.from
5634+
),
5635+
}
5636+
}
5637+
56005638
#[test]
56015639
fn parse_unnest() {
56025640
let sql = "SELECT UNNEST(make_array(1, 2, 3))";
@@ -6372,7 +6410,10 @@ fn parse_cte_renamed_columns() {
63726410
let sql = "WITH cte (col1, col2) AS (SELECT foo, bar FROM baz) SELECT * FROM cte";
63736411
let query = all_dialects().verified_query(sql);
63746412
assert_eq!(
6375-
vec![Ident::new("col1"), Ident::new("col2")],
6413+
vec![
6414+
TableAliasColumnDef::from_name("col1"),
6415+
TableAliasColumnDef::from_name("col2")
6416+
],
63766417
query
63776418
.with
63786419
.unwrap()
@@ -6401,10 +6442,7 @@ fn parse_recursive_cte() {
64016442
value: "nums".to_string(),
64026443
quote_style: None,
64036444
},
6404-
columns: vec![Ident {
6405-
value: "val".to_string(),
6406-
quote_style: None,
6407-
}],
6445+
columns: vec![TableAliasColumnDef::from_name("val")],
64086446
},
64096447
query: Box::new(cte_query),
64106448
from: None,
@@ -9347,7 +9385,10 @@ fn parse_pivot_table() {
93479385
value: "p".to_string(),
93489386
quote_style: None
93499387
},
9350-
columns: vec![Ident::new("c"), Ident::new("d")],
9388+
columns: vec![
9389+
TableAliasColumnDef::from_name("c"),
9390+
TableAliasColumnDef::from_name("d"),
9391+
],
93519392
}),
93529393
}
93539394
);
@@ -9408,8 +9449,8 @@ fn parse_unpivot_table() {
94089449
name: Ident::new("u"),
94099450
columns: ["product", "quarter", "quantity"]
94109451
.into_iter()
9411-
.map(Ident::new)
9412-
.collect()
9452+
.map(TableAliasColumnDef::from_name)
9453+
.collect(),
94139454
}),
94149455
}
94159456
);

0 commit comments

Comments
 (0)