Skip to content

Commit 1be060f

Browse files
committed
This adds support for
```sql SELECT * FROM some_fun() AS x (a TEXT, b INT) ``` fixes apache#1524
1 parent 724a1d1 commit 1be060f

File tree

4 files changed

+106
-14
lines changed

4 files changed

+106
-14
lines changed

src/ast/mod.rs

+2-2
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

+35-1
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,40 @@ 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+
pub fn from_column_name(name: &str) -> Self {
1630+
TableAliasColumnDef {
1631+
name: Ident::new(name),
1632+
data_type: None,
1633+
}
1634+
}
1635+
}
1636+
1637+
impl fmt::Display for TableAliasColumnDef {
1638+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1639+
write!(f, "{}", self.name)?;
1640+
if let Some(ref data_type) = self.data_type {
1641+
write!(f, " {}", data_type)?;
1642+
}
1643+
Ok(())
1644+
}
1645+
}
1646+
16131647
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
16141648
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16151649
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/parser/mod.rs

+19-2
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,23 @@ impl<'a> Parser<'a> {
86078607
}
86088608
}
86098609

8610+
/// Parse a parenthesized comma-separated list of unqualified, possibly quoted identifiers
8611+
pub fn parse_table_alias_column_defs(
8612+
&mut self,
8613+
) -> Result<Vec<TableAliasColumnDef>, ParserError> {
8614+
if self.consume_token(&Token::LParen) {
8615+
let cols = self.parse_comma_separated(|p| {
8616+
let name = p.parse_identifier(false)?;
8617+
let data_type = p.maybe_parse(|p| p.parse_data_type())?;
8618+
Ok(TableAliasColumnDef { name, data_type })
8619+
})?;
8620+
self.expect_token(&Token::RParen)?;
8621+
Ok(cols)
8622+
} else {
8623+
Ok(vec![])
8624+
}
8625+
}
8626+
86108627
pub fn parse_precision(&mut self) -> Result<u64, ParserError> {
86118628
self.expect_token(&Token::LParen)?;
86128629
let n = self.parse_literal_uint()?;
@@ -9174,7 +9191,7 @@ impl<'a> Parser<'a> {
91749191
materialized: is_materialized,
91759192
}
91769193
} else {
9177-
let columns = self.parse_parenthesized_column_list(Optional, false)?;
9194+
let columns = self.parse_table_alias_column_defs()?;
91789195
self.expect_keyword(Keyword::AS)?;
91799196
let mut is_materialized = None;
91809197
if dialect_of!(self is PostgreSqlDialect) {

tests/sqlparser_common.rs

+50-9
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,11 @@ fn parse_select_with_table_alias() {
532532
name: ObjectName(vec![Ident::new("lineitem")]),
533533
alias: Some(TableAlias {
534534
name: Ident::new("l"),
535-
columns: vec![Ident::new("A"), Ident::new("B"), Ident::new("C"),],
535+
columns: vec![
536+
TableAliasColumnDef::from_column_name("A"),
537+
TableAliasColumnDef::from_column_name("B"),
538+
TableAliasColumnDef::from_column_name("C"),
539+
],
536540
}),
537541
args: None,
538542
with_hints: vec![],
@@ -5576,6 +5580,40 @@ fn parse_table_function() {
55765580
);
55775581
}
55785582

5583+
#[test]
5584+
fn parse_table_valued_function_with_alias_and_column_defs() {
5585+
let sql = r#"SELECT * FROM jsonb_to_record('{"a": "x", "b": 2}'::JSONB) AS x (a TEXT, b INT)"#;
5586+
let select = verified_only_select(sql);
5587+
5588+
match only(&select.from) {
5589+
TableWithJoins {
5590+
relation: TableFactor::Table {
5591+
alias: Some(alias), ..
5592+
},
5593+
..
5594+
} => {
5595+
assert_eq!(alias.name.value, "x");
5596+
assert_eq!(
5597+
alias.columns,
5598+
vec![
5599+
TableAliasColumnDef {
5600+
name: Ident::new("a"),
5601+
data_type: Some(DataType::Text),
5602+
},
5603+
TableAliasColumnDef {
5604+
name: Ident::new("b"),
5605+
data_type: Some(DataType::Int(None)),
5606+
},
5607+
]
5608+
);
5609+
}
5610+
_ => unreachable!(
5611+
"Expecting only TableWithJoins with TableFactor::Table, got {:#?}",
5612+
select.from
5613+
),
5614+
}
5615+
}
5616+
55795617
#[test]
55805618
fn parse_unnest() {
55815619
let sql = "SELECT UNNEST(make_array(1, 2, 3))";
@@ -6335,7 +6373,10 @@ fn parse_cte_renamed_columns() {
63356373
let sql = "WITH cte (col1, col2) AS (SELECT foo, bar FROM baz) SELECT * FROM cte";
63366374
let query = all_dialects().verified_query(sql);
63376375
assert_eq!(
6338-
vec![Ident::new("col1"), Ident::new("col2")],
6376+
vec![
6377+
TableAliasColumnDef::from_column_name("col1"),
6378+
TableAliasColumnDef::from_column_name("col2")
6379+
],
63396380
query
63406381
.with
63416382
.unwrap()
@@ -6364,10 +6405,7 @@ fn parse_recursive_cte() {
63646405
value: "nums".to_string(),
63656406
quote_style: None,
63666407
},
6367-
columns: vec![Ident {
6368-
value: "val".to_string(),
6369-
quote_style: None,
6370-
}],
6408+
columns: vec![TableAliasColumnDef::from_column_name("val")],
63716409
},
63726410
query: Box::new(cte_query),
63736411
from: None,
@@ -9310,7 +9348,10 @@ fn parse_pivot_table() {
93109348
value: "p".to_string(),
93119349
quote_style: None
93129350
},
9313-
columns: vec![Ident::new("c"), Ident::new("d")],
9351+
columns: vec![
9352+
TableAliasColumnDef::from_column_name("c"),
9353+
TableAliasColumnDef::from_column_name("d"),
9354+
],
93149355
}),
93159356
}
93169357
);
@@ -9371,8 +9412,8 @@ fn parse_unpivot_table() {
93719412
name: Ident::new("u"),
93729413
columns: ["product", "quarter", "quantity"]
93739414
.into_iter()
9374-
.map(Ident::new)
9375-
.collect()
9415+
.map(TableAliasColumnDef::from_column_name)
9416+
.collect(),
93769417
}),
93779418
}
93789419
);

0 commit comments

Comments
 (0)