Skip to content

Arrays with a NULL element (int4[], text[], etc.) decode to null instead of preserving the null slot #73

Description

@aesslinger

Describe the bug

For the hardcoded array fast-paths in extract.rs (INT2_ARRAY, INT4_ARRAY, INT8_ARRAY, TEXT_ARRAY/VARCHAR_ARRAY, FLOAT4_ARRAY, FLOAT8_ARRAY, BOOL_ARRAY), an array containing even one NULL element decodes to null for the entire column value instead of [value, null].

Root cause: these fast-paths decode via Vec<T>: FromSql (e.g. Vec<i32>), not Vec<Option<T>>. tokio-postgres's Vec<T>::from_sql calls T::from_sql_nullable per element and propagates an error the moment any element is absent, since plain T has no "missing" representation. try_extract's catch-all then turns that error into JsonValue::Null for the whole column.

To Reproduce

SELECT ARRAY[1, NULL];        -- int4[] -> returns null, not [1, null]
SELECT ARRAY['a', NULL];      -- text[] -> returns null, not ["a", null]
SELECT ARRAY[true, NULL];     -- bool[] -> returns null, not [true, null]

Discovered via

Found while regression-testing #71/#72 (hstore and array-of-custom-OID-type fixes). Confirmed via git stash that this behavior is unchanged by those fixes — it exists identically on main. Notably, the new generic array decoder added in #72 (for enum[]/hstore[]/etc.) does not have this bug — it correctly decodes ARRAY['happy'::mood, NULL] as ["happy", null], since it parses each element's length prefix directly (-1 length = null) rather than going through Vec<T>: FromSql.

Suggested fix

Change the hardcoded fast-paths to decode via Vec<Option<T>> instead of Vec<T>, mapping each Option<T> to JsonValue (null-safe) instead of T directly. Matches the builtin driver's extract/array.rs, which fills a Null slot per failed/absent element rather than nulling the whole array (see extract_recursively_or_fill_nulls_into's fill_nulls fallback).

Relevant Log Output

N/A — no error, just silent whole-array null.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions