This is an automated email from the ASF dual-hosted git repository.
iffyio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 3b4dc0f2 MsSQL SET for session params (#1646)
3b4dc0f2 is described below
commit 3b4dc0f2272b636e741cdd23512e766659670a75
Author: Yoav Cohen <[email protected]>
AuthorDate: Sat Jan 11 10:51:01 2025 +0100
MsSQL SET for session params (#1646)
---
src/ast/mod.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++++
src/ast/spans.rs | 1 +
src/dialect/mod.rs | 6 +++
src/dialect/mssql.rs | 5 ++
src/keywords.rs | 5 ++
src/parser/mod.rs | 66 ++++++++++++++++++++++++
tests/sqlparser_mssql.rs | 61 +++++++++++++++++++++++
7 files changed, 271 insertions(+)
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 1f8df352..2d79f7d6 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -3437,6 +3437,10 @@ 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),
}
impl fmt::Display for Statement {
@@ -5024,6 +5028,7 @@ 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}"),
}
}
}
@@ -6441,6 +6446,7 @@ pub enum TransactionIsolationLevel {
ReadCommitted,
RepeatableRead,
Serializable,
+ Snapshot,
}
impl fmt::Display for TransactionIsolationLevel {
@@ -6451,6 +6457,7 @@ impl fmt::Display for TransactionIsolationLevel {
ReadCommitted => "READ COMMITTED",
RepeatableRead => "REPEATABLE READ",
Serializable => "SERIALIZABLE",
+ Snapshot => "SNAPSHOT",
})
}
}
@@ -7937,6 +7944,126 @@ impl fmt::Display for TableObject {
}
}
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum SetSessionParamKind {
+ Generic(SetSessionParamGeneric),
+ IdentityInsert(SetSessionParamIdentityInsert),
+ Offsets(SetSessionParamOffsets),
+ Statistics(SetSessionParamStatistics),
+}
+
+impl fmt::Display for SetSessionParamKind {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ SetSessionParamKind::Generic(x) => write!(f, "{x}"),
+ SetSessionParamKind::IdentityInsert(x) => write!(f, "{x}"),
+ SetSessionParamKind::Offsets(x) => write!(f, "{x}"),
+ SetSessionParamKind::Statistics(x) => write!(f, "{x}"),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct SetSessionParamGeneric {
+ pub names: Vec<String>,
+ pub value: String,
+}
+
+impl fmt::Display for SetSessionParamGeneric {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} {}", display_comma_separated(&self.names), self.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 SetSessionParamIdentityInsert {
+ pub obj: ObjectName,
+ pub value: SessionParamValue,
+}
+
+impl fmt::Display for SetSessionParamIdentityInsert {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "IDENTITY_INSERT {} {}", self.obj, self.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 SetSessionParamOffsets {
+ pub keywords: Vec<String>,
+ pub value: SessionParamValue,
+}
+
+impl fmt::Display for SetSessionParamOffsets {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "OFFSETS {} {}",
+ display_comma_separated(&self.keywords),
+ self.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 SetSessionParamStatistics {
+ pub topic: SessionParamStatsTopic,
+ pub value: SessionParamValue,
+}
+
+impl fmt::Display for SetSessionParamStatistics {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "STATISTICS {} {}", self.topic, self.value)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum SessionParamStatsTopic {
+ IO,
+ Profile,
+ Time,
+ Xml,
+}
+
+impl fmt::Display for SessionParamStatsTopic {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ SessionParamStatsTopic::IO => write!(f, "IO"),
+ SessionParamStatsTopic::Profile => write!(f, "PROFILE"),
+ SessionParamStatsTopic::Time => write!(f, "TIME"),
+ SessionParamStatsTopic::Xml => write!(f, "XML"),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum SessionParamValue {
+ On,
+ Off,
+}
+
+impl fmt::Display for SessionParamValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ SessionParamValue::On => write!(f, "ON"),
+ SessionParamValue::Off => write!(f, "OFF"),
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 19f6074b..183bebf8 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -496,6 +496,7 @@ impl Spanned for Statement {
Statement::UNLISTEN { .. } => Span::empty(),
Statement::RenameTable { .. } => Span::empty(),
Statement::List(..) | Statement::Remove(..) => Span::empty(),
+ Statement::SetSessionParam { .. } => Span::empty(),
}
}
}
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 32b0ed48..c66982d1 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -802,6 +802,12 @@ pub trait Dialect: Debug + Any {
fn supports_insert_format(&self) -> bool {
false
}
+
+ /// Returns true if this dialect supports `SET` statements without an
explicit
+ /// assignment operator such as `=`. For example: `SET SHOWPLAN_XML ON`.
+ fn supports_set_stmt_without_operator(&self) -> bool {
+ false
+ }
}
/// This represents the operators for which precedence must be defined
diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs
index fa77bdc1..67a64894 100644
--- a/src/dialect/mssql.rs
+++ b/src/dialect/mssql.rs
@@ -85,4 +85,9 @@ impl Dialect for MsSqlDialect {
fn supports_end_transaction_modifier(&self) -> bool {
true
}
+
+ /// See:
<https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
+ fn supports_set_stmt_without_operator(&self) -> bool {
+ true
+ }
}
diff --git a/src/keywords.rs b/src/keywords.rs
index 8c8860e1..8c8077f5 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -388,6 +388,7 @@ define_keywords!(
HOURS,
ID,
IDENTITY,
+ IDENTITY_INSERT,
IF,
IGNORE,
ILIKE,
@@ -426,6 +427,7 @@ define_keywords!(
INTERVAL,
INTO,
INVOKER,
+ IO,
IS,
ISODOW,
ISOLATION,
@@ -557,7 +559,9 @@ define_keywords!(
OCTETS,
OCTET_LENGTH,
OF,
+ OFF,
OFFSET,
+ OFFSETS,
OLD,
OMIT,
ON,
@@ -623,6 +627,7 @@ define_keywords!(
PRIOR,
PRIVILEGES,
PROCEDURE,
+ PROFILE,
PROGRAM,
PROJECTION,
PUBLIC,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index c1740251..3cf3c585 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -10428,11 +10428,75 @@ impl<'a> Parser<'a> {
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())
}
}
+ pub fn parse_set_session_params(&mut self) -> Result<Statement,
ParserError> {
+ if self.parse_keyword(Keyword::STATISTICS) {
+ let topic = match self.parse_one_of_keywords(&[
+ Keyword::IO,
+ Keyword::PROFILE,
+ Keyword::TIME,
+ Keyword::XML,
+ ]) {
+ Some(Keyword::IO) => SessionParamStatsTopic::IO,
+ Some(Keyword::PROFILE) => SessionParamStatsTopic::Profile,
+ Some(Keyword::TIME) => SessionParamStatsTopic::Time,
+ Some(Keyword::XML) => SessionParamStatsTopic::Xml,
+ _ => 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 },
+ )))
+ } 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
}),
+ ))
+ } else if self.parse_keyword(Keyword::OFFSETS) {
+ let keywords = self.parse_comma_separated(|parser| {
+ let next_token = parser.next_token();
+ match &next_token.token {
+ Token::Word(w) => Ok(w.to_string()),
+ _ => parser.expected("SQL keyword", next_token),
+ }
+ })?;
+ let value = self.parse_session_param_value()?;
+ Ok(Statement::SetSessionParam(SetSessionParamKind::Offsets(
+ SetSessionParamOffsets { keywords, value },
+ )))
+ } else {
+ let names = self.parse_comma_separated(|parser| {
+ let next_token = parser.next_token();
+ match next_token.token {
+ Token::Word(w) => Ok(w.to_string()),
+ _ => parser.expected("Session param name", next_token),
+ }
+ })?;
+ let value = self.parse_expr()?.to_string();
+ Ok(Statement::SetSessionParam(SetSessionParamKind::Generic(
+ SetSessionParamGeneric { names, value },
+ )))
+ }
+ }
+
+ fn parse_session_param_value(&mut self) -> Result<SessionParamValue,
ParserError> {
+ if self.parse_keyword(Keyword::ON) {
+ Ok(SessionParamValue::On)
+ } else if self.parse_keyword(Keyword::OFF) {
+ Ok(SessionParamValue::Off)
+ } else {
+ self.expected("ON or OFF", self.peek_token())
+ }
+ }
+
pub fn parse_show(&mut self) -> Result<Statement, ParserError> {
let terse = self.parse_keyword(Keyword::TERSE);
let extended = self.parse_keyword(Keyword::EXTENDED);
@@ -13004,6 +13068,8 @@ impl<'a> Parser<'a> {
TransactionIsolationLevel::RepeatableRead
} else if self.parse_keyword(Keyword::SERIALIZABLE) {
TransactionIsolationLevel::Serializable
+ } else if self.parse_keyword(Keyword::SNAPSHOT) {
+ TransactionIsolationLevel::Snapshot
} else {
self.expected("isolation level", self.peek_token())?
};
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index ecc874af..567cd538 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -1679,6 +1679,67 @@ fn parse_true_false_as_identifiers() {
);
}
+#[test]
+fn parse_mssql_set_session_value() {
+ ms().verified_stmt(
+ "SET OFFSETS SELECT, FROM, ORDER, TABLE, PROCEDURE, STATEMENT, PARAM,
EXECUTE ON",
+ );
+ ms().verified_stmt("SET IDENTITY_INSERT dbo.Tool ON");
+ ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
+ ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL READ COMMITTED");
+ ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");
+ ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL SNAPSHOT");
+ ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
+ ms().verified_stmt("SET STATISTICS IO ON");
+ ms().verified_stmt("SET STATISTICS XML ON");
+ ms().verified_stmt("SET STATISTICS PROFILE ON");
+ ms().verified_stmt("SET STATISTICS TIME ON");
+ ms().verified_stmt("SET DATEFIRST 7");
+ ms().verified_stmt("SET DATEFIRST @xxx");
+ ms().verified_stmt("SET DATEFIRST @@xxx");
+ ms().verified_stmt("SET DATEFORMAT dmy");
+ ms().verified_stmt("SET DATEFORMAT @datevar");
+ ms().verified_stmt("SET DATEFORMAT @@datevar");
+ ms().verified_stmt("SET DEADLOCK_PRIORITY 'LOW'");
+ ms().verified_stmt("SET DEADLOCK_PRIORITY LOW");
+ ms().verified_stmt("SET DEADLOCK_PRIORITY 8");
+ ms().verified_stmt("SET DEADLOCK_PRIORITY -8");
+ ms().verified_stmt("SET DEADLOCK_PRIORITY @xxx");
+ ms().verified_stmt("SET DEADLOCK_PRIORITY @@xxx");
+ ms().verified_stmt("SET LOCK_TIMEOUT 1800");
+ ms().verified_stmt("SET CONCAT_NULL_YIELDS_NULL ON");
+ ms().verified_stmt("SET CURSOR_CLOSE_ON_COMMIT ON");
+ ms().verified_stmt("SET FIPS_FLAGGER 'level'");
+ ms().verified_stmt("SET FIPS_FLAGGER OFF");
+ ms().verified_stmt("SET LANGUAGE Italian");
+ ms().verified_stmt("SET QUOTED_IDENTIFIER ON");
+ ms().verified_stmt("SET ARITHABORT ON");
+ ms().verified_stmt("SET ARITHIGNORE OFF");
+ ms().verified_stmt("SET FMTONLY ON");
+ ms().verified_stmt("SET NOCOUNT OFF");
+ ms().verified_stmt("SET NOEXEC ON");
+ ms().verified_stmt("SET NUMERIC_ROUNDABORT ON");
+ ms().verified_stmt("SET QUERY_GOVERNOR_COST_LIMIT 11");
+ ms().verified_stmt("SET ROWCOUNT 4");
+ ms().verified_stmt("SET ROWCOUNT @xxx");
+ ms().verified_stmt("SET ROWCOUNT @@xxx");
+ ms().verified_stmt("SET TEXTSIZE 11");
+ ms().verified_stmt("SET ANSI_DEFAULTS ON");
+ ms().verified_stmt("SET ANSI_NULL_DFLT_OFF ON");
+ ms().verified_stmt("SET ANSI_NULL_DFLT_ON ON");
+ ms().verified_stmt("SET ANSI_NULLS ON");
+ ms().verified_stmt("SET ANSI_PADDING ON");
+ ms().verified_stmt("SET ANSI_WARNINGS ON");
+ ms().verified_stmt("SET FORCEPLAN ON");
+ ms().verified_stmt("SET SHOWPLAN_ALL ON");
+ ms().verified_stmt("SET SHOWPLAN_TEXT ON");
+ ms().verified_stmt("SET SHOWPLAN_XML ON");
+ ms().verified_stmt("SET IMPLICIT_TRANSACTIONS ON");
+ ms().verified_stmt("SET REMOTE_PROC_TRANSACTIONS ON");
+ ms().verified_stmt("SET XACT_ABORT ON");
+ ms().verified_stmt("SET ANSI_NULLS, ANSI_PADDING ON");
+}
+
fn ms() -> TestedDialects {
TestedDialects::new(vec![Box::new(MsSqlDialect {})])
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]