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,
|
RightArrow,
|
||||||
/// function(arg1 := value1)
|
/// function(arg1 := value1)
|
||||||
Assignment,
|
Assignment,
|
||||||
|
/// function(arg1 : value1)
|
||||||
|
Colon,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FunctionArgOperator {
|
impl fmt::Display for FunctionArgOperator {
|
||||||
|
@ -5457,6 +5459,7 @@ impl fmt::Display for FunctionArgOperator {
|
||||||
FunctionArgOperator::Equals => f.write_str("="),
|
FunctionArgOperator::Equals => f.write_str("="),
|
||||||
FunctionArgOperator::RightArrow => f.write_str("=>"),
|
FunctionArgOperator::RightArrow => f.write_str("=>"),
|
||||||
FunctionArgOperator::Assignment => 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 = "serde", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
pub enum FunctionArg {
|
pub enum FunctionArg {
|
||||||
|
/// `name` is identifier
|
||||||
|
///
|
||||||
|
/// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'false'
|
||||||
Named {
|
Named {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
arg: FunctionArgExpr,
|
arg: FunctionArgExpr,
|
||||||
operator: FunctionArgOperator,
|
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),
|
Unnamed(FunctionArgExpr),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5481,6 +5495,11 @@ impl fmt::Display for FunctionArg {
|
||||||
arg,
|
arg,
|
||||||
operator,
|
operator,
|
||||||
} => write!(f, "{name} {operator} {arg}"),
|
} => write!(f, "{name} {operator} {arg}"),
|
||||||
|
FunctionArg::ExprNamed {
|
||||||
|
name,
|
||||||
|
arg,
|
||||||
|
operator,
|
||||||
|
} => write!(f, "{name} {operator} {arg}"),
|
||||||
FunctionArg::Unnamed(unnamed_arg) => write!(f, "{unnamed_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))?;
|
write!(f, "{}", display_comma_separated(&self.args))?;
|
||||||
if !self.clauses.is_empty() {
|
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(())
|
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
|
/// [`GROUP_CONCAT`]: https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat
|
||||||
Separator(Value),
|
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 {
|
impl fmt::Display for FunctionArgumentClause {
|
||||||
|
@ -5676,6 +5703,7 @@ impl fmt::Display for FunctionArgumentClause {
|
||||||
FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
|
FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
|
||||||
FunctionArgumentClause::Having(bound) => write!(f, "{bound}"),
|
FunctionArgumentClause::Having(bound) => write!(f, "{bound}"),
|
||||||
FunctionArgumentClause::Separator(sep) => write!(f, "SEPARATOR {sep}"),
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -47,6 +47,10 @@ impl Dialect for DuckDbDialect {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
// DuckDB uses this syntax for `STRUCT`s.
|
// DuckDB uses this syntax for `STRUCT`s.
|
||||||
//
|
//
|
||||||
// https://duckdb.org/docs/sql/data_types/struct.html#creating-structs
|
// 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 {
|
fn supports_load_extension(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,11 +231,34 @@ pub trait Dialect: Debug + Any {
|
||||||
false
|
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 {
|
fn supports_named_fn_args_with_eq_operator(&self) -> bool {
|
||||||
false
|
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
|
/// Returns true if the dialect supports identifiers starting with a numeric
|
||||||
/// prefix such as tables named `59901_user_login`
|
/// prefix such as tables named `59901_user_login`
|
||||||
fn supports_numeric_prefix(&self) -> bool {
|
fn supports_numeric_prefix(&self) -> bool {
|
||||||
|
|
|
@ -66,4 +66,16 @@ impl Dialect for MsSqlDialect {
|
||||||
fn supports_methods(&self) -> bool {
|
fn supports_methods(&self) -> bool {
|
||||||
true
|
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!(
|
define_keywords!(
|
||||||
ABORT,
|
ABORT,
|
||||||
ABS,
|
ABS,
|
||||||
|
ABSENT,
|
||||||
ABSOLUTE,
|
ABSOLUTE,
|
||||||
ACCESS,
|
ACCESS,
|
||||||
ACCOUNT,
|
ACCOUNT,
|
||||||
|
|
|
@ -11321,45 +11321,58 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {
|
pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {
|
||||||
if self.peek_nth_token(1) == Token::RArrow {
|
let arg = if self.dialect.supports_named_fn_args_with_expr_name() {
|
||||||
let name = self.parse_identifier(false)?;
|
self.maybe_parse(|p| {
|
||||||
|
let name = p.parse_expr()?;
|
||||||
self.expect_token(&Token::RArrow)?;
|
let operator = p.parse_function_named_arg_operator()?;
|
||||||
let arg = self.parse_wildcard_expr()?.into();
|
let arg = p.parse_wildcard_expr()?.into();
|
||||||
|
Ok(FunctionArg::ExprNamed {
|
||||||
Ok(FunctionArg::Named {
|
name,
|
||||||
name,
|
arg,
|
||||||
arg,
|
operator,
|
||||||
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,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Ok(FunctionArg::Unnamed(self.parse_wildcard_expr()?.into()))
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11403,19 +11416,24 @@ impl<'a> Parser<'a> {
|
||||||
/// FIRST_VALUE(x IGNORE NULL);
|
/// FIRST_VALUE(x IGNORE NULL);
|
||||||
/// ```
|
/// ```
|
||||||
fn parse_function_argument_list(&mut self) -> Result<FunctionArgumentList, ParserError> {
|
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) {
|
if self.consume_token(&Token::RParen) {
|
||||||
return Ok(FunctionArgumentList {
|
return Ok(FunctionArgumentList {
|
||||||
duplicate_treatment: None,
|
duplicate_treatment: None,
|
||||||
args: vec![],
|
args: vec![],
|
||||||
clauses: vec![],
|
clauses,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let duplicate_treatment = self.parse_duplicate_treatment()?;
|
let duplicate_treatment = self.parse_duplicate_treatment()?;
|
||||||
let args = self.parse_comma_separated(Parser::parse_function_args)?;
|
let args = self.parse_comma_separated(Parser::parse_function_args)?;
|
||||||
|
|
||||||
let mut clauses = vec![];
|
|
||||||
|
|
||||||
if self.dialect.supports_window_function_null_treatment_arg() {
|
if self.dialect.supports_window_function_null_treatment_arg() {
|
||||||
if let Some(null_treatment) = self.parse_null_treatment()? {
|
if let Some(null_treatment) = self.parse_null_treatment()? {
|
||||||
clauses.push(FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment));
|
clauses.push(FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment));
|
||||||
|
@ -11456,6 +11474,10 @@ impl<'a> Parser<'a> {
|
||||||
clauses.push(FunctionArgumentClause::OnOverflow(on_overflow));
|
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)?;
|
self.expect_token(&Token::RParen)?;
|
||||||
Ok(FunctionArgumentList {
|
Ok(FunctionArgumentList {
|
||||||
duplicate_treatment,
|
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> {
|
fn parse_duplicate_treatment(&mut self) -> Result<Option<DuplicateTreatment>, ParserError> {
|
||||||
let loc = self.peek_token().location;
|
let loc = self.peek_token().location;
|
||||||
match (
|
match (
|
||||||
|
|
|
@ -4402,8 +4402,9 @@ fn parse_explain_query_plan() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_named_argument_function() {
|
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 sql = "SELECT FUN(a => '1', b => '2') FROM foo";
|
||||||
let select = verified_only_select(sql);
|
let select = dialects.verified_only_select(sql);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Function(Function {
|
&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]
|
#[test]
|
||||||
fn parse_ampersand_arobase() {
|
fn parse_ampersand_arobase() {
|
||||||
// In SQL Server, a&@b means (a) & (@b), in PostgreSQL it means (a) &@ (b)
|
// 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