Skip to content

Commit 0f50545

Browse files
committed
[Oracle] Table alias for INSERTed table
1 parent cb0664f commit 0f50545

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
@@ -6456,6 +6456,17 @@ pub struct InsertAliases {
64566456
pub col_aliases: Option<Vec<Ident>>,
64576457
}
64586458

6459+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6460+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6461+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6462+
/// Optional alias for an `INSERT` table; i.e. the table to be inserted into
6463+
pub struct InsertTableAlias {
6464+
/// `true` if the aliases was explicitly introduced with the "AS" keyword
6465+
pub explicit: bool,
6466+
/// the alias name itself
6467+
pub alias: Ident,
6468+
}
6469+
64596470
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
64606471
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64616472
#[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
@@ -4545,7 +4545,13 @@ impl<'a> Parser<'a> {
45454545
///
45464546
/// Returns true if the current token matches the expected keyword.
45474547
pub fn peek_keyword(&self, expected: Keyword) -> bool {
4548-
matches!(&self.peek_token_ref().token, Token::Word(w) if expected == w.keyword)
4548+
self.peek_keyword_one_of(&[expected])
4549+
}
4550+
4551+
#[must_use]
4552+
/// Checks whether the current token is one of the expected keywords without consuming it.
4553+
fn peek_keyword_one_of(&self, expected: &[Keyword]) -> bool {
4554+
matches!(&self.peek_token_ref().token, Token::Word(w) if expected.contains(&w.keyword))
45494555
}
45504556

45514557
/// If the current token is the `expected` keyword followed by
@@ -17166,12 +17172,26 @@ impl<'a> Parser<'a> {
1716617172
let table = self.parse_keyword(Keyword::TABLE);
1716717173
let table_object = self.parse_table_object()?;
1716817174

17169-
let table_alias =
17170-
if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) {
17171-
Some(self.parse_identifier()?)
17175+
let table_alias = if dialect_of!(self is OracleDialect) {
17176+
if !self.peek_sub_query()
17177+
&& !self.peek_keyword_one_of(&[Keyword::DEFAULT, Keyword::VALUES])
17178+
{
17179+
self.maybe_parse(|parser| parser.parse_identifier())?
17180+
.map(|alias| InsertTableAlias {
17181+
explicit: false,
17182+
alias,
17183+
})
1717217184
} else {
1717317185
None
17174-
};
17186+
}
17187+
} else if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) {
17188+
Some(InsertTableAlias {
17189+
explicit: true,
17190+
alias: self.parse_identifier()?,
17191+
})
17192+
} else {
17193+
None
17194+
};
1717517195

1717617196
let is_mysql = dialect_of!(self is MySqlDialect);
1717717197

@@ -19412,14 +19432,7 @@ impl<'a> Parser<'a> {
1941219432

1941319433
/// Returns true if the next keyword indicates a sub query, i.e. SELECT or WITH
1941419434
fn peek_sub_query(&mut self) -> bool {
19415-
if self
19416-
.parse_one_of_keywords(&[Keyword::SELECT, Keyword::WITH])
19417-
.is_some()
19418-
{
19419-
self.prev_token();
19420-
return true;
19421-
}
19422-
false
19435+
self.peek_keyword_one_of(&[Keyword::SELECT, Keyword::WITH])
1942319436
}
1942419437

1942519438
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,
@@ -414,3 +417,55 @@ fn test_connect_by() {
414417
ORDER BY \"Employee\", \"Manager\", \"Pathlen\", \"Path\"",
415418
);
416419
}
420+
421+
#[test]
422+
fn test_insert_with_table_alias() {
423+
let oracle_dialect = oracle();
424+
425+
fn verify_table_name_with_alias(stmt: &Statement, exp_table_name: &str, exp_table_alias: &str) {
426+
assert!(matches!(stmt,
427+
Statement::Insert(Insert {
428+
table: TableObject::TableName(table_name),
429+
table_alias: Some(InsertTableAlias {
430+
explicit: false,
431+
alias: Ident {
432+
value: table_alias,
433+
quote_style: None,
434+
span: _
435+
}
436+
}),
437+
..
438+
})
439+
if table_alias == exp_table_alias
440+
&& table_name == &ObjectName::from(vec![Ident {
441+
value: exp_table_name.into(),
442+
quote_style: None,
443+
span: Span::empty(),
444+
}])
445+
));
446+
}
447+
448+
let stmt = oracle_dialect.verified_stmt(
449+
"INSERT INTO foo_t t \
450+
SELECT 1, 2, 3 FROM dual",
451+
);
452+
verify_table_name_with_alias(&stmt, "foo_t", "t");
453+
454+
let stmt = oracle_dialect.verified_stmt(
455+
"INSERT INTO foo_t asdf (a, b, c) \
456+
SELECT 1, 2, 3 FROM dual",
457+
);
458+
verify_table_name_with_alias(&stmt, "foo_t", "asdf");
459+
460+
let stmt = oracle_dialect.verified_stmt(
461+
"INSERT INTO foo_t t (a, b, c) \
462+
VALUES (1, 2, 3)",
463+
);
464+
verify_table_name_with_alias(&stmt, "foo_t", "t");
465+
466+
let stmt = oracle_dialect.verified_stmt(
467+
"INSERT INTO foo_t t \
468+
VALUES (1, 2, 3)",
469+
);
470+
verify_table_name_with_alias(&stmt, "foo_t", "t");
471+
}

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)