Thread: Minor inheritance/check bug: Inconsistent behavior

Minor inheritance/check bug: Inconsistent behavior

From
Chris Travers
Date:
Hi;

I figured I would report this as well, primarily because people
getting into table inheritance may try to use this to solve the set
exclusion problem (i.e. partly required to prevent duplicate key
values in an inheritance tree).  I see this as minor because I don't
see a lot of people using these aspects of the software in this way
now.

or_examples=# create table cities (city text, state text, is_capital
bool, altitude int, check(not(is_capital and tableoid::regclass::text
= 'cities')));

The intent of the check constraint is to force rows in the parent
table to use only a part of the key domain, while another portion
(where is_capital is true) can be reserved for child tables.

or_examples=# insert into cities values ('Seattle', 'Washington', false, 100);
INSERT 0 1
or_examples=# insert into cities values ('Olympia', 'Washington', true, 100);
INSERT 0 1

Ok, note that the check constraint was violated by the second row but
apparently this wasn't caught.

or_examples=# select *, tableoid::regclass::text from cities;
  city   |   state    | is_capital | altitude | tableoid
---------+------------+------------+----------+----------
 Seattle | Washington | f          |      100 | cities
 Olympia | Washington | t          |      100 | cities
(2 rows)

And indeed if we try to add the constraint again over the top
PostgreSQL will complain loudly.

or_examples=# alter table cities add check(not(is_capital and
tableoid::regclass::name = 'cities'));
ERROR:  check constraint "cities_check1" is violated by some row

My guess is that tableoid is not known when the check constraint is
checked.  It seems to me one option would be to either disallow
checking tableoid in the check constraint or making this known.
However as it is, PostgreSQL will not raise an error until after the
insert has already been made and the check constraint is re-applied.

or_examples=# select version();
                                                  version

---------------------------------------------------------------------------------
--------------------------
 PostgreSQL 9.1.4 on i386-redhat-linux-gnu, compiled by gcc (GCC)
4.7.0 20120507
(Red Hat 4.7.0-5), 32-bit
(1 row)

Re: Minor inheritance/check bug: Inconsistent behavior

From
Tom Lane
Date:
Chris Travers <chris@metatrontech.com> writes:
> My guess is that tableoid is not known when the check constraint is
> checked.

None of the system columns are set at the time check constraints are
checked.

            regards, tom lane

Re: Minor inheritance/check bug: Inconsistent behavior

From
Amit Kapila
Date:
From: pgsql-bugs-owner@postgresql.org
[mailto:pgsql-bugs-owner@postgresql.org] On Behalf Of Tom Lane
Sent: Friday, August 24, 2012 10:33 AM
Chris Travers <chris@metatrontech.com> writes:
>> My guess is that tableoid is not known when the check constraint is
>> checked.

>None of the system columns are set at the time check constraints are
>checked.

Is there any problem if set tableOID before calling ExecConstarints()?
I am not sure may be this is not so important problem for which we don't
want to
change existing code.

With Regards,
Amit Kapila.

Re: Minor inheritance/check bug: Inconsistent behavior

From
Tom Lane
Date:
Amit Kapila <amit.kapila@huawei.com> writes:
> From: pgsql-bugs-owner@postgresql.org
> [mailto:pgsql-bugs-owner@postgresql.org] On Behalf Of Tom Lane
>> None of the system columns are set at the time check constraints are
>> checked.

> Is there any problem if set tableOID before calling ExecConstarints()?

Well, possibly we could kluge things to make that particular case work,
but if someone expects that to be valid then why not oid, ctid, xmin,
etc?  And more to the point, what's the value of examining tableoid in
a check constraint?  The constraint is attached to a particular table,
so the tableoid would be a constant for it anyway.

            regards, tom lane

Re: Minor inheritance/check bug: Inconsistent behavior

From
Chris Travers
Date:
On Fri, Aug 24, 2012 at 3:52 AM, Amit Kapila <amit.kapila@huawei.com> wrote:
> From: pgsql-bugs-owner@postgresql.org
> [mailto:pgsql-bugs-owner@postgresql.org] On Behalf Of Tom Lane
> Sent: Friday, August 24, 2012 10:33 AM
> Chris Travers <chris@metatrontech.com> writes:
>>> My guess is that tableoid is not known when the check constraint is
>>> checked.
>
>>None of the system columns are set at the time check constraints are
>>checked.
>
> Is there any problem if set tableOID before calling ExecConstarints()?
> I am not sure may be this is not so important problem for which we don't
> want to
> change existing code.

Just to be sure my initial concern is this:

Table inheritance would be easier if there was a way to declare a
constraint such that it prevents insert for some rows on the parent
but not for a child and/or vice versa.  This can be used to partition
the primary key between a parent and child tables to avoid some key
overlap issues.  My concern was that this was the "clever" solution
that someone would try, insufficiently test, and find out that their
clever solution in fact did nothing except preventing the constraint
from being dropped and later re-applied.

As for the ideal way of looking at addressing this possibility:

I am further not going to speak for the developers here but I do
wonder about system columns generally and check constraints, and
whether the same solution is just to check the check constraint and
error if a system column is checked.  Some things seem obvious but
what happens if someone says "this table cannot grow beyind 5 pages
and we will do this by checking against ctid"?  If we start pulling
out some system columns for special treatment I am not sure where it
ends.  I am assuming that ctid cannot be safely known before the row
is formally stored on disk.

I think the cleanest fix interface-wise is to prevent check
constraints from being added to tables in this case.  I don't see it
as a high priority though.

My larger priority is to flag this as a possible thing someone is
likely to try to get around the issues storing rows in both parent and
child tables.

Best Wishes,
Chris Travers

Re: Minor inheritance/check bug: Inconsistent behavior

From
Tom Lane
Date:
Chris Travers <chris@metatrontech.com> writes:
> Table inheritance would be easier if there was a way to declare a
> constraint such that it prevents insert for some rows on the parent
> but not for a child and/or vice versa.

FWIW, 9.2 adds support for constraints that only apply to the named
table and don't get inherited to children.  I think use of such
constraints is probably better design than mucking around with tableoid,
even if we were to make that work.

            regards, tom lane

Re: Minor inheritance/check bug: Inconsistent behavior

From
Amit Kapila
Date:
From: Tom Lane [mailto:tgl@sss.pgh.pa.us]
Sent: Friday, August 24, 2012 7:46 PM
Amit Kapila <amit.kapila@huawei.com> writes:
> From: pgsql-bugs-owner@postgresql.org
> [mailto:pgsql-bugs-owner@postgresql.org] On Behalf Of Tom Lane
>>> None of the system columns are set at the time check constraints are
>>> checked.

>> Is there any problem if set tableOID before calling ExecConstarints()?

> Well, possibly we could kluge things to make that particular case work,
> but if someone expects that to be valid then why not oid, ctid, xmin,
> etc?
  Initially, I thought of saying like that but the same is done in
heap_prepare_insert()
  So doing it 2 times doesn't make sense and if we move them out then all
places from
  where heap_insert gets called, we have to do it like that. However why I
have asked to
  set tableOID, as for it, already functions CopyFrom and AtRewriteTable
does it(set
  tableOid before Constraints check).

> And more to the point, what's the value of examining tableoid in
> a check constraint?  The constraint is attached to a particular table,
> so the tableoid would be a constant for it anyway.
  Here what you are suggesting if I understood correctly is while defining
such
  constraints make sure its not allowed, because once defined, during
execution we might
  not be able to identify. Please correct me if I have misunderstood?

With Regards,
Amit Kapila.

Re: Minor inheritance/check bug: Inconsistent behavior

From
Robert Haas
Date:
On Fri, Aug 24, 2012 at 10:38 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Chris Travers <chris@metatrontech.com> writes:
>> Table inheritance would be easier if there was a way to declare a
>> constraint such that it prevents insert for some rows on the parent
>> but not for a child and/or vice versa.
>
> FWIW, 9.2 adds support for constraints that only apply to the named
> table and don't get inherited to children.  I think use of such
> constraints is probably better design than mucking around with tableoid,
> even if we were to make that work.

Maybe, but in that case shouldn't referencing a system column chuck an error?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Re: Minor inheritance/check bug: Inconsistent behavior

From
Tom Lane
Date:
Robert Haas <robertmhaas@gmail.com> writes:
> Maybe, but in that case shouldn't referencing a system column chuck an error?

Yeah, possibly.  I think none of them are populated with anything useful
during INSERT checks (though OID might be an exception?).  Less sure
about the state during UPDATE checks, though.

            regards, tom lane

Re: Minor inheritance/check bug: Inconsistent behavior

From
Chris Travers
Date:
On Mon, Aug 27, 2012 at 1:34 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

> Robert Haas <robertmhaas@gmail.com> writes:
> > Maybe, but in that case shouldn't referencing a system column chuck an
> error?
>
> Yeah, possibly.  I think none of them are populated with anything useful
> during INSERT checks (though OID might be an exception?).  Less sure
> about the state during UPDATE checks, though.
>
> My vote honestly would be to make it be an error.  A check constraint
which fails only after it has been saved and then updated strikes me as
behavior outside the role of a check constraint and dangerously so.  It
doesn't work as advertised, and people will find this out only after their
data is shown not to be consistent with the check constraint.

This being said, again, my sense is that no inherit check constraints will
make it quite unlikely that this will ever affect production servers.  So
failing this it's sufficient I think in future versions (maybe 9.3 forward)
to add a paragraph to the docs.  Something like:

Warning:  The behavior of a check constraint operating against a system
column is undefined. Check constraints are not intended to be used this way
and behavior may change without notice.

Maybe worth bringing up on the docs list.  I don't mind the fact that
behavior is undefined in some cases.  However, it might be a good idea to
let people know that they are moving into "we won't commit not to breaking
your app even if you get this to work" territory.

Best Wishes,
Chris Travers

Re: Minor inheritance/check bug: Inconsistent behavior

From
Amit Kapila
Date:
From: Tom Lane [mailto:tgl@sss.pgh.pa.us]
Sent: Tuesday, August 28, 2012 2:04 AM
Robert Haas <robertmhaas@gmail.com> writes:
>> Maybe, but in that case shouldn't referencing a system column chuck an
error?

> Yeah, possibly.  I think none of them are populated with anything useful
> during INSERT checks (though OID might be an exception?).  Less sure
> about the state during UPDATE checks, though.

  AFAICT during Update also, it doesn't contain useful. The only chance it
would have contain something useful is when it goes for EvalPlanQual and
then again comes to check for constraints. However these attributes get
filled in heap_update much later.

So now should the fix be that it returns an error for system column
reference except for OID case?

With Regards,
Amit Kapila.

Re: Minor inheritance/check bug: Inconsistent behavior

From
Robert Haas
Date:
On Tue, Aug 28, 2012 at 6:40 AM, Amit Kapila <amit.kapila@huawei.com> wrote:
>   AFAICT during Update also, it doesn't contain useful. The only chance it
> would have contain something useful is when it goes for EvalPlanQual and
> then again comes to check for constraints. However these attributes get
> filled in heap_update much later.
>
> So now should the fix be that it returns an error for system column
> reference except for OID case?

+1.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Re: Minor inheritance/check bug: Inconsistent behavior

From
Amit Kapila
Date:
On Friday, September 07, 2012 12:20 AM Amit Kapila wrote:
On Tue, Aug 28, 2012 at 6:40 AM, Amit Kapila <amit.kapila@huawei.com> wrote:
>>   AFAICT during Update also, it doesn't contain useful. The only chance
it
>> would have contain something useful is when it goes for EvalPlanQual and
>> then again comes to check for constraints. However these attributes get
>> filled in heap_update much later.
>
>> So now should the fix be that it returns an error for system column
>> reference except for OID case?

> +1.

I will start working on preparing a patch for the fix, if any problems, I
will discuss.


With Regards,
Amit Kapila.