Skip to content

Commit

Permalink
support json_object('k':'v') in postgres (#1546)
Browse files Browse the repository at this point in the history
  • Loading branch information
lovasoa authored Nov 25, 2024
1 parent fd21fae commit 525d178
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 160 deletions.
20 changes: 20 additions & 0 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,26 @@ impl Dialect for PostgreSqlDialect {
fn supports_load_extension(&self) -> bool {
true
}

/// See <https://www.postgresql.org/docs/current/functions-json.html>
///
/// Required to support the colon in:
/// ```sql
/// SELECT json_object('a': 'b')
/// ```
fn supports_named_fn_args_with_colon_operator(&self) -> bool {
true
}

/// See <https://www.postgresql.org/docs/current/functions-json.html>
///
/// Required to support the label in:
/// ```sql
/// SELECT json_object('label': 'value')
/// ```
fn supports_named_fn_args_with_expr_name(&self) -> bool {
true
}
}

pub fn parse_create(parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
Expand Down
172 changes: 171 additions & 1 deletion tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,173 @@ fn parse_json_ops_without_colon() {
}
}

#[test]
fn parse_json_object() {
let dialects = TestedDialects::new(vec![
Box::new(MsSqlDialect {}),
Box::new(PostgreSqlDialect {}),
]);
let select = dialects.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 = dialects
.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 = dialects.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 = dialects.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 = dialects.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 = dialects.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!(),
}
}

#[test]
fn parse_mod_no_spaces() {
use self::Expr::*;
Expand Down Expand Up @@ -4416,7 +4583,10 @@ 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 dialects = all_dialects_where(|d| {
d.supports_named_fn_args_with_rarrow_operator()
&& !d.supports_named_fn_args_with_expr_name()
});
let sql = "SELECT FUN(a => '1', b => '2') FROM foo";
let select = dialects.verified_only_select(sql);

Expand Down
159 changes: 0 additions & 159 deletions tests/sqlparser_mssql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,165 +793,6 @@ 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)",
);
Expand Down

0 comments on commit 525d178

Please sign in to comment.