Thread: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
This seems to be a bug in master, v12, and (probably) v11, where "FOR EACH FOR"
was first allowed on partition tables (86f575948).

I thought this would work like partitioned indexes (8b08f7d48), where detaching
a partition makes its index non-inherited, and attaching a partition marks a
pre-existing, matching partition as inherited rather than creating a new one.

DROP TABLE t, t1;
CREATE TABLE t(i int)PARTITION BY RANGE(i);
CREATE TABLE t1 PARTITION OF t FOR VALUES FROM(1)TO(2);
CREATE OR REPLACE FUNCTION trigf() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN END $$;
CREATE TRIGGER trig AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION trigf();
SELECT tgrelid::regclass, * FROM pg_trigger WHERE tgrelid='t1'::regclass;
ALTER TABLE t DETACH PARTITION t1;
ALTER TABLE t ATTACH PARTITION t1 FOR VALUES FROM (1)TO(2);
ERROR:  trigger "trig" for relation "t1" already exists

DROP TRIGGER trig ON t1;
ERROR:  cannot drop trigger trig on table t1 because trigger trig on table t requires it
HINT:  You can drop trigger trig on table t instead.

I remember these, but they don't seem to be relevant to this issue, which seems
to be independant.

1fa846f1c9 Fix cloning of row triggers to sub-partitions
b9b408c487 Record parents of triggers

The commit for partitioned indexes talks about using an pre-existing index on
the child as a "convenience gadget", puts indexes into pg_inherit, and
introduces "ALTER INDEX..ATTACH PARTITION" and "CREATE INDEX..ON ONLY".

It's probably rare for a duplicate index to be useful (unless rebuilding to be
more optimal, which is probably not reasonably interspersed with altering
inheritence).  But I don't know if that's equally true for triggers.  So I'm
not sure what the intended behavior is, so I've stopped after implementing
a partial fix.

Attachment

Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Alvaro Herrera
Date:
On 2020-Apr-08, Justin Pryzby wrote:

> This seems to be a bug in master, v12, and (probably) v11, where "FOR EACH FOR"
> was first allowed on partition tables (86f575948).
> 
> I thought this would work like partitioned indexes (8b08f7d48), where detaching
> a partition makes its index non-inherited, and attaching a partition marks a
> pre-existing, matching partition as inherited rather than creating a new one.

Hmm.  Let's agree to what behavior we want, and then we implement that.
It seems to me there are two choices:

1. on detach, keep the trigger but make it independent of the trigger on
parent.  (This requires that the trigger is made dependent on the
trigger on parent, if the table is attached as partition again;
otherwise you'd end up with multiple copies of the trigger if you
detach/attach multiple times).

2. on detach, remove the trigger from the partition.

I think (2) is easier to implement, but (1) is the more convenient
behavior.

(The current behavior is obviously a bug.)

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
On Wed, Apr 08, 2020 at 12:02:39PM -0400, Alvaro Herrera wrote:
> On 2020-Apr-08, Justin Pryzby wrote:
> 
> > This seems to be a bug in master, v12, and (probably) v11, where "FOR EACH FOR"
> > was first allowed on partition tables (86f575948).
> > 
> > I thought this would work like partitioned indexes (8b08f7d48), where detaching
> > a partition makes its index non-inherited, and attaching a partition marks a
> > pre-existing, matching partition as inherited rather than creating a new one.
> 
> Hmm.  Let's agree to what behavior we want, and then we implement that.
> It seems to me there are two choices:
> 
> 1. on detach, keep the trigger but make it independent of the trigger on
> parent.  (This requires that the trigger is made dependent on the
> trigger on parent, if the table is attached as partition again;
> otherwise you'd end up with multiple copies of the trigger if you
> detach/attach multiple times).
> 
> 2. on detach, remove the trigger from the partition.
> 
> I think (2) is easier to implement, but (1) is the more convenient
> behavior.

At telsasoft, we don't care (we uninherit tables before ALTERing parents to
avoid disruptive locking and to avoid worst-case disk use).

(1) is consistent with the behavior for indexes, which is a slight advantage
for users' ability to understand and keep track of the behavior.  But adding
triggers is pretty different so I'm not sure it's a totally compelling
parallel.

-- 
Justin



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Tom Lane
Date:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> Hmm.  Let's agree to what behavior we want, and then we implement that.
> It seems to me there are two choices:

> 1. on detach, keep the trigger but make it independent of the trigger on
> parent.  (This requires that the trigger is made dependent on the
> trigger on parent, if the table is attached as partition again;
> otherwise you'd end up with multiple copies of the trigger if you
> detach/attach multiple times).

> 2. on detach, remove the trigger from the partition.

> I think (2) is easier to implement, but (1) is the more convenient
> behavior.

I think that #1 would soon lead to needing all the same infrastructure
as we have for inherited columns and constraints, ie triggers would need
equivalents of attislocal and attinhcount.  I don't really want to go
there, so I'd vote for #2.

            regards, tom lane



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Alvaro Herrera
Date:
On 2020-Apr-08, Tom Lane wrote:

> Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> > Hmm.  Let's agree to what behavior we want, and then we implement that.
> > It seems to me there are two choices:
> 
> > 1. on detach, keep the trigger but make it independent of the trigger on
> > parent.  (This requires that the trigger is made dependent on the
> > trigger on parent, if the table is attached as partition again;
> > otherwise you'd end up with multiple copies of the trigger if you
> > detach/attach multiple times).
> 
> > 2. on detach, remove the trigger from the partition.
> 
> > I think (2) is easier to implement, but (1) is the more convenient
> > behavior.
> 
> I think that #1 would soon lead to needing all the same infrastructure
> as we have for inherited columns and constraints, ie triggers would need
> equivalents of attislocal and attinhcount.  I don't really want to go
> there, so I'd vote for #2.

Hmm.  Those things are used for the legacy inheritance case supporting
multiple inheritance, where we need to figure out which parent the table
is being detached (disinherited) from.  But for partitioning we know
which parent it is, since there can only be one.  So I don't think that
argument applies.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Tom Lane
Date:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> On 2020-Apr-08, Tom Lane wrote:
>> I think that #1 would soon lead to needing all the same infrastructure
>> as we have for inherited columns and constraints, ie triggers would need
>> equivalents of attislocal and attinhcount.  I don't really want to go
>> there, so I'd vote for #2.

> Hmm.  Those things are used for the legacy inheritance case supporting
> multiple inheritance, where we need to figure out which parent the table
> is being detached (disinherited) from.  But for partitioning we know
> which parent it is, since there can only be one.  So I don't think that
> argument applies.

My point is that so long as you only allow the case of exactly one parent,
you can just delete the child trigger, because it must belong to that
parent.  As soon as there's any flexibility, you are going to end up
reinventing all the stuff we had to invent to manage
maybe-or-maybe-not-inherited columns.  So I think the "detach" idea
is the first step on that road, and I counsel not taking that step.

(This implies that when creating a child trigger, we should error out,
*not* allow the case, if there's already a trigger by that name.  Not
sure if that's what happens today, but again I'd say that's what we
should do to avoid complicated cases.)

            regards, tom lane



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Amit Langote
Date:
On Thu, Apr 9, 2020 at 3:09 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> > On 2020-Apr-08, Tom Lane wrote:
> >> I think that #1 would soon lead to needing all the same infrastructure
> >> as we have for inherited columns and constraints, ie triggers would need
> >> equivalents of attislocal and attinhcount.  I don't really want to go
> >> there, so I'd vote for #2.
>
> > Hmm.  Those things are used for the legacy inheritance case supporting
> > multiple inheritance, where we need to figure out which parent the table
> > is being detached (disinherited) from.  But for partitioning we know
> > which parent it is, since there can only be one.  So I don't think that
> > argument applies.
>
> My point is that so long as you only allow the case of exactly one parent,
> you can just delete the child trigger, because it must belong to that
> parent.  As soon as there's any flexibility, you are going to end up
> reinventing all the stuff we had to invent to manage
> maybe-or-maybe-not-inherited columns.  So I think the "detach" idea
> is the first step on that road, and I counsel not taking that step.

As mentioned upthread, we have behavior #1 for indexes (attach
existing / detach & keep), without any of the *islocal, *inhcount
infrastructure. It is a bit complex, because we need logic to check
the equivalence of an existing index on the partition being attached,
so implementing the same behavior for trigger is going to have to be
almost as complex.  Considering that #2 will be much simpler to
implement, but would be asymmetric with everything else.

-- 

Amit Langote
EnterpriseDB: http://www.enterprisedb.com



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Tom Lane
Date:
Amit Langote <amitlangote09@gmail.com> writes:
> On Thu, Apr 9, 2020 at 3:09 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> My point is that so long as you only allow the case of exactly one parent,
>> you can just delete the child trigger, because it must belong to that
>> parent.  As soon as there's any flexibility, you are going to end up
>> reinventing all the stuff we had to invent to manage
>> maybe-or-maybe-not-inherited columns.  So I think the "detach" idea
>> is the first step on that road, and I counsel not taking that step.

> As mentioned upthread, we have behavior #1 for indexes (attach
> existing / detach & keep), without any of the *islocal, *inhcount
> infrastructure. It is a bit complex, because we need logic to check
> the equivalence of an existing index on the partition being attached,
> so implementing the same behavior for trigger is going to have to be
> almost as complex.  Considering that #2 will be much simpler to
> implement, but would be asymmetric with everything else.

I think there is justification for jumping through some hoops for
indexes, because they can be extremely expensive to recreate.
The same argument doesn't hold even a little bit for child
triggers, though.

Also it can be expected that an index will still behave sensibly after
its table is standalone, whereas that's far from obvious for a trigger
that was meant to work on partition member tables.

            regards, tom lane



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
On Thu, Apr 09, 2020 at 09:46:38AM -0400, Tom Lane wrote:
> Amit Langote <amitlangote09@gmail.com> writes:
> > On Thu, Apr 9, 2020 at 3:09 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> >> My point is that so long as you only allow the case of exactly one parent,
> >> you can just delete the child trigger, because it must belong to that
> >> parent.  As soon as there's any flexibility, you are going to end up
> >> reinventing all the stuff we had to invent to manage
> >> maybe-or-maybe-not-inherited columns.  So I think the "detach" idea
> >> is the first step on that road, and I counsel not taking that step.
> 
> > As mentioned upthread, we have behavior #1 for indexes (attach
> > existing / detach & keep), without any of the *islocal, *inhcount
> > infrastructure. It is a bit complex, because we need logic to check
> > the equivalence of an existing index on the partition being attached,
> > so implementing the same behavior for trigger is going to have to be
> > almost as complex.  Considering that #2 will be much simpler to
> > implement, but would be asymmetric with everything else.
> 
> I think there is justification for jumping through some hoops for
> indexes, because they can be extremely expensive to recreate.
> The same argument doesn't hold even a little bit for child
> triggers, though.
> 
> Also it can be expected that an index will still behave sensibly after
> its table is standalone, whereas that's far from obvious for a trigger
> that was meant to work on partition member tables.

I haven't heard a compelling argument for or against either way.

Maybe the worst behavior might be if, during ATTACH, we searched for a matching
trigger, and "merged" it (marked it inherited) if it matched.  That could be
bad if someone *wanted* two triggers, which seems unlikely, but to each their
own.

I implemented the simple way (and, as an experiment, 75% of the hard way).

It occured to me that we don't currently distinguish between a trigger on a
child table, and a trigger on a parent table which was recursively created on a
child.  That makes sense for indexes and constraints, since the objects persist
if the table is detached, so it doesn't matter how it was defined.

