Alternative to array comparisons in postgres

20/04/2020

translations

Array comparisons make comparisons between groups of values easy:

DELETE FROM user WHERE name IN ('fred', 'bean');

In a general purpose language like typescript, the filter fragment is often considered to be equivalent to:

const where = ['fred', 'bean'].includes(name);

However array comparisons in postgres do not handle emptyness. e.g:

DELETE FROM user WHERE name IN (); // sql error

Which often leads to various bugs when building SQL queries from a GPL. e.g https://github.com/typeorm/typeorm/issues/2195.

There are various workarounds, the most common is to handle the emptyness at the application level:

if (safeInput.length) {
    const query = `DELETE FROM user WHERE name IN (${ safeInput.join(", ") });`
}

which works for most cases but requires some special logic for the negation query:

DELETE FROM user WHERE name NOT IN ('fred', 'bean');

An alternative way of achieving the same result is to use array functions. Array functions handle empty arrays and have similar semantics to array functions in a GPL.

DELETE FROM user WHERE array[name] && array['fred', 'bean]::text[];

The drawback here is postgres cannot determine a type of an empty array, which means we have to explicitly cast to the correct array type. But I like it better than using an array comparison because it will handle the negation as well. e.g:

DELETE FROM user WHERE NOT (array[name] && array['fred', 'bean]::text[]);

Full sql example below:

CREATE TABLE IF NOT EXISTS "user" (name text PRIMARY KEY);

INSERT INTO "user" ("name") VALUES ('c') ON CONFLICT (name) DO NOTHING;

SELECT * FROM "user";

DELETE FROM "user" WHERE NOT (array[name] && array['c']::text[]);

DELETE FROM "user" WHERE name NOT IN ('c');