Thread: date_trunc on date is immutable?

date_trunc on date is immutable?

From
Kian Wright
Date:
I'm trying to create an index on the month and year of a date field (in 8.3), and I'm getting the "functions in index expression must be marked IMMUTABLE" error message.

I thought dates were immutable, and didn't think that DATE_TRUNC did anything to change that.  These all fail:
  create index enrollments_app_recvd_month_idx on enrollments ( date_trunc('month', appl_recvd_date) );
  create index enrollments_app_recvd_month_idx on enrollments ( (date_trunc('month', appl_recvd_date) at time zone 'pst') );
  create index enrollments_app_recvd_month_idx on enrollments ( to_char(appl_recvd_date, 'YYYYMM') );
  create index enrollments_app_recvd_month_idx on enrollments ( (to_char(extract(year from appl_recvd_date), '0000') || to_char(extract( month from appl_recvd_date), '00')) );

After much experimentation, I finally was able to get this to work:
  create index enrollments_app_recvd_month_idx on enrollments ( (cast(extract(year from appl_recvd_date) as text) || cast(extract(month from appl_recvd_date) as text)) );

I am guessing to_char is mutable because the format string could use a locale specific character, and PG doesn't bother to check the format string when determining whether a function call is immutable. But I'm lost on why date_trunc is mutable, especially after applying a specific time zone. Am I missing something here?

Re: date_trunc on date is immutable?

From
Scott Marlowe
Date:
On Thu, Dec 24, 2009 at 4:36 PM, Kian Wright
<kian.wright@senioreducators.com> wrote:
> I'm trying to create an index on the month and year of a date field (in
> 8.3), and I'm getting the "functions in index expression must be marked
> IMMUTABLE" error message.

If applied to a timestamp, it is immutable.  If it's a timestamp with
timezone it's not, because the timezone can change, which would change
the index.

Re: date_trunc on date is immutable?

From
Greg Stark
Date:
On Fri, Dec 25, 2009 at 12:56 AM, Scott Marlowe <scott.marlowe@gmail.com> wrote:
> On Thu, Dec 24, 2009 at 4:36 PM, Kian Wright
> <kian.wright@senioreducators.com> wrote:
>> I'm trying to create an index on the month and year of a date field (in
>> 8.3), and I'm getting the "functions in index expression must be marked
>> IMMUTABLE" error message.
>
> If applied to a timestamp, it is immutable.  If it's a timestamp with
> timezone it's not, because the timezone can change, which would change
> the index.

Put another way, a given point in time doesn't necessarily lie in a
particular month or on a particular day because it depends what time
zone the system is set to. So right now it's a day earlier or later in
part of the globe.

To do what you want define the index on date_trunc('month',
appl_recvd_date at time zone 'America/Los_Angeles') or something like that.

You'll have to make sure your queries have the same expression in them
though :( It won't work if you just happen to have the system time
zone set to the matching time zone.

--
greg

Re: date_trunc on date is immutable?

From
Scott Marlowe
Date:
On Thu, Dec 24, 2009 at 6:47 PM, Greg Stark <gsstark@mit.edu> wrote:
> On Fri, Dec 25, 2009 at 12:56 AM, Scott Marlowe <scott.marlowe@gmail.com> wrote:
>> On Thu, Dec 24, 2009 at 4:36 PM, Kian Wright
>> <kian.wright@senioreducators.com> wrote:
>>> I'm trying to create an index on the month and year of a date field (in
>>> 8.3), and I'm getting the "functions in index expression must be marked
>>> IMMUTABLE" error message.
>>
>> If applied to a timestamp, it is immutable.  If it's a timestamp with
>> timezone it's not, because the timezone can change, which would change
>> the index.
>
> Put another way, a given point in time doesn't necessarily lie in a
> particular month or on a particular day because it depends what time
> zone the system is set to. So right now it's a day earlier or later in
> part of the globe.
>
> To do what you want define the index on date_trunc('month',
> appl_recvd_date at time zone 'America/Los_Angeles') or something like that.
>
> You'll have to make sure your queries have the same expression in them
> though :( It won't work if you just happen to have the system time
> zone set to the matching time zone.

Isn't it the client timezone and not the system timezone that actually
sets the tz the tstz is set to on retrieval?

Re: date_trunc on date is immutable?

From
Greg Stark
Date:
On Fri, Dec 25, 2009 at 1:58 AM, Scott Marlowe <scott.marlowe@gmail.com> wrote:
> Isn't it the client timezone and not the system timezone that actually
> sets the tz the tstz is set to on retrieval?

It's the GUC:

stark=> set timezone = 'America/Los_Angeles';
SET
stark=> select now();
              now
-------------------------------
 2009-12-25 06:44:33.238187-08
(1 row)


I'm not sure if and how we initialize the GUC on a new connection
though. It might vary depending on the driver you use too.


--
greg

Re: date_trunc on date is immutable?

From
Tom Lane
Date:
Greg Stark <gsstark@mit.edu> writes:
> On Fri, Dec 25, 2009 at 12:56 AM, Scott Marlowe <scott.marlowe@gmail.com> wrote:
>> On Thu, Dec 24, 2009 at 4:36 PM, Kian Wright
>> <kian.wright@senioreducators.com> wrote:
>>> I'm trying to create an index on the month and year of a date field (in
>>> 8.3), and I'm getting the "functions in index expression must be marked
>>> IMMUTABLE" error message.
>>
>> If applied to a timestamp, it is immutable. �If it's a timestamp with
>> timezone it's not, because the timezone can change, which would change
>> the index.

> Put another way, a given point in time doesn't necessarily lie in a
> particular month or on a particular day because it depends what time
> zone the system is set to. So right now it's a day earlier or later in
> part of the globe.

> To do what you want define the index on date_trunc('month',
> appl_recvd_date at time zone 'America/Los_Angeles') or something like that.

Given the way the question was phrased, I think the real situation is
that the OP has a column of type date.  There isn't any date_trunc()
function on date; there are such functions for timestamp with and
without timezone.  The parser prefers the former because with-tz is a
preferred type, so what he's really got is
    date_trunc('month', datecol::timestamptz)
which is doubly not immutable: both the cast and the trunc function
are timezone-sensitive.

So one possible answer is to make sure the cast is to without-tz:
    date_trunc('month', datecol::timestamp)
which in fact is indexable.  However:

> You'll have to make sure your queries have the same expression in them
> though :( It won't work if you just happen to have the system time
> zone set to the matching time zone.

This point is still a problem, because he'd need the same explicit
cast in the queries he wants to use the index.  It might be worth making
a special-purpose function monthof(date) or something like that to
reduce the notational burden.

(More generally, I wonder if it is worth creating a built-in date_trunc
for date input, just to avoid this gotcha.  At least in this context,
it seems like date->timestamp ought to be a preferable promotion over
date->timestamptz.)

            regards, tom lane