Skip to content

Commit 024a878

Browse files
Support Snowflake Update-From-Select (#1604)
Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent 14cefc4 commit 024a878

File tree

6 files changed

+58
-15
lines changed

6 files changed

+58
-15
lines changed

src/ast/mod.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ pub use self::query::{
7272
TableAlias, TableAliasColumnDef, TableFactor, TableFunctionArgs, TableSample,
7373
TableSampleBucket, TableSampleKind, TableSampleMethod, TableSampleModifier,
7474
TableSampleQuantity, TableSampleSeed, TableSampleSeedModifier, TableSampleUnit, TableVersion,
75-
TableWithJoins, Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With,
76-
WithFill,
75+
TableWithJoins, Top, TopQuantity, UpdateTableFromKind, ValueTableMode, Values,
76+
WildcardAdditionalOptions, With, WithFill,
7777
};
7878

7979
pub use self::trigger::{
@@ -2473,7 +2473,7 @@ pub enum Statement {
24732473
/// Column assignments
24742474
assignments: Vec<Assignment>,
24752475
/// Table which provide value to be set
2476-
from: Option<TableWithJoins>,
2476+
from: Option<UpdateTableFromKind>,
24772477
/// WHERE
24782478
selection: Option<Expr>,
24792479
/// RETURNING
@@ -3745,10 +3745,13 @@ impl fmt::Display for Statement {
37453745
write!(f, "{or} ")?;
37463746
}
37473747
write!(f, "{table}")?;
3748+
if let Some(UpdateTableFromKind::BeforeSet(from)) = from {
3749+
write!(f, " FROM {from}")?;
3750+
}
37483751
if !assignments.is_empty() {
37493752
write!(f, " SET {}", display_comma_separated(assignments))?;
37503753
}
3751-
if let Some(from) = from {
3754+
if let Some(UpdateTableFromKind::AfterSet(from)) = from {
37523755
write!(f, " FROM {from}")?;
37533756
}
37543757
if let Some(selection) = selection {

src/ast/query.rs

+13
Original file line numberDiff line numberDiff line change
@@ -2790,3 +2790,16 @@ impl fmt::Display for ValueTableMode {
27902790
}
27912791
}
27922792
}
2793+
2794+
/// The `FROM` clause of an `UPDATE TABLE` statement
2795+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2796+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2797+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2798+
pub enum UpdateTableFromKind {
2799+
/// Update Statment where the 'FROM' clause is before the 'SET' keyword (Supported by Snowflake)
2800+
/// For Example: `UPDATE FROM t1 SET t1.name='aaa'`
2801+
BeforeSet(TableWithJoins),
2802+
/// Update Statment where the 'FROM' clause is after the 'SET' keyword (Which is the standard way)
2803+
/// For Example: `UPDATE SET t1.name='aaa' FROM t1`
2804+
AfterSet(TableWithJoins),
2805+
}

src/ast/spans.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ use super::{
3232
OrderBy, OrderByExpr, Partition, PivotValueSource, ProjectionSelect, Query, ReferentialAction,
3333
RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem,
3434
SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef,
35-
TableConstraint, TableFactor, TableOptionsClustered, TableWithJoins, Use, Value, Values,
36-
ViewColumnDef, WildcardAdditionalOptions, With, WithFill,
35+
TableConstraint, TableFactor, TableOptionsClustered, TableWithJoins, UpdateTableFromKind, Use,
36+
Value, Values, ViewColumnDef, WildcardAdditionalOptions, With, WithFill,
3737
};
3838

3939
/// Given an iterator of spans, return the [Span::union] of all spans.
@@ -2106,6 +2106,15 @@ impl Spanned for SelectInto {
21062106
}
21072107
}
21082108

2109+
impl Spanned for UpdateTableFromKind {
2110+
fn span(&self) -> Span {
2111+
match self {
2112+
UpdateTableFromKind::BeforeSet(from) => from.span(),
2113+
UpdateTableFromKind::AfterSet(from) => from.span(),
2114+
}
2115+
}
2116+
}
2117+
21092118
#[cfg(test)]
21102119
pub mod tests {
21112120
use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect};

src/keywords.rs

+1
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
941941
// Reserved for Snowflake table sample
942942
Keyword::SAMPLE,
943943
Keyword::TABLESAMPLE,
944+
Keyword::FROM,
944945
];
945946

946947
/// Can't be used as a column alias, so that `SELECT <expr> alias`

src/parser/mod.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -11791,14 +11791,19 @@ impl<'a> Parser<'a> {
1179111791
pub fn parse_update(&mut self) -> Result<Statement, ParserError> {
1179211792
let or = self.parse_conflict_clause();
1179311793
let table = self.parse_table_and_joins()?;
11794+
let from_before_set = if self.parse_keyword(Keyword::FROM) {
11795+
Some(UpdateTableFromKind::BeforeSet(
11796+
self.parse_table_and_joins()?,
11797+
))
11798+
} else {
11799+
None
11800+
};
1179411801
self.expect_keyword(Keyword::SET)?;
1179511802
let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
11796-
let from = if self.parse_keyword(Keyword::FROM)
11797-
&& dialect_of!(self is GenericDialect | PostgreSqlDialect | DuckDbDialect | BigQueryDialect | SnowflakeDialect | RedshiftSqlDialect | MsSqlDialect | SQLiteDialect )
11798-
{
11799-
Some(self.parse_table_and_joins()?)
11803+
let from = if from_before_set.is_none() && self.parse_keyword(Keyword::FROM) {
11804+
Some(UpdateTableFromKind::AfterSet(self.parse_table_and_joins()?))
1180011805
} else {
11801-
None
11806+
from_before_set
1180211807
};
1180311808
let selection = if self.parse_keyword(Keyword::WHERE) {
1180411809
Some(self.parse_expr()?)

tests/sqlparser_common.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ fn parse_update_set_from() {
366366
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new("name")])),
367367
value: Expr::CompoundIdentifier(vec![Ident::new("t2"), Ident::new("name")])
368368
}],
369-
from: Some(TableWithJoins {
369+
from: Some(UpdateTableFromKind::AfterSet(TableWithJoins {
370370
relation: TableFactor::Derived {
371371
lateral: false,
372372
subquery: Box::new(Query {
@@ -417,8 +417,8 @@ fn parse_update_set_from() {
417417
columns: vec![],
418418
})
419419
},
420-
joins: vec![],
421-
}),
420+
joins: vec![]
421+
})),
422422
selection: Some(Expr::BinaryOp {
423423
left: Box::new(Expr::CompoundIdentifier(vec![
424424
Ident::new("t1"),
@@ -12577,9 +12577,21 @@ fn overflow() {
1257712577
let statement = statements.pop().unwrap();
1257812578
assert_eq!(statement.to_string(), sql);
1257912579
}
12580-
1258112580
#[test]
1258212581
fn parse_select_without_projection() {
1258312582
let dialects = all_dialects_where(|d| d.supports_empty_projections());
1258412583
dialects.verified_stmt("SELECT FROM users");
1258512584
}
12585+
12586+
#[test]
12587+
fn parse_update_from_before_select() {
12588+
all_dialects()
12589+
.verified_stmt("UPDATE t1 FROM (SELECT name, id FROM t1 GROUP BY id) AS t2 SET name = t2.name WHERE t1.id = t2.id");
12590+
12591+
let query =
12592+
"UPDATE t1 FROM (SELECT name, id FROM t1 GROUP BY id) AS t2 SET name = t2.name FROM (SELECT name from t2) AS t2";
12593+
assert_eq!(
12594+
ParserError::ParserError("Expected: end of statement, found: FROM".to_string()),
12595+
parse_sql_statements(query).unwrap_err()
12596+
);
12597+
}

0 commit comments

Comments
 (0)