Postgres SQL Exclusive OR (XOR) CHECK CONSTRAINT, is it possible? Postgres SQL Exclusive OR (XOR) CHECK CONSTRAINT, is it possible? postgresql postgresql

Postgres SQL Exclusive OR (XOR) CHECK CONSTRAINT, is it possible?


Right, the a = NULL and b = NULL bit was the issue as @a_horse_with_no_name indicated. You might also consider this derivative, which doesn't require the OR operator:

create table test (  id integer primary key,   a integer,   b integer,   check ((a IS NULL) != (b IS NULL)));

Of course that works exclusively with only two column XOR comparison. With three or more column XOR comparison in a similar test table you could resort to a similar approach more like this:

create table test (  id integer primary key,   a integer,   b integer,   c integer,   check ((a IS NOT NULL)::INTEGER +          (b IS NOT NULL)::INTEGER +          (c IS NOT NULL)::INTEGER = 1));


You can't compare NULL values with =, you need IS NULL

(a IS NOT NULL AND b is NULL) OR (b IS NOT NULL AND a is NULL)

For a check constraint you need to enclose the whole expression in parentheses:

create table xor_test (  id integer primary key,   a integer,   b integer,   check ((a IS NOT NULL AND b is NULL) OR (b IS NOT NULL AND a is NULL)));-- worksINSERT INTO xor_test(id, a, b) VALUES (1, null, 1);-- worksINSERT INTO xor_test(id, a, b) VALUES (2, 1, null);-- failseINSERT INTO xor_test(id, a, b) VALUES (3, 1, 1); 

Alternatively the check constraint can be simplified to

check ( num_nonnulls(a,b) = 1 )

That's also easier to adjust to more columns


This is clear Exclusive-OR. Why not define it as a boolean operator first? It might be useful for other cases too.

CREATE OR REPLACE FUNCTION public.xor (a boolean, b boolean) returns boolean immutable language sql AS$$SELECT (a and not b) or (b and not a);$$;CREATE OPERATOR # (    PROCEDURE = public.xor,     LEFTARG = boolean,     RIGHTARG = boolean);

Then CHECK ((a IS NULL) # (b IS NULL))