I have timestampz column called created, and I want to create generated columns for month, day and year from it using Postgres 12+ generated columns.
Since there is a timezone in timestampz, expressions like date_part(create, ..) and EXTRACT (month from created) fail with the error:
ERROR: generation expression is not immutable
I tried to convert to timestamp and fix the timezone, but both would still be considered mutable generation expressions:
GENERATED ALWAYS AS (date_part('month', created::timestamp))GENERATED ALWAYS AS (date_part('month', created::timestamp AT TIME ZONE 'UTC'))
However, as indicated by Erwin Brandstetter in his comment on this answer, this works:
ALTER TABLE tbl
ADD COLUMN created_year numeric GENERATED ALWAYS AS
(date_part('month', created AT TIME ZONE 'UTC')) STORED;
Of course EXTRACT(month from created AT TIME ZONE 'UTC')) also works.
That leads me to the conclusion that if I want N timezones, I need N generated columns.
Why does this work and not #2 above? Does it have to do with the servers' configuration params when doing ::timestamp datatype conversion?
Both #1 and #2 do not work because the cast from
timestamptztotimestampis not immutable. It depends on the currenttimezonesetting. And generated columns only accept immutable expressions for obvious reasons. To note:timezoneis not a "server setting", it's a setting for each individual session. The default is typically set inpostgresql.conf, but clients can (and regularly will) set it as needed.On the other hand,
date_part('month', created AT TIME ZONE 'UTCis immutable. Deriving UTC time (or the local time for any given time zone offset) always produces the same result, and the time zone is a given constant in this expression.The name of the data type
timestamp with time zoneis a bit misleading, unfortunately. A given time zone is not stored at all. It just serves as input modifier to compute the corresponding UTC time, which is stored internally. (And as output decorator, adjusted to thetimezoneof the requesting client.) See:Basics: