Re: CREATE TABLE LIKE INCLUDING TRIGGERS

From: Henson Choi <assam258(at)gmail(dot)com>
To: jian he <jian(dot)universality(at)gmail(dot)com>
Cc: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, Andrey Borodin <x4mmm(at)yandex-team(dot)ru>, PostgreSQL-development <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: CREATE TABLE LIKE INCLUDING TRIGGERS
Date: 2026-05-07 01:38:35
Message-ID: CAAAe_zAHAjTVGE9fAuTZ9df7Y7n7Zctn8WtEkUfytAu+VKQE7w@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Jian,

This is the line-by-line code review I promised earlier. The overall
conclusion is that the patch is in good shape. I have some minor
items below, none of which are blockers.

WHAT LOOKS GOOD
---------------

The keyword placement is correct. TRIGGERS is classified as
UNRESERVED_KEYWORD and also listed in bare_label_keyword, so existing
SQL that uses "triggers" as an identifier continues to work without
quoting. No compatibility concern here.

generateClonedTriggerStmt() is well-structured. The WHEN-clause
remapping via map_variable_attnos() and the transformed flag to prevent
re-transformation in CreateTriggerFiringOn() are correct.

pg_dump needs no changes. LIKE-copied triggers are stored as
independent pg_trigger rows, so the existing dump logic picks them up
automatically.

MINOR ISSUES
------------

1. Test: cross-file dependency on trigger_func (create_table_like.sql)

create_table_like.sql references trigger_func without creating it,
relying on triggers.sql having run first. The dependency is noted
in a comment, and parallel_schedule guarantees the correct order for
a full "make check" run. However, "make check TESTS=create_table_like"
will fail because trigger_func does not exist.

PostgreSQL convention is that each test file creates and drops the
objects it needs. Please add a local definition of trigger_func in
create_table_like.sql (and drop it at the end of the new block).

2. Test: EXCLUDING TRIGGERS not exercised

The grammar now accepts EXCLUDING TRIGGERS, but no test uses it.
The default (no option) is equivalent, but a one-line smoke test
would confirm the keyword is accepted and has the expected effect:

CREATE TABLE t_excl (LIKE source_table EXCLUDING TRIGGERS);

3. Documentation: create_table.sgml wording inconsistent with
create_foreign_table.sgml

create_foreign_table.sgml already reads:

"All non-internal triggers are copied to the new table."

create_table.sgml reads:

"All non-internal triggers on the original table will be created
on the new table."

Two problems with the latter:
a) "on the original table" is redundant; no other INCLUDING option
includes this phrase (compare INCLUDING STATISTICS: "Extended
statistics are copied to the new table.").
b) "will be created" misrepresents the action; the correct verb
is "are copied", matching the pattern used elsewhere.

Recommended wording for create_table.sgml:

"All non-internal triggers are copied to the new table."

4. Code comment: capitalisation in generateClonedTriggerStmt()
(trigger.c)

/* Reconstruct trigger function String list */

"String" should be lowercase "string".

5. Code comment: overly verbose in expandTableLikeClause()
(parse_utilcmd.c)

/* We make use of CreateTrigStmt's trigcomment option */

The code is self-explanatory. I would recommend removing it or
replacing it with something like:

/* pass comment through to CreateTrigger */

6. Commit message grammar (commit e73f551)

Several sentences contain grammatical errors:

a) "This will copy all source table's trigger to the new table."
-> "...triggers..."

b) "Internal trigger (such as foreign key associated trigger) won't
being copied to new table."
-> "Internal triggers (such as foreign-key-associated triggers)
won't be copied to the new table."

c) "However this command will fail if the source table's trigger
contain whole-row reference."
-> "However, this command will fail if the source table's triggers
contain a whole-row reference."

7. Whole-row reference restriction: implementation gap or deliberate?

Triggers whose WHEN clause contains a whole-row reference (OLD.*,
NEW.*) are rejected. Is this a deliberate decision, or a known gap
left for a follow-up? If the latter, a XXX comment at the rejection
site would help future contributors:

/*
* XXX: whole-row Vars could in principle be handled by passing the
* target table's composite type OID as to_rowtype, but
* generateClonedTriggerStmt() currently has no access to it.
*/

SUMMARY OF ITEMS TO ADDRESS
----------------------------

[1] create_table_like.sql: define and drop trigger_func locally so
the file can be run in isolation.

[2] Add a test for EXCLUDING TRIGGERS.

[3] create_table.sgml: align the INCLUDING TRIGGERS description with
create_foreign_table.sgml, which already reads correctly:
"All non-internal triggers are copied to the new table."

[4] trigger.c generateClonedTriggerStmt(): lowercase "string" in
comment "/* Reconstruct trigger function String list */".

[5] parse_utilcmd.c expandTableLikeClause(): simplify or remove the
comment "/* We make use of CreateTrigStmt's trigcomment option */".

[6] Commit message: fix grammar in items (a), (b), (c) listed above.

Items [3]-[6] are purely cosmetic. [1] and [2] are worth considering
before commit, but none of these are blockers.

Thanks for the patch.

Best regards,
Henson Choi

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Michael Paquier 2026-05-07 01:47:32 Re: Fix DROP PROPERTY GRAPH "unsupported object class" error
Previous Message jian he 2026-05-07 01:14:47 Re: FOR PORTION OF does not recompute GENERATED STORED columns that depend on the range column