Skip to content

Commit dbb7c33

Browse files
committed
Support mssql json and xml extensions
fixes #1042 See the documentation for the FOR clause here: https://learn.microsoft.com/en-us/sql/t-sql/queries/select-for-clause-transact-sql?view=sql-server-ver16
1 parent 4cdaa40 commit dbb7c33

File tree

8 files changed

+317
-8
lines changed

8 files changed

+317
-8
lines changed

src/ast/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ pub use self::ddl::{
3737
};
3838
pub use self::operator::{BinaryOperator, UnaryOperator};
3939
pub use self::query::{
40-
Cte, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch, GroupByExpr, IdentWithAlias, Join,
41-
JoinConstraint, JoinOperator, LateralView, LockClause, LockType, NamedWindowDefinition,
42-
NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem, ReplaceSelectElement,
43-
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Table,
44-
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, Values, WildcardAdditionalOptions,
45-
With,
40+
Cte, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch, ForClause, ForJson, ForXml,
41+
GroupByExpr, IdentWithAlias, Join, JoinConstraint, JoinOperator, LateralView, LockClause,
42+
LockType, NamedWindowDefinition, NonBlock, Offset, OffsetRows, OrderByExpr, Query,
43+
RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem,
44+
SetExpr, SetOperator, SetQuantifier, Table, TableAlias, TableFactor, TableVersion,
45+
TableWithJoins, Top, Values, WildcardAdditionalOptions, With,
4646
};
4747
pub use self::value::{
4848
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,

src/ast/query.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ pub struct Query {
4545
pub fetch: Option<Fetch>,
4646
/// `FOR { UPDATE | SHARE } [ OF table_name ] [ SKIP LOCKED | NOWAIT ]`
4747
pub locks: Vec<LockClause>,
48+
/// `FOR XML { RAW | AUTO | EXPLICIT | PATH } [ , ELEMENTS ]`
49+
/// `FOR JSON { AUTO | PATH } [ , INCLUDE_NULL_VALUES ]`
50+
/// (MSSQL-specific)
51+
pub for_clause: Option<ForClause>,
4852
}
4953

5054
impl fmt::Display for Query {
@@ -71,6 +75,9 @@ impl fmt::Display for Query {
7175
if !self.locks.is_empty() {
7276
write!(f, " {}", display_separated(&self.locks, " "))?;
7377
}
78+
if let Some(ref for_clause) = self.for_clause {
79+
write!(f, " {}", for_clause)?;
80+
}
7481
Ok(())
7582
}
7683
}
@@ -1315,3 +1322,125 @@ impl fmt::Display for GroupByExpr {
13151322
}
13161323
}
13171324
}
1325+
1326+
/// FOR XML or FOR JSON clause, specific to MSSQL
1327+
/// (formats the output of a query as XML or JSON)
1328+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1329+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1330+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1331+
pub enum ForClause {
1332+
Browse,
1333+
Json {
1334+
for_json: ForJson,
1335+
root: Option<String>,
1336+
include_null_values: bool,
1337+
without_array_wrapper: bool,
1338+
},
1339+
Xml {
1340+
for_xml: ForXml,
1341+
elements: bool,
1342+
binary_base64: bool,
1343+
root: Option<String>,
1344+
r#type: bool,
1345+
},
1346+
}
1347+
1348+
impl fmt::Display for ForClause {
1349+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1350+
match self {
1351+
ForClause::Browse => write!(f, "FOR BROWSE"),
1352+
ForClause::Json {
1353+
for_json,
1354+
root,
1355+
include_null_values,
1356+
without_array_wrapper,
1357+
} => {
1358+
write!(f, "FOR JSON ")?;
1359+
write!(f, "{}", for_json)?;
1360+
if let Some(root) = root {
1361+
write!(f, ", ROOT('{}')", root)?;
1362+
}
1363+
if *include_null_values {
1364+
write!(f, ", INCLUDE_NULL_VALUES")?;
1365+
}
1366+
if *without_array_wrapper {
1367+
write!(f, ", WITHOUT_ARRAY_WRAPPER")?;
1368+
}
1369+
Ok(())
1370+
}
1371+
ForClause::Xml {
1372+
for_xml,
1373+
elements,
1374+
binary_base64,
1375+
root,
1376+
r#type,
1377+
} => {
1378+
write!(f, "FOR XML ")?;
1379+
write!(f, "{}", for_xml)?;
1380+
if *binary_base64 {
1381+
write!(f, ", BINARY BASE64")?;
1382+
}
1383+
if *r#type {
1384+
write!(f, ", TYPE")?;
1385+
}
1386+
if let Some(root) = root {
1387+
write!(f, ", ROOT('{}')", root)?;
1388+
}
1389+
if *elements {
1390+
write!(f, ", ELEMENTS")?;
1391+
}
1392+
Ok(())
1393+
}
1394+
}
1395+
}
1396+
}
1397+
1398+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1399+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1400+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1401+
pub enum ForXml {
1402+
Raw(Option<String>),
1403+
Auto,
1404+
Explicit,
1405+
Path(Option<String>),
1406+
}
1407+
1408+
impl fmt::Display for ForXml {
1409+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1410+
match self {
1411+
ForXml::Raw(root) => {
1412+
write!(f, "RAW")?;
1413+
if let Some(root) = root {
1414+
write!(f, "('{}')", root)?;
1415+
}
1416+
Ok(())
1417+
}
1418+
ForXml::Auto => write!(f, "AUTO"),
1419+
ForXml::Explicit => write!(f, "EXPLICIT"),
1420+
ForXml::Path(root) => {
1421+
write!(f, "PATH")?;
1422+
if let Some(root) = root {
1423+
write!(f, "('{}')", root)?;
1424+
}
1425+
Ok(())
1426+
}
1427+
}
1428+
}
1429+
}
1430+
1431+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1432+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1433+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1434+
pub enum ForJson {
1435+
Auto,
1436+
Path,
1437+
}
1438+
1439+
impl fmt::Display for ForJson {
1440+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1441+
match self {
1442+
ForJson::Auto => write!(f, "AUTO"),
1443+
ForJson::Path => write!(f, "PATH"),
1444+
}
1445+
}
1446+
}

src/keywords.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,13 @@ define_keywords!(
9797
ATOMIC,
9898
ATTACH,
9999
AUTHORIZATION,
100+
AUTO,
100101
AUTOINCREMENT,
101102
AUTO_INCREMENT,
102103
AVG,
103104
AVRO,
104105
BACKWARD,
106+
BASE64,
105107
BEGIN,
106108
BEGIN_FRAME,
107109
BEGIN_PARTITION,
@@ -116,6 +118,7 @@ define_keywords!(
116118
BOOL,
117119
BOOLEAN,
118120
BOTH,
121+
BROWSE,
119122
BTREE,
120123
BY,
121124
BYPASSRLS,
@@ -232,6 +235,7 @@ define_keywords!(
232235
DYNAMIC,
233236
EACH,
234237
ELEMENT,
238+
ELEMENTS,
235239
ELSE,
236240
ENCODING,
237241
ENCRYPTION,
@@ -256,6 +260,7 @@ define_keywords!(
256260
EXP,
257261
EXPANSION,
258262
EXPLAIN,
263+
EXPLICIT,
259264
EXTENDED,
260265
EXTERNAL,
261266
EXTRACT,
@@ -319,6 +324,7 @@ define_keywords!(
319324
IMMUTABLE,
320325
IN,
321326
INCLUDE,
327+
INCLUDE_NULL_VALUES,
322328
INCREMENT,
323329
INDEX,
324330
INDICATOR,
@@ -463,6 +469,7 @@ define_keywords!(
463469
PARTITIONED,
464470
PARTITIONS,
465471
PASSWORD,
472+
PATH,
466473
PATTERN,
467474
PERCENT,
468475
PERCENTILE_CONT,
@@ -494,6 +501,7 @@ define_keywords!(
494501
QUOTE,
495502
RANGE,
496503
RANK,
504+
RAW,
497505
RCFILE,
498506
READ,
499507
READS,
@@ -535,6 +543,7 @@ define_keywords!(
535543
ROLE,
536544
ROLLBACK,
537545
ROLLUP,
546+
ROOT,
538547
ROW,
539548
ROWID,
540549
ROWS,
@@ -682,8 +691,10 @@ define_keywords!(
682691
WITH,
683692
WITHIN,
684693
WITHOUT,
694+
WITHOUT_ARRAY_WRAPPER,
685695
WORK,
686696
WRITE,
697+
XML,
687698
XOR,
688699
YEAR,
689700
ZONE,
@@ -732,6 +743,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
732743
Keyword::QUALIFY,
733744
Keyword::WINDOW,
734745
Keyword::END,
746+
Keyword::FOR,
735747
// for MYSQL PARTITION SELECTION
736748
Keyword::PARTITION,
737749
];

0 commit comments

Comments
 (0)