Skip to content

Commit 3b4dc0f

Browse files
authored
MsSQL SET for session params (#1646)
1 parent 0c3b6c0 commit 3b4dc0f

File tree

7 files changed

+271
-0
lines changed

7 files changed

+271
-0
lines changed

src/ast/mod.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3437,6 +3437,10 @@ pub enum Statement {
34373437
/// Snowflake `REMOVE`
34383438
/// See: <https://docs.snowflake.com/en/sql-reference/sql/remove>
34393439
Remove(FileStagingCommand),
3440+
/// MS-SQL session
3441+
///
3442+
/// See <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
3443+
SetSessionParam(SetSessionParamKind),
34403444
}
34413445

34423446
impl fmt::Display for Statement {
@@ -5024,6 +5028,7 @@ impl fmt::Display for Statement {
50245028
}
50255029
Statement::List(command) => write!(f, "LIST {command}"),
50265030
Statement::Remove(command) => write!(f, "REMOVE {command}"),
5031+
Statement::SetSessionParam(kind) => write!(f, "SET {kind}"),
50275032
}
50285033
}
50295034
}
@@ -6441,6 +6446,7 @@ pub enum TransactionIsolationLevel {
64416446
ReadCommitted,
64426447
RepeatableRead,
64436448
Serializable,
6449+
Snapshot,
64446450
}
64456451

64466452
impl fmt::Display for TransactionIsolationLevel {
@@ -6451,6 +6457,7 @@ impl fmt::Display for TransactionIsolationLevel {
64516457
ReadCommitted => "READ COMMITTED",
64526458
RepeatableRead => "REPEATABLE READ",
64536459
Serializable => "SERIALIZABLE",
6460+
Snapshot => "SNAPSHOT",
64546461
})
64556462
}
64566463
}
@@ -7937,6 +7944,126 @@ impl fmt::Display for TableObject {
79377944
}
79387945
}
79397946

7947+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7948+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7949+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7950+
pub enum SetSessionParamKind {
7951+
Generic(SetSessionParamGeneric),
7952+
IdentityInsert(SetSessionParamIdentityInsert),
7953+
Offsets(SetSessionParamOffsets),
7954+
Statistics(SetSessionParamStatistics),
7955+
}
7956+
7957+
impl fmt::Display for SetSessionParamKind {
7958+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7959+
match self {
7960+
SetSessionParamKind::Generic(x) => write!(f, "{x}"),
7961+
SetSessionParamKind::IdentityInsert(x) => write!(f, "{x}"),
7962+
SetSessionParamKind::Offsets(x) => write!(f, "{x}"),
7963+
SetSessionParamKind::Statistics(x) => write!(f, "{x}"),
7964+
}
7965+
}
7966+
}
7967+
7968+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7969+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7970+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7971+
pub struct SetSessionParamGeneric {
7972+
pub names: Vec<String>,
7973+
pub value: String,
7974+
}
7975+
7976+
impl fmt::Display for SetSessionParamGeneric {
7977+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7978+
write!(f, "{} {}", display_comma_separated(&self.names), self.value)
7979+
}
7980+
}
7981+
7982+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7983+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7984+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7985+
pub struct SetSessionParamIdentityInsert {
7986+
pub obj: ObjectName,
7987+
pub value: SessionParamValue,
7988+
}
7989+
7990+
impl fmt::Display for SetSessionParamIdentityInsert {
7991+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7992+
write!(f, "IDENTITY_INSERT {} {}", self.obj, self.value)
7993+
}
7994+
}
7995+
7996+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7997+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7998+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7999+
pub struct SetSessionParamOffsets {
8000+
pub keywords: Vec<String>,
8001+
pub value: SessionParamValue,
8002+
}
8003+
8004+
impl fmt::Display for SetSessionParamOffsets {
8005+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8006+
write!(
8007+
f,
8008+
"OFFSETS {} {}",
8009+
display_comma_separated(&self.keywords),
8010+
self.value
8011+
)
8012+
}
8013+
}
8014+
8015+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8016+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8017+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8018+
pub struct SetSessionParamStatistics {
8019+
pub topic: SessionParamStatsTopic,
8020+
pub value: SessionParamValue,
8021+
}
8022+
8023+
impl fmt::Display for SetSessionParamStatistics {
8024+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8025+
write!(f, "STATISTICS {} {}", self.topic, self.value)
8026+
}
8027+
}
8028+
8029+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8030+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8031+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8032+
pub enum SessionParamStatsTopic {
8033+
IO,
8034+
Profile,
8035+
Time,
8036+
Xml,
8037+
}
8038+
8039+
impl fmt::Display for SessionParamStatsTopic {
8040+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8041+
match self {
8042+
SessionParamStatsTopic::IO => write!(f, "IO"),
8043+
SessionParamStatsTopic::Profile => write!(f, "PROFILE"),
8044+
SessionParamStatsTopic::Time => write!(f, "TIME"),
8045+
SessionParamStatsTopic::Xml => write!(f, "XML"),
8046+
}
8047+
}
8048+
}
8049+
8050+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8051+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8052+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8053+
pub enum SessionParamValue {
8054+
On,
8055+
Off,
8056+
}
8057+
8058+
impl fmt::Display for SessionParamValue {
8059+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8060+
match self {
8061+
SessionParamValue::On => write!(f, "ON"),
8062+
SessionParamValue::Off => write!(f, "OFF"),
8063+
}
8064+
}
8065+
}
8066+
79408067
#[cfg(test)]
79418068
mod tests {
79428069
use super::*;

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ impl Spanned for Statement {
496496
Statement::UNLISTEN { .. } => Span::empty(),
497497
Statement::RenameTable { .. } => Span::empty(),
498498
Statement::List(..) | Statement::Remove(..) => Span::empty(),
499+
Statement::SetSessionParam { .. } => Span::empty(),
499500
}
500501
}
501502
}

src/dialect/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,12 @@ pub trait Dialect: Debug + Any {
802802
fn supports_insert_format(&self) -> bool {
803803
false
804804
}
805+
806+
/// Returns true if this dialect supports `SET` statements without an explicit
807+
/// assignment operator such as `=`. For example: `SET SHOWPLAN_XML ON`.
808+
fn supports_set_stmt_without_operator(&self) -> bool {
809+
false
810+
}
805811
}
806812

807813
/// This represents the operators for which precedence must be defined

src/dialect/mssql.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,9 @@ impl Dialect for MsSqlDialect {
8585
fn supports_end_transaction_modifier(&self) -> bool {
8686
true
8787
}
88+
89+
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
90+
fn supports_set_stmt_without_operator(&self) -> bool {
91+
true
92+
}
8893
}

src/keywords.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ define_keywords!(
388388
HOURS,
389389
ID,
390390
IDENTITY,
391+
IDENTITY_INSERT,
391392
IF,
392393
IGNORE,
393394
ILIKE,
@@ -426,6 +427,7 @@ define_keywords!(
426427
INTERVAL,
427428
INTO,
428429
INVOKER,
430+
IO,
429431
IS,
430432
ISODOW,
431433
ISOLATION,
@@ -557,7 +559,9 @@ define_keywords!(
557559
OCTETS,
558560
OCTET_LENGTH,
559561
OF,
562+
OFF,
560563
OFFSET,
564+
OFFSETS,
561565
OLD,
562566
OMIT,
563567
ON,
@@ -623,6 +627,7 @@ define_keywords!(
623627
PRIOR,
624628
PRIVILEGES,
625629
PROCEDURE,
630+
PROFILE,
626631
PROGRAM,
627632
PROJECTION,
628633
PUBLIC,

src/parser/mod.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10428,11 +10428,75 @@ impl<'a> Parser<'a> {
1042810428
snapshot: None,
1042910429
session: false,
1043010430
})
10431+
} else if self.dialect.supports_set_stmt_without_operator() {
10432+
self.prev_token();
10433+
self.parse_set_session_params()
1043110434
} else {
1043210435
self.expected("equals sign or TO", self.peek_token())
1043310436
}
1043410437
}
1043510438

10439+
pub fn parse_set_session_params(&mut self) -> Result<Statement, ParserError> {
10440+
if self.parse_keyword(Keyword::STATISTICS) {
10441+
let topic = match self.parse_one_of_keywords(&[
10442+
Keyword::IO,
10443+
Keyword::PROFILE,
10444+
Keyword::TIME,
10445+
Keyword::XML,
10446+
]) {
10447+
Some(Keyword::IO) => SessionParamStatsTopic::IO,
10448+
Some(Keyword::PROFILE) => SessionParamStatsTopic::Profile,
10449+
Some(Keyword::TIME) => SessionParamStatsTopic::Time,
10450+
Some(Keyword::XML) => SessionParamStatsTopic::Xml,
10451+
_ => return self.expected("IO, PROFILE, TIME or XML", self.peek_token()),
10452+
};
10453+
let value = self.parse_session_param_value()?;
10454+
Ok(Statement::SetSessionParam(SetSessionParamKind::Statistics(
10455+
SetSessionParamStatistics { topic, value },
10456+
)))
10457+
} else if self.parse_keyword(Keyword::IDENTITY_INSERT) {
10458+
let obj = self.parse_object_name(false)?;
10459+
let value = self.parse_session_param_value()?;
10460+
Ok(Statement::SetSessionParam(
10461+
SetSessionParamKind::IdentityInsert(SetSessionParamIdentityInsert { obj, value }),
10462+
))
10463+
} else if self.parse_keyword(Keyword::OFFSETS) {
10464+
let keywords = self.parse_comma_separated(|parser| {
10465+
let next_token = parser.next_token();
10466+
match &next_token.token {
10467+
Token::Word(w) => Ok(w.to_string()),
10468+
_ => parser.expected("SQL keyword", next_token),
10469+
}
10470+
})?;
10471+
let value = self.parse_session_param_value()?;
10472+
Ok(Statement::SetSessionParam(SetSessionParamKind::Offsets(
10473+
SetSessionParamOffsets { keywords, value },
10474+
)))
10475+
} else {
10476+
let names = self.parse_comma_separated(|parser| {
10477+
let next_token = parser.next_token();
10478+
match next_token.token {
10479+
Token::Word(w) => Ok(w.to_string()),
10480+
_ => parser.expected("Session param name", next_token),
10481+
}
10482+
})?;
10483+
let value = self.parse_expr()?.to_string();
10484+
Ok(Statement::SetSessionParam(SetSessionParamKind::Generic(
10485+
SetSessionParamGeneric { names, value },
10486+
)))
10487+
}
10488+
}
10489+
10490+
fn parse_session_param_value(&mut self) -> Result<SessionParamValue, ParserError> {
10491+
if self.parse_keyword(Keyword::ON) {
10492+
Ok(SessionParamValue::On)
10493+
} else if self.parse_keyword(Keyword::OFF) {
10494+
Ok(SessionParamValue::Off)
10495+
} else {
10496+
self.expected("ON or OFF", self.peek_token())
10497+
}
10498+
}
10499+
1043610500
pub fn parse_show(&mut self) -> Result<Statement, ParserError> {
1043710501
let terse = self.parse_keyword(Keyword::TERSE);
1043810502
let extended = self.parse_keyword(Keyword::EXTENDED);
@@ -13004,6 +13068,8 @@ impl<'a> Parser<'a> {
1300413068
TransactionIsolationLevel::RepeatableRead
1300513069
} else if self.parse_keyword(Keyword::SERIALIZABLE) {
1300613070
TransactionIsolationLevel::Serializable
13071+
} else if self.parse_keyword(Keyword::SNAPSHOT) {
13072+
TransactionIsolationLevel::Snapshot
1300713073
} else {
1300813074
self.expected("isolation level", self.peek_token())?
1300913075
};

tests/sqlparser_mssql.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,6 +1679,67 @@ fn parse_true_false_as_identifiers() {
16791679
);
16801680
}
16811681

1682+
#[test]
1683+
fn parse_mssql_set_session_value() {
1684+
ms().verified_stmt(
1685+
"SET OFFSETS SELECT, FROM, ORDER, TABLE, PROCEDURE, STATEMENT, PARAM, EXECUTE ON",
1686+
);
1687+
ms().verified_stmt("SET IDENTITY_INSERT dbo.Tool ON");
1688+
ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
1689+
ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL READ COMMITTED");
1690+
ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");
1691+
ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL SNAPSHOT");
1692+
ms().verified_stmt("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
1693+
ms().verified_stmt("SET STATISTICS IO ON");
1694+
ms().verified_stmt("SET STATISTICS XML ON");
1695+
ms().verified_stmt("SET STATISTICS PROFILE ON");
1696+
ms().verified_stmt("SET STATISTICS TIME ON");
1697+
ms().verified_stmt("SET DATEFIRST 7");
1698+
ms().verified_stmt("SET DATEFIRST @xxx");
1699+
ms().verified_stmt("SET DATEFIRST @@xxx");
1700+
ms().verified_stmt("SET DATEFORMAT dmy");
1701+
ms().verified_stmt("SET DATEFORMAT @datevar");
1702+
ms().verified_stmt("SET DATEFORMAT @@datevar");
1703+
ms().verified_stmt("SET DEADLOCK_PRIORITY 'LOW'");
1704+
ms().verified_stmt("SET DEADLOCK_PRIORITY LOW");
1705+
ms().verified_stmt("SET DEADLOCK_PRIORITY 8");
1706+
ms().verified_stmt("SET DEADLOCK_PRIORITY -8");
1707+
ms().verified_stmt("SET DEADLOCK_PRIORITY @xxx");
1708+
ms().verified_stmt("SET DEADLOCK_PRIORITY @@xxx");
1709+
ms().verified_stmt("SET LOCK_TIMEOUT 1800");
1710+
ms().verified_stmt("SET CONCAT_NULL_YIELDS_NULL ON");
1711+
ms().verified_stmt("SET CURSOR_CLOSE_ON_COMMIT ON");
1712+
ms().verified_stmt("SET FIPS_FLAGGER 'level'");
1713+
ms().verified_stmt("SET FIPS_FLAGGER OFF");
1714+
ms().verified_stmt("SET LANGUAGE Italian");
1715+
ms().verified_stmt("SET QUOTED_IDENTIFIER ON");
1716+
ms().verified_stmt("SET ARITHABORT ON");
1717+
ms().verified_stmt("SET ARITHIGNORE OFF");
1718+
ms().verified_stmt("SET FMTONLY ON");
1719+
ms().verified_stmt("SET NOCOUNT OFF");
1720+
ms().verified_stmt("SET NOEXEC ON");
1721+
ms().verified_stmt("SET NUMERIC_ROUNDABORT ON");
1722+
ms().verified_stmt("SET QUERY_GOVERNOR_COST_LIMIT 11");
1723+
ms().verified_stmt("SET ROWCOUNT 4");
1724+
ms().verified_stmt("SET ROWCOUNT @xxx");
1725+
ms().verified_stmt("SET ROWCOUNT @@xxx");
1726+
ms().verified_stmt("SET TEXTSIZE 11");
1727+
ms().verified_stmt("SET ANSI_DEFAULTS ON");
1728+
ms().verified_stmt("SET ANSI_NULL_DFLT_OFF ON");
1729+
ms().verified_stmt("SET ANSI_NULL_DFLT_ON ON");
1730+
ms().verified_stmt("SET ANSI_NULLS ON");
1731+
ms().verified_stmt("SET ANSI_PADDING ON");
1732+
ms().verified_stmt("SET ANSI_WARNINGS ON");
1733+
ms().verified_stmt("SET FORCEPLAN ON");
1734+
ms().verified_stmt("SET SHOWPLAN_ALL ON");
1735+
ms().verified_stmt("SET SHOWPLAN_TEXT ON");
1736+
ms().verified_stmt("SET SHOWPLAN_XML ON");
1737+
ms().verified_stmt("SET IMPLICIT_TRANSACTIONS ON");
1738+
ms().verified_stmt("SET REMOTE_PROC_TRANSACTIONS ON");
1739+
ms().verified_stmt("SET XACT_ABORT ON");
1740+
ms().verified_stmt("SET ANSI_NULLS, ANSI_PADDING ON");
1741+
}
1742+
16821743
fn ms() -> TestedDialects {
16831744
TestedDialects::new(vec![Box::new(MsSqlDialect {})])
16841745
}

0 commit comments

Comments
 (0)