Skip to content

Commit f6ad126

Browse files
committed
[Oracle] Table alias for INSERTed table
1 parent 0048c9f commit f6ad126

6 files changed

Lines changed: 128 additions & 34 deletions

File tree

src/ast/dml.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ use crate::{
3131

3232
use super::{
3333
display_comma_separated, helpers::attached_token::AttachedToken, query::InputFormatClause,
34-
Assignment, Expr, FromTable, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnInsert,
35-
OptimizerHint, OrderByExpr, Query, SelectInto, SelectItem, Setting, SqliteOnConflict,
36-
TableFactor, TableObject, TableWithJoins, UpdateTableFromKind, Values,
34+
Assignment, Expr, FromTable, Ident, InsertAliases, InsertTableAlias, MysqlInsertPriority,
35+
ObjectName, OnInsert, OptimizerHint, OrderByExpr, Query, SelectInto, SelectItem, Setting,
36+
SqliteOnConflict, TableFactor, TableObject, TableWithJoins, UpdateTableFromKind, Values,
3737
};
3838

3939
/// INSERT statement.
@@ -56,8 +56,9 @@ pub struct Insert {
5656
pub into: bool,
5757
/// TABLE
5858
pub table: TableObject,
59-
/// table_name as foo (for PostgreSQL)
60-
pub table_alias: Option<Ident>,
59+
/// `table_name as foo` (for PostgreSQL)
60+
/// `table_name foo` (for Oracle)
61+
pub table_alias: Option<InsertTableAlias>,
6162
/// COLUMNS
6263
pub columns: Vec<Ident>,
6364
/// Overwrite (Hive)
@@ -125,8 +126,13 @@ pub struct Insert {
125126
impl Display for Insert {
126127
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127128
// SQLite OR conflict has a special format: INSERT OR ... INTO table_name
128-
let table_name = if let Some(alias) = &self.table_alias {
129-
format!("{0} AS {alias}", self.table)
129+
let table_name = if let Some(table_alias) = &self.table_alias {
130+
format!(
131+
"{table} {as_keyword}{alias}",
132+
table = self.table,
133+
as_keyword = if table_alias.explicit { "AS " } else { "" },
134+
alias = table_alias.alias
135+
)
130136
} else {
131137
self.table.to_string()
132138
};

src/ast/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6480,6 +6480,17 @@ pub struct InsertAliases {
64806480
pub col_aliases: Option<Vec<Ident>>,
64816481
}
64826482

6483+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6484+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6485+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6486+
/// Optional alias for an `INSERT` table; i.e. the table to be inserted into
6487+
pub struct InsertTableAlias {
6488+
/// `true` if the aliases was explicitly introduced with the "AS" keyword
6489+
pub explicit: bool,
6490+
/// the alias name itself
6491+
pub alias: Ident,
6492+
}
6493+
64836494
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
64846495
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64856496
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/ast/spans.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,7 @@ impl Spanned for Insert {
13271327
union_spans(
13281328
core::iter::once(insert_token.0.span)
13291329
.chain(core::iter::once(table.span()))
1330-
.chain(table_alias.as_ref().map(|i| i.span))
1330+
.chain(table_alias.iter().map(|k| k.alias.span))
13311331
.chain(columns.iter().map(|i| i.span))
13321332
.chain(source.as_ref().map(|q| q.span()))
13331333
.chain(assignments.iter().map(|i| i.span()))

src/parser/mod.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4581,7 +4581,13 @@ impl<'a> Parser<'a> {
45814581
///
45824582
/// Returns true if the current token matches the expected keyword.
45834583
pub fn peek_keyword(&self, expected: Keyword) -> bool {
4584-
matches!(&self.peek_token_ref().token, Token::Word(w) if expected == w.keyword)
4584+
self.peek_keyword_one_of(&[expected])
4585+
}
4586+
4587+
#[must_use]
4588+
/// Checks whether the current token is one of the expected keywords without consuming it.
4589+
fn peek_keyword_one_of(&self, expected: &[Keyword]) -> bool {
4590+
matches!(&self.peek_token_ref().token, Token::Word(w) if expected.contains(&w.keyword))
45854591
}
45864592

45874593
/// If the current token is the `expected` keyword followed by
@@ -17229,12 +17235,26 @@ impl<'a> Parser<'a> {
1722917235
let table = self.parse_keyword(Keyword::TABLE);
1723017236
let table_object = self.parse_table_object()?;
1723117237

17232-
let table_alias =
17233-
if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) {
17234-
Some(self.parse_identifier()?)
17238+
let table_alias = if dialect_of!(self is OracleDialect) {
17239+
if !self.peek_sub_query()
17240+
&& !self.peek_keyword_one_of(&[Keyword::DEFAULT, Keyword::VALUES])
17241+
{
17242+
self.maybe_parse(|parser| parser.parse_identifier())?
17243+
.map(|alias| InsertTableAlias {
17244+
explicit: false,
17245+
alias,
17246+
})
1723517247
} else {
1723617248
None
17237-
};
17249+
}
17250+
} else if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) {
17251+
Some(InsertTableAlias {
17252+
explicit: true,
17253+
alias: self.parse_identifier()?,
17254+
})
17255+
} else {
17256+
None
17257+
};
1723817258

1723917259
let is_mysql = dialect_of!(self is MySqlDialect);
1724017260

@@ -19475,14 +19495,7 @@ impl<'a> Parser<'a> {
1947519495

1947619496
/// Returns true if the next keyword indicates a sub query, i.e. SELECT or WITH
1947719497
fn peek_sub_query(&mut self) -> bool {
19478-
if self
19479-
.parse_one_of_keywords(&[Keyword::SELECT, Keyword::WITH])
19480-
.is_some()
19481-
{
19482-
self.prev_token();
19483-
return true;
19484-
}
19485-
false
19498+
self.peek_keyword_one_of(&[Keyword::SELECT, Keyword::WITH])
1948619499
}
1948719500

1948819501
pub(crate) fn parse_show_stmt_options(&mut self) -> Result<ShowStatementOptions, ParserError> {

tests/sqlparser_oracle.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
use pretty_assertions::assert_eq;
2222

2323
use sqlparser::{
24-
ast::{BinaryOperator, Expr, Ident, QuoteDelimitedString, Value, ValueWithSpan},
24+
ast::{
25+
BinaryOperator, Expr, Ident, Insert, InsertTableAlias, ObjectName, QuoteDelimitedString,
26+
Statement, TableObject, Value, ValueWithSpan,
27+
},
2528
dialect::OracleDialect,
2629
parser::ParserError,
2730
tokenizer::Span,
@@ -421,3 +424,55 @@ fn test_connect_by() {
421424
ORDER BY \"Employee\", \"Manager\", \"Pathlen\", \"Path\"",
422425
);
423426
}
427+
428+
#[test]
429+
fn test_insert_with_table_alias() {
430+
let oracle_dialect = oracle();
431+
432+
fn verify_table_name_with_alias(stmt: &Statement, exp_table_name: &str, exp_table_alias: &str) {
433+
assert!(matches!(stmt,
434+
Statement::Insert(Insert {
435+
table: TableObject::TableName(table_name),
436+
table_alias: Some(InsertTableAlias {
437+
explicit: false,
438+
alias: Ident {
439+
value: table_alias,
440+
quote_style: None,
441+
span: _
442+
}
443+
}),
444+
..
445+
})
446+
if table_alias == exp_table_alias
447+
&& table_name == &ObjectName::from(vec![Ident {
448+
value: exp_table_name.into(),
449+
quote_style: None,
450+
span: Span::empty(),
451+
}])
452+
));
453+
}
454+
455+
let stmt = oracle_dialect.verified_stmt(
456+
"INSERT INTO foo_t t \
457+
SELECT 1, 2, 3 FROM dual",
458+
);
459+
verify_table_name_with_alias(&stmt, "foo_t", "t");
460+
461+
let stmt = oracle_dialect.verified_stmt(
462+
"INSERT INTO foo_t asdf (a, b, c) \
463+
SELECT 1, 2, 3 FROM dual",
464+
);
465+
verify_table_name_with_alias(&stmt, "foo_t", "asdf");
466+
467+
let stmt = oracle_dialect.verified_stmt(
468+
"INSERT INTO foo_t t (a, b, c) \
469+
VALUES (1, 2, 3)",
470+
);
471+
verify_table_name_with_alias(&stmt, "foo_t", "t");
472+
473+
let stmt = oracle_dialect.verified_stmt(
474+
"INSERT INTO foo_t t \
475+
VALUES (1, 2, 3)",
476+
);
477+
verify_table_name_with_alias(&stmt, "foo_t", "t");
478+
}

tests/sqlparser_postgres.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5445,10 +5445,13 @@ fn test_simple_postgres_insert_with_alias() {
54455445
quote_style: None,
54465446
span: Span::empty(),
54475447
}])),
5448-
table_alias: Some(Ident {
5449-
value: "test_table".to_string(),
5450-
quote_style: None,
5451-
span: Span::empty(),
5448+
table_alias: Some(InsertTableAlias {
5449+
explicit: true,
5450+
alias: Ident {
5451+
value: "test_table".to_string(),
5452+
quote_style: None,
5453+
span: Span::empty(),
5454+
}
54525455
}),
54535456
columns: vec![
54545457
Ident {
@@ -5521,10 +5524,13 @@ fn test_simple_postgres_insert_with_alias() {
55215524
quote_style: None,
55225525
span: Span::empty(),
55235526
}])),
5524-
table_alias: Some(Ident {
5525-
value: "test_table".to_string(),
5526-
quote_style: None,
5527-
span: Span::empty(),
5527+
table_alias: Some(InsertTableAlias {
5528+
explicit: true,
5529+
alias: Ident {
5530+
value: "test_table".to_string(),
5531+
quote_style: None,
5532+
span: Span::empty(),
5533+
}
55285534
}),
55295535
columns: vec![
55305536
Ident {
@@ -5599,10 +5605,13 @@ fn test_simple_insert_with_quoted_alias() {
55995605
quote_style: None,
56005606
span: Span::empty(),
56015607
}])),
5602-
table_alias: Some(Ident {
5603-
value: "Test_Table".to_string(),
5604-
quote_style: Some('"'),
5605-
span: Span::empty(),
5608+
table_alias: Some(InsertTableAlias {
5609+
explicit: true,
5610+
alias: Ident {
5611+
value: "Test_Table".to_string(),
5612+
quote_style: Some('"'),
5613+
span: Span::empty(),
5614+
}
56065615
}),
56075616
columns: vec![
56085617
Ident {

0 commit comments

Comments
 (0)