Change trigger type to AFTER trigger.

During repacking table, if a transaction executes INSERT CONFLICT
ON UPDATE/DO NOTHING, because we define BEFORE trigger on target
table, the contents of operation log table becomes inconsistent
easliy. As a result, pg_reapck fails with a high probability.

To resolve this issue, this changes the trigger type from BEFORE
to AFTER. We define AFTER trigger that is the first of the AFTER
trigger to fire on the table.
This commit is contained in:
Masahiko Sawada 2017-01-19 13:47:18 +09:00
parent e1056c003c
commit d83ee3d6a0
7 changed files with 43 additions and 43 deletions

View File

@ -186,8 +186,8 @@ typedef struct repack_table
Oid ckid; /* target: CK OID */
const char *create_pktype; /* CREATE TYPE pk */
const char *create_log; /* CREATE TABLE log */
const char *create_trigger; /* CREATE TRIGGER z_repack_trigger */
const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER z_repack_trigger */
const char *create_trigger; /* CREATE TRIGGER a_repack_trigger */
const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER a_repack_trigger */
const char *create_table; /* CREATE TABLE table AS SELECT */
const char *drop_columns; /* ALTER TABLE DROP COLUMNs */
const char *delete_log; /* DELETE FROM log */
@ -1024,7 +1024,7 @@ repack_one_table(repack_table *table, const char *orderby)
const char *appname = getenv("PGAPPNAME");
/* Keep track of whether we have gotten through setup to install
* the z_repack_trigger, log table, etc. ourselves. We don't want to
* the a_repack_trigger, log table, etc. ourselves. We don't want to
* go through repack_cleanup() if we didn't actually set up the
* trigger ourselves, lest we be cleaning up another pg_repack's mess,
* or worse, interfering with a still-running pg_repack.
@ -1126,13 +1126,13 @@ repack_one_table(repack_table *table, const char *orderby)
/*
* Check z_repack_trigger is the trigger executed last so that
* other before triggers cannot modify triggered tuples.
* Check a_repack_trigger is the after trigger executed first
* so that other before triggers cannot modify triggered tuples.
*/
res = execute("SELECT repack.conflicted_triggers($1)", 1, params);
if (PQntuples(res) > 0)
{
if (0 == strcmp("z_repack_trigger", PQgetvalue(res, 0, 0)))
if (0 == strcmp("a_repack_trigger", PQgetvalue(res, 0, 0)))
{
ereport(WARNING,
(errcode(E_PG_COMMAND),
@ -1153,10 +1153,10 @@ repack_one_table(repack_table *table, const char *orderby)
errmsg("trigger \"%s\" conflicting on table \"%s\"",
PQgetvalue(res, 0, 0), table->target_name),
errdetail(
"The trigger \"z_repack_trigger\" must be the last of the"
" BEFORE triggers to fire on the table (triggers fire in"
"The trigger \"a_repack_trigger\" must be the first of the"
" AFTER triggers to fire on the table (triggers fire in"
" alphabetical order). Please rename the trigger so that"
" it sorts before \"z_repack_trigger\": you can use"
" it sorts after \"a_repack_trigger\": you can use"
" \"ALTER TRIGGER %s ON %s RENAME TO newname\".",
PQgetvalue(res, 0, 0), table->target_name)));
}
@ -1232,7 +1232,7 @@ repack_one_table(repack_table *table, const char *orderby)
*/
command("COMMIT", 0, NULL);
/* The main connection has now committed its z_repack_trigger,
/* The main connection has now committed its a_repack_trigger,
* log table, and temp. table. If any error occurs from this point
* on and we bail out, we should try to clean those up.
*/

View File

@ -362,7 +362,7 @@ ERROR: query failed: ERROR: column "col" does not exist
Specify existing columns.
WARNING: the table "tbl" already has a trigger called z_repack_trigger
WARNING: the table "tbl" already has a trigger called a_repack_trigger
The trigger was probably installed during a previous attempt to run
pg_repack on the table which was interrupted and for some reason failed
to clean up the temporary objects.
@ -371,14 +371,14 @@ WARNING: the table "tbl" already has a trigger called z_repack_trigger
extension: see the installation_ section for the details.
WARNING: trigger "trg" conflicting on table "tbl"
The target table has a trigger whose name follows ``z_repack_trigger``
The target table has a trigger whose name follows ``a_repack_trigger``
in alphabetical order.
The ``z_repack_trigger`` should be the last BEFORE trigger to fire.
The ``a_repack_trigger`` should be the first AFTER trigger to fire.
Please rename your trigger so that it sorts alphabetically before
pg_repack's one; you can use::
ALTER TRIGGER zzz_my_trigger ON sometable RENAME TO yyy_my_trigger;
ALTER TRIGGER aaa_my_trigger ON sometable RENAME TO bbb_my_trigger;
ERROR: Another pg_repack command may be running on the table. Please try again
later.

View File

@ -651,7 +651,7 @@ ERROR: query failed: ERROR: column "col" does not exist
対象のテーブルが ``--order-by`` オプションで指定したカラムを持っていない場合に表示されます。
存在しているカラムを指定してください。
.. WARNING: the table "tbl" already has a trigger called z_repack_trigger
.. WARNING: the table "tbl" already has a trigger called a_repack_trigger
The trigger was probably installed during a previous attempt to run
pg_repack on the table which was interrupted and for some reason failed
to clean up the temporary objects.
@ -661,31 +661,31 @@ ERROR: query failed: ERROR: column "col" does not exist
.. class:: diag
WARNING: the table "tbl" already has a trigger called z_repack_trigger
WARNING: the table "tbl" already has a trigger called a_repack_trigger
以前に実行したが何らかの理由で中断したか、あるいは失敗したpg_repackコマンドにより、
対象テーブルにpg_repackが利用するトリガが残存している場合に表示されます。
pg_repackを一度削除して、再度登録することで、こうした一時オブジェクトを削除できます。
`インストール`_ を参照してください。
.. WARNING: trigger "trg" conflicting on table "tbl"
The target table has a trigger whose name follows ``z_repack_trigger``
The target table has a trigger whose name follows ``a_repack_trigger``
in alphabetical order.
The ``z_repack_trigger`` should be the last BEFORE trigger to fire.
The ``a_repack_trigger`` should be the first AFTER trigger to fire.
Please rename your trigger so that it sorts alphabetically before
pg_repack's one; you can use::
ALTER TRIGGER zzz_my_trigger ON sometable RENAME TO yyy_my_trigger;
ALTER TRIGGER aaa_my_trigger ON sometable RENAME TO bbb_my_trigger;
.. class:: diag
WARNING: trigger "trg" conflicting on table "tbl"
対象のテーブルが、pg_repackが利用する ``z_repack_trigger`` という名前のトリガ
よりもアルファベット順で後ろになるような名前のトリガを持っている場合に表示されます。
``z_repack_trigger`` トリガは最後に実行されるBEFOREトリガになる必要があります。
対象のテーブルが、pg_repackが利用する ``a_repack_trigger`` という名前のトリガ
よりもアルファベット順でになるような名前のトリガを持っている場合に表示されます。
``a_repack_trigger`` トリガは最初に実行されるAFTERトリガになる必要があります。
該当のトリガ名称を変更してください。::
ALTER TRIGGER zzz_my_trigger ON sometable RENAME TO yyy_my_trigger;
ALTER TRIGGER aaa_my_trigger ON sometable RENAME TO bbb_my_trigger;
.. ERROR: Another pg_repack command may be running on the table. Please try again
later.

View File

@ -68,8 +68,8 @@ LANGUAGE sql STABLE STRICT;
CREATE FUNCTION repack.get_create_trigger(relid oid, pkid oid)
RETURNS text AS
$$
SELECT 'CREATE TRIGGER z_repack_trigger' ||
' BEFORE INSERT OR DELETE OR UPDATE ON ' || repack.oid2text($1) ||
SELECT 'CREATE TRIGGER a_repack_trigger' ||
' AFTER INSERT OR DELETE OR UPDATE ON ' || repack.oid2text($1) ||
' FOR EACH ROW EXECUTE PROCEDURE repack.repack_trigger(' ||
'''INSERT INTO repack.log_' || $1 || '(pk, row) VALUES(' ||
' CASE WHEN $1 IS NULL THEN NULL ELSE (ROW($1.' ||
@ -82,7 +82,7 @@ CREATE FUNCTION repack.get_enable_trigger(relid oid)
RETURNS text AS
$$
SELECT 'ALTER TABLE ' || repack.oid2text($1) ||
' ENABLE ALWAYS TRIGGER z_repack_trigger';
' ENABLE ALWAYS TRIGGER a_repack_trigger';
$$
LANGUAGE sql STABLE STRICT;
@ -223,8 +223,8 @@ LANGUAGE C VOLATILE STRICT SECURITY DEFINER;
CREATE FUNCTION repack.conflicted_triggers(oid) RETURNS SETOF name AS
$$
SELECT tgname FROM pg_trigger
WHERE tgrelid = $1 AND tgname >= 'z_repack_trigger'
AND (tgtype & 2) = 2 -- BEFORE trigger
WHERE tgrelid = $1 AND tgname <= 'a_repack_trigger'
AND (tgtype & 2) = 0 -- AFTER trigger
ORDER BY tgname;
$$
LANGUAGE sql STABLE STRICT;

View File

@ -151,7 +151,7 @@ repack_trigger(PG_FUNCTION_ARGS)
/* make sure it's called as a trigger at all */
if (!CALLED_AS_TRIGGER(fcinfo) ||
!TRIGGER_FIRED_BEFORE(trigdata->tg_event) ||
!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event) ||
trigdata->tg_trigger->tgnargs != 1)
elog(ERROR, "repack_trigger: invalid trigger call");
@ -921,7 +921,7 @@ repack_swap(PG_FUNCTION_ARGS)
/* drop repack trigger */
execute_with_format(
SPI_OK_UTILITY,
"DROP TRIGGER IF EXISTS z_repack_trigger ON %s.%s CASCADE",
"DROP TRIGGER IF EXISTS a_repack_trigger ON %s.%s CASCADE",
nspname, relname);
SPI_finish();
@ -962,7 +962,7 @@ repack_drop(PG_FUNCTION_ARGS)
* To prevent concurrent lockers of the repack target table from causing
* deadlocks, take an exclusive lock on it. Consider that the following
* commands take exclusive lock on tables log_xxx and the target table
* itself when deleting the z_repack_trigger on it, while concurrent
* itself when deleting the a_repack_trigger on it, while concurrent
* updaters require row exclusive lock on the target table and in
* addition, on the log_xxx table, because of the trigger.
*
@ -1011,7 +1011,7 @@ repack_drop(PG_FUNCTION_ARGS)
{
execute_with_format(
SPI_OK_UTILITY,
"DROP TRIGGER IF EXISTS z_repack_trigger ON %s.%s CASCADE",
"DROP TRIGGER IF EXISTS a_repack_trigger ON %s.%s CASCADE",
nspname, relname);
--numobj;
}

View File

@ -338,23 +338,23 @@ CREATE FUNCTION trgtest() RETURNS trigger AS
$$BEGIN RETURN NEW; END$$
LANGUAGE plpgsql;
CREATE TABLE trg1 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_triggeq BEFORE UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest();
CREATE TRIGGER a_repack_trigges AFTER UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg1
INFO: repacking table "trg1"
CREATE TABLE trg2 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigger BEFORE UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest();
CREATE TRIGGER a_repack_trigger AFTER UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg2
INFO: repacking table "trg2"
WARNING: the table "trg2" already has a trigger called "z_repack_trigger"
WARNING: the table "trg2" already has a trigger called "a_repack_trigger"
DETAIL: The trigger was probably installed during a previous attempt to run pg_repack on the table which was interrupted and for some reason failed to clean up the temporary objects. Please drop the trigger or drop and recreate the pg_repack extension altogether to remove all the temporary objects left over.
CREATE TABLE trg3 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigges BEFORE UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest();
CREATE TRIGGER a_repack_triggeq AFTER UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg3
INFO: repacking table "trg3"
WARNING: trigger "z_repack_trigges" conflicting on table "trg3"
DETAIL: The trigger "z_repack_trigger" must be the last of the BEFORE triggers to fire on the table (triggers fire in alphabetical order). Please rename the trigger so that it sorts before "z_repack_trigger": you can use "ALTER TRIGGER z_repack_trigges ON trg3 RENAME TO newname".
WARNING: trigger "a_repack_triggeq" conflicting on table "trg3"
DETAIL: The trigger "a_repack_trigger" must be the first of the AFTER triggers to fire on the table (triggers fire in alphabetical order). Please rename the trigger so that it sorts after "a_repack_trigger": you can use "ALTER TRIGGER a_repack_triggeq ON trg3 RENAME TO newname".
CREATE TABLE trg4 (id integer PRIMARY KEY);
CREATE TRIGGER zzzzzz AFTER UPDATE ON trg4 FOR EACH ROW EXECUTE PROCEDURE trgtest();
CREATE TRIGGER a BEFORE UPDATE ON trg4 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg4
INFO: repacking table "trg4"
--

View File

@ -197,16 +197,16 @@ CREATE FUNCTION trgtest() RETURNS trigger AS
$$BEGIN RETURN NEW; END$$
LANGUAGE plpgsql;
CREATE TABLE trg1 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_triggeq BEFORE UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest();
CREATE TRIGGER a_repack_trigges AFTER UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg1
CREATE TABLE trg2 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigger BEFORE UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest();
CREATE TRIGGER a_repack_trigger AFTER UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg2
CREATE TABLE trg3 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigges BEFORE UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest();
CREATE TRIGGER a_repack_triggeq AFTER UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg3
CREATE TABLE trg4 (id integer PRIMARY KEY);
CREATE TRIGGER zzzzzz AFTER UPDATE ON trg4 FOR EACH ROW EXECUTE PROCEDURE trgtest();
CREATE TRIGGER a BEFORE UPDATE ON trg4 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg4
--