diff options
author | Simon Riggs <simon@2ndQuadrant.com> | 2014-12-08 00:55:28 +0900 |
---|---|---|
committer | Simon Riggs <simon@2ndQuadrant.com> | 2014-12-08 00:55:28 +0900 |
commit | 618c9430a82860c84a3be2711eec2c3b43573b2a (patch) | |
tree | e8415e009b1cfd89a73d6211271f5d6d414bfba5 /src/backend/commands/tablecmds.c | |
parent | b8e33a85d4e86a8391118c3d5cdb249b560dec4f (diff) | |
download | postgresql-618c9430a82860c84a3be2711eec2c3b43573b2a.tar.gz postgresql-618c9430a82860c84a3be2711eec2c3b43573b2a.zip |
Event Trigger for table_rewrite
Generate a table_rewrite event when ALTER TABLE
attempts to rewrite a table. Provide helper
functions to identify table and reason.
Intended use case is to help assess or to react
to schema changes that might hold exclusive locks
for long periods.
Dimitri Fontaine, triggering an edit by Simon Riggs
Reviewed in detail by Michael Paquier
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r-- | src/backend/commands/tablecmds.c | 64 |
1 files changed, 45 insertions, 19 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 2333e1bed92..1e737a01c9f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -46,6 +46,7 @@ #include "commands/cluster.h" #include "commands/comment.h" #include "commands/defrem.h" +#include "commands/event_trigger.h" #include "commands/policy.h" #include "commands/sequence.h" #include "commands/tablecmds.h" @@ -153,7 +154,7 @@ typedef struct AlteredTableInfo List *constraints; /* List of NewConstraint */ List *newvals; /* List of NewColumnValue */ bool new_notnull; /* T if we added new NOT NULL constraints */ - bool rewrite; /* T if a rewrite is forced */ + int rewrite; /* Reason for forced rewrite, if any */ Oid newTableSpace; /* new tablespace; 0 means no change */ bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */ char newrelpersistence; /* if above is true */ @@ -303,13 +304,15 @@ static void validateForeignKeyConstraint(char *conname, static void createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid); -static void ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode); +static void ATController(AlterTableStmt *parsetree, + Relation rel, List *cmds, bool recurse, LOCKMODE lockmode); static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode); static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode); static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode); -static void ATRewriteTables(List **wqueue, LOCKMODE lockmode); +static void ATRewriteTables(AlterTableStmt *parsetree, + List **wqueue, LOCKMODE lockmode); static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode); static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); static void ATSimplePermissions(Relation rel, int allowed_targets); @@ -2724,7 +2727,8 @@ AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt) CheckTableNotInUse(rel, "ALTER TABLE"); - ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt), + ATController(stmt, + rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt), lockmode); } @@ -2747,7 +2751,7 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse) rel = relation_open(relid, lockmode); - ATController(rel, cmds, recurse, lockmode); + ATController(NULL, rel, cmds, recurse, lockmode); } /* @@ -3015,8 +3019,15 @@ AlterTableGetLockLevel(List *cmds) return lockmode; } +/* + * ATController provides top level control over the phases. + * + * parsetree is passed in to allow it to be passed to event triggers + * when requested. + */ static void -ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode) +ATController(AlterTableStmt *parsetree, + Relation rel, List *cmds, bool recurse, LOCKMODE lockmode) { List *wqueue = NIL; ListCell *lcmd; @@ -3036,7 +3047,7 @@ ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode) ATRewriteCatalogs(&wqueue, lockmode); /* Phase 3: scan/rewrite tables as needed */ - ATRewriteTables(&wqueue, lockmode); + ATRewriteTables(parsetree, &wqueue, lockmode); } /* @@ -3195,7 +3206,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, /* force rewrite if necessary; see comment in ATRewriteTables */ if (tab->chgPersistence) { - tab->rewrite = true; + tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE; tab->newrelpersistence = RELPERSISTENCE_PERMANENT; } pass = AT_PASS_MISC; @@ -3206,7 +3217,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, /* force rewrite if necessary; see comment in ATRewriteTables */ if (tab->chgPersistence) { - tab->rewrite = true; + tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE; tab->newrelpersistence = RELPERSISTENCE_UNLOGGED; } pass = AT_PASS_MISC; @@ -3607,7 +3618,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, * ATRewriteTables: ALTER TABLE phase 3 */ static void -ATRewriteTables(List **wqueue, LOCKMODE lockmode) +ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode) { ListCell *ltab; @@ -3634,7 +3645,7 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) * constraints, so it's not necessary/appropriate to enforce them just * during ALTER.) */ - if (tab->newvals != NIL || tab->rewrite) + if (tab->newvals != NIL || tab->rewrite > 0) { Relation rel; @@ -3655,7 +3666,7 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) * and assigns a new relfilenode, we automatically create or drop an * init fork for the relation as appropriate. */ - if (tab->rewrite) + if (tab->rewrite > 0) { /* Build a temporary relation and copy data */ Relation OldHeap; @@ -3710,6 +3721,21 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) heap_close(OldHeap, NoLock); /* + * Fire off an Event Trigger now, before actually rewriting the + * table. + * + * We don't support Event Trigger for nested commands anywhere, + * here included, and parsetree is given NULL when coming from + * AlterTableInternal. + * + * And fire it only once. + */ + if (parsetree) + EventTriggerTableRewrite((Node *)parsetree, + tab->relid, + tab->rewrite); + + /* * Create transient table that will receive the modified data. * * Ensure it is marked correctly as logged or unlogged. We have @@ -4002,7 +4028,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { - if (tab->rewrite) + if (tab->rewrite > 0) { Oid tupOid = InvalidOid; @@ -4828,7 +4854,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, newval->expr = expression_planner(defval); tab->newvals = lappend(tab->newvals, newval); - tab->rewrite = true; + tab->rewrite |= AT_REWRITE_DEFAULT_VAL; } /* @@ -4845,7 +4871,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, * table to fix that. */ if (isOid) - tab->rewrite = true; + tab->rewrite |= AT_REWRITE_ALTER_OID; /* * Add needed dependency entries for the new column. @@ -5657,7 +5683,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, tab = ATGetQueueEntry(wqueue, rel); /* Tell Phase 3 to physically remove the OID column */ - tab->rewrite = true; + tab->rewrite |= AT_REWRITE_ALTER_OID; } } @@ -5683,7 +5709,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel, /* suppress schema rights check when rebuilding existing index */ check_rights = !is_rebuild; /* skip index build if phase 3 will do it or we're reusing an old one */ - skip_build = tab->rewrite || OidIsValid(stmt->oldNode); + skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode); /* suppress notices when rebuilding existing index */ quiet = is_rebuild; @@ -7686,7 +7712,7 @@ ATPrepAlterColumnType(List **wqueue, tab->newvals = lappend(tab->newvals, newval); if (ATColumnChangeRequiresRewrite(transform, attnum)) - tab->rewrite = true; + tab->rewrite |= AT_REWRITE_COLUMN_REWRITE; } else if (transform) ereport(ERROR, @@ -8431,7 +8457,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, con->old_pktable_oid = refRelId; /* rewriting neither side of a FK */ if (con->contype == CONSTR_FOREIGN && - !rewrite && !tab->rewrite) + !rewrite && tab->rewrite == 0) TryReuseForeignKey(oldId, con); cmd->subtype = AT_ReAddConstraint; tab->subcmds[AT_PASS_OLD_CONSTR] = |