Skip to content

Commit 8b06631

Browse files
committed
[Oracle] Table alias for INSERTed table
1 parent d3ed3c9 commit 8b06631

File tree

6 files changed

+128
-34
lines changed

6 files changed

+128
-34
lines changed

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
@@ -6451,6 +6451,17 @@ pub struct InsertAliases {
64516451
pub col_aliases: Option<Vec<Ident>>,
64526452
}
64536453

6454+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6455+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6456+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6457+
/// Optional alias for an `INSERT` table; i.e. the table to be inserted into
6458+
pub struct InsertTableAlias {
6459+
/// `true` if the aliases was explicitly introduced with the "AS" keyword
6460+
pub explicit: bool,
6461+
/// the alias name itself
6462+
pub alias: Ident,
6463+
}
6464+
64546465
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
64556466
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64566467
#[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
@@ -1324,7 +1324,7 @@ impl Spanned for Insert {
13241324
union_spans(
13251325
core::iter::once(insert_token.0.span)
13261326
.chain(core::iter::once(table.span()))
1327-
.chain(table_alias.as_ref().map(|i| i.span))
1327+
.chain(table_alias.iter().map(|k| k.alias.span))
13281328
.chain(columns.iter().map(|i| i.span))
13291329
.chain(source.as_ref().map(|q| q.span()))
13301330
.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
@@ -4543,7 +4543,13 @@ impl<'a> Parser<'a> {
45434543
///
45444544
/// Returns true if the current token matches the expected keyword.
45454545
pub fn peek_keyword(&self, expected: Keyword) -> bool {
4546-
matches!(&self.peek_token_ref().token, Token::Word(w) if expected == w.keyword)
4546+
self.peek_keyword_one_of(&[expected])
4547+
}
4548+
4549+
#[must_use]
4550+
/// Checks whether the current token is one of the expected keywords without consuming it.
4551+
fn peek_keyword_one_of(&self, expected: &[Keyword]) -> bool {
4552+
matches!(&self.peek_token_ref().token, Token::Word(w) if expected.contains(&w.keyword))
45474553
}
45484554

45494555
/// If the current token is the `expected` keyword followed by
@@ -17133,12 +17139,26 @@ impl<'a> Parser<'a> {
1713317139
let table = self.parse_keyword(Keyword::TABLE);
1713417140
let table_object = self.parse_table_object()?;
1713517141

17136-
let table_alias =
17137-
if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) {
17138-
Some(self.parse_identifier()?)
17142+
let table_alias = if dialect_of!(self is OracleDialect) {
17143+
if !self.peek_sub_query()
17144+
&& !self.peek_keyword_one_of(&[Keyword::DEFAULT, Keyword::VALUES])
17145+
{
17146+
self.maybe_parse(|parser| parser.parse_identifier())?
17147+
.map(|alias| InsertTableAlias {
17148+
explicit: false,
17149+
alias,
17150+
})
1713917151
} else {
1714017152
None
17141-
};
17153+
}
17154+
} else if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) {
17155+
Some(InsertTableAlias {
17156+
explicit: true,
17157+
alias: self.parse_identifier()?,
17158+
})
17159+
} else {
17160+
None
17161+
};
1714217162

1714317163
let is_mysql = dialect_of!(self is MySqlDialect);
1714417164

@@ -19364,14 +19384,7 @@ impl<'a> Parser<'a> {
1936419384

1936519385
/// Returns true if the next keyword indicates a sub query, i.e. SELECT or WITH
1936619386
fn peek_sub_query(&mut self) -> bool {
19367-
if self
19368-
.parse_one_of_keywords(&[Keyword::SELECT, Keyword::WITH])
19369-
.is_some()
19370-
{
19371-
self.prev_token();
19372-
return true;
19373-
}
19374-
false
19387+
self.peek_keyword_one_of(&[Keyword::SELECT, Keyword::WITH])
1937519388
}
1937619389

1937719390
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
@@ -5406,10 +5406,13 @@ fn test_simple_postgres_insert_with_alias() {
54065406
quote_style: None,
54075407
span: Span::empty(),
54085408
}])),
5409-
table_alias: Some(Ident {
5410-
value: "test_table".to_string(),
5411-
quote_style: None,
5412-
span: Span::empty(),
5409+
table_alias: Some(InsertTableAlias {
5410+
explicit: true,
5411+
alias: Ident {
5412+
value: "test_table".to_string(),
5413+
quote_style: None,
5414+
span: Span::empty(),
5415+
}
54135416
}),
54145417
columns: vec![
54155418
Ident {
@@ -5482,10 +5485,13 @@ fn test_simple_postgres_insert_with_alias() {
54825485
quote_style: None,
54835486
span: Span::empty(),
54845487
}])),
5485-
table_alias: Some(Ident {
5486-
value: "test_table".to_string(),
5487-
quote_style: None,
5488-
span: Span::empty(),
5488+
table_alias: Some(InsertTableAlias {
5489+
explicit: true,
5490+
alias: Ident {
5491+
value: "test_table".to_string(),
5492+
quote_style: None,
5493+
span: Span::empty(),
5494+
}
54895495
}),
54905496
columns: vec![
54915497
Ident {
@@ -5560,10 +5566,13 @@ fn test_simple_insert_with_quoted_alias() {
55605566
quote_style: None,
55615567
span: Span::empty(),
55625568
}])),
5563-
table_alias: Some(Ident {
5564-
value: "Test_Table".to_string(),
5565-
quote_style: Some('"'),
5566-
span: Span::empty(),
5569+
table_alias: Some(InsertTableAlias {
5570+
explicit: true,
5571+
alias: Ident {
5572+
value: "Test_Table".to_string(),
5573+
quote_style: Some('"'),
5574+
span: Span::empty(),
5575+
}
55675576
}),
55685577
columns: vec![
55695578
Ident {

0 commit comments

Comments
 (0)