mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-08 01:15:00 +00:00
Add support for MSSQL's JSON_ARRAY
/JSON_OBJECT
expr (#1507)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
parent
f961efc0c9
commit
92be237cfc
9 changed files with 617 additions and 44 deletions
|
@ -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,7 +5638,10 @@ impl fmt::Display for FunctionArgumentList {
|
|||
}
|
||||
write!(f, "{}", display_comma_separated(&self.args))?;
|
||||
if !self.clauses.is_empty() {
|
||||
write!(f, " {}", display_separated(&self.clauses, " "))?;
|
||||
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::*;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ macro_rules! define_keywords {
|
|||
define_keywords!(
|
||||
ABORT,
|
||||
ABS,
|
||||
ABSENT,
|
||||
ABSOLUTE,
|
||||
ACCESS,
|
||||
ACCOUNT,
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue