Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,16 @@ fn decode_blob_wire_format(value: &str) -> Option<Vec<u8>> {
/// Convert a JSON array to a PostgreSQL `ARRAY[...]` literal string.
/// Recursively handles nested arrays (multi-dimensional PG arrays).
fn json_array_to_pg_literal(arr: &[Value]) -> Result<String, String> {
// `ARRAY[]` (an ARRAY constructor with no elements) is only accepted
// when the target column type is unambiguous — in many contexts
// (e.g. INSERT ... VALUES) it's a syntax/type error. `'{}'` is the
// canonical PostgreSQL empty-array literal the server always accepts;
// it must be quoted — a bare `{}` is itself a syntax error. Matches the
// builtin driver's `json_array_to_pg_literal` exactly.
if arr.is_empty() {
return Ok("'{}'".to_string());
}

let mut parts = Vec::with_capacity(arr.len());
for elem in arr {
let part = match elem {
Expand Down
27 changes: 27 additions & 0 deletions src/binding_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ mod bind_pg_value_tests {
assert_eq!(bound.sql, "ARRAY[ARRAY[1, 2], ARRAY[3, 4]]");
}

#[test]
fn empty_array_binds_as_quoted_empty_literal_not_array_constructor() {
// ARRAY[] (an ARRAY constructor with no elements) is only accepted
// when the target column type is unambiguous -- in many contexts
// (e.g. plain INSERT ... VALUES) it's a syntax/type error. '{}' is
// the canonical PostgreSQL empty-array literal the server always
// accepts. See #86.
let bound = bind_pg_value(json!([]), 1, &BindOptions::default()).unwrap();
assert_eq!(bound.sql, "'{}'");
assert!(bound.param.is_none());
}

#[test]
fn nested_empty_array_binds_as_quoted_empty_literal_at_every_level() {
let bound = bind_pg_value(json!([[]]), 1, &BindOptions::default()).unwrap();
assert_eq!(bound.sql, "ARRAY['{}']");
}

#[test]
fn string_array_escapes_single_quotes() {
let bound = bind_pg_value(json!(["it's", "ok"]), 1, &BindOptions::default()).unwrap();
Expand Down Expand Up @@ -260,6 +278,15 @@ mod bind_pg_value_tests {
assert!(bound.param.is_none());
}

#[test]
fn empty_array_literal_embedded_in_string_binds_as_quoted_empty_literal() {
// Same #86 guard, reached via bind_pg_string's inline array-literal
// parse ("[]") rather than a native JSON array value.
let bound = bind_pg_value(json!("[]"), 1, &BindOptions::default()).unwrap();
assert_eq!(bound.sql, "'{}'");
assert!(bound.param.is_none());
}

#[test]
fn plain_string_falls_through_to_text_binding() {
let bound = bind_pg_value(json!("hello world"), 1, &BindOptions::default()).unwrap();
Expand Down
Loading