CAST Within EXCLUSION constraint

Lists: pgsql-hackers
From: "David E(dot) Wheeler" <david(at)justatheory(dot)com>
To: pgsql-hackers Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: CAST Within EXCLUSION constraint
Date: 2013-08-20 15:49:27
Message-ID: 3C24671E-AA46-4B4D-965F-1F7F371F1F5E@justatheory.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

Hackers,

I am trying to do something like this:

CREATE TYPE source AS ENUM(
'fred', 'wilma', 'barney', 'betty'
);

CREATE EXTENSION btree_gist;

CREATE TABLE things (
source source NOT NULL,
within tstzrange NOT NULL,
EXCLUDE USING gist (source WITH =, within WITH &&)
);

Alas, enums are not supported by btree_gist:

try.sql:13: ERROR: data type source has no default operator class for access method "gist"
HINT: You must specify an operator class for the index or define a default operator class for the data type.

Well, maybe I can cast it? But no, changing the EXCLUDE line to

EXCLUDE USING gist (source::text WITH =, within WITH &&)

Yields a syntax error:

try.sql:13: ERROR: syntax error at or near "::"
LINE 4: EXCLUDE USING gist (source::text WITH =, within WITH &&)

So that's out. Why shouldn't :: be allowed?

No problem, I can use CAST(), right? So I try:

EXCLUDE USING gist (CAST(source AS text) WITH =, within WITH &&)

Not so much:

try.sql:13: ERROR: functions in index expression must be marked IMMUTABLE

I guess it's because locale settings might change, and therefore change the text representation? Seems unlikely, though.

I guess I can create my own IMMUTABLE function over the ENUM:

CREATE FUNCTION source_to_text(
source
) RETURNS TEXT LANGUAGE sql STRICT IMMUTABLE AS $$
SELECT $1::text;
$$;

So this works:

EXCLUDE USING gist (source_to_text(source) WITH =, within WITH &&)

So I guess that’s good enough for now. But should :: really be a syntax error in index expressions?

Thanks,

David


From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: "David E(dot) Wheeler" <david(at)justatheory(dot)com>
Cc: pgsql-hackers Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: CAST Within EXCLUSION constraint
Date: 2013-08-20 16:50:11
Message-ID: 7838.1377017411@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

"David E. Wheeler" <david(at)justatheory(dot)com> writes:
> Well, maybe I can cast it? But no, changing the EXCLUDE line to
> EXCLUDE USING gist (source::text WITH =, within WITH &&)
> Yields a syntax error:
> try.sql:13: ERROR: syntax error at or near "::"
> LINE 4: EXCLUDE USING gist (source::text WITH =, within WITH &&)
> So that's out. Why shouldn't :: be allowed?

You need more parentheses -- (source::text) would've worked.

> No problem, I can use CAST(), right? So I try:
> EXCLUDE USING gist (CAST(source AS text) WITH =, within WITH &&)
> Not so much:
> try.sql:13: ERROR: functions in index expression must be marked IMMUTABLE
> I guess it's because locale settings might change, and therefore change the text representation? Seems unlikely, though.

Not locale, just renaming one of the values would be enough to break that.
Admittedly we don't provide an official way to do that ATM, but you can do
an UPDATE on pg_enum.

regards, tom lane


From: "David E(dot) Wheeler" <david(at)justatheory(dot)com>
To: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Cc: pgsql-hackers Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: CAST Within EXCLUSION constraint
Date: 2013-08-20 16:53:54
Message-ID: 6BFFDFDB-24B2-41D6-9B05-9237BE9CF271@justatheory.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

On Aug 20, 2013, at 6:50 PM, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us> wrote:

> You need more parentheses -- (source::text) would've worked.

Alas, no, same problem as for CAST():

ERROR: functions in index expression must be marked IMMUTABLE

>> No problem, I can use CAST(), right? So I try:
>> EXCLUDE USING gist (CAST(source AS text) WITH =, within WITH &&)
>> Not so much:
>> try.sql:13: ERROR: functions in index expression must be marked IMMUTABLE
>> I guess it's because locale settings might change, and therefore change the text representation? Seems unlikely, though.
>
> Not locale, just renaming one of the values would be enough to break that.
> Admittedly we don't provide an official way to do that ATM, but you can do
> an UPDATE on pg_enum.

Ah, right. Maybe if there was a way to get at some immutable numeric value…

Thanks,

David


From: Alexander Korotkov <aekorotkov(at)gmail(dot)com>
To: "David E(dot) Wheeler" <david(at)justatheory(dot)com>
Cc: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, pgsql-hackers Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: CAST Within EXCLUSION constraint
Date: 2013-08-21 12:04:36
Message-ID: CAPpHfdt6xnTEDShj1BvgzAT5avO00wrKaybCNVCkQmtKTxZkMA@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

On Tue, Aug 20, 2013 at 8:53 PM, David E. Wheeler <david(at)justatheory(dot)com>wrote:

> On Aug 20, 2013, at 6:50 PM, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us> wrote:
>
> > You need more parentheses -- (source::text) would've worked.
>
> Alas, no, same problem as for CAST():
>
> ERROR: functions in index expression must be marked IMMUTABLE
>
> >> No problem, I can use CAST(), right? So I try:
> >> EXCLUDE USING gist (CAST(source AS text) WITH =, within WITH &&)
> >> Not so much:
> >> try.sql:13: ERROR: functions in index expression must be marked
> IMMUTABLE
> >> I guess it's because locale settings might change, and therefore change
> the text representation? Seems unlikely, though.
> >
> > Not locale, just renaming one of the values would be enough to break
> that.
> > Admittedly we don't provide an official way to do that ATM, but you can
> do
> > an UPDATE on pg_enum.
>
> Ah, right. Maybe if there was a way to get at some immutable numeric value…
>

It seems reasonable to me to cast enum to oid. However, creating casts
without function isn't allowed for enums.

test=# create cast (source as oid) without function;
ERROR: enum data types are not binary-compatible

However, this restriction can be avoided either by writing dummy C-function
or touching catalog directly:

test=# insert into pg_cast values ((select oid from pg_type where typname =
'source'), (select oid from pg_type where typname = 'oid'), 0, 'e', 'b');
INSERT 341001 1

Then you can define desired restriction.

CREATE TABLE things (
source source NOT NULL,
within tstzrange NOT NULL,
EXCLUDE USING gist ((source::oid) WITH =, within WITH &&)
);

Probably, I'm missing something and casting enum to oid is somehow unsafe?

------
With best regards,
Alexander Korotkov.


From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: Alexander Korotkov <aekorotkov(at)gmail(dot)com>
Cc: "David E(dot) Wheeler" <david(at)justatheory(dot)com>, pgsql-hackers Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: CAST Within EXCLUSION constraint
Date: 2013-08-21 14:13:15
Message-ID: 4022.1377094395@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

Alexander Korotkov <aekorotkov(at)gmail(dot)com> writes:
> It seems reasonable to me to cast enum to oid. However, creating casts
> without function isn't allowed for enums.

> test=# create cast (source as oid) without function;
> ERROR: enum data types are not binary-compatible

The reason for that is you'd get randomly different results on another
installation. In this particular application, I think David doesn't
really care about what values he gets as long as they're distinct,
so this might be an OK workaround for him. But that's the reasoning
for the general prohibition.

regards, tom lane


From: "David E(dot) Wheeler" <david(at)justatheory(dot)com>
To: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Cc: Alexander Korotkov <aekorotkov(at)gmail(dot)com>, pgsql-hackers Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: CAST Within EXCLUSION constraint
Date: 2013-08-21 15:30:09
Message-ID: 2B33E1CB-26BA-4258-A9AC-9B5A137F02B0@justatheory.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

On Aug 21, 2013, at 4:13 PM, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us> wrote:

>> test=# create cast (source as oid) without function;
>> ERROR: enum data types are not binary-compatible
>
> The reason for that is you'd get randomly different results on another
> installation. In this particular application, I think David doesn't
> really care about what values he gets as long as they're distinct,
> so this might be an OK workaround for him. But that's the reasoning
> for the general prohibition.

I’m okay with my function that casts to text, at least for now. An integer would be nicer, likely smaller for my index, but not a big deal.

David


From: Noah Misch <noah(at)leadboat(dot)com>
To: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Cc: Alexander Korotkov <aekorotkov(at)gmail(dot)com>, "David E(dot) Wheeler" <david(at)justatheory(dot)com>, pgsql-hackers Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: CAST Within EXCLUSION constraint
Date: 2013-08-22 03:24:09
Message-ID: 20130822032409.GA607191@tornado.leadboat.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

On Wed, Aug 21, 2013 at 10:13:15AM -0400, Tom Lane wrote:
> Alexander Korotkov <aekorotkov(at)gmail(dot)com> writes:
> > It seems reasonable to me to cast enum to oid. However, creating casts
> > without function isn't allowed for enums.
>
> > test=# create cast (source as oid) without function;
> > ERROR: enum data types are not binary-compatible
>
> The reason for that is you'd get randomly different results on another
> installation. In this particular application, I think David doesn't
> really care about what values he gets as long as they're distinct,
> so this might be an OK workaround for him. But that's the reasoning
> for the general prohibition.

While a WITHOUT FUNCTION cast does *guarantee* that flaw, working around the
restriction with a cast function is all too likely to create the same flaw.
Here's the comment about the restriction:

* Theoretically you could build a user-defined base type that is
* binary-compatible with a composite, enum, or array type. But we
* disallow that too, as in practice such a cast is surely a mistake.
* You can always work around that by writing a cast function.

That's reasonable enough, but we could reduce this to a WARNING. Alexander
shows a credible use case. A superuser can easily introduce breakage through
careless addition of WITHOUT FUNCTION casts. Permitting borderline cases
seems more consistent with the level of user care already expected in this
vicinity.

--
Noah Misch
EnterpriseDB http://www.enterprisedb.com


From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: Noah Misch <noah(at)leadboat(dot)com>
Cc: Alexander Korotkov <aekorotkov(at)gmail(dot)com>, "David E(dot) Wheeler" <david(at)justatheory(dot)com>, pgsql-hackers Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: CAST Within EXCLUSION constraint
Date: 2013-08-22 06:15:26
Message-ID: 24732.1377152126@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-hackers

Noah Misch <noah(at)leadboat(dot)com> writes:
> On Wed, Aug 21, 2013 at 10:13:15AM -0400, Tom Lane wrote:
>> The reason for that is you'd get randomly different results on another
>> installation. In this particular application, I think David doesn't
>> really care about what values he gets as long as they're distinct,
>> so this might be an OK workaround for him. But that's the reasoning
>> for the general prohibition.

> While a WITHOUT FUNCTION cast does *guarantee* that flaw, working around the
> restriction with a cast function is all too likely to create the same flaw.
> Here's the comment about the restriction:

> * Theoretically you could build a user-defined base type that is
> * binary-compatible with a composite, enum, or array type. But we
> * disallow that too, as in practice such a cast is surely a mistake.
> * You can always work around that by writing a cast function.

> That's reasonable enough, but we could reduce this to a WARNING. Alexander
> shows a credible use case. A superuser can easily introduce breakage through
> careless addition of WITHOUT FUNCTION casts. Permitting borderline cases
> seems more consistent with the level of user care already expected in this
> vicinity.

Well, if we're gonna allow it, let's just allow it --- I don't see much
point in a WARNING here. As you say, superusers are presumed to be
responsible adults.

regards, tom lane