mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Support Snowflake Update-From-Select (#1604)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
parent
14cefc47ed
commit
024a878ee7
6 changed files with 59 additions and 16 deletions
|
@ -72,8 +72,8 @@ pub use self::query::{
|
|||
TableAlias, TableAliasColumnDef, TableFactor, TableFunctionArgs, TableSample,
|
||||
TableSampleBucket, TableSampleKind, TableSampleMethod, TableSampleModifier,
|
||||
TableSampleQuantity, TableSampleSeed, TableSampleSeedModifier, TableSampleUnit, TableVersion,
|
||||
TableWithJoins, Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With,
|
||||
WithFill,
|
||||
TableWithJoins, Top, TopQuantity, UpdateTableFromKind, ValueTableMode, Values,
|
||||
WildcardAdditionalOptions, With, WithFill,
|
||||
};
|
||||
|
||||
pub use self::trigger::{
|
||||
|
@ -2473,7 +2473,7 @@ pub enum Statement {
|
|||
/// Column assignments
|
||||
assignments: Vec<Assignment>,
|
||||
/// Table which provide value to be set
|
||||
from: Option<TableWithJoins>,
|
||||
from: Option<UpdateTableFromKind>,
|
||||
/// WHERE
|
||||
selection: Option<Expr>,
|
||||
/// RETURNING
|
||||
|
@ -3745,10 +3745,13 @@ impl fmt::Display for Statement {
|
|||
write!(f, "{or} ")?;
|
||||
}
|
||||
write!(f, "{table}")?;
|
||||
if let Some(UpdateTableFromKind::BeforeSet(from)) = from {
|
||||
write!(f, " FROM {from}")?;
|
||||
}
|
||||
if !assignments.is_empty() {
|
||||
write!(f, " SET {}", display_comma_separated(assignments))?;
|
||||
}
|
||||
if let Some(from) = from {
|
||||
if let Some(UpdateTableFromKind::AfterSet(from)) = from {
|
||||
write!(f, " FROM {from}")?;
|
||||
}
|
||||
if let Some(selection) = selection {
|
||||
|
|
|
@ -2790,3 +2790,16 @@ impl fmt::Display for ValueTableMode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `FROM` clause of an `UPDATE TABLE` statement
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum UpdateTableFromKind {
|
||||
/// Update Statment where the 'FROM' clause is before the 'SET' keyword (Supported by Snowflake)
|
||||
/// For Example: `UPDATE FROM t1 SET t1.name='aaa'`
|
||||
BeforeSet(TableWithJoins),
|
||||
/// Update Statment where the 'FROM' clause is after the 'SET' keyword (Which is the standard way)
|
||||
/// For Example: `UPDATE SET t1.name='aaa' FROM t1`
|
||||
AfterSet(TableWithJoins),
|
||||
}
|
||||
|
|
|
@ -32,8 +32,8 @@ use super::{
|
|||
OrderBy, OrderByExpr, Partition, PivotValueSource, ProjectionSelect, Query, ReferentialAction,
|
||||
RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem,
|
||||
SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef,
|
||||
TableConstraint, TableFactor, TableOptionsClustered, TableWithJoins, Use, Value, Values,
|
||||
ViewColumnDef, WildcardAdditionalOptions, With, WithFill,
|
||||
TableConstraint, TableFactor, TableOptionsClustered, TableWithJoins, UpdateTableFromKind, Use,
|
||||
Value, Values, ViewColumnDef, WildcardAdditionalOptions, With, WithFill,
|
||||
};
|
||||
|
||||
/// Given an iterator of spans, return the [Span::union] of all spans.
|
||||
|
@ -2106,6 +2106,15 @@ impl Spanned for SelectInto {
|
|||
}
|
||||
}
|
||||
|
||||
impl Spanned for UpdateTableFromKind {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
UpdateTableFromKind::BeforeSet(from) => from.span(),
|
||||
UpdateTableFromKind::AfterSet(from) => from.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect};
|
||||
|
|
|
@ -941,6 +941,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
|
|||
// Reserved for Snowflake table sample
|
||||
Keyword::SAMPLE,
|
||||
Keyword::TABLESAMPLE,
|
||||
Keyword::FROM,
|
||||
];
|
||||
|
||||
/// Can't be used as a column alias, so that `SELECT <expr> alias`
|
||||
|
|
|
@ -11791,15 +11791,20 @@ impl<'a> Parser<'a> {
|
|||
pub fn parse_update(&mut self) -> Result<Statement, ParserError> {
|
||||
let or = self.parse_conflict_clause();
|
||||
let table = self.parse_table_and_joins()?;
|
||||
self.expect_keyword(Keyword::SET)?;
|
||||
let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
|
||||
let from = if self.parse_keyword(Keyword::FROM)
|
||||
&& dialect_of!(self is GenericDialect | PostgreSqlDialect | DuckDbDialect | BigQueryDialect | SnowflakeDialect | RedshiftSqlDialect | MsSqlDialect | SQLiteDialect )
|
||||
{
|
||||
Some(self.parse_table_and_joins()?)
|
||||
let from_before_set = if self.parse_keyword(Keyword::FROM) {
|
||||
Some(UpdateTableFromKind::BeforeSet(
|
||||
self.parse_table_and_joins()?,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.expect_keyword(Keyword::SET)?;
|
||||
let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
|
||||
let from = if from_before_set.is_none() && self.parse_keyword(Keyword::FROM) {
|
||||
Some(UpdateTableFromKind::AfterSet(self.parse_table_and_joins()?))
|
||||
} else {
|
||||
from_before_set
|
||||
};
|
||||
let selection = if self.parse_keyword(Keyword::WHERE) {
|
||||
Some(self.parse_expr()?)
|
||||
} else {
|
||||
|
|
|
@ -366,7 +366,7 @@ fn parse_update_set_from() {
|
|||
target: AssignmentTarget::ColumnName(ObjectName(vec![Ident::new("name")])),
|
||||
value: Expr::CompoundIdentifier(vec![Ident::new("t2"), Ident::new("name")])
|
||||
}],
|
||||
from: Some(TableWithJoins {
|
||||
from: Some(UpdateTableFromKind::AfterSet(TableWithJoins {
|
||||
relation: TableFactor::Derived {
|
||||
lateral: false,
|
||||
subquery: Box::new(Query {
|
||||
|
@ -417,8 +417,8 @@ fn parse_update_set_from() {
|
|||
columns: vec![],
|
||||
})
|
||||
},
|
||||
joins: vec![],
|
||||
}),
|
||||
joins: vec![]
|
||||
})),
|
||||
selection: Some(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("t1"),
|
||||
|
@ -12577,9 +12577,21 @@ fn overflow() {
|
|||
let statement = statements.pop().unwrap();
|
||||
assert_eq!(statement.to_string(), sql);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_select_without_projection() {
|
||||
let dialects = all_dialects_where(|d| d.supports_empty_projections());
|
||||
dialects.verified_stmt("SELECT FROM users");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_update_from_before_select() {
|
||||
all_dialects()
|
||||
.verified_stmt("UPDATE t1 FROM (SELECT name, id FROM t1 GROUP BY id) AS t2 SET name = t2.name WHERE t1.id = t2.id");
|
||||
|
||||
let query =
|
||||
"UPDATE t1 FROM (SELECT name, id FROM t1 GROUP BY id) AS t2 SET name = t2.name FROM (SELECT name from t2) AS t2";
|
||||
assert_eq!(
|
||||
ParserError::ParserError("Expected: end of statement, found: FROM".to_string()),
|
||||
parse_sql_statements(query).unwrap_err()
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue