Skip to content

Commit ec60b5c

Browse files
tests: cover PostgreSQL SQL/JSON clauses and keep JSON_TABLE AST expectations aligned
1 parent 1c59827 commit ec60b5c

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

tests/sqlparser_mysql.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3772,6 +3772,7 @@ fn parse_json_table() {
37723772
on_error: Some(JsonTableColumnErrorHandling::Null),
37733773
}),
37743774
],
3775+
on_error: None,
37753776
alias: table_alias(true, "t"),
37763777
}
37773778
);

tests/sqlparser_postgres.rs

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3553,6 +3553,163 @@ fn parse_json_table_is_not_reserved() {
35533553
}
35543554
}
35553555

3556+
fn parse_pg_select(sql: &str) -> Select {
3557+
let stmts = pg().parse_sql_statements(sql).unwrap();
3558+
let stmt = stmts.into_iter().next().unwrap();
3559+
let Statement::Query(query) = stmt else {
3560+
panic!("Expected query statement");
3561+
};
3562+
let SetExpr::Select(select) = *query.body else {
3563+
panic!("Expected select query body");
3564+
};
3565+
*select
3566+
}
3567+
3568+
fn parse_pg_projection_expr(sql: &str) -> Expr {
3569+
let select = parse_pg_select(sql);
3570+
expr_from_projection(&select.projection[0]).clone()
3571+
}
3572+
3573+
#[test]
3574+
fn parse_postgres_sql_json_query_functions() {
3575+
let expr = parse_pg_projection_expr("SELECT JSON_EXISTS(jsonb '1', '$.a' FALSE ON ERROR)");
3576+
let Expr::Function(Function {
3577+
args: FunctionArguments::List(FunctionArgumentList { clauses, .. }),
3578+
..
3579+
}) = expr
3580+
else {
3581+
panic!("Expected JSON_EXISTS to parse as a function call");
3582+
};
3583+
assert_eq!(
3584+
clauses,
3585+
vec![FunctionArgumentClause::JsonExistsOnErrorClause(
3586+
JsonExistsOnErrorBehavior::False
3587+
)]
3588+
);
3589+
3590+
let expr = parse_pg_projection_expr(
3591+
"SELECT JSON_VALUE(jsonb '{\"a\": 1}', '$.a ? (@ > $x)' PASSING 0 AS x RETURNING int DEFAULT 1 ON EMPTY ERROR ON ERROR)",
3592+
);
3593+
let Expr::Function(Function {
3594+
args: FunctionArguments::List(FunctionArgumentList { clauses, .. }),
3595+
..
3596+
}) = expr
3597+
else {
3598+
panic!("Expected JSON_VALUE to parse as a function call");
3599+
};
3600+
assert!(matches!(
3601+
&clauses[0],
3602+
FunctionArgumentClause::JsonPassingClause(JsonPassingClause { args })
3603+
if args.len() == 1 && args[0].name.value == "x"
3604+
));
3605+
assert!(matches!(
3606+
&clauses[1],
3607+
FunctionArgumentClause::JsonReturningClause(JsonReturningClause {
3608+
data_type: DataType::Int(None)
3609+
})
3610+
));
3611+
assert!(matches!(
3612+
&clauses[2],
3613+
FunctionArgumentClause::JsonValueBehaviorClause(JsonValueBehaviorClause {
3614+
behavior: JsonValueBehavior::Default(_),
3615+
target: JsonBehaviorTarget::Empty,
3616+
})
3617+
));
3618+
assert!(matches!(
3619+
&clauses[3],
3620+
FunctionArgumentClause::JsonValueBehaviorClause(JsonValueBehaviorClause {
3621+
behavior: JsonValueBehavior::Error,
3622+
target: JsonBehaviorTarget::Error,
3623+
})
3624+
));
3625+
3626+
let expr = parse_pg_projection_expr(
3627+
"SELECT JSON_QUERY(jsonb '[\"1\"]', '$[*]' RETURNING bytea FORMAT JSON WITH UNCONDITIONAL ARRAY WRAPPER KEEP QUOTES ON SCALAR STRING EMPTY ARRAY ON EMPTY EMPTY OBJECT ON ERROR)",
3628+
);
3629+
let Expr::Function(Function {
3630+
args: FunctionArguments::List(FunctionArgumentList { clauses, .. }),
3631+
..
3632+
}) = expr
3633+
else {
3634+
panic!("Expected JSON_QUERY to parse as a function call");
3635+
};
3636+
assert!(matches!(
3637+
&clauses[0],
3638+
FunctionArgumentClause::JsonReturningClause(JsonReturningClause {
3639+
data_type: DataType::Bytea
3640+
})
3641+
));
3642+
assert!(matches!(
3643+
&clauses[1],
3644+
FunctionArgumentClause::JsonFormatClause(JsonFormatClause {
3645+
format: JsonFormatType::Json,
3646+
encoding: None,
3647+
})
3648+
));
3649+
assert!(matches!(
3650+
&clauses[2],
3651+
FunctionArgumentClause::JsonQueryWrapperClause(
3652+
JsonQueryWrapperClause::WithUnconditionalArrayWrapper
3653+
)
3654+
));
3655+
assert!(matches!(
3656+
&clauses[3],
3657+
FunctionArgumentClause::JsonQueryQuotesClause(JsonQueryQuotesClause {
3658+
mode: JsonQueryQuotesMode::Keep,
3659+
on_scalar_string: true,
3660+
})
3661+
));
3662+
assert!(matches!(
3663+
&clauses[4],
3664+
FunctionArgumentClause::JsonQueryBehaviorClause(JsonQueryBehaviorClause {
3665+
behavior: JsonQueryBehavior::EmptyArray,
3666+
target: JsonBehaviorTarget::Empty,
3667+
})
3668+
));
3669+
assert!(matches!(
3670+
&clauses[5],
3671+
FunctionArgumentClause::JsonQueryBehaviorClause(JsonQueryBehaviorClause {
3672+
behavior: JsonQueryBehavior::EmptyObject,
3673+
target: JsonBehaviorTarget::Error,
3674+
})
3675+
));
3676+
}
3677+
3678+
#[test]
3679+
fn parse_postgres_json_table_on_error() {
3680+
let select = parse_pg_select(
3681+
"SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH '$.a' ERROR ON ERROR) ERROR ON ERROR) jt",
3682+
);
3683+
let relation = &select.from[0].relation;
3684+
assert!(matches!(
3685+
relation,
3686+
TableFactor::JsonTable {
3687+
on_error: Some(JsonTableOnErrorHandling::Error),
3688+
..
3689+
}
3690+
));
3691+
3692+
let select = parse_pg_select(
3693+
"SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH '$.a') EMPTY ARRAY ON ERROR) jt",
3694+
);
3695+
let relation = &select.from[0].relation;
3696+
assert!(matches!(
3697+
relation,
3698+
TableFactor::JsonTable {
3699+
on_error: Some(JsonTableOnErrorHandling::EmptyArray),
3700+
..
3701+
}
3702+
));
3703+
}
3704+
3705+
#[test]
3706+
fn parse_postgres_sql_json_duplicate_behavior_clauses() {
3707+
let sql = "SELECT JSON_VALUE(jsonb '1', '$.a' NULL ON EMPTY ERROR ON EMPTY)";
3708+
assert!(pg().parse_sql_statements(sql).is_err());
3709+
let sql = "SELECT JSON_QUERY(jsonb '[]', '$[*]' NULL ON ERROR EMPTY ARRAY ON ERROR)";
3710+
assert!(pg().parse_sql_statements(sql).is_err());
3711+
}
3712+
35563713
#[test]
35573714
fn test_composite_value() {
35583715
let sql = "SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9";

0 commit comments

Comments
 (0)