From: Yeb Havinga on
Robert Haas wrote:
>> It seems to me that it should only recurse to its children
>> if the constraint was really added, rather than merged into an
>> existing constraint, because if it was merged into an existing
>> constraint, then the children already have it.
Yes. (then the children already have it -> already have it from the
current parent) - now I understand how AddRelationNewConstraints is
used, this effectively blocks > 1 times propagation to childs from the
same parent, and that is what our patch was written todo too.
> OK, it looks like level_2_parent is actually irrelevant to this issue.
> So here's a slightly simplified test case:
>
> CREATE TABLE top (i int);
> CREATE TABLE mid1 () INHERITS (top);
> CREATE TABLE mid2 () INHERITS (top);
> CREATE TABLE bottom () INHERITS (mid1, mid2);
> CREATE TABLE basement () INHERITS (bottom);
>
This is, probably provable, the smallest case where this problem can
occur. Removing any table would mean that this bug is not hit anymore.
> This still leaves open the question of what to do about the similar
> case involving attributes:
>
> That problem looks significantly more difficult to solve, though.
>
I'm looking at ATPrepAddColumn right now, where there is actually some
comments about getting the right attinhcount in the case of multiple
inherited children, but it's the first time I'm looking at this part of
PostgreSQL and it needs some time to sink in. It looks a bit like at the
place of the comment /* child should see column as singly inherited */,
maybe the recursion could be stopped there as well, i.e. not only
setting inhcount to 1, but actually adding the child relation one time
to the wqueue.

regards,
Yeb Havinga


--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

From: Robert Haas on
On Fri, Jul 30, 2010 at 10:46 AM, Tom Lane <tgl(a)sss.pgh.pa.us> wrote:
> Robert Haas <robertmhaas(a)gmail.com> writes:
>> On Fri, Jul 30, 2010 at 10:23 AM, Tom Lane <tgl(a)sss.pgh.pa.us> wrote:
>>> Uh, full stop there. �If you think that the multiply-inherited column
>>> logic is wrong, it's you that is mistaken --- or at least you're going
>>> to have to do a lot more than just assert that you don't like it.
>>> We spent a *lot* of time hashing that behavior out, back around 7.3.
>
>> Since the output in the previous email apparently wasn't sufficient
>> for you to understand what the problem is, here it is in more detail.
>> ...
>> Adding a column to the toplevel parent of the inheritance hierarchy
>> and then dropping it again shouldn't leave a leftover copy of the
>> column in the grandchild.
>
> Actually, it probably should. �The inheritance rules were intentionally
> designed to avoid dropping inherited columns that could conceivably
> still contain valuable data. �There isn't enough information in the
> inhcount/islocal data structure to recognize that a multiply-inherited
> column is ultimately derived from only one distant ancestor, but it was
> agreed that an exact tracking scheme would be more complication than it
> was worth. �Instead, the mechanism is designed to ensure that no column
> will be dropped if it conceivably is still wanted --- not that columns
> might not be left behind and require another drop step.

While I certainly agree that accidentally dropping a column that
should be retained is a lot worse than accidentally retaining a column
that should be dropped, that doesn't make the current behavior
correct. I don't think that's really an arguable point. Another drop
step doesn't help: the leftover column is undroppable short of nuking
the whole table.

So let's focus on what the actual problem is here, what is required to
fix it, and whether or not and how far the fix can be back-patched.
There are two separate bugs here, so we should consider them
individually. In each case, the problem is that with a certain
(admittedly rather strange) inheritance hierarchy, adding a column to
the top-level parent results in the inheritance count in the
bottom-most child ending up as 3 rather than 2. 2 is the correct
value because the bottom-most child has 2 immediate parents. The
consequence of ending up with a count of 3 rather than 2 is that if
the constraint/column added at the top-level is subsequent dropped,
necessarily also from the top-level, the bottom-level child ends up
with the constraint/column still in existence and with an inheritance
count of 1. This isn't the correct behavior, and furthermore the
child object can't be dropped, because it still believes that it's
inherited (even though its parents no longer have a copy to inherit).

In the case of coninhcount, I believe that the fix actually is quite
simple, although of course it's possible that I'm missing something.
Currently, ATAddConstraint() first calls AddRelationNewConstraints()
to add the constraint, or possibly merge it into an existing,
inherited constraint; it then adds the constraint to the phase-3 work
queue so that it will be checked; and finally it recurses to its own
children. It seems to me that it should only recurse to its children
if the constraint was really added, rather than merged into an
existing constraint, because if it was merged into an existing
constraint, then the children already have it. I'm still trying to
figure out why this bug doesn't show up in simpler cases, but I think
that's the root of the issue.

attinhcount exhibits analogous misbehavior, but if you're saying the
fix for that case is going to be a lot more complicated, I think I
agree with you. In that case, it seems that we traverse the
inheritance hierarchy during the prep phase, at which point we don't
yet know whether we're going to end up merging anything. It might be
that a fix for this part of the problem ends up being too complex to
back-patch.

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

--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

From: Robert Haas on
On Fri, Jul 30, 2010 at 11:39 AM, Tom Lane <tgl(a)sss.pgh.pa.us> wrote:
> Yeb Havinga <yebhavinga(a)gmail.com> writes:
>> Regard the following lattice (direction from top to bottom):
>
>> 1
>> |\
>> 2 3
>> �\|\
>> � 4 5
>> � �\|
>> � � 6
>
>> When adding a constraint to 1, the proper coninhcount for that
>> constraint on relation 6 is 2. But the code currently counts to 3, since
>> 6 is reached by paths 1-2-4-5, 1-3-4-6, 1-3-5-6.
>
> Mph. �I'm not sure that 3 is wrong. �You have to consider what happens
> during a DROP CONSTRAINT, which as far as I saw this patch didn't
> address.

The behavior of DROP CONSTRAINT matches the fine documentation. The
behavior of ADD CONSTRAINT does not. Perhaps there's a definition of
coninhcount that would make 3 the right answer, but (a) I'm not sure
what that definition would be and (b) it's certainly not the one in
the manual.

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

--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

From: Yeb Havinga on
Yeb Havinga wrote:
> Robert Haas wrote:
>> This still leaves open the question of what to do about the similar
>> case involving attributes:
>>
>> That problem looks significantly more difficult to solve, though.
>>
> I'm looking at ATPrepAddColumn right now, where there is actually some
> comments about getting the right attinhcount in the case of multiple
> inherited children, but it's the first time I'm looking at this part
> of PostgreSQL and it needs some time to sink in. It looks a bit like
> at the place of the comment /* child should see column as singly
> inherited */, maybe the recursion could be stopped there as well, i.e.
> not only setting inhcount to 1, but actually adding the child relation
> one time to the wqueue.
I believe the crux is to stop double recursion from a parent in
ATOneLevelRecursion. I did a quick test by adding a globally defined

static List *visitedrels = NIL;

together by adding in front of ATOneLevelRecursion this:

if (list_member_oid(visitedrels, relid))
return;
visitedrels = lappend_oid(visitedrels, relid);

Running Roberts example gives the correct attinhcount for the basement.

SELECT t.oid, t.relname, a.attinhcount
FROM pg_class t
JOIN pg_attribute a ON (a.attrelid = t.oid)
JOIN pg_namespace n ON (t.relnamespace = n.oid)
WHERE n.nspname = 'test_inheritance' AND a.attname = 'a_table_column'
ORDER BY oid;

oid | relname | attinhcount
-------+----------+-------------
16566 | top | 0
16569 | mid1 | 1
16572 | mid2 | 1
16575 | bottom | 2
16578 | basement | 1

regards,
Yeb Havinga

PS: forgot to say thanks for looking into this problem earlier in the
thread. Thanks!


--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

From: Robert Haas on
On Fri, Jul 30, 2010 at 4:38 PM, Yeb Havinga <yebhavinga(a)gmail.com> wrote:
>> I'm looking at ATPrepAddColumn right now, where there is actually some
>> comments about getting the right attinhcount in the case of multiple
>> inherited children, but it's the first time I'm looking at this part of
>> PostgreSQL and it needs some time to sink in. It looks a bit like at the
>> place of the comment /* child should see column as singly inherited */,
>> maybe the recursion could be stopped there as well, i.e. not only setting
>> inhcount to 1, but actually adding the child relation one time to the
>> wqueue.
>
> I believe the crux is to stop double recursion from a parent in
> ATOneLevelRecursion. I did a quick test by adding a globally defined
>
> static List *visitedrels = NIL;

I agree that's the crux of the problem, but I can't see solving it
with a global variable. I realize you were just testing...

> PS: forgot to say thanks for looking into this problem earlier in the
> thread. Thanks!

yw

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

--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers