Skip to content

Fix IN's empty list problem by using = any('{}') #164

@jmoore34

Description

@jmoore34

The In newtype has the problem of not being able to handle empty lists:

Note that In [] expands to (null), which works as expected in the query above, but evaluates to the logical null value on every row instead of TRUE. This means that changing the query above to ... id NOT in ? and supplying the empty list as the parameter returns zero rows, instead of all of them as one would expect.

The current workarounds are:

  1. Use the Values newtype instead. However, because Values requires you to specify the postgres types each time, you can do something like accidentally use int4 instead of int8, which can e.g. pass an integration test suite but eventually blow up in prod. Also it's tricky to get Values to work with lists of a single column rather than a list of tuples.
  2. Use coalesce, but the query planner not being able to see inside is a problem (e.g. for hitting indices on huge tables)

There is, however, a third option which does indeed allow you to pass in empty lists without getting a type error from postgres. Namely, using array literal syntax '{}' with ANY/ALL.

Examples:

SELECT 2 = ANY ('{1,2,3}'); -- true
SELECT 2 = ANY ('{1,2}');   -- true
SELECT 2 = ANY ('{1 }');    -- false
SELECT 2 = ANY ('{}');      -- false (neither NULL nor a type error!)

Similarly, NOT IN can be achieved by either using NOT to negate the expression above, or by using ALL:

SELECT 2 <> ALL ('{1,2,3}'); -- false
SELECT 2 <> ALL ('{1,2}');   -- false
SELECT 2 <> ALL ('{1 }');    -- true
SELECT 2 <> ALL ('{}');      -- true (neither NULL nor a type error!)

Note that passing an empty array only works if it is passed as an array literal '{}', not if it is passed using the array function, e.g.:

SELECT 2 = ANY (ARRAY[1,2,3]); -- true
SELECT 2 = ANY (ARRAY[1,2]);   -- true
SELECT 2 = ANY (ARRAY[1]);     -- false
SELECT 2 = ANY (ARRAY[]);      -- SQL Error [42P18]: ERROR: cannot determine type of empty array. Hint: Explicitly cast to the desired type, for example ARRAY[]::integer[].

Thus, the empty array problem can be solved if the following two conditions are met:

  1. postresql-simple passes in empty arrays using the array literal syntax '{}'
  2. tuples or rows can be passed in within PGArrays

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions