Skip to content

Commit 9abcac3

Browse files
committed
Support aliasing columns
A table alias can specify new names for the columns within the aliased table, in addition to a new name for the table itself.
1 parent 73ed685 commit 9abcac3

File tree

4 files changed

+60
-14
lines changed

4 files changed

+60
-14
lines changed

src/sqlast/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::ops::Deref;
2525
pub use self::ddl::{AlterTableOperation, TableConstraint};
2626
pub use self::query::{
2727
Cte, Fetch, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect,
28-
SQLSelectItem, SQLSetExpr, SQLSetOperator, SQLValues, TableFactor,
28+
SQLSelectItem, SQLSetExpr, SQLSetOperator, SQLValues, TableAlias, TableFactor,
2929
};
3030
pub use self::sqltype::SQLType;
3131
pub use self::value::Value;

src/sqlast/query.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ impl ToString for SQLSelectItem {
202202
pub enum TableFactor {
203203
Table {
204204
name: SQLObjectName,
205-
alias: Option<SQLIdent>,
205+
alias: Option<TableAlias>,
206206
/// Arguments of a table-valued function, as supported by Postgres
207207
/// and MSSQL. Note that deprecated MSSQL `FROM foo (NOLOCK)` syntax
208208
/// will also be parsed as `args`.
@@ -213,7 +213,7 @@ pub enum TableFactor {
213213
Derived {
214214
lateral: bool,
215215
subquery: Box<SQLQuery>,
216-
alias: Option<SQLIdent>,
216+
alias: Option<TableAlias>,
217217
},
218218
}
219219

@@ -231,7 +231,7 @@ impl ToString for TableFactor {
231231
s += &format!("({})", comma_separated_string(args))
232232
};
233233
if let Some(alias) = alias {
234-
s += &format!(" AS {}", alias);
234+
s += &format!(" AS {}", alias.to_string());
235235
}
236236
if !with_hints.is_empty() {
237237
s += &format!(" WITH ({})", comma_separated_string(with_hints));
@@ -249,14 +249,30 @@ impl ToString for TableFactor {
249249
}
250250
s += &format!("({})", subquery.to_string());
251251
if let Some(alias) = alias {
252-
s += &format!(" AS {}", alias);
252+
s += &format!(" AS {}", alias.to_string());
253253
}
254254
s
255255
}
256256
}
257257
}
258258
}
259259

260+
#[derive(Debug, Clone, PartialEq, Hash)]
261+
pub struct TableAlias {
262+
pub name: SQLIdent,
263+
pub columns: Vec<SQLIdent>,
264+
}
265+
266+
impl ToString for TableAlias {
267+
fn to_string(&self) -> String {
268+
let mut s = self.name.clone();
269+
if !self.columns.is_empty() {
270+
s += &format!(" ({})", comma_separated_string(&self.columns));
271+
}
272+
s
273+
}
274+
}
275+
260276
#[derive(Debug, Clone, PartialEq, Hash)]
261277
pub struct Join {
262278
pub relation: TableFactor,

src/sqlparser.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,23 @@ impl Parser {
12081208
}
12091209
}
12101210

1211+
/// Parse `AS identifier` when the AS is describing a table-valued object,
1212+
/// like in `... FROM generate_series(1, 10) AS t (col)`. In this case
1213+
/// the alias is allowed to optionally name the columns in the table, in
1214+
/// addition to the table itself.
1215+
pub fn parse_optional_table_alias(
1216+
&mut self,
1217+
reserved_kwds: &[&str],
1218+
) -> Result<Option<TableAlias>, ParserError> {
1219+
match self.parse_optional_alias(reserved_kwds)? {
1220+
Some(name) => {
1221+
let columns = self.parse_parenthesized_column_list(Optional)?;
1222+
Ok(Some(TableAlias { name, columns }))
1223+
}
1224+
None => Ok(None),
1225+
}
1226+
}
1227+
12111228
/// Parse one or more identifiers with the specified separator between them
12121229
pub fn parse_list_of_ids(&mut self, separator: &Token) -> Result<Vec<SQLIdent>, ParserError> {
12131230
let mut idents = vec![];
@@ -1491,7 +1508,7 @@ impl Parser {
14911508
if self.consume_token(&Token::LParen) {
14921509
let subquery = Box::new(self.parse_query()?);
14931510
self.expect_token(&Token::RParen)?;
1494-
let alias = self.parse_optional_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
1511+
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
14951512
Ok(TableFactor::Derived {
14961513
lateral,
14971514
subquery,
@@ -1507,7 +1524,7 @@ impl Parser {
15071524
} else {
15081525
vec![]
15091526
};
1510-
let alias = self.parse_optional_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
1527+
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
15111528
// MSSQL-specific table hints:
15121529
let mut with_hints = vec![];
15131530
if self.parse_keyword("WITH") {

tests/sqlparser_common.rs

+20-7
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ fn parse_delimited_identifiers() {
10771077
with_hints,
10781078
} => {
10791079
assert_eq!(vec![r#""a table""#.to_string()], name.0);
1080-
assert_eq!(r#""alias""#, alias.unwrap());
1080+
assert_eq!(r#""alias""#, alias.unwrap().name);
10811081
assert!(args.is_empty());
10821082
assert!(with_hints.is_empty());
10831083
}
@@ -1230,11 +1230,18 @@ fn parse_cross_join() {
12301230
);
12311231
}
12321232

1233+
fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
1234+
Some(TableAlias {
1235+
name: name.into(),
1236+
columns: vec![],
1237+
})
1238+
}
1239+
12331240
#[test]
12341241
fn parse_joins_on() {
12351242
fn join_with_constraint(
12361243
relation: impl Into<String>,
1237-
alias: Option<SQLIdent>,
1244+
alias: Option<TableAlias>,
12381245
f: impl Fn(JoinConstraint) -> JoinOperator,
12391246
) -> Join {
12401247
Join {
@@ -1256,7 +1263,7 @@ fn parse_joins_on() {
12561263
verified_only_select("SELECT * FROM t1 JOIN t2 AS foo ON c1 = c2").joins,
12571264
vec![join_with_constraint(
12581265
"t2",
1259-
Some("foo".to_string()),
1266+
table_alias("foo"),
12601267
JoinOperator::Inner
12611268
)]
12621269
);
@@ -1287,7 +1294,7 @@ fn parse_joins_on() {
12871294
fn parse_joins_using() {
12881295
fn join_with_constraint(
12891296
relation: impl Into<String>,
1290-
alias: Option<SQLIdent>,
1297+
alias: Option<TableAlias>,
12911298
f: impl Fn(JoinConstraint) -> JoinOperator,
12921299
) -> Join {
12931300
Join {
@@ -1305,7 +1312,7 @@ fn parse_joins_using() {
13051312
verified_only_select("SELECT * FROM t1 JOIN t2 AS foo USING(c1)").joins,
13061313
vec![join_with_constraint(
13071314
"t2",
1308-
Some("foo".to_string()),
1315+
table_alias("foo"),
13091316
JoinOperator::Inner
13101317
)]
13111318
);
@@ -1465,6 +1472,12 @@ fn parse_derived_tables() {
14651472
let sql = "SELECT a.x, b.y FROM (SELECT x FROM foo) AS a CROSS JOIN (SELECT y FROM bar) AS b";
14661473
let _ = verified_only_select(sql);
14671474
//TODO: add assertions
1475+
1476+
let sql = "SELECT a.x, b.y \
1477+
FROM (SELECT x FROM foo) AS a (x) \
1478+
CROSS JOIN (SELECT y FROM bar) AS b (y)";
1479+
let _ = verified_only_select(sql);
1480+
//TODO: add assertions
14681481
}
14691482

14701483
#[test]
@@ -1947,11 +1960,11 @@ fn lateral_derived() {
19471960
if let TableFactor::Derived {
19481961
lateral,
19491962
ref subquery,
1950-
ref alias,
1963+
alias: Some(ref alias),
19511964
} = select.joins[0].relation
19521965
{
19531966
assert_eq!(lateral_in, lateral);
1954-
assert_eq!(Some("order".to_string()), *alias);
1967+
assert_eq!("order".to_string(), alias.name);
19551968
assert_eq!(
19561969
subquery.to_string(),
19571970
"SELECT * FROM order WHERE order.customer = customer.id LIMIT 3"

0 commit comments

Comments
 (0)