Skip to content

Commit dc91258

Browse files
committed
Publish MERGE INSERT columns as ObjectName; no semantic validation
1 parent 5a08099 commit dc91258

File tree

8 files changed

+79
-203
lines changed

8 files changed

+79
-203
lines changed

src/ast/dml.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ pub struct MergeInsertExpr {
539539
/// INSERT (product, quantity) VALUES(product, quantity)
540540
/// INSERT (product, quantity) ROW
541541
/// ```
542-
pub columns: Vec<Ident>,
542+
pub columns: Vec<ObjectName>,
543543
/// The token, `[VALUES | ROW]` starting `kind`.
544544
pub kind_token: AttachedToken,
545545
/// The insert type used by the statement.

src/ast/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,12 @@ impl From<Vec<Ident>> for ObjectName {
347347
}
348348
}
349349

350+
impl From<Ident> for ObjectName {
351+
fn from(ident: Ident) -> Self {
352+
ObjectName(vec![ObjectNamePart::Identifier(ident)])
353+
}
354+
}
355+
350356
impl fmt::Display for ObjectName {
351357
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
352358
write!(f, "{}", display_separated(&self.0, "."))

src/ast/spans.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2434,7 +2434,7 @@ impl Spanned for MergeInsertExpr {
24342434
]
24352435
.into_iter()
24362436
.chain(self.insert_predicate.iter().map(Spanned::span))
2437-
.chain(self.columns.iter().map(|i| i.span)),
2437+
.chain(self.columns.iter().map(|i| i.span())),
24382438
)
24392439
}
24402440
}

src/dialect/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,15 @@ pub trait Dialect: Debug + Any {
632632
/// ```
633633
/// or
634634
/// ```sql
635+
/// MERGE INTO FOO
636+
/// USING FOO_IMP
637+
/// ON (FOO.ID = FOO_IMP.ID)
638+
/// WHEN NOT MATCHED THEN
639+
/// -- here: qualified with array subscripts
640+
/// INSERT (FOO.ID[1], FOO.NAME[1:12])
641+
/// VALUES (FOO_IMP.ID, UPPER(FOO_IMP.NAME))
642+
/// or
643+
/// ```sql
635644
/// MERGE INTO FOO X
636645
/// USING FOO_IMP
637646
/// ON (X.ID = FOO_IMP.ID)
@@ -641,9 +650,6 @@ pub trait Dialect: Debug + Any {
641650
/// VALUES (FOO_IMP.ID, UPPER(FOO_IMP.NAME))
642651
/// ```
643652
///
644-
/// Note: in the latter case, the qualifier must match the target table
645-
/// name or its alias if one is present. The parser will enforce this.
646-
///
647653
/// The default implementation always returns `false` not allowing the
648654
/// qualifiers.
649655
fn supports_merge_insert_qualified_columns(&self) -> bool {

src/dialect/postgresql.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,12 @@ impl Dialect for PostgreSqlDialect {
280280
fn supports_interval_options(&self) -> bool {
281281
true
282282
}
283+
284+
/// [Postgres] supports column names with a subfield name or an array
285+
/// subscript in the MERGE INSERT column lists.
286+
///
287+
/// [Postgres]: https://www.postgresql.org/docs/current/sql-merge.html
288+
fn supports_merge_insert_qualified_columns(&self) -> bool {
289+
true
290+
}
283291
}

src/parser/merge.rs

Lines changed: 21 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,12 @@ use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec};
1717

1818
use crate::{
1919
ast::{
20-
Ident, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr, MergeInsertKind,
21-
MergeUpdateExpr, ObjectName, ObjectNamePart, OutputClause, SetExpr, Spanned, Statement,
22-
TableFactor,
20+
Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, ObjectName, ObjectNamePart, OutputClause, SetExpr, Statement
2321
},
2422
dialect::{BigQueryDialect, GenericDialect, MySqlDialect},
2523
keywords::Keyword,
2624
parser::IsOptional,
27-
tokenizer::{Location, TokenWithSpan},
25+
tokenizer::TokenWithSpan,
2826
};
2927

3028
use super::{Parser, ParserError};
@@ -49,7 +47,7 @@ impl Parser<'_> {
4947
let source = self.parse_table_factor()?;
5048
self.expect_keyword_is(Keyword::ON)?;
5149
let on = self.parse_expr()?;
52-
let clauses = self.parse_merge_clauses(&table)?;
50+
let clauses = self.parse_merge_clauses()?;
5351
let output = match self.parse_one_of_keywords(&[Keyword::OUTPUT, Keyword::RETURNING]) {
5452
Some(keyword) => Some(self.parse_output(keyword, self.get_current_token().clone())?),
5553
None => None,
@@ -66,10 +64,7 @@ impl Parser<'_> {
6664
}))
6765
}
6866

69-
fn parse_merge_clauses(
70-
&mut self,
71-
target_table: &TableFactor,
72-
) -> Result<Vec<MergeClause>, ParserError> {
67+
fn parse_merge_clauses(&mut self) -> Result<Vec<MergeClause>, ParserError> {
7368
let mut clauses = vec![];
7469
loop {
7570
if !(self.parse_keyword(Keyword::WHEN)) {
@@ -172,11 +167,7 @@ impl Parser<'_> {
172167
let insert_token = self.get_current_token().clone();
173168
let is_mysql = dialect_of!(self is MySqlDialect);
174169

175-
let columns = self.parse_merge_clause_insert_columns(
176-
target_table,
177-
&clause_kind,
178-
is_mysql,
179-
)?;
170+
let columns = self.parse_merge_clause_insert_columns(is_mysql)?;
180171
let (kind, kind_token) = if dialect_of!(self is BigQueryDialect | GenericDialect)
181172
&& self.parse_keyword(Keyword::ROW)
182173
{
@@ -220,67 +211,27 @@ impl Parser<'_> {
220211
Ok(clauses)
221212
}
222213

223-
fn parse_merge_clause_insert_columns(
224-
&mut self,
225-
target_table: &TableFactor,
226-
clause_kind: &MergeClauseKind,
227-
allow_empty: bool,
228-
) -> Result<Vec<Ident>, ParserError> {
214+
fn parse_merge_clause_insert_columns(&mut self, allow_empty: bool) -> Result<Vec<ObjectName>, ParserError> {
229215
if self.dialect.supports_merge_insert_qualified_columns() {
230-
let cols =
231-
self.parse_parenthesized_qualified_column_list(IsOptional::Optional, allow_empty)?;
232-
if let TableFactor::Table { name, alias, .. } = target_table {
233-
if let Some(alias) = alias {
234-
if alias.columns.is_empty() {
235-
// ~ only the alias is supported at this point
236-
match unqualify_columns(cols, None, Some(&alias.name)) {
237-
Ok(column) => Ok(column),
238-
Err((err, loc)) => parser_err!(
239-
format_args!("Invalid column for INSERT in a {clause_kind} merge clause: {err}"),
240-
loc
241-
),
242-
}
243-
} else {
244-
parser_err!(
245-
format_args!("Invalid target ALIAS for INSERT in a {clause_kind} merge clause; must be an identifier"),
246-
alias.name.span.start
247-
)
248-
}
249-
} else {
250-
// ~ allow the full qualifier, but also just the table name
251-
if name.0.len() == 1 {
252-
match unqualify_columns(cols, Some(name), None) {
253-
Ok(column) => Ok(column),
254-
Err((err, loc)) => parser_err!(
255-
format_args!("Invalid column for INSERT in a {clause_kind} merge clause: {err}"),
256-
loc)
257-
}
258-
} else if let Some(unqualified_name) =
259-
name.0.last().and_then(ObjectNamePart::as_ident)
260-
{
261-
match unqualify_columns(cols, Some(name), Some(unqualified_name)) {
262-
Ok(column) => Ok(column),
263-
Err((err, loc)) => parser_err!(
264-
format_args!("Invalid column for INSERT in a {clause_kind} merge clause: {err}"),
265-
loc)
266-
}
267-
} else {
268-
parser_err!(
269-
format_args!("Invalid target table NAME for INSERT in a {clause_kind} merge clause; must be an identifier"),
270-
name.span().start
271-
)
272-
}
273-
}
274-
} else {
275-
parser_err!(
276-
format_args!("Invalid target for INSERT in a {clause_kind} merge clause; must be a TABLE identifier"),
277-
target_table.span().start)
278-
}
216+
self.parse_parenthesized_qualified_column_list(IsOptional::Optional, allow_empty)
279217
} else {
280-
self.parse_parenthesized_column_list(IsOptional::Optional, allow_empty)
218+
self.parse_parenthesized_column_list_as_object_names(IsOptional::Optional, allow_empty)
281219
}
282220
}
283221

222+
/// Just like [Parser::parse_parenthesized_column_list] parses a
223+
/// parenthesized list of (simple) column names but returns them as object
224+
/// names.
225+
fn parse_parenthesized_column_list_as_object_names(
226+
&mut self,
227+
optional: IsOptional,
228+
allow_empty: bool,
229+
) -> Result<Vec<ObjectName>, ParserError> {
230+
self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| {
231+
p.parse_identifier().map(|ident| ObjectName(vec![ObjectNamePart::Identifier(ident)]))
232+
})
233+
}
234+
284235
fn parse_output(
285236
&mut self,
286237
start_keyword: Keyword,
@@ -308,96 +259,3 @@ impl Parser<'_> {
308259
})
309260
}
310261
}
311-
312-
/// Helper to unqualify a list of columns with either a qualified prefix
313-
/// (`allowed_qualifier_1`) or a qualifier identifier (`allowed_qualifier_2`.)
314-
///
315-
/// Oracle allows `INSERT ([qualifier.]column_name, ...)` in MERGE statements
316-
/// with `qualifier` referring to the alias of the target table (if one is
317-
/// present) or, if no alias is present, to the target table name itself -
318-
/// either qualified or unqualified.
319-
fn unqualify_columns(
320-
columns: Vec<ObjectName>,
321-
allowed_qualifier_1: Option<&ObjectName>,
322-
allowed_qualifier_2: Option<&Ident>,
323-
) -> Result<Vec<Ident>, (&'static str, Location)> {
324-
// ~ helper to turn a column name (part) into a plain `ident`
325-
// possibly bailing with error
326-
fn to_ident(name: ObjectNamePart) -> Result<Ident, (&'static str, Location)> {
327-
match name {
328-
ObjectNamePart::Identifier(ident) => Ok(ident),
329-
ObjectNamePart::Function(_) => Err(("not an identifier", name.span().start)),
330-
}
331-
}
332-
333-
// ~ helper to return the last part of `name` if it is
334-
// preceded by `prefix`
335-
fn unqualify_column(
336-
mut name: ObjectName,
337-
prefix: &ObjectName,
338-
) -> Result<ObjectNamePart, ObjectName> {
339-
let mut name_iter = name.0.iter();
340-
let mut prefix_iter = prefix.0.iter();
341-
loop {
342-
match (name_iter.next(), prefix_iter.next()) {
343-
(Some(_), None) => {
344-
if name_iter.next().is_none() {
345-
return Ok(name.0.pop().expect("missing name part"));
346-
} else {
347-
return Err(name);
348-
}
349-
}
350-
(Some(c), Some(q)) if c == q => {
351-
// ~ continue matching next part
352-
}
353-
_ => {
354-
return Err(name);
355-
}
356-
}
357-
}
358-
}
359-
360-
let mut unqualified = Vec::<Ident>::with_capacity(columns.len());
361-
for mut name in columns {
362-
if name.0.is_empty() {
363-
return Err(("empty column name", name.span().start));
364-
}
365-
366-
if name.0.len() == 1 {
367-
unqualified.push(to_ident(name.0.pop().expect("missing name part"))?);
368-
continue;
369-
}
370-
371-
// ~ try matching by the primary prefix
372-
if let Some(allowed_qualifier) = allowed_qualifier_1 {
373-
match unqualify_column(name, allowed_qualifier) {
374-
Ok(ident) => {
375-
unqualified.push(to_ident(ident)?);
376-
continue;
377-
}
378-
Err(n) => {
379-
// ~ continue trying with the alternate prefix below
380-
name = n;
381-
}
382-
}
383-
}
384-
385-
// ~ try matching by the alternate prefix
386-
if let Some(allowed_qualifier) = allowed_qualifier_2 {
387-
if name.0.len() == 2
388-
&& name
389-
.0
390-
.first()
391-
.and_then(ObjectNamePart::as_ident)
392-
.map(|i| i == allowed_qualifier)
393-
.unwrap_or(false)
394-
{
395-
unqualified.push(to_ident(name.0.pop().expect("missing name part"))?);
396-
continue;
397-
}
398-
}
399-
400-
return Err(("not matching target table", name.span().start));
401-
}
402-
Ok(unqualified)
403-
}

tests/sqlparser_bigquery.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,7 +1806,7 @@ fn parse_merge() {
18061806
);
18071807
let insert_action = MergeAction::Insert(MergeInsertExpr {
18081808
insert_token: AttachedToken::empty(),
1809-
columns: vec![Ident::new("product"), Ident::new("quantity")],
1809+
columns: vec![Ident::new("product").into(), Ident::new("quantity").into()],
18101810
kind_token: AttachedToken::empty(),
18111811
kind: MergeInsertKind::Values(Values {
18121812
value_keyword: false,
@@ -1920,7 +1920,7 @@ fn parse_merge() {
19201920
predicate: Some(Expr::value(number("1"))),
19211921
action: MergeAction::Insert(MergeInsertExpr {
19221922
insert_token: AttachedToken::empty(),
1923-
columns: vec![Ident::new("product"), Ident::new("quantity"),],
1923+
columns: vec![Ident::new("product").into(), Ident::new("quantity").into(),],
19241924
kind_token: AttachedToken::empty(),
19251925
kind: MergeInsertKind::Row,
19261926
insert_predicate: None,
@@ -1932,7 +1932,7 @@ fn parse_merge() {
19321932
predicate: None,
19331933
action: MergeAction::Insert(MergeInsertExpr {
19341934
insert_token: AttachedToken::empty(),
1935-
columns: vec![Ident::new("product"), Ident::new("quantity"),],
1935+
columns: vec![Ident::new("product").into(), Ident::new("quantity").into(),],
19361936
kind_token: AttachedToken::empty(),
19371937
kind: MergeInsertKind::Row,
19381938
insert_predicate: None,
@@ -1982,7 +1982,7 @@ fn parse_merge() {
19821982
predicate: None,
19831983
action: MergeAction::Insert(MergeInsertExpr {
19841984
insert_token: AttachedToken::empty(),
1985-
columns: vec![Ident::new("a"), Ident::new("b"),],
1985+
columns: vec![Ident::new("a").into(), Ident::new("b").into(),],
19861986
kind_token: AttachedToken::empty(),
19871987
kind: MergeInsertKind::Values(Values {
19881988
value_keyword: false,

0 commit comments

Comments
 (0)