Add support for MSSQL's JSON_ARRAY/JSON_OBJECT expr (#1507)

Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
gaoqiangz 2024-11-18 22:22:18 +08:00 committed by GitHub
parent f961efc0c9
commit 92be237cfc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 617 additions and 44 deletions

View file

@ -5449,6 +5449,8 @@ pub enum FunctionArgOperator {
RightArrow,
/// function(arg1 := value1)
Assignment,
/// function(arg1 : value1)
Colon,
}
impl fmt::Display for FunctionArgOperator {
@ -5457,6 +5459,7 @@ impl fmt::Display for FunctionArgOperator {
FunctionArgOperator::Equals => f.write_str("="),
FunctionArgOperator::RightArrow => f.write_str("=>"),
FunctionArgOperator::Assignment => f.write_str(":="),
FunctionArgOperator::Colon => f.write_str(":"),
}
}
}
@ -5465,11 +5468,22 @@ impl fmt::Display for FunctionArgOperator {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionArg {
/// `name` is identifier
///
/// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'false'
Named {
name: Ident,
arg: FunctionArgExpr,
operator: FunctionArgOperator,
},
/// `name` is arbitrary expression
///
/// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'true'
ExprNamed {
name: Expr,
arg: FunctionArgExpr,
operator: FunctionArgOperator,
},
Unnamed(FunctionArgExpr),
}
@ -5481,6 +5495,11 @@ impl fmt::Display for FunctionArg {
arg,
operator,
} => write!(f, "{name} {operator} {arg}"),
FunctionArg::ExprNamed {
name,
arg,
operator,
} => write!(f, "{name} {operator} {arg}"),
FunctionArg::Unnamed(unnamed_arg) => write!(f, "{unnamed_arg}"),
}
}
@ -5619,6 +5638,9 @@ impl fmt::Display for FunctionArgumentList {
}
write!(f, "{}", display_comma_separated(&self.args))?;
if !self.clauses.is_empty() {
if !self.args.is_empty() {
write!(f, " ")?;
}
write!(f, "{}", display_separated(&self.clauses, " "))?;
}
Ok(())
@ -5661,6 +5683,11 @@ pub enum FunctionArgumentClause {
///
/// [`GROUP_CONCAT`]: https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat
Separator(Value),
/// The json-null-clause to the [`JSON_ARRAY`]/[`JSON_OBJECT`] function in MSSQL.
///
/// [`JSON_ARRAY`]: <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-array-transact-sql?view=sql-server-ver16>
/// [`JSON_OBJECT`]: <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16>
JsonNullClause(JsonNullClause),
}
impl fmt::Display for FunctionArgumentClause {
@ -5676,6 +5703,7 @@ impl fmt::Display for FunctionArgumentClause {
FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
FunctionArgumentClause::Having(bound) => write!(f, "{bound}"),
FunctionArgumentClause::Separator(sep) => write!(f, "SEPARATOR {sep}"),
FunctionArgumentClause::JsonNullClause(null_clause) => write!(f, "{null_clause}"),
}
}
}
@ -7564,6 +7592,32 @@ impl fmt::Display for ShowStatementIn {
}
}
/// MSSQL's json null clause
///
/// ```plaintext
/// <json_null_clause> ::=
/// NULL ON NULL
/// | ABSENT ON NULL
/// ```
///
/// <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16#json_null_clause>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum JsonNullClause {
NullOnNull,
AbsentOnNull,
}
impl Display for JsonNullClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JsonNullClause::NullOnNull => write!(f, "NULL ON NULL"),
JsonNullClause::AbsentOnNull => write!(f, "ABSENT ON NULL"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -47,6 +47,10 @@ impl Dialect for DuckDbDialect {
true
}
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
true
}
// DuckDB uses this syntax for `STRUCT`s.
//
// https://duckdb.org/docs/sql/data_types/struct.html#creating-structs

View file

@ -119,4 +119,8 @@ impl Dialect for GenericDialect {
fn supports_load_extension(&self) -> bool {
true
}
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
true
}
}

View file

@ -231,11 +231,34 @@ pub trait Dialect: Debug + Any {
false
}
/// Returns true if the dialect supports named arguments of the form FUN(a = '1', b = '2').
/// Returns true if the dialect supports named arguments of the form `FUN(a = '1', b = '2')`.
fn supports_named_fn_args_with_eq_operator(&self) -> bool {
false
}
/// Returns true if the dialect supports named arguments of the form `FUN(a : '1', b : '2')`.
fn supports_named_fn_args_with_colon_operator(&self) -> bool {
false
}
/// Returns true if the dialect supports named arguments of the form `FUN(a := '1', b := '2')`.
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
false
}
/// Returns true if the dialect supports named arguments of the form `FUN(a => '1', b => '2')`.
fn supports_named_fn_args_with_rarrow_operator(&self) -> bool {
true
}
/// Returns true if dialect supports argument name as arbitrary expression.
/// e.g. `FUN(LOWER('a'):'1', b:'2')`
/// Such function arguments are represented in the AST by the `FunctionArg::ExprNamed` variant,
/// otherwise use the `FunctionArg::Named` variant (compatible reason).
fn supports_named_fn_args_with_expr_name(&self) -> bool {
false
}
/// Returns true if the dialect supports identifiers starting with a numeric
/// prefix such as tables named `59901_user_login`
fn supports_numeric_prefix(&self) -> bool {

View file

@ -66,4 +66,16 @@ impl Dialect for MsSqlDialect {
fn supports_methods(&self) -> bool {
true
}
fn supports_named_fn_args_with_colon_operator(&self) -> bool {
true
}
fn supports_named_fn_args_with_expr_name(&self) -> bool {
true
}
fn supports_named_fn_args_with_rarrow_operator(&self) -> bool {
false
}
}

View file

@ -74,6 +74,7 @@ macro_rules! define_keywords {
define_keywords!(
ABORT,
ABS,
ABSENT,
ABSOLUTE,
ACCESS,
ACCOUNT,

View file

@ -11321,46 +11321,59 @@ impl<'a> Parser<'a> {
}
pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {
if self.peek_nth_token(1) == Token::RArrow {
let name = self.parse_identifier(false)?;
self.expect_token(&Token::RArrow)?;
let arg = self.parse_wildcard_expr()?.into();
Ok(FunctionArg::Named {
let arg = if self.dialect.supports_named_fn_args_with_expr_name() {
self.maybe_parse(|p| {
let name = p.parse_expr()?;
let operator = p.parse_function_named_arg_operator()?;
let arg = p.parse_wildcard_expr()?.into();
Ok(FunctionArg::ExprNamed {
name,
arg,
operator: FunctionArgOperator::RightArrow,
})
} else if self.dialect.supports_named_fn_args_with_eq_operator()
&& self.peek_nth_token(1) == Token::Eq
{
let name = self.parse_identifier(false)?;
self.expect_token(&Token::Eq)?;
let arg = self.parse_wildcard_expr()?.into();
Ok(FunctionArg::Named {
name,
arg,
operator: FunctionArgOperator::Equals,
})
} else if dialect_of!(self is DuckDbDialect | GenericDialect)
&& self.peek_nth_token(1) == Token::Assignment
{
let name = self.parse_identifier(false)?;
self.expect_token(&Token::Assignment)?;
let arg = self.parse_expr()?.into();
Ok(FunctionArg::Named {
name,
arg,
operator: FunctionArgOperator::Assignment,
operator,
})
})?
} else {
self.maybe_parse(|p| {
let name = p.parse_identifier(false)?;
let operator = p.parse_function_named_arg_operator()?;
let arg = p.parse_wildcard_expr()?.into();
Ok(FunctionArg::Named {
name,
arg,
operator,
})
})?
};
if let Some(arg) = arg {
return Ok(arg);
}
Ok(FunctionArg::Unnamed(self.parse_wildcard_expr()?.into()))
}
fn parse_function_named_arg_operator(&mut self) -> Result<FunctionArgOperator, ParserError> {
let tok = self.next_token();
match tok.token {
Token::RArrow if self.dialect.supports_named_fn_args_with_rarrow_operator() => {
Ok(FunctionArgOperator::RightArrow)
}
Token::Eq if self.dialect.supports_named_fn_args_with_eq_operator() => {
Ok(FunctionArgOperator::Equals)
}
Token::Assignment
if self
.dialect
.supports_named_fn_args_with_assignment_operator() =>
{
Ok(FunctionArgOperator::Assignment)
}
Token::Colon if self.dialect.supports_named_fn_args_with_colon_operator() => {
Ok(FunctionArgOperator::Colon)
}
_ => {
self.prev_token();
self.expected("argument operator", tok)
}
}
}
pub fn parse_optional_args(&mut self) -> Result<Vec<FunctionArg>, ParserError> {
@ -11403,19 +11416,24 @@ impl<'a> Parser<'a> {
/// FIRST_VALUE(x IGNORE NULL);
/// ```
fn parse_function_argument_list(&mut self) -> Result<FunctionArgumentList, ParserError> {
let mut clauses = vec![];
// For MSSQL empty argument list with json-null-clause case, e.g. `JSON_ARRAY(NULL ON NULL)`
if let Some(null_clause) = self.parse_json_null_clause() {
clauses.push(FunctionArgumentClause::JsonNullClause(null_clause));
}
if self.consume_token(&Token::RParen) {
return Ok(FunctionArgumentList {
duplicate_treatment: None,
args: vec![],
clauses: vec![],
clauses,
});
}
let duplicate_treatment = self.parse_duplicate_treatment()?;
let args = self.parse_comma_separated(Parser::parse_function_args)?;
let mut clauses = vec![];
if self.dialect.supports_window_function_null_treatment_arg() {
if let Some(null_treatment) = self.parse_null_treatment()? {
clauses.push(FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment));
@ -11456,6 +11474,10 @@ impl<'a> Parser<'a> {
clauses.push(FunctionArgumentClause::OnOverflow(on_overflow));
}
if let Some(null_clause) = self.parse_json_null_clause() {
clauses.push(FunctionArgumentClause::JsonNullClause(null_clause));
}
self.expect_token(&Token::RParen)?;
Ok(FunctionArgumentList {
duplicate_treatment,
@ -11464,6 +11486,17 @@ impl<'a> Parser<'a> {
})
}
/// Parses MSSQL's json-null-clause
fn parse_json_null_clause(&mut self) -> Option<JsonNullClause> {
if self.parse_keywords(&[Keyword::ABSENT, Keyword::ON, Keyword::NULL]) {
Some(JsonNullClause::AbsentOnNull)
} else if self.parse_keywords(&[Keyword::NULL, Keyword::ON, Keyword::NULL]) {
Some(JsonNullClause::NullOnNull)
} else {
None
}
}
fn parse_duplicate_treatment(&mut self) -> Result<Option<DuplicateTreatment>, ParserError> {
let loc = self.peek_token().location;
match (

View file

@ -4402,8 +4402,9 @@ fn parse_explain_query_plan() {
#[test]
fn parse_named_argument_function() {
let dialects = all_dialects_where(|d| d.supports_named_fn_args_with_rarrow_operator());
let sql = "SELECT FUN(a => '1', b => '2') FROM foo";
let select = verified_only_select(sql);
let select = dialects.verified_only_select(sql);
assert_eq!(
&Expr::Function(Function {

View file

@ -784,6 +784,447 @@ fn parse_for_json_expect_ast() {
);
}
#[test]
fn parse_mssql_json_object() {
let select = ms().verified_only_select("SELECT JSON_OBJECT('name' : 'value', 'type' : 1)");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, .. }),
..
}) => assert_eq!(
&[
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString("name".into())),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"value".into()
))),
operator: FunctionArgOperator::Colon
},
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString("type".into())),
arg: FunctionArgExpr::Expr(Expr::Value(number("1"))),
operator: FunctionArgOperator::Colon
}
],
&args[..]
),
_ => unreachable!(),
}
let select = ms()
.verified_only_select("SELECT JSON_OBJECT('name' : 'value', 'type' : NULL ABSENT ON NULL)");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert_eq!(
&[
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString("name".into())),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"value".into()
))),
operator: FunctionArgOperator::Colon
},
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString("type".into())),
arg: FunctionArgExpr::Expr(Expr::Value(Value::Null)),
operator: FunctionArgOperator::Colon
}
],
&args[..]
);
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::AbsentOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select("SELECT JSON_OBJECT(NULL ON NULL)");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert!(args.is_empty());
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::NullOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select("SELECT JSON_OBJECT(ABSENT ON NULL)");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert!(args.is_empty());
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::AbsentOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select(
"SELECT JSON_OBJECT('name' : 'value', 'type' : JSON_ARRAY(1, 2) ABSENT ON NULL)",
);
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert_eq!(
&FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString("name".into())),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"value".into()
))),
operator: FunctionArgOperator::Colon
},
&args[0]
);
assert!(matches!(
args[1],
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString(_)),
arg: FunctionArgExpr::Expr(Expr::Function(_)),
operator: FunctionArgOperator::Colon
}
));
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::AbsentOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select(
"SELECT JSON_OBJECT('name' : 'value', 'type' : JSON_OBJECT('type_id' : 1, 'name' : 'a') NULL ON NULL)",
);
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert_eq!(
&FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString("name".into())),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"value".into()
))),
operator: FunctionArgOperator::Colon
},
&args[0]
);
assert!(matches!(
args[1],
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString(_)),
arg: FunctionArgExpr::Expr(Expr::Function(_)),
operator: FunctionArgOperator::Colon
}
));
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::NullOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select(
"SELECT JSON_OBJECT('user_name' : USER_NAME(), LOWER(@id_key) : @id_value, 'sid' : (SELECT @@SPID) ABSENT ON NULL)",
);
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert!(matches!(
args[0],
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString(_)),
arg: FunctionArgExpr::Expr(Expr::Function(_)),
operator: FunctionArgOperator::Colon
}
));
assert!(matches!(
args[1],
FunctionArg::ExprNamed {
name: Expr::Function(_),
arg: FunctionArgExpr::Expr(Expr::Identifier(_)),
operator: FunctionArgOperator::Colon
}
));
assert!(matches!(
args[2],
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString(_)),
arg: FunctionArgExpr::Expr(Expr::Subquery(_)),
operator: FunctionArgOperator::Colon
}
));
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::AbsentOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select(
"SELECT s.session_id, JSON_OBJECT('security_id' : s.security_id, 'login' : s.login_name, 'status' : s.status) AS info \
FROM sys.dm_exec_sessions AS s \
WHERE s.is_user_process = 1",
);
match &select.projection[1] {
SelectItem::ExprWithAlias {
expr:
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, .. }),
..
}),
..
} => {
assert!(matches!(
args[0],
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString(_)),
arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)),
operator: FunctionArgOperator::Colon
}
));
assert!(matches!(
args[1],
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString(_)),
arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)),
operator: FunctionArgOperator::Colon
}
));
assert!(matches!(
args[2],
FunctionArg::ExprNamed {
name: Expr::Value(Value::SingleQuotedString(_)),
arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)),
operator: FunctionArgOperator::Colon
}
));
}
_ => unreachable!(),
}
}
#[test]
fn parse_mssql_json_array() {
let select = ms().verified_only_select("SELECT JSON_ARRAY('a', 1, NULL, 2 NULL ON NULL)");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert_eq!(
&[
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
Value::SingleQuotedString("a".into())
))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("1")))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(Value::Null))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("2")))),
],
&args[..]
);
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::NullOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select("SELECT JSON_ARRAY('a', 1, NULL, 2 ABSENT ON NULL)");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert_eq!(
&[
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
Value::SingleQuotedString("a".into())
))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("1")))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(Value::Null))),
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("2")))),
],
&args[..]
);
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::AbsentOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select("SELECT JSON_ARRAY(NULL ON NULL)");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert!(args.is_empty());
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::NullOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select("SELECT JSON_ARRAY(ABSENT ON NULL)");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert!(args.is_empty());
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::AbsentOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select(
"SELECT JSON_ARRAY('a', JSON_OBJECT('name' : 'value', 'type' : 1) NULL ON NULL)",
);
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}) => {
assert_eq!(
&FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
Value::SingleQuotedString("a".into())
))),
&args[0]
);
assert!(matches!(
args[1],
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_)))
));
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::NullOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
let select = ms().verified_only_select(
"SELECT JSON_ARRAY('a', JSON_OBJECT('name' : 'value', 'type' : 1), JSON_ARRAY(1, NULL, 2 NULL ON NULL))",
);
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, .. }),
..
}) => {
assert_eq!(
&FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
Value::SingleQuotedString("a".into())
))),
&args[0]
);
assert!(matches!(
args[1],
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_)))
));
assert!(matches!(
args[2],
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_)))
));
}
_ => unreachable!(),
}
let select = ms().verified_only_select("SELECT JSON_ARRAY(1, @id_value, (SELECT @@SPID))");
match expr_from_projection(&select.projection[0]) {
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, .. }),
..
}) => {
assert_eq!(
&FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("1")))),
&args[0]
);
assert!(matches!(
args[1],
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(_)))
));
assert!(matches!(
args[2],
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Subquery(_)))
));
}
_ => unreachable!(),
}
let select = ms().verified_only_select(
"SELECT s.session_id, JSON_ARRAY(s.host_name, s.program_name, s.client_interface_name NULL ON NULL) AS info \
FROM sys.dm_exec_sessions AS s \
WHERE s.is_user_process = 1",
);
match &select.projection[1] {
SelectItem::ExprWithAlias {
expr:
Expr::Function(Function {
args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }),
..
}),
..
} => {
assert!(matches!(
args[0],
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)))
));
assert!(matches!(
args[1],
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)))
));
assert!(matches!(
args[2],
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)))
));
assert_eq!(
&[FunctionArgumentClause::JsonNullClause(
JsonNullClause::NullOnNull
)],
&clauses[..]
);
}
_ => unreachable!(),
}
}
#[test]
fn parse_ampersand_arobase() {
// In SQL Server, a&@b means (a) & (@b), in PostgreSQL it means (a) &@ (b)