But if we remove trigger during DETACH, then it's *not* the same as a trigger
that was defined on the child, and I suggest that in v13 we should make that
visible.

-- 
Justin

Attachment

Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
v3 fixes a brown-paper-bag logic error.

-- 
Justin

Attachment

Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Alvaro Herrera
Date:
On 2020-Apr-18, Justin Pryzby wrote:

> I haven't heard a compelling argument for or against either way.
> 
> Maybe the worst behavior might be if, during ATTACH, we searched for a matching
> trigger, and "merged" it (marked it inherited) if it matched.  That could be
> bad if someone *wanted* two triggers, which seems unlikely, but to each their
> own.

I think the simplicity argument trumps the other ones, so I agree to go
with your v3 patch proposed downthread.

What happens if you detach the parent?  I mean, should the trigger
removal recurse to children?

> It occured to me that we don't currently distinguish between a trigger on a
> child table, and a trigger on a parent table which was recursively created on a
> child.  That makes sense for indexes and constraints, since the objects persist
> if the table is detached, so it doesn't matter how it was defined.
> 
> But if we remove trigger during DETACH, then it's *not* the same as a trigger
> that was defined on the child, and I suggest that in v13 we should make that
> visible.

Hmm, interesting point -- whether the trigger is partition or not is
important because it affects what happens on detach.  I agree that we
should make it visible.  Is the proposed single bit "PARTITION" good
enough, or should we indicate what's the ancestor table that defines the
partition?

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
On Wed, Apr 08, 2020 at 11:44:33AM -0500, Justin Pryzby wrote:
> On Wed, Apr 08, 2020 at 12:02:39PM -0400, Alvaro Herrera wrote:
> > On 2020-Apr-08, Justin Pryzby wrote:
> > 
> > > This seems to be a bug in master, v12, and (probably) v11, where "FOR EACH FOR"
> > > was first allowed on partition tables (86f575948).
> > > 
> > > I thought this would work like partitioned indexes (8b08f7d48), where detaching
> > > a partition makes its index non-inherited, and attaching a partition marks a
> > > pre-existing, matching partition as inherited rather than creating a new one.
> > 
> > Hmm.  Let's agree to what behavior we want, and then we implement that.
> > It seems to me there are two choices:
> > 
> > 1. on detach, keep the trigger but make it independent of the trigger on
> > parent.  (This requires that the trigger is made dependent on the
> > trigger on parent, if the table is attached as partition again;
> > otherwise you'd end up with multiple copies of the trigger if you
> > detach/attach multiple times).
> > 
> > 2. on detach, remove the trigger from the partition.
> > 
> > I think (2) is easier to implement, but (1) is the more convenient
> > behavior.
> 
> At telsasoft, we don't care (we uninherit tables before ALTERing parents to
> avoid disruptive locking and to avoid worst-case disk use).

I realized that I was wrong about what would be most desirable for us, for an
uncommon case:

Our loader INSERTs into the child table, not the parent (I think I did that to
try to implement UPSERT before partitioned indexes in v11).

All but the newest partitions are DETACHed when we need to promote a column.

It's probably rare that we'd be inserting into a table old enough to be
detached, and normally that would be ok, but if a trigger were missing, it
would misbehave.  In our use-case, we're creating trigger on the parent as a
convenient way to maintain them on the partitions, which doesn't work if a
table exists but detached..

So we'd actually prefer the behavior of indexes/constraints, where the trigger
is preserved if the child is detached.  I'm not requesting to do that just for
our use case, which may be atypical or not a good model, but adding our one
data point.

-- 
Justin



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Alvaro Herrera
Date:
On 2020-Apr-19, Justin Pryzby wrote:

