Skip to content

Commit f4ca760

Browse files
committed
Implicit aliases
Right now `sqlparser-rs` inject the `AS` keyword in front of aliases unconditionally. As reported by #1875 or #1784 this leads to problems on Oracle databases. This patch preserves the original absence / presence of the keyword (implicit/explicit aliases) in "table-factor" position when rendered via `Display`. 1. Some more effort could be invested to apply the same behavior for `SelectItem`s (ie. projections in queries) and further nodes of the AST with an alias for which `AS` is optional. To unify the implementation within the parser and for clients, representing aliases could then be exposed not as pure `Ident`s but maybe as something as: ```rust struct Alias { explicit: bool, name: Ident, } impl Deref for Alias { type Target = Ident; ... } impl From<Alias> for Ident { ... } ``` 2. The parser could be instructed / configured (either by ParserOptions or through a Dialect setting) to always produce "explicit" alias tokens. This would then always produce the `AS` keyword when render via `Display`. Ideally, there would be a `VisitorMut::visit_(mut_)alias` and clients could easily apply their own preference. 3. I'd greatly appreciate a critical look since my know-how regarding different DBs is quite limited. I hope I've not broken any of the existing dialects and also hope this PR helps "preserving the syntax round trip".
1 parent 2b8e99c commit f4ca760

File tree

10 files changed

+202
-269
lines changed

10 files changed

+202
-269
lines changed

src/ast/query.rs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,7 +1902,7 @@ impl fmt::Display for TableFactor {
19021902
write!(f, " {sample}")?;
19031903
}
19041904
if let Some(alias) = alias {
1905-
write!(f, " AS {alias}")?;
1905+
write!(f, " {alias}")?;
19061906
}
19071907
if !index_hints.is_empty() {
19081908
write!(f, " {}", display_separated(index_hints, " "))?;
@@ -1932,7 +1932,7 @@ impl fmt::Display for TableFactor {
19321932
NewLine.fmt(f)?;
19331933
f.write_str(")")?;
19341934
if let Some(alias) = alias {
1935-
write!(f, " AS {alias}")?;
1935+
write!(f, " {alias}")?;
19361936
}
19371937
Ok(())
19381938
}
@@ -1948,14 +1948,14 @@ impl fmt::Display for TableFactor {
19481948
write!(f, "{name}")?;
19491949
write!(f, "({})", display_comma_separated(args))?;
19501950
if let Some(alias) = alias {
1951-
write!(f, " AS {alias}")?;
1951+
write!(f, " {alias}")?;
19521952
}
19531953
Ok(())
19541954
}
19551955
TableFactor::TableFunction { expr, alias } => {
19561956
write!(f, "TABLE({expr})")?;
19571957
if let Some(alias) = alias {
1958-
write!(f, " AS {alias}")?;
1958+
write!(f, " {alias}")?;
19591959
}
19601960
Ok(())
19611961
}
@@ -1973,13 +1973,13 @@ impl fmt::Display for TableFactor {
19731973
}
19741974

19751975
if let Some(alias) = alias {
1976-
write!(f, " AS {alias}")?;
1976+
write!(f, " {alias}")?;
19771977
}
19781978
if *with_offset {
19791979
write!(f, " WITH OFFSET")?;
19801980
}
19811981
if let Some(alias) = with_offset_alias {
1982-
write!(f, " AS {alias}")?;
1982+
write!(f, " {alias}")?;
19831983
}
19841984
Ok(())
19851985
}
@@ -1995,7 +1995,7 @@ impl fmt::Display for TableFactor {
19951995
columns = display_comma_separated(columns)
19961996
)?;
19971997
if let Some(alias) = alias {
1998-
write!(f, " AS {alias}")?;
1998+
write!(f, " {alias}")?;
19991999
}
20002000
Ok(())
20012001
}
@@ -2014,7 +2014,7 @@ impl fmt::Display for TableFactor {
20142014
write!(f, " WITH ({})", display_comma_separated(columns))?;
20152015
}
20162016
if let Some(alias) = alias {
2017-
write!(f, " AS {alias}")?;
2017+
write!(f, " {alias}")?;
20182018
}
20192019
Ok(())
20202020
}
@@ -2024,7 +2024,7 @@ impl fmt::Display for TableFactor {
20242024
} => {
20252025
write!(f, "({table_with_joins})")?;
20262026
if let Some(alias) = alias {
2027-
write!(f, " AS {alias}")?;
2027+
write!(f, " {alias}")?;
20282028
}
20292029
Ok(())
20302030
}
@@ -2051,8 +2051,8 @@ impl fmt::Display for TableFactor {
20512051
write!(f, " DEFAULT ON NULL ({expr})")?;
20522052
}
20532053
write!(f, ")")?;
2054-
if alias.is_some() {
2055-
write!(f, " AS {}", alias.as_ref().unwrap())?;
2054+
if let Some(alias) = alias {
2055+
write!(f, " {alias}")?;
20562056
}
20572057
Ok(())
20582058
}
@@ -2075,8 +2075,8 @@ impl fmt::Display for TableFactor {
20752075
name,
20762076
display_comma_separated(columns)
20772077
)?;
2078-
if alias.is_some() {
2079-
write!(f, " AS {}", alias.as_ref().unwrap())?;
2078+
if let Some(alias) = alias {
2079+
write!(f, " {alias}")?;
20802080
}
20812081
Ok(())
20822082
}
@@ -2109,8 +2109,8 @@ impl fmt::Display for TableFactor {
21092109
}
21102110
write!(f, "PATTERN ({pattern}) ")?;
21112111
write!(f, "DEFINE {})", display_comma_separated(symbols))?;
2112-
if alias.is_some() {
2113-
write!(f, " AS {}", alias.as_ref().unwrap())?;
2112+
if let Some(alias) = alias {
2113+
write!(f, " {alias}")?;
21142114
}
21152115
Ok(())
21162116
}
@@ -2135,7 +2135,7 @@ impl fmt::Display for TableFactor {
21352135
columns = display_comma_separated(columns)
21362136
)?;
21372137
if let Some(alias) = alias {
2138-
write!(f, " AS {alias}")?;
2138+
write!(f, " {alias}")?;
21392139
}
21402140
Ok(())
21412141
}
@@ -2168,7 +2168,7 @@ impl fmt::Display for TableFactor {
21682168
write!(f, ")")?;
21692169

21702170
if let Some(alias) = alias {
2171-
write!(f, " AS {alias}")?;
2171+
write!(f, " {alias}")?;
21722172
}
21732173

21742174
Ok(())
@@ -2181,13 +2181,17 @@ impl fmt::Display for TableFactor {
21812181
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21822182
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
21832183
pub struct TableAlias {
2184+
/// Tells whether the alias was introduced with an explicit, preceding "AS"
2185+
/// keyword, e.g. `AS name`. Typically, the keyword is preceding the name
2186+
/// (e.g. `.. FROM table AS t ..`).
2187+
pub explicit: bool,
21842188
pub name: Ident,
21852189
pub columns: Vec<TableAliasColumnDef>,
21862190
}
21872191

21882192
impl fmt::Display for TableAlias {
21892193
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2190-
write!(f, "{}", self.name)?;
2194+
write!(f, "{}{}", if self.explicit { "AS " } else { "" }, self.name)?;
21912195
if !self.columns.is_empty() {
21922196
write!(f, " ({})", display_comma_separated(&self.columns))?;
21932197
}

src/ast/spans.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use crate::ast::{
19-
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterSchemaOperation, AlterTable,
20-
ColumnOptions, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreateView,
21-
ExportData, Owner, TypedString,
18+
use crate::{
19+
ast::{
20+
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterSchemaOperation, AlterTable,
21+
ColumnOptions, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreateView,
22+
ExportData, Owner, TypedString,
23+
},
24+
tokenizer::TokenWithSpan,
2225
};
2326
use core::iter;
2427

@@ -96,6 +99,12 @@ pub trait Spanned {
9699
fn span(&self) -> Span;
97100
}
98101

102+
impl Spanned for TokenWithSpan {
103+
fn span(&self) -> Span {
104+
self.span
105+
}
106+
}
107+
99108
impl Spanned for Query {
100109
fn span(&self) -> Span {
101110
let Query {
@@ -2079,9 +2088,12 @@ impl Spanned for FunctionArgExpr {
20792088

20802089
impl Spanned for TableAlias {
20812090
fn span(&self) -> Span {
2082-
let TableAlias { name, columns } = self;
2083-
2084-
union_spans(iter::once(name.span).chain(columns.iter().map(|i| i.span())))
2091+
let TableAlias {
2092+
explicit: _,
2093+
name,
2094+
columns,
2095+
} = self;
2096+
union_spans(core::iter::once(name.span).chain(columns.iter().map(Spanned::span)))
20852097
}
20862098
}
20872099

src/parser/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11140,10 +11140,15 @@ impl<'a> Parser<'a> {
1114011140
fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
1114111141
parser.dialect.is_table_factor_alias(explicit, kw, parser)
1114211142
}
11143+
let explicit = self.peek_keyword(Keyword::AS);
1114311144
match self.parse_optional_alias_inner(None, validator)? {
1114411145
Some(name) => {
1114511146
let columns = self.parse_table_alias_column_defs()?;
11146-
Ok(Some(TableAlias { name, columns }))
11147+
Ok(Some(TableAlias {
11148+
explicit,
11149+
name,
11150+
columns,
11151+
}))
1114711152
}
1114811153
None => Ok(None),
1114911154
}
@@ -12775,6 +12780,7 @@ impl<'a> Parser<'a> {
1277512780
let closing_paren_token = self.expect_token(&Token::RParen)?;
1277612781

1277712782
let alias = TableAlias {
12783+
explicit: false,
1277812784
name,
1277912785
columns: vec![],
1278012786
};
@@ -12801,7 +12807,11 @@ impl<'a> Parser<'a> {
1280112807
let query = self.parse_query()?;
1280212808
let closing_paren_token = self.expect_token(&Token::RParen)?;
1280312809

12804-
let alias = TableAlias { name, columns };
12810+
let alias = TableAlias {
12811+
explicit: false,
12812+
name,
12813+
columns,
12814+
};
1280512815
Cte {
1280612816
alias,
1280712817
query,

src/test_utils.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,9 @@ pub fn single_quoted_string(s: impl Into<String>) -> Value {
368368
Value::SingleQuotedString(s.into())
369369
}
370370

371-
pub fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
371+
pub fn table_alias(explicit: bool, name: impl Into<String>) -> Option<TableAlias> {
372372
Some(TableAlias {
373+
explicit,
373374
name: Ident::new(name),
374375
columns: vec![],
375376
})
@@ -405,13 +406,14 @@ pub fn table_from_name(name: ObjectName) -> TableFactor {
405406
}
406407
}
407408

408-
pub fn table_with_alias(name: impl Into<String>, alias: impl Into<String>) -> TableFactor {
409+
pub fn table_with_alias(
410+
name: impl Into<String>,
411+
with_as_keyword: bool,
412+
alias: impl Into<String>,
413+
) -> TableFactor {
409414
TableFactor::Table {
410415
name: ObjectName::from(vec![Ident::new(name)]),
411-
alias: Some(TableAlias {
412-
name: Ident::new(alias),
413-
columns: vec![],
414-
}),
416+
alias: table_alias(with_as_keyword, alias),
415417
args: None,
416418
with_hints: vec![],
417419
version: None,

tests/sqlparser_bigquery.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,7 +1690,7 @@ fn parse_table_identifiers() {
16901690
fn parse_hyphenated_table_identifiers() {
16911691
bigquery().one_statement_parses_to(
16921692
"select * from foo-bar f join baz-qux b on f.id = b.id",
1693-
"SELECT * FROM foo-bar AS f JOIN baz-qux AS b ON f.id = b.id",
1693+
"SELECT * FROM foo-bar f JOIN baz-qux b ON f.id = b.id",
16941694
);
16951695

16961696
assert_eq!(
@@ -1766,7 +1766,7 @@ fn parse_join_constraint_unnest_alias() {
17661766
.joins,
17671767
vec![Join {
17681768
relation: TableFactor::UNNEST {
1769-
alias: table_alias("f"),
1769+
alias: table_alias(true, "f"),
17701770
array_exprs: vec![Expr::CompoundIdentifier(vec![
17711771
Ident::new("t1"),
17721772
Ident::new("a")
@@ -1841,10 +1841,7 @@ fn parse_merge() {
18411841
assert_eq!(
18421842
TableFactor::Table {
18431843
name: ObjectName::from(vec![Ident::new("inventory")]),
1844-
alias: Some(TableAlias {
1845-
name: Ident::new("T"),
1846-
columns: vec![],
1847-
}),
1844+
alias: table_alias(true, "T"),
18481845
args: Default::default(),
18491846
with_hints: Default::default(),
18501847
version: Default::default(),
@@ -1859,10 +1856,7 @@ fn parse_merge() {
18591856
assert_eq!(
18601857
TableFactor::Table {
18611858
name: ObjectName::from(vec![Ident::new("newArrivals")]),
1862-
alias: Some(TableAlias {
1863-
name: Ident::new("S"),
1864-
columns: vec![],
1865-
}),
1859+
alias: table_alias(true, "S"),
18661860
args: Default::default(),
18671861
with_hints: Default::default(),
18681862
version: Default::default(),

0 commit comments

Comments
 (0)