mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
SET with a list of comma separated assignments (#1757)
This commit is contained in:
parent
3392623b00
commit
85f855150f
12 changed files with 564 additions and 375 deletions
361
src/ast/mod.rs
361
src/ast/mod.rs
|
@ -2394,6 +2394,168 @@ pub enum CreatePolicyCommand {
|
|||
Delete,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum Set {
|
||||
/// SQL Standard-style
|
||||
/// SET a = 1;
|
||||
SingleAssignment {
|
||||
local: bool,
|
||||
hivevar: bool,
|
||||
variable: ObjectName,
|
||||
values: Vec<Expr>,
|
||||
},
|
||||
/// Snowflake-style
|
||||
/// SET (a, b, ..) = (1, 2, ..);
|
||||
ParenthesizedAssignments {
|
||||
variables: Vec<ObjectName>,
|
||||
values: Vec<Expr>,
|
||||
},
|
||||
/// MySQL-style
|
||||
/// SET a = 1, b = 2, ..;
|
||||
MultipleAssignments { assignments: Vec<SetAssignment> },
|
||||
/// MS-SQL session
|
||||
///
|
||||
/// See <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
|
||||
SetSessionParam(SetSessionParamKind),
|
||||
/// ```sql
|
||||
/// SET [ SESSION | LOCAL ] ROLE role_name
|
||||
/// ```
|
||||
///
|
||||
/// Sets session state. Examples: [ANSI][1], [Postgresql][2], [MySQL][3], and [Oracle][4]
|
||||
///
|
||||
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#set-role-statement
|
||||
/// [2]: https://www.postgresql.org/docs/14/sql-set-role.html
|
||||
/// [3]: https://dev.mysql.com/doc/refman/8.0/en/set-role.html
|
||||
/// [4]: https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10004.htm
|
||||
SetRole {
|
||||
/// Non-ANSI optional identifier to inform if the role is defined inside the current session (`SESSION`) or transaction (`LOCAL`).
|
||||
context_modifier: ContextModifier,
|
||||
/// Role name. If NONE is specified, then the current role name is removed.
|
||||
role_name: Option<Ident>,
|
||||
},
|
||||
/// ```sql
|
||||
/// SET TIME ZONE <value>
|
||||
/// ```
|
||||
///
|
||||
/// Note: this is a PostgreSQL-specific statements
|
||||
/// `SET TIME ZONE <value>` is an alias for `SET timezone TO <value>` in PostgreSQL
|
||||
/// However, we allow it for all dialects.
|
||||
SetTimeZone { local: bool, value: Expr },
|
||||
/// ```sql
|
||||
/// SET NAMES 'charset_name' [COLLATE 'collation_name']
|
||||
/// ```
|
||||
SetNames {
|
||||
charset_name: Ident,
|
||||
collation_name: Option<String>,
|
||||
},
|
||||
/// ```sql
|
||||
/// SET NAMES DEFAULT
|
||||
/// ```
|
||||
///
|
||||
/// Note: this is a MySQL-specific statement.
|
||||
SetNamesDefault {},
|
||||
/// ```sql
|
||||
/// SET TRANSACTION ...
|
||||
/// ```
|
||||
SetTransaction {
|
||||
modes: Vec<TransactionMode>,
|
||||
snapshot: Option<Value>,
|
||||
session: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for Set {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::ParenthesizedAssignments { variables, values } => write!(
|
||||
f,
|
||||
"SET ({}) = ({})",
|
||||
display_comma_separated(variables),
|
||||
display_comma_separated(values)
|
||||
),
|
||||
Self::MultipleAssignments { assignments } => {
|
||||
write!(f, "SET {}", display_comma_separated(assignments))
|
||||
}
|
||||
Self::SetRole {
|
||||
context_modifier,
|
||||
role_name,
|
||||
} => {
|
||||
let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE"));
|
||||
write!(f, "SET{context_modifier} ROLE {role_name}")
|
||||
}
|
||||
Self::SetSessionParam(kind) => write!(f, "SET {kind}"),
|
||||
Self::SetTransaction {
|
||||
modes,
|
||||
snapshot,
|
||||
session,
|
||||
} => {
|
||||
if *session {
|
||||
write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?;
|
||||
} else {
|
||||
write!(f, "SET TRANSACTION")?;
|
||||
}
|
||||
if !modes.is_empty() {
|
||||
write!(f, " {}", display_comma_separated(modes))?;
|
||||
}
|
||||
if let Some(snapshot_id) = snapshot {
|
||||
write!(f, " SNAPSHOT {snapshot_id}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::SetTimeZone { local, value } => {
|
||||
f.write_str("SET ")?;
|
||||
if *local {
|
||||
f.write_str("LOCAL ")?;
|
||||
}
|
||||
write!(f, "TIME ZONE {value}")
|
||||
}
|
||||
Self::SetNames {
|
||||
charset_name,
|
||||
collation_name,
|
||||
} => {
|
||||
write!(f, "SET NAMES {}", charset_name)?;
|
||||
|
||||
if let Some(collation) = collation_name {
|
||||
f.write_str(" COLLATE ")?;
|
||||
f.write_str(collation)?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Self::SetNamesDefault {} => {
|
||||
f.write_str("SET NAMES DEFAULT")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Set::SingleAssignment {
|
||||
local,
|
||||
hivevar,
|
||||
variable,
|
||||
values,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"SET {}{}{} = {}",
|
||||
if *local { "LOCAL " } else { "" },
|
||||
if *hivevar { "HIVEVAR:" } else { "" },
|
||||
variable,
|
||||
display_comma_separated(values)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a `Set` into a `Statement`.
|
||||
/// Convenience function, instead of writing `Statement::Set(Set::Set...{...})`
|
||||
impl From<Set> for Statement {
|
||||
fn from(set: Set) -> Self {
|
||||
Statement::Set(set)
|
||||
}
|
||||
}
|
||||
|
||||
/// A top-level statement (SELECT, INSERT, CREATE, etc.)
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
|
@ -2419,6 +2581,7 @@ pub enum Statement {
|
|||
compute_statistics: bool,
|
||||
has_table_keyword: bool,
|
||||
},
|
||||
Set(Set),
|
||||
/// ```sql
|
||||
/// TRUNCATE
|
||||
/// ```
|
||||
|
@ -2846,7 +3009,10 @@ pub enum Statement {
|
|||
/// DROP CONNECTOR
|
||||
/// ```
|
||||
/// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-DropConnector)
|
||||
DropConnector { if_exists: bool, name: Ident },
|
||||
DropConnector {
|
||||
if_exists: bool,
|
||||
name: Ident,
|
||||
},
|
||||
/// ```sql
|
||||
/// DECLARE
|
||||
/// ```
|
||||
|
@ -2854,7 +3020,9 @@ pub enum Statement {
|
|||
///
|
||||
/// Note: this is a PostgreSQL-specific statement,
|
||||
/// but may also compatible with other SQL.
|
||||
Declare { stmts: Vec<Declare> },
|
||||
Declare {
|
||||
stmts: Vec<Declare>,
|
||||
},
|
||||
/// ```sql
|
||||
/// CREATE EXTENSION [ IF NOT EXISTS ] extension_name
|
||||
/// [ WITH ] [ SCHEMA schema_name ]
|
||||
|
@ -2916,67 +3084,23 @@ pub enum Statement {
|
|||
///
|
||||
/// Note: this is a PostgreSQL-specific statement,
|
||||
/// but may also compatible with other SQL.
|
||||
Discard { object_type: DiscardObject },
|
||||
/// ```sql
|
||||
/// SET [ SESSION | LOCAL ] ROLE role_name
|
||||
/// ```
|
||||
///
|
||||
/// Sets session state. Examples: [ANSI][1], [Postgresql][2], [MySQL][3], and [Oracle][4]
|
||||
///
|
||||
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#set-role-statement
|
||||
/// [2]: https://www.postgresql.org/docs/14/sql-set-role.html
|
||||
/// [3]: https://dev.mysql.com/doc/refman/8.0/en/set-role.html
|
||||
/// [4]: https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10004.htm
|
||||
SetRole {
|
||||
/// Non-ANSI optional identifier to inform if the role is defined inside the current session (`SESSION`) or transaction (`LOCAL`).
|
||||
context_modifier: ContextModifier,
|
||||
/// Role name. If NONE is specified, then the current role name is removed.
|
||||
role_name: Option<Ident>,
|
||||
Discard {
|
||||
object_type: DiscardObject,
|
||||
},
|
||||
/// ```sql
|
||||
/// SET <variable> = expression;
|
||||
/// SET (variable[, ...]) = (expression[, ...]);
|
||||
/// ```
|
||||
///
|
||||
/// Note: this is not a standard SQL statement, but it is supported by at
|
||||
/// least MySQL and PostgreSQL. Not all MySQL-specific syntactic forms are
|
||||
/// supported yet.
|
||||
SetVariable {
|
||||
local: bool,
|
||||
hivevar: bool,
|
||||
variables: OneOrManyWithParens<ObjectName>,
|
||||
value: Vec<Expr>,
|
||||
},
|
||||
/// ```sql
|
||||
/// SET TIME ZONE <value>
|
||||
/// ```
|
||||
///
|
||||
/// Note: this is a PostgreSQL-specific statements
|
||||
/// `SET TIME ZONE <value>` is an alias for `SET timezone TO <value>` in PostgreSQL
|
||||
SetTimeZone { local: bool, value: Expr },
|
||||
/// ```sql
|
||||
/// SET NAMES 'charset_name' [COLLATE 'collation_name']
|
||||
/// ```
|
||||
SetNames {
|
||||
charset_name: Ident,
|
||||
collation_name: Option<String>,
|
||||
},
|
||||
/// ```sql
|
||||
/// SET NAMES DEFAULT
|
||||
/// ```
|
||||
///
|
||||
/// Note: this is a MySQL-specific statement.
|
||||
SetNamesDefault {},
|
||||
/// `SHOW FUNCTIONS`
|
||||
///
|
||||
/// Note: this is a Presto-specific statement.
|
||||
ShowFunctions { filter: Option<ShowStatementFilter> },
|
||||
ShowFunctions {
|
||||
filter: Option<ShowStatementFilter>,
|
||||
},
|
||||
/// ```sql
|
||||
/// SHOW <variable>
|
||||
/// ```
|
||||
///
|
||||
/// Note: this is a PostgreSQL-specific statement.
|
||||
ShowVariable { variable: Vec<Ident> },
|
||||
ShowVariable {
|
||||
variable: Vec<Ident>,
|
||||
},
|
||||
/// ```sql
|
||||
/// SHOW [GLOBAL | SESSION] STATUS [LIKE 'pattern' | WHERE expr]
|
||||
/// ```
|
||||
|
@ -3060,7 +3184,9 @@ pub enum Statement {
|
|||
/// ```
|
||||
///
|
||||
/// Note: this is a MySQL-specific statement.
|
||||
ShowCollation { filter: Option<ShowStatementFilter> },
|
||||
ShowCollation {
|
||||
filter: Option<ShowStatementFilter>,
|
||||
},
|
||||
/// ```sql
|
||||
/// `USE ...`
|
||||
/// ```
|
||||
|
@ -3103,14 +3229,6 @@ pub enum Statement {
|
|||
has_end_keyword: bool,
|
||||
},
|
||||
/// ```sql
|
||||
/// SET TRANSACTION ...
|
||||
/// ```
|
||||
SetTransaction {
|
||||
modes: Vec<TransactionMode>,
|
||||
snapshot: Option<Value>,
|
||||
session: bool,
|
||||
},
|
||||
/// ```sql
|
||||
/// COMMENT ON ...
|
||||
/// ```
|
||||
///
|
||||
|
@ -3329,7 +3447,10 @@ pub enum Statement {
|
|||
/// ```
|
||||
///
|
||||
/// Note: this is a PostgreSQL-specific statement.
|
||||
Deallocate { name: Ident, prepare: bool },
|
||||
Deallocate {
|
||||
name: Ident,
|
||||
prepare: bool,
|
||||
},
|
||||
/// ```sql
|
||||
/// An `EXECUTE` statement
|
||||
/// ```
|
||||
|
@ -3415,11 +3536,15 @@ pub enum Statement {
|
|||
/// SAVEPOINT
|
||||
/// ```
|
||||
/// Define a new savepoint within the current transaction
|
||||
Savepoint { name: Ident },
|
||||
Savepoint {
|
||||
name: Ident,
|
||||
},
|
||||
/// ```sql
|
||||
/// RELEASE [ SAVEPOINT ] savepoint_name
|
||||
/// ```
|
||||
ReleaseSavepoint { name: Ident },
|
||||
ReleaseSavepoint {
|
||||
name: Ident,
|
||||
},
|
||||
/// A `MERGE` statement.
|
||||
///
|
||||
/// ```sql
|
||||
|
@ -3499,7 +3624,9 @@ pub enum Statement {
|
|||
/// LOCK TABLES <table_name> [READ [LOCAL] | [LOW_PRIORITY] WRITE]
|
||||
/// ```
|
||||
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
|
||||
LockTables { tables: Vec<LockTable> },
|
||||
LockTables {
|
||||
tables: Vec<LockTable>,
|
||||
},
|
||||
/// ```sql
|
||||
/// UNLOCK TABLES
|
||||
/// ```
|
||||
|
@ -3533,14 +3660,18 @@ pub enum Statement {
|
|||
/// listen for a notification channel
|
||||
///
|
||||
/// See Postgres <https://www.postgresql.org/docs/current/sql-listen.html>
|
||||
LISTEN { channel: Ident },
|
||||
LISTEN {
|
||||
channel: Ident,
|
||||
},
|
||||
/// ```sql
|
||||
/// UNLISTEN
|
||||
/// ```
|
||||
/// stop listening for a notification
|
||||
///
|
||||
/// See Postgres <https://www.postgresql.org/docs/current/sql-unlisten.html>
|
||||
UNLISTEN { channel: Ident },
|
||||
UNLISTEN {
|
||||
channel: Ident,
|
||||
},
|
||||
/// ```sql
|
||||
/// NOTIFY channel [ , payload ]
|
||||
/// ```
|
||||
|
@ -3580,10 +3711,6 @@ pub enum Statement {
|
|||
/// Snowflake `REMOVE`
|
||||
/// See: <https://docs.snowflake.com/en/sql-reference/sql/remove>
|
||||
Remove(FileStagingCommand),
|
||||
/// MS-SQL session
|
||||
///
|
||||
/// See <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
|
||||
SetSessionParam(SetSessionParamKind),
|
||||
/// RaiseError (MSSQL)
|
||||
/// RAISERROR ( { msg_id | msg_str | @local_variable }
|
||||
/// { , severity , state }
|
||||
|
@ -4644,59 +4771,7 @@ impl fmt::Display for Statement {
|
|||
write!(f, "DISCARD {object_type}")?;
|
||||
Ok(())
|
||||
}
|
||||
Self::SetRole {
|
||||
context_modifier,
|
||||
role_name,
|
||||
} => {
|
||||
let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE"));
|
||||
write!(f, "SET{context_modifier} ROLE {role_name}")
|
||||
}
|
||||
Statement::SetVariable {
|
||||
local,
|
||||
variables,
|
||||
hivevar,
|
||||
value,
|
||||
} => {
|
||||
f.write_str("SET ")?;
|
||||
if *local {
|
||||
f.write_str("LOCAL ")?;
|
||||
}
|
||||
let parenthesized = matches!(variables, OneOrManyWithParens::Many(_));
|
||||
write!(
|
||||
f,
|
||||
"{hivevar}{name} = {l_paren}{value}{r_paren}",
|
||||
hivevar = if *hivevar { "HIVEVAR:" } else { "" },
|
||||
name = variables,
|
||||
l_paren = parenthesized.then_some("(").unwrap_or_default(),
|
||||
value = display_comma_separated(value),
|
||||
r_paren = parenthesized.then_some(")").unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
Statement::SetTimeZone { local, value } => {
|
||||
f.write_str("SET ")?;
|
||||
if *local {
|
||||
f.write_str("LOCAL ")?;
|
||||
}
|
||||
write!(f, "TIME ZONE {value}")
|
||||
}
|
||||
Statement::SetNames {
|
||||
charset_name,
|
||||
collation_name,
|
||||
} => {
|
||||
write!(f, "SET NAMES {}", charset_name)?;
|
||||
|
||||
if let Some(collation) = collation_name {
|
||||
f.write_str(" COLLATE ")?;
|
||||
f.write_str(collation)?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Statement::SetNamesDefault {} => {
|
||||
f.write_str("SET NAMES DEFAULT")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Self::Set(set) => write!(f, "{set}"),
|
||||
Statement::ShowVariable { variable } => {
|
||||
write!(f, "SHOW")?;
|
||||
if !variable.is_empty() {
|
||||
|
@ -4885,24 +4960,6 @@ impl fmt::Display for Statement {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Statement::SetTransaction {
|
||||
modes,
|
||||
snapshot,
|
||||
session,
|
||||
} => {
|
||||
if *session {
|
||||
write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?;
|
||||
} else {
|
||||
write!(f, "SET TRANSACTION")?;
|
||||
}
|
||||
if !modes.is_empty() {
|
||||
write!(f, " {}", display_comma_separated(modes))?;
|
||||
}
|
||||
if let Some(snapshot_id) = snapshot {
|
||||
write!(f, " SNAPSHOT {snapshot_id}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Statement::Commit {
|
||||
chain,
|
||||
end: end_syntax,
|
||||
|
@ -5333,7 +5390,6 @@ impl fmt::Display for Statement {
|
|||
|
||||
Statement::List(command) => write!(f, "LIST {command}"),
|
||||
Statement::Remove(command) => write!(f, "REMOVE {command}"),
|
||||
Statement::SetSessionParam(kind) => write!(f, "SET {kind}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5397,6 +5453,21 @@ impl fmt::Display for SequenceOptions {
|
|||
}
|
||||
}
|
||||
|
||||
/// Assignment for a `SET` statement (name [=|TO] value)
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct SetAssignment {
|
||||
pub name: ObjectName,
|
||||
pub value: Expr,
|
||||
}
|
||||
|
||||
impl fmt::Display for SetAssignment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} = {}", self.name, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Target of a `TRUNCATE TABLE` command
|
||||
///
|
||||
/// Note this is its own struct because `visit_relation` requires an `ObjectName` (not a `Vec<ObjectName>`)
|
||||
|
|
|
@ -230,11 +230,7 @@ impl Spanned for Values {
|
|||
/// - [Statement::Fetch]
|
||||
/// - [Statement::Flush]
|
||||
/// - [Statement::Discard]
|
||||
/// - [Statement::SetRole]
|
||||
/// - [Statement::SetVariable]
|
||||
/// - [Statement::SetTimeZone]
|
||||
/// - [Statement::SetNames]
|
||||
/// - [Statement::SetNamesDefault]
|
||||
/// - [Statement::Set]
|
||||
/// - [Statement::ShowFunctions]
|
||||
/// - [Statement::ShowVariable]
|
||||
/// - [Statement::ShowStatus]
|
||||
|
@ -244,7 +240,6 @@ impl Spanned for Values {
|
|||
/// - [Statement::ShowTables]
|
||||
/// - [Statement::ShowCollation]
|
||||
/// - [Statement::StartTransaction]
|
||||
/// - [Statement::SetTransaction]
|
||||
/// - [Statement::Comment]
|
||||
/// - [Statement::Commit]
|
||||
/// - [Statement::Rollback]
|
||||
|
@ -445,11 +440,7 @@ impl Spanned for Statement {
|
|||
Statement::Fetch { .. } => Span::empty(),
|
||||
Statement::Flush { .. } => Span::empty(),
|
||||
Statement::Discard { .. } => Span::empty(),
|
||||
Statement::SetRole { .. } => Span::empty(),
|
||||
Statement::SetVariable { .. } => Span::empty(),
|
||||
Statement::SetTimeZone { .. } => Span::empty(),
|
||||
Statement::SetNames { .. } => Span::empty(),
|
||||
Statement::SetNamesDefault {} => Span::empty(),
|
||||
Statement::Set(_) => Span::empty(),
|
||||
Statement::ShowFunctions { .. } => Span::empty(),
|
||||
Statement::ShowVariable { .. } => Span::empty(),
|
||||
Statement::ShowStatus { .. } => Span::empty(),
|
||||
|
@ -460,7 +451,6 @@ impl Spanned for Statement {
|
|||
Statement::ShowCollation { .. } => Span::empty(),
|
||||
Statement::Use(u) => u.span(),
|
||||
Statement::StartTransaction { .. } => Span::empty(),
|
||||
Statement::SetTransaction { .. } => Span::empty(),
|
||||
Statement::Comment { .. } => Span::empty(),
|
||||
Statement::Commit { .. } => Span::empty(),
|
||||
Statement::Rollback { .. } => Span::empty(),
|
||||
|
@ -509,7 +499,6 @@ impl Spanned for Statement {
|
|||
Statement::RenameTable { .. } => Span::empty(),
|
||||
Statement::RaisError { .. } => Span::empty(),
|
||||
Statement::List(..) | Statement::Remove(..) => Span::empty(),
|
||||
Statement::SetSessionParam { .. } => Span::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -399,6 +399,16 @@ pub trait Dialect: Debug + Any {
|
|||
false
|
||||
}
|
||||
|
||||
/// Returns true if the dialect supports multiple `SET` statements
|
||||
/// in a single statement.
|
||||
///
|
||||
/// ```sql
|
||||
/// SET variable = expression [, variable = expression];
|
||||
/// ```
|
||||
fn supports_comma_separated_set_assignments(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if the dialect supports an `EXCEPT` clause following a
|
||||
/// wildcard in a select list.
|
||||
///
|
||||
|
|
|
@ -82,6 +82,7 @@ impl Dialect for MsSqlDialect {
|
|||
fn supports_start_transaction_modifier(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn supports_end_transaction_modifier(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -141,6 +141,10 @@ impl Dialect for MySqlDialect {
|
|||
fn supports_set_names(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn supports_comma_separated_set_assignments(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// `LOCK TABLES`
|
||||
|
|
|
@ -173,6 +173,7 @@ define_keywords!(
|
|||
CHANNEL,
|
||||
CHAR,
|
||||
CHARACTER,
|
||||
CHARACTERISTICS,
|
||||
CHARACTERS,
|
||||
CHARACTER_LENGTH,
|
||||
CHARSET,
|
||||
|
@ -557,6 +558,7 @@ define_keywords!(
|
|||
MULTISET,
|
||||
MUTATION,
|
||||
NAME,
|
||||
NAMES,
|
||||
NANOSECOND,
|
||||
NANOSECONDS,
|
||||
NATIONAL,
|
||||
|
|
|
@ -4314,7 +4314,8 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
/// Run a parser method `f`, reverting back to the current position if unsuccessful.
|
||||
/// Returns `None` if `f` returns an error
|
||||
/// Returns `ParserError::RecursionLimitExceeded` if `f` returns a `RecursionLimitExceeded`.
|
||||
/// Returns `Ok(None)` if `f` returns any other error.
|
||||
pub fn maybe_parse<T, F>(&mut self, f: F) -> Result<Option<T>, ParserError>
|
||||
where
|
||||
F: FnMut(&mut Parser) -> Result<T, ParserError>,
|
||||
|
@ -10978,47 +10979,108 @@ impl<'a> Parser<'a> {
|
|||
} else {
|
||||
Some(self.parse_identifier()?)
|
||||
};
|
||||
Ok(Statement::SetRole {
|
||||
Ok(Statement::Set(Set::SetRole {
|
||||
context_modifier,
|
||||
role_name,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn parse_set(&mut self) -> Result<Statement, ParserError> {
|
||||
let modifier =
|
||||
self.parse_one_of_keywords(&[Keyword::SESSION, Keyword::LOCAL, Keyword::HIVEVAR]);
|
||||
if let Some(Keyword::HIVEVAR) = modifier {
|
||||
self.expect_token(&Token::Colon)?;
|
||||
} else if let Some(set_role_stmt) =
|
||||
self.maybe_parse(|parser| parser.parse_set_role(modifier))?
|
||||
{
|
||||
return Ok(set_role_stmt);
|
||||
fn parse_set_values(
|
||||
&mut self,
|
||||
parenthesized_assignment: bool,
|
||||
) -> Result<Vec<Expr>, ParserError> {
|
||||
let mut values = vec![];
|
||||
|
||||
if parenthesized_assignment {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
}
|
||||
|
||||
let variables = if self.parse_keywords(&[Keyword::TIME, Keyword::ZONE]) {
|
||||
OneOrManyWithParens::One(ObjectName::from(vec!["TIMEZONE".into()]))
|
||||
} else if self.dialect.supports_parenthesized_set_variables()
|
||||
loop {
|
||||
let value = if let Some(expr) = self.try_parse_expr_sub_query()? {
|
||||
expr
|
||||
} else if let Ok(expr) = self.parse_expr() {
|
||||
expr
|
||||
} else {
|
||||
self.expected("variable value", self.peek_token())?
|
||||
};
|
||||
|
||||
values.push(value);
|
||||
if self.consume_token(&Token::Comma) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if parenthesized_assignment {
|
||||
self.expect_token(&Token::RParen)?;
|
||||
}
|
||||
return Ok(values);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_set_assignment(
|
||||
&mut self,
|
||||
) -> Result<(OneOrManyWithParens<ObjectName>, Expr), ParserError> {
|
||||
let variables = if self.dialect.supports_parenthesized_set_variables()
|
||||
&& self.consume_token(&Token::LParen)
|
||||
{
|
||||
let variables = OneOrManyWithParens::Many(
|
||||
let vars = OneOrManyWithParens::Many(
|
||||
self.parse_comma_separated(|parser: &mut Parser<'a>| parser.parse_identifier())?
|
||||
.into_iter()
|
||||
.map(|ident| ObjectName::from(vec![ident]))
|
||||
.collect(),
|
||||
);
|
||||
self.expect_token(&Token::RParen)?;
|
||||
variables
|
||||
vars
|
||||
} else {
|
||||
OneOrManyWithParens::One(self.parse_object_name(false)?)
|
||||
};
|
||||
|
||||
let names = matches!(&variables, OneOrManyWithParens::One(variable) if variable.to_string().eq_ignore_ascii_case("NAMES"));
|
||||
if !(self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO)) {
|
||||
return self.expected("assignment operator", self.peek_token());
|
||||
}
|
||||
|
||||
if names && self.dialect.supports_set_names() {
|
||||
if self.parse_keyword(Keyword::DEFAULT) {
|
||||
return Ok(Statement::SetNamesDefault {});
|
||||
let values = self.parse_expr()?;
|
||||
|
||||
Ok((variables, values))
|
||||
}
|
||||
|
||||
fn parse_set(&mut self) -> Result<Statement, ParserError> {
|
||||
let modifier =
|
||||
self.parse_one_of_keywords(&[Keyword::SESSION, Keyword::LOCAL, Keyword::HIVEVAR]);
|
||||
|
||||
if let Some(Keyword::HIVEVAR) = modifier {
|
||||
self.expect_token(&Token::Colon)?;
|
||||
}
|
||||
|
||||
if let Some(set_role_stmt) = self.maybe_parse(|parser| parser.parse_set_role(modifier))? {
|
||||
return Ok(set_role_stmt);
|
||||
}
|
||||
|
||||
// Handle special cases first
|
||||
if self.parse_keywords(&[Keyword::TIME, Keyword::ZONE])
|
||||
|| self.parse_keyword(Keyword::TIMEZONE)
|
||||
{
|
||||
if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) {
|
||||
return Ok(Set::SingleAssignment {
|
||||
local: modifier == Some(Keyword::LOCAL),
|
||||
hivevar: modifier == Some(Keyword::HIVEVAR),
|
||||
variable: ObjectName::from(vec!["TIMEZONE".into()]),
|
||||
values: self.parse_set_values(false)?,
|
||||
}
|
||||
.into());
|
||||
} else {
|
||||
// A shorthand alias for SET TIME ZONE that doesn't require
|
||||
// the assignment operator. It's originally PostgreSQL specific,
|
||||
// but we allow it for all the dialects
|
||||
return Ok(Set::SetTimeZone {
|
||||
local: modifier == Some(Keyword::LOCAL),
|
||||
value: self.parse_expr()?,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
} else if self.dialect.supports_set_names() && self.parse_keyword(Keyword::NAMES) {
|
||||
if self.parse_keyword(Keyword::DEFAULT) {
|
||||
return Ok(Set::SetNamesDefault {}.into());
|
||||
}
|
||||
|
||||
let charset_name = self.parse_identifier()?;
|
||||
let collation_name = if self.parse_one_of_keywords(&[Keyword::COLLATE]).is_some() {
|
||||
Some(self.parse_literal_string()?)
|
||||
|
@ -11026,86 +11088,117 @@ impl<'a> Parser<'a> {
|
|||
None
|
||||
};
|
||||
|
||||
return Ok(Statement::SetNames {
|
||||
return Ok(Set::SetNames {
|
||||
charset_name,
|
||||
collation_name,
|
||||
});
|
||||
}
|
||||
|
||||
let parenthesized_assignment = matches!(&variables, OneOrManyWithParens::Many(_));
|
||||
|
||||
if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) {
|
||||
if parenthesized_assignment {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
}
|
||||
|
||||
let mut values = vec![];
|
||||
loop {
|
||||
let value = if let Some(expr) = self.try_parse_expr_sub_query()? {
|
||||
expr
|
||||
} else if let Ok(expr) = self.parse_expr() {
|
||||
expr
|
||||
} else {
|
||||
self.expected("variable value", self.peek_token())?
|
||||
};
|
||||
|
||||
values.push(value);
|
||||
if self.consume_token(&Token::Comma) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if parenthesized_assignment {
|
||||
self.expect_token(&Token::RParen)?;
|
||||
}
|
||||
return Ok(Statement::SetVariable {
|
||||
local: modifier == Some(Keyword::LOCAL),
|
||||
hivevar: Some(Keyword::HIVEVAR) == modifier,
|
||||
variables,
|
||||
value: values,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let OneOrManyWithParens::One(variable) = variables else {
|
||||
return self.expected("set variable", self.peek_token());
|
||||
};
|
||||
|
||||
if variable.to_string().eq_ignore_ascii_case("TIMEZONE") {
|
||||
// for some db (e.g. postgresql), SET TIME ZONE <value> is an alias for SET TIMEZONE [TO|=] <value>
|
||||
match self.parse_expr() {
|
||||
Ok(expr) => Ok(Statement::SetTimeZone {
|
||||
local: modifier == Some(Keyword::LOCAL),
|
||||
value: expr,
|
||||
}),
|
||||
_ => self.expected("timezone value", self.peek_token())?,
|
||||
}
|
||||
} else if variable.to_string() == "CHARACTERISTICS" {
|
||||
.into());
|
||||
} else if self.parse_keyword(Keyword::CHARACTERISTICS) {
|
||||
self.expect_keywords(&[Keyword::AS, Keyword::TRANSACTION])?;
|
||||
Ok(Statement::SetTransaction {
|
||||
return Ok(Set::SetTransaction {
|
||||
modes: self.parse_transaction_modes()?,
|
||||
snapshot: None,
|
||||
session: true,
|
||||
})
|
||||
} else if variable.to_string() == "TRANSACTION" && modifier.is_none() {
|
||||
}
|
||||
.into());
|
||||
} else if self.parse_keyword(Keyword::TRANSACTION) {
|
||||
if self.parse_keyword(Keyword::SNAPSHOT) {
|
||||
let snapshot_id = self.parse_value()?.value;
|
||||
return Ok(Statement::SetTransaction {
|
||||
return Ok(Set::SetTransaction {
|
||||
modes: vec![],
|
||||
snapshot: Some(snapshot_id),
|
||||
session: false,
|
||||
});
|
||||
}
|
||||
.into());
|
||||
}
|
||||
Ok(Statement::SetTransaction {
|
||||
return Ok(Set::SetTransaction {
|
||||
modes: self.parse_transaction_modes()?,
|
||||
snapshot: None,
|
||||
session: false,
|
||||
})
|
||||
} else if self.dialect.supports_set_stmt_without_operator() {
|
||||
self.prev_token();
|
||||
self.parse_set_session_params()
|
||||
} else {
|
||||
self.expected("equals sign or TO", self.peek_token())
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
if self.dialect.supports_comma_separated_set_assignments() {
|
||||
if let Some(assignments) = self
|
||||
.maybe_parse(|parser| parser.parse_comma_separated(Parser::parse_set_assignment))?
|
||||
{
|
||||
return if assignments.len() > 1 {
|
||||
let assignments = assignments
|
||||
.into_iter()
|
||||
.map(|(var, val)| match var {
|
||||
OneOrManyWithParens::One(v) => Ok(SetAssignment {
|
||||
name: v,
|
||||
value: val,
|
||||
}),
|
||||
OneOrManyWithParens::Many(_) => {
|
||||
self.expected("List of single identifiers", self.peek_token())
|
||||
}
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(Set::MultipleAssignments { assignments }.into())
|
||||
} else {
|
||||
let (vars, values): (Vec<_>, Vec<_>) = assignments.into_iter().unzip();
|
||||
|
||||
let variable = match vars.into_iter().next() {
|
||||
Some(OneOrManyWithParens::One(v)) => Ok(v),
|
||||
Some(OneOrManyWithParens::Many(_)) => self.expected(
|
||||
"Single assignment or list of assignments",
|
||||
self.peek_token(),
|
||||
),
|
||||
None => self.expected("At least one identifier", self.peek_token()),
|
||||
}?;
|
||||
|
||||
Ok(Set::SingleAssignment {
|
||||
local: modifier == Some(Keyword::LOCAL),
|
||||
hivevar: modifier == Some(Keyword::HIVEVAR),
|
||||
variable,
|
||||
values,
|
||||
}
|
||||
.into())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let variables = if self.dialect.supports_parenthesized_set_variables()
|
||||
&& self.consume_token(&Token::LParen)
|
||||
{
|
||||
let vars = OneOrManyWithParens::Many(
|
||||
self.parse_comma_separated(|parser: &mut Parser<'a>| parser.parse_identifier())?
|
||||
.into_iter()
|
||||
.map(|ident| ObjectName::from(vec![ident]))
|
||||
.collect(),
|
||||
);
|
||||
self.expect_token(&Token::RParen)?;
|
||||
vars
|
||||
} else {
|
||||
OneOrManyWithParens::One(self.parse_object_name(false)?)
|
||||
};
|
||||
|
||||
if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) {
|
||||
let stmt = match variables {
|
||||
OneOrManyWithParens::One(var) => Set::SingleAssignment {
|
||||
local: modifier == Some(Keyword::LOCAL),
|
||||
hivevar: modifier == Some(Keyword::HIVEVAR),
|
||||
variable: var,
|
||||
values: self.parse_set_values(false)?,
|
||||
},
|
||||
OneOrManyWithParens::Many(vars) => Set::ParenthesizedAssignments {
|
||||
variables: vars,
|
||||
values: self.parse_set_values(true)?,
|
||||
},
|
||||
};
|
||||
|
||||
return Ok(stmt.into());
|
||||
}
|
||||
|
||||
if self.dialect.supports_set_stmt_without_operator() {
|
||||
self.prev_token();
|
||||
return self.parse_set_session_params();
|
||||
};
|
||||
|
||||
self.expected("equals sign or TO", self.peek_token())
|
||||
}
|
||||
|
||||
pub fn parse_set_session_params(&mut self) -> Result<Statement, ParserError> {
|
||||
|
@ -11123,15 +11216,20 @@ impl<'a> Parser<'a> {
|
|||
_ => return self.expected("IO, PROFILE, TIME or XML", self.peek_token()),
|
||||
};
|
||||
let value = self.parse_session_param_value()?;
|
||||
Ok(Statement::SetSessionParam(SetSessionParamKind::Statistics(
|
||||
SetSessionParamStatistics { topic, value },
|
||||
)))
|
||||
Ok(
|
||||
Set::SetSessionParam(SetSessionParamKind::Statistics(SetSessionParamStatistics {
|
||||
topic,
|
||||
value,
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
} else if self.parse_keyword(Keyword::IDENTITY_INSERT) {
|
||||
let obj = self.parse_object_name(false)?;
|
||||
let value = self.parse_session_param_value()?;
|
||||
Ok(Statement::SetSessionParam(
|
||||
SetSessionParamKind::IdentityInsert(SetSessionParamIdentityInsert { obj, value }),
|
||||
Ok(Set::SetSessionParam(SetSessionParamKind::IdentityInsert(
|
||||
SetSessionParamIdentityInsert { obj, value },
|
||||
))
|
||||
.into())
|
||||
} else if self.parse_keyword(Keyword::OFFSETS) {
|
||||
let keywords = self.parse_comma_separated(|parser| {
|
||||
let next_token = parser.next_token();
|
||||
|
@ -11141,9 +11239,13 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
})?;
|
||||
let value = self.parse_session_param_value()?;
|
||||
Ok(Statement::SetSessionParam(SetSessionParamKind::Offsets(
|
||||
SetSessionParamOffsets { keywords, value },
|
||||
)))
|
||||
Ok(
|
||||
Set::SetSessionParam(SetSessionParamKind::Offsets(SetSessionParamOffsets {
|
||||
keywords,
|
||||
value,
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
let names = self.parse_comma_separated(|parser| {
|
||||
let next_token = parser.next_token();
|
||||
|
@ -11153,9 +11255,13 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
})?;
|
||||
let value = self.parse_expr()?.to_string();
|
||||
Ok(Statement::SetSessionParam(SetSessionParamKind::Generic(
|
||||
SetSessionParamGeneric { names, value },
|
||||
)))
|
||||
Ok(
|
||||
Set::SetSessionParam(SetSessionParamKind::Generic(SetSessionParamGeneric {
|
||||
names,
|
||||
value,
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8555,11 +8555,11 @@ fn parse_set_transaction() {
|
|||
// TRANSACTION, so no need to duplicate the tests here. We just do a quick
|
||||
// sanity check.
|
||||
match verified_stmt("SET TRANSACTION READ ONLY, READ WRITE, ISOLATION LEVEL SERIALIZABLE") {
|
||||
Statement::SetTransaction {
|
||||
Statement::Set(Set::SetTransaction {
|
||||
modes,
|
||||
session,
|
||||
snapshot,
|
||||
} => {
|
||||
}) => {
|
||||
assert_eq!(
|
||||
modes,
|
||||
vec![
|
||||
|
@ -8578,20 +8578,17 @@ fn parse_set_transaction() {
|
|||
#[test]
|
||||
fn parse_set_variable() {
|
||||
match verified_stmt("SET SOMETHING = '1'") {
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local,
|
||||
hivevar,
|
||||
variables,
|
||||
value,
|
||||
} => {
|
||||
variable,
|
||||
values,
|
||||
}) => {
|
||||
assert!(!local);
|
||||
assert!(!hivevar);
|
||||
assert_eq!(variable, ObjectName::from(vec!["SOMETHING".into()]));
|
||||
assert_eq!(
|
||||
variables,
|
||||
OneOrManyWithParens::One(ObjectName::from(vec!["SOMETHING".into()]))
|
||||
);
|
||||
assert_eq!(
|
||||
value,
|
||||
values,
|
||||
vec![Expr::Value(
|
||||
(Value::SingleQuotedString("1".into())).with_empty_span()
|
||||
)]
|
||||
|
@ -8603,24 +8600,17 @@ fn parse_set_variable() {
|
|||
let multi_variable_dialects = all_dialects_where(|d| d.supports_parenthesized_set_variables());
|
||||
let sql = r#"SET (a, b, c) = (1, 2, 3)"#;
|
||||
match multi_variable_dialects.verified_stmt(sql) {
|
||||
Statement::SetVariable {
|
||||
local,
|
||||
hivevar,
|
||||
variables,
|
||||
value,
|
||||
} => {
|
||||
assert!(!local);
|
||||
assert!(!hivevar);
|
||||
Statement::Set(Set::ParenthesizedAssignments { variables, values }) => {
|
||||
assert_eq!(
|
||||
variables,
|
||||
OneOrManyWithParens::Many(vec![
|
||||
vec![
|
||||
ObjectName::from(vec!["a".into()]),
|
||||
ObjectName::from(vec!["b".into()]),
|
||||
ObjectName::from(vec!["c".into()]),
|
||||
])
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
value,
|
||||
values,
|
||||
vec![
|
||||
Expr::value(number("1")),
|
||||
Expr::value(number("2")),
|
||||
|
@ -8680,20 +8670,17 @@ fn parse_set_variable() {
|
|||
#[test]
|
||||
fn parse_set_role_as_variable() {
|
||||
match verified_stmt("SET role = 'foobar'") {
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local,
|
||||
hivevar,
|
||||
variables,
|
||||
value,
|
||||
} => {
|
||||
variable,
|
||||
values,
|
||||
}) => {
|
||||
assert!(!local);
|
||||
assert!(!hivevar);
|
||||
assert_eq!(variable, ObjectName::from(vec!["role".into()]));
|
||||
assert_eq!(
|
||||
variables,
|
||||
OneOrManyWithParens::One(ObjectName::from(vec!["role".into()]))
|
||||
);
|
||||
assert_eq!(
|
||||
value,
|
||||
values,
|
||||
vec![Expr::Value(
|
||||
(Value::SingleQuotedString("foobar".into())).with_empty_span()
|
||||
)]
|
||||
|
@ -8730,20 +8717,17 @@ fn parse_double_colon_cast_at_timezone() {
|
|||
#[test]
|
||||
fn parse_set_time_zone() {
|
||||
match verified_stmt("SET TIMEZONE = 'UTC'") {
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local,
|
||||
hivevar,
|
||||
variables: variable,
|
||||
value,
|
||||
} => {
|
||||
variable,
|
||||
values,
|
||||
}) => {
|
||||
assert!(!local);
|
||||
assert!(!hivevar);
|
||||
assert_eq!(variable, ObjectName::from(vec!["TIMEZONE".into()]));
|
||||
assert_eq!(
|
||||
variable,
|
||||
OneOrManyWithParens::One(ObjectName::from(vec!["TIMEZONE".into()]))
|
||||
);
|
||||
assert_eq!(
|
||||
value,
|
||||
values,
|
||||
vec![Expr::Value(
|
||||
(Value::SingleQuotedString("UTC".into())).with_empty_span()
|
||||
)]
|
||||
|
@ -8755,20 +8739,6 @@ fn parse_set_time_zone() {
|
|||
one_statement_parses_to("SET TIME ZONE TO 'UTC'", "SET TIMEZONE = 'UTC'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_set_time_zone_alias() {
|
||||
match verified_stmt("SET TIME ZONE 'UTC'") {
|
||||
Statement::SetTimeZone { local, value } => {
|
||||
assert!(!local);
|
||||
assert_eq!(
|
||||
value,
|
||||
Expr::Value((Value::SingleQuotedString("UTC".into())).with_empty_span())
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_commit() {
|
||||
match verified_stmt("COMMIT") {
|
||||
|
@ -14681,3 +14651,44 @@ fn parse_set_names() {
|
|||
dialects.verified_stmt("SET NAMES 'utf8'");
|
||||
dialects.verified_stmt("SET NAMES UTF8 COLLATE bogus");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_set_statements() -> Result<(), ParserError> {
|
||||
let dialects = all_dialects_where(|d| d.supports_comma_separated_set_assignments());
|
||||
let stmt = dialects.verified_stmt("SET @a = 1, b = 2");
|
||||
|
||||
match stmt {
|
||||
Statement::Set(Set::MultipleAssignments { assignments }) => {
|
||||
assert_eq!(
|
||||
assignments,
|
||||
vec![
|
||||
SetAssignment {
|
||||
name: ObjectName::from(vec!["@a".into()]),
|
||||
value: Expr::value(number("1"))
|
||||
},
|
||||
SetAssignment {
|
||||
name: ObjectName::from(vec!["b".into()]),
|
||||
value: Expr::value(number("2"))
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
_ => panic!("Expected SetVariable with 2 variables and 2 values"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_set_time_zone_alias() {
|
||||
match all_dialects().verified_stmt("SET TIME ZONE 'UTC'") {
|
||||
Statement::Set(Set::SetTimeZone { local, value }) => {
|
||||
assert!(!local);
|
||||
assert_eq!(
|
||||
value,
|
||||
Expr::Value((Value::SingleQuotedString("UTC".into())).with_empty_span())
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,8 @@
|
|||
|
||||
use sqlparser::ast::{
|
||||
ClusteredBy, CommentDef, CreateFunction, CreateFunctionBody, CreateFunctionUsing, CreateTable,
|
||||
Expr, Function, FunctionArgumentList, FunctionArguments, Ident, ObjectName,
|
||||
OneOrManyWithParens, OrderByExpr, OrderByOptions, SelectItem, Statement, TableFactor,
|
||||
UnaryOperator, Use, Value,
|
||||
Expr, Function, FunctionArgumentList, FunctionArguments, Ident, ObjectName, OrderByExpr,
|
||||
OrderByOptions, SelectItem, Set, Statement, TableFactor, UnaryOperator, Use, Value,
|
||||
};
|
||||
use sqlparser::dialect::{GenericDialect, HiveDialect, MsSqlDialect};
|
||||
use sqlparser::parser::ParserError;
|
||||
|
@ -92,7 +91,7 @@ fn parse_msck() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn parse_set() {
|
||||
fn parse_set_hivevar() {
|
||||
let set = "SET HIVEVAR:name = a, b, c_d";
|
||||
hive().verified_stmt(set);
|
||||
}
|
||||
|
@ -369,20 +368,20 @@ fn from_cte() {
|
|||
fn set_statement_with_minus() {
|
||||
assert_eq!(
|
||||
hive().verified_stmt("SET hive.tez.java.opts = -Xmx4g"),
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: false,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![
|
||||
variable: ObjectName::from(vec![
|
||||
Ident::new("hive"),
|
||||
Ident::new("tez"),
|
||||
Ident::new("java"),
|
||||
Ident::new("opts")
|
||||
])),
|
||||
value: vec![Expr::UnaryOp {
|
||||
]),
|
||||
values: vec![Expr::UnaryOp {
|
||||
op: UnaryOperator::Minus,
|
||||
expr: Box::new(Expr::Identifier(Ident::new("Xmx4g")))
|
||||
}],
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
|
|
@ -1254,14 +1254,14 @@ fn parse_mssql_declare() {
|
|||
for_query: None
|
||||
}]
|
||||
},
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: false,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![Ident::new("@bar")])),
|
||||
value: vec![Expr::Value(
|
||||
variable: ObjectName::from(vec![Ident::new("@bar")]),
|
||||
values: vec![Expr::Value(
|
||||
(Value::Number("2".parse().unwrap(), false)).with_empty_span()
|
||||
)],
|
||||
},
|
||||
}),
|
||||
Statement::Query(Box::new(Query {
|
||||
with: None,
|
||||
limit: None,
|
||||
|
|
|
@ -617,12 +617,12 @@ fn parse_set_variables() {
|
|||
mysql_and_generic().verified_stmt("SET sql_mode = CONCAT(@@sql_mode, ',STRICT_TRANS_TABLES')");
|
||||
assert_eq!(
|
||||
mysql_and_generic().verified_stmt("SET LOCAL autocommit = 1"),
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: true,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec!["autocommit".into()])),
|
||||
value: vec![Expr::value(number("1"))],
|
||||
}
|
||||
variable: ObjectName::from(vec!["autocommit".into()]),
|
||||
values: vec![Expr::value(number("1"))],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2705,19 +2705,19 @@ fn parse_set_names() {
|
|||
let stmt = mysql_and_generic().verified_stmt("SET NAMES utf8mb4");
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetNames {
|
||||
Statement::Set(Set::SetNames {
|
||||
charset_name: "utf8mb4".into(),
|
||||
collation_name: None,
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let stmt = mysql_and_generic().verified_stmt("SET NAMES utf8mb4 COLLATE bogus");
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetNames {
|
||||
Statement::Set(Set::SetNames {
|
||||
charset_name: "utf8mb4".into(),
|
||||
collation_name: Some("bogus".to_string()),
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let stmt = mysql_and_generic()
|
||||
|
@ -2725,14 +2725,14 @@ fn parse_set_names() {
|
|||
.unwrap();
|
||||
assert_eq!(
|
||||
stmt,
|
||||
vec![Statement::SetNames {
|
||||
vec![Statement::Set(Set::SetNames {
|
||||
charset_name: "utf8mb4".into(),
|
||||
collation_name: Some("bogus".to_string()),
|
||||
}]
|
||||
})]
|
||||
);
|
||||
|
||||
let stmt = mysql_and_generic().verified_stmt("SET NAMES DEFAULT");
|
||||
assert_eq!(stmt, Statement::SetNamesDefault {});
|
||||
assert_eq!(stmt, Statement::Set(Set::SetNamesDefault {}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1432,81 +1432,77 @@ fn parse_set() {
|
|||
let stmt = pg_and_generic().verified_stmt("SET a = b");
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: false,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![Ident::new("a")])),
|
||||
value: vec![Expr::Identifier(Ident {
|
||||
variable: ObjectName::from(vec![Ident::new("a")]),
|
||||
values: vec![Expr::Identifier(Ident {
|
||||
value: "b".into(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
})],
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let stmt = pg_and_generic().verified_stmt("SET a = 'b'");
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: false,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![Ident::new("a")])),
|
||||
value: vec![Expr::Value(
|
||||
variable: ObjectName::from(vec![Ident::new("a")]),
|
||||
values: vec![Expr::Value(
|
||||
(Value::SingleQuotedString("b".into())).with_empty_span()
|
||||
)],
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let stmt = pg_and_generic().verified_stmt("SET a = 0");
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: false,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![Ident::new("a")])),
|
||||
value: vec![Expr::value(number("0"))],
|
||||
}
|
||||
variable: ObjectName::from(vec![Ident::new("a")]),
|
||||
values: vec![Expr::value(number("0"))],
|
||||
})
|
||||
);
|
||||
|
||||
let stmt = pg_and_generic().verified_stmt("SET a = DEFAULT");
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: false,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![Ident::new("a")])),
|
||||
value: vec![Expr::Identifier(Ident::new("DEFAULT"))],
|
||||
}
|
||||
variable: ObjectName::from(vec![Ident::new("a")]),
|
||||
values: vec![Expr::Identifier(Ident::new("DEFAULT"))],
|
||||
})
|
||||
);
|
||||
|
||||
let stmt = pg_and_generic().verified_stmt("SET LOCAL a = b");
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: true,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![Ident::new("a")])),
|
||||
value: vec![Expr::Identifier("b".into())],
|
||||
}
|
||||
variable: ObjectName::from(vec![Ident::new("a")]),
|
||||
values: vec![Expr::Identifier("b".into())],
|
||||
})
|
||||
);
|
||||
|
||||
let stmt = pg_and_generic().verified_stmt("SET a.b.c = b");
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: false,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![
|
||||
Ident::new("a"),
|
||||
Ident::new("b"),
|
||||
Ident::new("c")
|
||||
])),
|
||||
value: vec![Expr::Identifier(Ident {
|
||||
variable: ObjectName::from(vec![Ident::new("a"), Ident::new("b"), Ident::new("c")]),
|
||||
values: vec![Expr::Identifier(Ident {
|
||||
value: "b".into(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
})],
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let stmt = pg_and_generic().one_statement_parses_to(
|
||||
|
@ -1515,18 +1511,18 @@ fn parse_set() {
|
|||
);
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetVariable {
|
||||
Statement::Set(Set::SingleAssignment {
|
||||
local: false,
|
||||
hivevar: false,
|
||||
variables: OneOrManyWithParens::One(ObjectName::from(vec![
|
||||
variable: ObjectName::from(vec![
|
||||
Ident::new("hive"),
|
||||
Ident::new("tez"),
|
||||
Ident::new("auto"),
|
||||
Ident::new("reducer"),
|
||||
Ident::new("parallelism")
|
||||
])),
|
||||
value: vec![Expr::Value((Value::Boolean(false)).with_empty_span())],
|
||||
}
|
||||
]),
|
||||
values: vec![Expr::Value((Value::Boolean(false)).with_empty_span())],
|
||||
})
|
||||
);
|
||||
|
||||
pg_and_generic().one_statement_parses_to("SET a TO b", "SET a = b");
|
||||
|
@ -1560,10 +1556,10 @@ fn parse_set_role() {
|
|||
let stmt = pg_and_generic().verified_stmt(query);
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetRole {
|
||||
Statement::Set(Set::SetRole {
|
||||
context_modifier: ContextModifier::Session,
|
||||
role_name: None,
|
||||
}
|
||||
})
|
||||
);
|
||||
assert_eq!(query, stmt.to_string());
|
||||
|
||||
|
@ -1571,14 +1567,14 @@ fn parse_set_role() {
|
|||
let stmt = pg_and_generic().verified_stmt(query);
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetRole {
|
||||
Statement::Set(Set::SetRole {
|
||||
context_modifier: ContextModifier::Local,
|
||||
role_name: Some(Ident {
|
||||
value: "rolename".to_string(),
|
||||
quote_style: Some('\"'),
|
||||
span: Span::empty(),
|
||||
}),
|
||||
}
|
||||
})
|
||||
);
|
||||
assert_eq!(query, stmt.to_string());
|
||||
|
||||
|
@ -1586,14 +1582,14 @@ fn parse_set_role() {
|
|||
let stmt = pg_and_generic().verified_stmt(query);
|
||||
assert_eq!(
|
||||
stmt,
|
||||
Statement::SetRole {
|
||||
Statement::Set(Set::SetRole {
|
||||
context_modifier: ContextModifier::None,
|
||||
role_name: Some(Ident {
|
||||
value: "rolename".to_string(),
|
||||
quote_style: Some('\''),
|
||||
span: Span::empty(),
|
||||
}),
|
||||
}
|
||||
})
|
||||
);
|
||||
assert_eq!(query, stmt.to_string());
|
||||
}
|
||||
|
@ -2982,16 +2978,16 @@ fn test_transaction_statement() {
|
|||
let statement = pg().verified_stmt("SET TRANSACTION SNAPSHOT '000003A1-1'");
|
||||
assert_eq!(
|
||||
statement,
|
||||
Statement::SetTransaction {
|
||||
Statement::Set(Set::SetTransaction {
|
||||
modes: vec![],
|
||||
snapshot: Some(Value::SingleQuotedString(String::from("000003A1-1"))),
|
||||
session: false
|
||||
}
|
||||
})
|
||||
);
|
||||
let statement = pg().verified_stmt("SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY, READ WRITE, ISOLATION LEVEL SERIALIZABLE");
|
||||
assert_eq!(
|
||||
statement,
|
||||
Statement::SetTransaction {
|
||||
Statement::Set(Set::SetTransaction {
|
||||
modes: vec![
|
||||
TransactionMode::AccessMode(TransactionAccessMode::ReadOnly),
|
||||
TransactionMode::AccessMode(TransactionAccessMode::ReadWrite),
|
||||
|
@ -2999,7 +2995,7 @@ fn test_transaction_statement() {
|
|||
],
|
||||
snapshot: None,
|
||||
session: true
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue