Skip to content

Commit 92be237

Browse files
gaoqiangziffyio
andauthored
Add support for MSSQL's JSON_ARRAY/JSON_OBJECT expr (#1507)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
1 parent f961efc commit 92be237

File tree

9 files changed

+617
-44
lines changed

9 files changed

+617
-44
lines changed

src/ast/mod.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5449,6 +5449,8 @@ pub enum FunctionArgOperator {
54495449
RightArrow,
54505450
/// function(arg1 := value1)
54515451
Assignment,
5452+
/// function(arg1 : value1)
5453+
Colon,
54525454
}
54535455

54545456
impl fmt::Display for FunctionArgOperator {
@@ -5457,6 +5459,7 @@ impl fmt::Display for FunctionArgOperator {
54575459
FunctionArgOperator::Equals => f.write_str("="),
54585460
FunctionArgOperator::RightArrow => f.write_str("=>"),
54595461
FunctionArgOperator::Assignment => f.write_str(":="),
5462+
FunctionArgOperator::Colon => f.write_str(":"),
54605463
}
54615464
}
54625465
}
@@ -5465,11 +5468,22 @@ impl fmt::Display for FunctionArgOperator {
54655468
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
54665469
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
54675470
pub enum FunctionArg {
5471+
/// `name` is identifier
5472+
///
5473+
/// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'false'
54685474
Named {
54695475
name: Ident,
54705476
arg: FunctionArgExpr,
54715477
operator: FunctionArgOperator,
54725478
},
5479+
/// `name` is arbitrary expression
5480+
///
5481+
/// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'true'
5482+
ExprNamed {
5483+
name: Expr,
5484+
arg: FunctionArgExpr,
5485+
operator: FunctionArgOperator,
5486+
},
54735487
Unnamed(FunctionArgExpr),
54745488
}
54755489

@@ -5481,6 +5495,11 @@ impl fmt::Display for FunctionArg {
54815495
arg,
54825496
operator,
54835497
} => write!(f, "{name} {operator} {arg}"),
5498+
FunctionArg::ExprNamed {
5499+
name,
5500+
arg,
5501+
operator,
5502+
} => write!(f, "{name} {operator} {arg}"),
54845503
FunctionArg::Unnamed(unnamed_arg) => write!(f, "{unnamed_arg}"),
54855504
}
54865505
}
@@ -5619,7 +5638,10 @@ impl fmt::Display for FunctionArgumentList {
56195638
}
56205639
write!(f, "{}", display_comma_separated(&self.args))?;
56215640
if !self.clauses.is_empty() {
5622-
write!(f, " {}", display_separated(&self.clauses, " "))?;
5641+
if !self.args.is_empty() {
5642+
write!(f, " ")?;
5643+
}
5644+
write!(f, "{}", display_separated(&self.clauses, " "))?;
56235645
}
56245646
Ok(())
56255647
}
@@ -5661,6 +5683,11 @@ pub enum FunctionArgumentClause {
56615683
///
56625684
/// [`GROUP_CONCAT`]: https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat
56635685
Separator(Value),
5686+
/// The json-null-clause to the [`JSON_ARRAY`]/[`JSON_OBJECT`] function in MSSQL.
5687+
///
5688+
/// [`JSON_ARRAY`]: <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-array-transact-sql?view=sql-server-ver16>
5689+
/// [`JSON_OBJECT`]: <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16>
5690+
JsonNullClause(JsonNullClause),
56645691
}
56655692

56665693
impl fmt::Display for FunctionArgumentClause {
@@ -5676,6 +5703,7 @@ impl fmt::Display for FunctionArgumentClause {
56765703
FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
56775704
FunctionArgumentClause::Having(bound) => write!(f, "{bound}"),
56785705
FunctionArgumentClause::Separator(sep) => write!(f, "SEPARATOR {sep}"),
5706+
FunctionArgumentClause::JsonNullClause(null_clause) => write!(f, "{null_clause}"),
56795707
}
56805708
}
56815709
}
@@ -7564,6 +7592,32 @@ impl fmt::Display for ShowStatementIn {
75647592
}
75657593
}
75667594

7595+
/// MSSQL's json null clause
7596+
///
7597+
/// ```plaintext
7598+
/// <json_null_clause> ::=
7599+
/// NULL ON NULL
7600+
/// | ABSENT ON NULL
7601+
/// ```
7602+
///
7603+
/// <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16#json_null_clause>
7604+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7605+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7606+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7607+
pub enum JsonNullClause {
7608+
NullOnNull,
7609+
AbsentOnNull,
7610+
}
7611+
7612+
impl Display for JsonNullClause {
7613+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7614+
match self {
7615+
JsonNullClause::NullOnNull => write!(f, "NULL ON NULL"),
7616+
JsonNullClause::AbsentOnNull => write!(f, "ABSENT ON NULL"),
7617+
}
7618+
}
7619+
}
7620+
75677621
#[cfg(test)]
75687622
mod tests {
75697623
use super::*;

src/dialect/duckdb.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ impl Dialect for DuckDbDialect {
4747
true
4848
}
4949

50+
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
51+
true
52+
}
53+
5054
// DuckDB uses this syntax for `STRUCT`s.
5155
//
5256
// https://duckdb.org/docs/sql/data_types/struct.html#creating-structs

src/dialect/generic.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,8 @@ impl Dialect for GenericDialect {
119119
fn supports_load_extension(&self) -> bool {
120120
true
121121
}
122+
123+
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
124+
true
125+
}
122126
}

src/dialect/mod.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,34 @@ pub trait Dialect: Debug + Any {
231231
false
232232
}
233233

234-
/// Returns true if the dialect supports named arguments of the form FUN(a = '1', b = '2').
234+
/// Returns true if the dialect supports named arguments of the form `FUN(a = '1', b = '2')`.
235235
fn supports_named_fn_args_with_eq_operator(&self) -> bool {
236236
false
237237
}
238238

239+
/// Returns true if the dialect supports named arguments of the form `FUN(a : '1', b : '2')`.
240+
fn supports_named_fn_args_with_colon_operator(&self) -> bool {
241+
false
242+
}
243+
244+
/// Returns true if the dialect supports named arguments of the form `FUN(a := '1', b := '2')`.
245+
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
246+
false
247+
}
248+
249+
/// Returns true if the dialect supports named arguments of the form `FUN(a => '1', b => '2')`.
250+
fn supports_named_fn_args_with_rarrow_operator(&self) -> bool {
251+
true
252+
}
253+
254+
/// Returns true if dialect supports argument name as arbitrary expression.
255+
/// e.g. `FUN(LOWER('a'):'1', b:'2')`
256+
/// Such function arguments are represented in the AST by the `FunctionArg::ExprNamed` variant,
257+
/// otherwise use the `FunctionArg::Named` variant (compatible reason).
258+
fn supports_named_fn_args_with_expr_name(&self) -> bool {
259+
false
260+
}
261+
239262
/// Returns true if the dialect supports identifiers starting with a numeric
240263
/// prefix such as tables named `59901_user_login`
241264
fn supports_numeric_prefix(&self) -> bool {

src/dialect/mssql.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,16 @@ impl Dialect for MsSqlDialect {
6666
fn supports_methods(&self) -> bool {
6767
true
6868
}
69+
70+
fn supports_named_fn_args_with_colon_operator(&self) -> bool {
71+
true
72+
}
73+
74+
fn supports_named_fn_args_with_expr_name(&self) -> bool {
75+
true
76+
}
77+
78+
fn supports_named_fn_args_with_rarrow_operator(&self) -> bool {
79+
false
80+
}
6981
}

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ macro_rules! define_keywords {
7474
define_keywords!(
7575
ABORT,
7676
ABS,
77+
ABSENT,
7778
ABSOLUTE,
7879
ACCESS,
7980
ACCOUNT,

src/parser/mod.rs

Lines changed: 74 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11321,45 +11321,58 @@ impl<'a> Parser<'a> {
1132111321
}
1132211322

1132311323
pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {
11324-
if self.peek_nth_token(1) == Token::RArrow {
11325-
let name = self.parse_identifier(false)?;
11326-
11327-
self.expect_token(&Token::RArrow)?;
11328-
let arg = self.parse_wildcard_expr()?.into();
11329-
11330-
Ok(FunctionArg::Named {
11331-
name,
11332-
arg,
11333-
operator: FunctionArgOperator::RightArrow,
11334-
})
11335-
} else if self.dialect.supports_named_fn_args_with_eq_operator()
11336-
&& self.peek_nth_token(1) == Token::Eq
11337-
{
11338-
let name = self.parse_identifier(false)?;
11339-
11340-
self.expect_token(&Token::Eq)?;
11341-
let arg = self.parse_wildcard_expr()?.into();
11342-
11343-
Ok(FunctionArg::Named {
11344-
name,
11345-
arg,
11346-
operator: FunctionArgOperator::Equals,
11347-
})
11348-
} else if dialect_of!(self is DuckDbDialect | GenericDialect)
11349-
&& self.peek_nth_token(1) == Token::Assignment
11350-
{
11351-
let name = self.parse_identifier(false)?;
11352-
11353-
self.expect_token(&Token::Assignment)?;
11354-
let arg = self.parse_expr()?.into();
11355-
11356-
Ok(FunctionArg::Named {
11357-
name,
11358-
arg,
11359-
operator: FunctionArgOperator::Assignment,
11360-
})
11324+
let arg = if self.dialect.supports_named_fn_args_with_expr_name() {
11325+
self.maybe_parse(|p| {
11326+
let name = p.parse_expr()?;
11327+
let operator = p.parse_function_named_arg_operator()?;
11328+
let arg = p.parse_wildcard_expr()?.into();
11329+
Ok(FunctionArg::ExprNamed {
11330+
name,
11331+
arg,
11332+
operator,
11333+
})
11334+
})?
1136111335
} else {
11362-
Ok(FunctionArg::Unnamed(self.parse_wildcard_expr()?.into()))
11336+
self.maybe_parse(|p| {
11337+
let name = p.parse_identifier(false)?;
11338+
let operator = p.parse_function_named_arg_operator()?;
11339+
let arg = p.parse_wildcard_expr()?.into();
11340+
Ok(FunctionArg::Named {
11341+
name,
11342+
arg,
11343+
operator,
11344+
})
11345+
})?
11346+
};
11347+
if let Some(arg) = arg {
11348+
return Ok(arg);
11349+
}
11350+
Ok(FunctionArg::Unnamed(self.parse_wildcard_expr()?.into()))
11351+
}
11352+
11353+
fn parse_function_named_arg_operator(&mut self) -> Result<FunctionArgOperator, ParserError> {
11354+
let tok = self.next_token();
11355+
match tok.token {
11356+
Token::RArrow if self.dialect.supports_named_fn_args_with_rarrow_operator() => {
11357+
Ok(FunctionArgOperator::RightArrow)
11358+
}
11359+
Token::Eq if self.dialect.supports_named_fn_args_with_eq_operator() => {
11360+
Ok(FunctionArgOperator::Equals)
11361+
}
11362+
Token::Assignment
11363+
if self
11364+
.dialect
11365+
.supports_named_fn_args_with_assignment_operator() =>
11366+
{
11367+
Ok(FunctionArgOperator::Assignment)
11368+
}
11369+
Token::Colon if self.dialect.supports_named_fn_args_with_colon_operator() => {
11370+
Ok(FunctionArgOperator::Colon)
11371+
}
11372+
_ => {
11373+
self.prev_token();
11374+
self.expected("argument operator", tok)
11375+
}
1136311376
}
1136411377
}
1136511378

@@ -11403,19 +11416,24 @@ impl<'a> Parser<'a> {
1140311416
/// FIRST_VALUE(x IGNORE NULL);
1140411417
/// ```
1140511418
fn parse_function_argument_list(&mut self) -> Result<FunctionArgumentList, ParserError> {
11419+
let mut clauses = vec![];
11420+
11421+
// For MSSQL empty argument list with json-null-clause case, e.g. `JSON_ARRAY(NULL ON NULL)`
11422+
if let Some(null_clause) = self.parse_json_null_clause() {
11423+
clauses.push(FunctionArgumentClause::JsonNullClause(null_clause));
11424+
}
11425+
1140611426
if self.consume_token(&Token::RParen) {
1140711427
return Ok(FunctionArgumentList {
1140811428
duplicate_treatment: None,
1140911429
args: vec![],
11410-
clauses: vec![],
11430+
clauses,
1141111431
});
1141211432
}
1141311433

1141411434
let duplicate_treatment = self.parse_duplicate_treatment()?;
1141511435
let args = self.parse_comma_separated(Parser::parse_function_args)?;
1141611436

11417-
let mut clauses = vec![];
11418-
1141911437
if self.dialect.supports_window_function_null_treatment_arg() {
1142011438
if let Some(null_treatment) = self.parse_null_treatment()? {
1142111439
clauses.push(FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment));
@@ -11456,6 +11474,10 @@ impl<'a> Parser<'a> {
1145611474
clauses.push(FunctionArgumentClause::OnOverflow(on_overflow));
1145711475
}
1145811476

11477+
if let Some(null_clause) = self.parse_json_null_clause() {
11478+
clauses.push(FunctionArgumentClause::JsonNullClause(null_clause));
11479+
}
11480+
1145911481
self.expect_token(&Token::RParen)?;
1146011482
Ok(FunctionArgumentList {
1146111483
duplicate_treatment,
@@ -11464,6 +11486,17 @@ impl<'a> Parser<'a> {
1146411486
})
1146511487
}
1146611488

11489+
/// Parses MSSQL's json-null-clause
11490+
fn parse_json_null_clause(&mut self) -> Option<JsonNullClause> {
11491+
if self.parse_keywords(&[Keyword::ABSENT, Keyword::ON, Keyword::NULL]) {
11492+
Some(JsonNullClause::AbsentOnNull)
11493+
} else if self.parse_keywords(&[Keyword::NULL, Keyword::ON, Keyword::NULL]) {
11494+
Some(JsonNullClause::NullOnNull)
11495+
} else {
11496+
None
11497+
}
11498+
}
11499+
1146711500
fn parse_duplicate_treatment(&mut self) -> Result<Option<DuplicateTreatment>, ParserError> {
1146811501
let loc = self.peek_token().location;
1146911502
match (

tests/sqlparser_common.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4402,8 +4402,9 @@ fn parse_explain_query_plan() {
44024402

44034403
#[test]
44044404
fn parse_named_argument_function() {
4405+
let dialects = all_dialects_where(|d| d.supports_named_fn_args_with_rarrow_operator());
44054406
let sql = "SELECT FUN(a => '1', b => '2') FROM foo";
4406-
let select = verified_only_select(sql);
4407+
let select = dialects.verified_only_select(sql);
44074408

44084409
assert_eq!(
44094410
&Expr::Function(Function {

0 commit comments

Comments
 (0)