I’m building a web app, and I noticed that one of my queries is returning a null
where I expect a boolean
. However the database is indicating it’s actually returning a boolean
.
I can see in the documentation that Postgres considers NULL
to be an unknown boolean value. I’m confused why the <=
operator would return t
/NULL
instead of t
/f
though.
experiments-2=# select version();
version
-----------------------------------------------------------------------------------------------------------------------------
PostgreSQL 13.13 (Debian 13.13-0+deb11u1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
(1 row)
experiments-2=# SELECT a.email_verified_at <= NOW() AS "emailVerified", pg_typeof(a.email_verified_at <= NOW()) AS "emailVerifiedType", pg_typeof((a.email_verified_at <= NOW())::boolean) AS "emailVerifiedType2" FROM profiles AS p INNER JOIN accounts AS a ON p.id = a.profile_id;
emailVerified | emailVerifiedType | emailVerifiedType2
---------------+-------------------+--------------------
| boolean | boolean
t | boolean | boolean
| boolean | boolean
(3 rows)
How can I tell Postgres to return an explicit t
/f
to my application?
2
Answers
The problem is
a.email_verified_at
is a nullable field. Comparison results involvingNULL
returnNULL
, which I didn't know. The solution is to check forNULL
before comparing toNOW()
:SQL has a three-valued logic: a boolean expression can be
TRUE
,FALSE
or NULL (unknown). If you compare NULL (unknown) with any value, the result is NULL.There are several ways to rewrite your comparison so that it returns
FALSE
ifemail_verified_at
is a NULL value:use
coalesce()
:use an explicit NULL check
Another option would be to forbid NULL values in
email_verified_at
and storeinfinity
if the e-mail address was never verified. This solution would simplify the comparison, but it may be undesirable for other reasons.