> It's probably rare that we'd be inserting into a table old enough to be
> detached, and normally that would be ok, but if a trigger were missing, it
> would misbehave.  In our use-case, we're creating trigger on the parent as a
> convenient way to maintain them on the partitions, which doesn't work if a
> table exists but detached..
> 
> So we'd actually prefer the behavior of indexes/constraints, where the trigger
> is preserved if the child is detached.  I'm not requesting to do that just for
> our use case, which may be atypical or not a good model, but adding our one
> data point.

I think the easiest way to implement this is to have two triggers -- the
one that's direct in the partition checks whether the table is a
partition and does nothing in that case.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
On Sun, Apr 19, 2020 at 03:13:29PM -0400, Alvaro Herrera wrote:
> On 2020-Apr-18, Justin Pryzby wrote:
> > I haven't heard a compelling argument for or against either way.
> > 
> > Maybe the worst behavior might be if, during ATTACH, we searched for a matching
> > trigger, and "merged" it (marked it inherited) if it matched.  That could be
> > bad if someone *wanted* two triggers, which seems unlikely, but to each their
> > own.
> 
> I think the simplicity argument trumps the other ones, so I agree to go
> with your v3 patch proposed downthread.
> 
> What happens if you detach the parent?  I mean, should the trigger
> removal recurse to children?

It think it should probably exactly undo what CloneRowTriggersToPartition does.
..and I guess you're trying to politely say that it didn't.  I tried to fix in
v4 - please check if that's right.

> > It occured to me that we don't currently distinguish between a trigger on a
> > child table, and a trigger on a parent table which was recursively created on a
> > child.  That makes sense for indexes and constraints, since the objects persist
> > if the table is detached, so it doesn't matter how it was defined.
> > 
> > But if we remove trigger during DETACH, then it's *not* the same as a trigger
> > that was defined on the child, and I suggest that in v13 we should make that
> > visible.
> 
> Hmm, interesting point -- whether the trigger is partition or not is
> important because it affects what happens on detach.  I agree that we
> should make it visible.  Is the proposed single bit "PARTITION" good
> enough, or should we indicate what's the ancestor table that defines the
> partition?

Yea, it's an obvious thing to do.

One issue is that tgparentid is new, so showing the partition status of the
trigger when connected to an pre-13 server would be nontrivial: b9b408c48.

-- 
Justin

Attachment

Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Amit Langote
Date:
On Mon, Apr 20, 2020 at 5:49 AM Justin Pryzby <pryzby@telsasoft.com> wrote:
> On Sun, Apr 19, 2020 at 03:13:29PM -0400, Alvaro Herrera wrote:
> > On 2020-Apr-18, Justin Pryzby wrote:
> > > I haven't heard a compelling argument for or against either way.
> > >
> > > Maybe the worst behavior might be if, during ATTACH, we searched for a matching
> > > trigger, and "merged" it (marked it inherited) if it matched.  That could be
> > > bad if someone *wanted* two triggers, which seems unlikely, but to each their
> > > own.
> >
> > I think the simplicity argument trumps the other ones, so I agree to go
> > with your v3 patch proposed downthread.
> >
> > What happens if you detach the parent?  I mean, should the trigger
> > removal recurse to children?
>
> It think it should probably exactly undo what CloneRowTriggersToPartition does.
> ..and I guess you're trying to politely say that it didn't.  I tried to fix in
> v4 - please check if that's right.

Looks correct to me.  Maybe have a test cover that?

> > > It occured to me that we don't currently distinguish between a trigger on a
> > > child table, and a trigger on a parent table which was recursively created on a
> > > child.  That makes sense for indexes and constraints, since the objects persist
> > > if the table is detached, so it doesn't matter how it was defined.
> > >
> > > But if we remove trigger during DETACH, then it's *not* the same as a trigger
> > > that was defined on the child, and I suggest that in v13 we should make that
> > > visible.
> >
> > Hmm, interesting point -- whether the trigger is partition or not is
> > important because it affects what happens on detach.  I agree that we
> > should make it visible.  Is the proposed single bit "PARTITION" good
> > enough, or should we indicate what's the ancestor table that defines the
> > partition?
>
> Yea, it's an obvious thing to do.

This:

+                          "false AS tgisinternal"),
+                         (pset.sversion >= 13000 ?
+                          "pg_partition_root(t.tgrelid) AS parent" :
+                          "'' AS parent"),
+                         oid);


looks wrong, because the actual partition root may not also be the
trigger parent root, for example:

create table f (a int references p) partition by list (a);
create table f1 partition of f for values in (1) partition by list (a);
create table f11 partition of f for values in (1);
create function trigfunc() returns trigger language plpgsql as $$
begin raise notice '%', new; return new; end; $$;
create trigger trig before insert on f1 for each row execute function
trigfunc();
\d f11
                 Table "public.f11"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 a      | integer |           |          |
Partition of: f1 FOR VALUES IN (1)
Triggers:
    trig BEFORE INSERT ON f11 FOR EACH ROW EXECUTE FUNCTION
trigfunc(), ON TABLE f

Here, ON TABLE should say "f1".

The following gets the correct parent for me:

-                         (pset.sversion >= 13000 ?
-                          "pg_partition_root(t.tgrelid) AS parent" :
-                          "'' AS parent"),
+                         (pset.sversion >= 130000 ?
+                          "(SELECT relid"
+                          " FROM pg_trigger, pg_partition_ancestors(t.tgrelid)"
+                          " WHERE tgname = t.tgname AND tgrelid = relid"
+                          " AND tgparentid = 0) AS parent" :
+                          " null AS parent"),

The server version number being compared against was missing a zero in
your patch.

Also, how about, for consistency, making the parent table labeling of
the trigger look similar to that for the foreign constraint, so
instead of:

Triggers:
    trig BEFORE INSERT ON f11 FOR EACH ROW EXECUTE FUNCTION
trigfunc(), ON TABLE f1

how about:

Triggers:
    TABLE "f1" TRIGGER "trig" BEFORE INSERT ON f11 FOR EACH ROW
EXECUTE FUNCTION trigfunc()

--
Amit Langote
EnterpriseDB: http://www.enterprisedb.com



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
On Mon, Apr 20, 2020 at 06:35:44PM +0900, Amit Langote wrote:
> On Mon, Apr 20, 2020 at 5:49 AM Justin Pryzby <pryzby@telsasoft.com> wrote:
> > On Sun, Apr 19, 2020 at 03:13:29PM -0400, Alvaro Herrera wrote:
> > > What happens if you detach the parent?  I mean, should the trigger
> > > removal recurse to children?
> >
> > It think it should probably exactly undo what CloneRowTriggersToPartition does.
> > ..and I guess you're trying to politely say that it didn't.  I tried to fix in
> > v4 - please check if that's right.
> 
> Looks correct to me.  Maybe have a test cover that?

I included such a test with the v4 patch.

> > > > But if we remove trigger during DETACH, then it's *not* the same as a trigger
> > > > that was defined on the child, and I suggest that in v13 we should make that
> > > > visible.
> > >
> > > Hmm, interesting point -- whether the trigger is partition or not is
> > > important because it affects what happens on detach.  I agree that we
> > > should make it visible.  Is the proposed single bit "PARTITION" good
> > > enough, or should we indicate what's the ancestor table that defines the
> > > partition?
> >
> > Yea, it's an obvious thing to do.
> 
> This:
> 
> +                          "false AS tgisinternal"),
> +                         (pset.sversion >= 13000 ?
> +                          "pg_partition_root(t.tgrelid) AS parent" :
> +                          "'' AS parent"),
> +                         oid);
> 
> 
> looks wrong, because the actual partition root may not also be the
> trigger parent root, for example:

Sigh, right.

> The following gets the correct parent for me:
> 
> -                         (pset.sversion >= 13000 ?
> -                          "pg_partition_root(t.tgrelid) AS parent" :
> -                          "'' AS parent"),
> +                         (pset.sversion >= 130000 ?
> +                          "(SELECT relid"
> +                          " FROM pg_trigger, pg_partition_ancestors(t.tgrelid)"
> +                          " WHERE tgname = t.tgname AND tgrelid = relid"
> +                          " AND tgparentid = 0) AS parent" :
> +                          " null AS parent"),

I'm happy to see that this doesn't require a recursive cte, at least.
I was trying to think how to break it by returning multiple results or results
out of order, but I think that can't happen.

> Also, how about, for consistency, making the parent table labeling of
> the trigger look similar to that for the foreign constraint, so
> Triggers:
>     TABLE "f1" TRIGGER "trig" BEFORE INSERT ON f11 FOR EACH ROW EXECUTE FUNCTION trigfunc()

I'll leave that for committer to decide.

I split into separate patches since only 0001 should be backpatched (with
s/OidIsValid(tgparentid)/isPartitionTrigger/).

-- 
Justin

Attachment

Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Alvaro Herrera
Date:
> +            deleteDependencyRecordsFor(TriggerRelationId,
> +                    pg_trigger->oid,
> +                    false);
> +            deleteDependencyRecordsFor(RelationRelationId,
> +                    pg_trigger->oid,
> +                    false);
> +
> +            CommandCounterIncrement();
> +            ObjectAddressSet(object, TriggerRelationId, pg_trigger->oid);
> +            performDeletion(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
> +        }
> +
> +        systable_endscan(scan);
> +        table_close(tgrel, RowExclusiveLock);
> +    }

Two small issues here.  First, your second call to
deleteDependencyRecordsFor did nothing, because your first call deleted
all the dependency records.  I changed that to two
deleteDependencyRecordsForClass() calls, that actually do what you
intended.

The other is that instead of deleting each trigger, we can accumulate
them to delete with a single performMultipleDeletions call; this also
means we get to do CommandCounterIncrement just once.

v6 fixes those things and AFAICS is ready to push.

I haven't reviewed your 0002 carefully, but (as inventor of the "TABLE
t" marker for FK constraints) I agree with Amit that we should imitate
that instead of coming up with a new way to show it.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachment

Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Alvaro Herrera
Date:
On 2020-Apr-20, Alvaro Herrera wrote:

> +    while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
> +    {
> +        Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
> +        ObjectAddress trig;
> +
> +        /* Ignore triggers that weren't cloned */
> +        if (!OidIsValid(pg_trigger->tgparentid) ||
> +            !pg_trigger->tgisinternal ||
> +            !TRIGGER_FOR_ROW(pg_trigger->tgtype))
> +            continue;

Actually, shouldn't we be checking just "!OidIsValid(pg_trigger->tgparentid)"
here?  Surely the other two conditions should already not matter either
way if tgparentid is set.  I can't see us starting to clone
for-statement triggers, but I'm not sure I trust the internal marking to
remain one way or the other.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Alvaro Herrera
Date:
I think I also owe the attached doc updates.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachment

Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
On Tue, Apr 21, 2020 at 12:20:38PM -0400, Alvaro Herrera wrote:
> diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
> index 7595e609b5..233905552c 100644
> --- a/doc/src/sgml/ref/alter_table.sgml
> +++ b/doc/src/sgml/ref/alter_table.sgml
> @@ -941,13 +943,14 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
>      <term><literal>DETACH PARTITION</literal> <replaceable class="parameter">partition_name</replaceable></term>
>      <listitem>
>       <para>
>        This form detaches specified partition of the target table.  The detached
>        partition continues to exist as a standalone table, but no longer has any
>        ties to the table from which it was detached.  Any indexes that were
> -      attached to the target table's indexes are detached.
> +      attached to the target table's indexes are detached.  Any triggers that
> +      were created to mirror those in the target table are removed.

Can you say "cloned" here instead of mirror ?
> +      attached to the target table's indexes are detached.  Any triggers that
> +      were created as clones of triggers in the target table are removed.

Also, I see in the surrounding context a missing word? 
This form detaches THE specified partition of the target table.

-- 
Justin



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Alvaro Herrera
Date:
On 2020-Apr-20, Justin Pryzby wrote:

> On Mon, Apr 20, 2020 at 06:35:44PM +0900, Amit Langote wrote:

> > Also, how about, for consistency, making the parent table labeling of
> > the trigger look similar to that for the foreign constraint, so
> > Triggers:
> >     TABLE "f1" TRIGGER "trig" BEFORE INSERT ON f11 FOR EACH ROW EXECUTE FUNCTION trigfunc()
> 
> I'll leave that for committer to decide.

Pushed.  Many thanks for this!

Changes: I thought that printing the "ON TABLE" bit when it's defined in
the same table is pointless and ugly, so I added a NULLIF to prevent it
in that case (it's not every day that you can put NULLIF to work).  I
also changed the empty string to NULL for the case with older servers,
so that it doesn't print a lame "ON TABLE " clause for them.  Lastly,
added pg_catalog qualifications everywhere needed.

Contrary to what I had said, I decided to leave the output as submitted;
the constraint lines are not really precedent against it:

55432 13devel 24286=# \d lev3
          Partitioned table "public.lev3"
 Column │  Type   │ Collation │ Nullable │ Default 
────────┼─────────┼───────────┼──────────┼─────────
 a      │ integer │           │ not null │ 
Partition of: lev2 FOR VALUES IN (3)
Partition key: LIST (a)
Indexes:
    "lev3_pkey" PRIMARY KEY, btree (a)
Foreign-key constraints:
    TABLE "lev1" CONSTRAINT "lev1_a_fkey" FOREIGN KEY (a) REFERENCES lev1(a)
Referenced by:
    TABLE "lev1" CONSTRAINT "lev1_a_fkey" FOREIGN KEY (a) REFERENCES lev1(a)
Triggers:
    tt AFTER UPDATE ON lev3 FOR EACH ROW EXECUTE FUNCTION trigger_nothing(), ON TABLE lev2
Number of partitions: 1 (Use \d+ to list them.)

In the "FK constraints" and "referenced by" entries, it looks natural
since the constraint refers to a table.  Not so in the trigger case.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: DETACH PARTITION and FOR EACH ROW triggers on partitioned tables

From
Justin Pryzby
Date:
On Tue, Apr 21, 2020 at 07:03:30PM -0400, Alvaro Herrera wrote:
> On 2020-Apr-20, Justin Pryzby wrote:
> 
> > On Mon, Apr 20, 2020 at 06:35:44PM +0900, Amit Langote wrote:
> 
> > > Also, how about, for consistency, making the parent table labeling of
> > > the trigger look similar to that for the foreign constraint, so
> > > Triggers:
> > >     TABLE "f1" TRIGGER "trig" BEFORE INSERT ON f11 FOR EACH ROW EXECUTE FUNCTION trigfunc()
> > 
> > I'll leave that for committer to decide.
> 
> Pushed.  Many thanks for this!

Thanks for polishing it.

I was just about to convince myself of the merits of doing it Amit's way :)

I noticed a few issues:

 - should put \n's around Amit's subquery to make psql -E look pretty;
 - maybe should quote the TABLE, like \"%s\" ?

#3 is that *if* we did it Amit's way, I *think* maybe we should show the
parent's triggerdef, not the childs.
It seems strange to me to say "TABLE trigpart .* INSERT ON trigpart3"

-    TABLE "trigpart" TRIGGER trg1 AFTER INSERT ON trigpart3 FOR EACH ROW EXECUTE FUNCTION trigger_nothing()
+    TABLE "trigpart" TRIGGER trg1 AFTER INSERT ON trigpart FOR EACH ROW EXECUTE FUNCTION trigger_nothing()


-- 
Justin

Attachment