diff --git a/src/binding.rs b/src/binding.rs index d9bb940..4cfee24 100644 --- a/src/binding.rs +++ b/src/binding.rs @@ -436,6 +436,16 @@ fn decode_blob_wire_format(value: &str) -> Option> { /// 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 { + // `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 { diff --git a/src/binding_tests.rs b/src/binding_tests.rs index 25bb930..99bf693 100644 --- a/src/binding_tests.rs +++ b/src/binding_tests.rs @@ -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(); @@ -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();