resolve conflicts

This commit is contained in:
Dmitry Ivanov 2017-03-23 14:14:18 +03:00
commit e89559a813
9 changed files with 440 additions and 145 deletions

View File

@ -152,6 +152,11 @@ const char *PROGRAM_VERSION = "unknown";
" AND granted = false AND relation = %u"\ " AND granted = false AND relation = %u"\
" AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()"
#define COUNT_COMPETING_LOCKS \
"SELECT pid FROM pg_locks WHERE locktype = 'relation'" \
" AND granted = false AND relation = %u" \
" AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()"
/* Will be used as a unique prefix for advisory locks. */ /* Will be used as a unique prefix for advisory locks. */
#define REPACK_LOCK_PREFIX_STR "16185446" #define REPACK_LOCK_PREFIX_STR "16185446"
@ -186,8 +191,8 @@ typedef struct repack_table
Oid ckid; /* target: CK OID */ Oid ckid; /* target: CK OID */
const char *create_pktype; /* CREATE TYPE pk */ const char *create_pktype; /* CREATE TYPE pk */
const char *create_log; /* CREATE TABLE log */ const char *create_log; /* CREATE TABLE log */
const char *create_trigger; /* CREATE TRIGGER z_repack_trigger */ const char *create_trigger; /* CREATE TRIGGER repack_trigger */
const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER z_repack_trigger */ const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */
const char *create_table; /* CREATE TABLE table AS SELECT */ const char *create_table; /* CREATE TABLE table AS SELECT */
const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ const char *drop_columns; /* ALTER TABLE DROP COLUMNs */
const char *delete_log; /* DELETE FROM log */ const char *delete_log; /* DELETE FROM log */
@ -244,6 +249,8 @@ static int wait_timeout = 60; /* in seconds */
static int jobs = 0; /* number of concurrent worker conns. */ static int jobs = 0; /* number of concurrent worker conns. */
static bool dryrun = false; static bool dryrun = false;
static unsigned int temp_obj_num = 0; /* temporary objects counter */ static unsigned int temp_obj_num = 0; /* temporary objects counter */
static bool no_kill_backend = false; /* abandon when timed-out */
static bool no_superuser_check = false;
static bool include_extensions = false; /* repack tables of extensions */ static bool include_extensions = false; /* repack tables of extensions */
/* buffer should have at least 11 bytes */ /* buffer should have at least 11 bytes */
@ -270,6 +277,8 @@ static pgut_option options[] =
{ 'i', 'T', "wait-timeout", &wait_timeout }, { 'i', 'T', "wait-timeout", &wait_timeout },
{ 'B', 'Z', "no-analyze", &analyze }, { 'B', 'Z', "no-analyze", &analyze },
{ 'i', 'j', "jobs", &jobs }, { 'i', 'j', "jobs", &jobs },
{ 'b', 'D', "no-kill-backend", &no_kill_backend },
{ 'b', 'k', "no-superuser-check", &no_superuser_check },
{ 'b', 'C', "include-extensions", &include_extensions }, { 'b', 'C', "include-extensions", &include_extensions },
{ 0 }, { 0 },
}; };
@ -373,6 +382,9 @@ is_superuser(void)
{ {
const char *val; const char *val;
if (no_superuser_check)
return true;
if (!connection) if (!connection)
return false; return false;
@ -1042,7 +1054,7 @@ repack_one_table(repack_table *table, const char *orderby)
const char *appname = getenv("PGAPPNAME"); const char *appname = getenv("PGAPPNAME");
/* Keep track of whether we have gotten through setup to install /* 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 repack_trigger, log table, etc. ourselves. We don't want to
* go through repack_cleanup() if we didn't actually set up the * go through repack_cleanup() if we didn't actually set up the
* trigger ourselves, lest we be cleaning up another pg_repack's mess, * trigger ourselves, lest we be cleaning up another pg_repack's mess,
* or worse, interfering with a still-running pg_repack. * or worse, interfering with a still-running pg_repack.
@ -1092,6 +1104,9 @@ repack_one_table(repack_table *table, const char *orderby)
if (!(lock_exclusive(connection, buffer, table->lock_table, TRUE))) if (!(lock_exclusive(connection, buffer, table->lock_table, TRUE)))
{ {
if (no_kill_backend)
elog(INFO, "Skipping repack %s due to timeout", table->target_name);
else
elog(WARNING, "lock_exclusive() failed for %s", table->target_name); elog(WARNING, "lock_exclusive() failed for %s", table->target_name);
goto cleanup; goto cleanup;
} }
@ -1144,18 +1159,18 @@ repack_one_table(repack_table *table, const char *orderby)
/* /*
* Check z_repack_trigger is the trigger executed last so that * Check if repack_trigger is not conflict with existing trigger. We can
* other before triggers cannot modify triggered tuples. * find it out later but we check it in advance and go to cleanup if needed.
* In AFTER trigger context, since triggered tuple is not changed by other
* trigger we don't care about the fire order.
*/ */
res = execute("SELECT repack.conflicted_triggers($1)", 1, params); res = execute("SELECT repack.conflicted_triggers($1)", 1, params);
if (PQntuples(res) > 0) if (PQntuples(res) > 0)
{
if (0 == strcmp("z_repack_trigger", PQgetvalue(res, 0, 0)))
{ {
ereport(WARNING, ereport(WARNING,
(errcode(E_PG_COMMAND), (errcode(E_PG_COMMAND),
errmsg("the table \"%s\" already has a trigger called \"%s\"", errmsg("the table \"%s\" already has a trigger called \"%s\"",
table->target_name, PQgetvalue(res, 0, 0)), table->target_name, "repack_trigger"),
errdetail( errdetail(
"The trigger was probably installed during a previous" "The trigger was probably installed during a previous"
" attempt to run pg_repack on the table which was" " attempt to run pg_repack on the table which was"
@ -1163,24 +1178,9 @@ repack_one_table(repack_table *table, const char *orderby)
" the temporary objects. Please drop the trigger or drop" " the temporary objects. Please drop the trigger or drop"
" and recreate the pg_repack extension altogether" " and recreate the pg_repack extension altogether"
" to remove all the temporary objects left over."))); " to remove all the temporary objects left over.")));
}
else
{
ereport(WARNING,
(errcode(E_PG_COMMAND),
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"
" alphabetical order). Please rename the trigger so that"
" it sorts before \"z_repack_trigger\": you can use"
" \"ALTER TRIGGER %s ON %s RENAME TO newname\".",
PQgetvalue(res, 0, 0), table->target_name)));
}
goto cleanup; goto cleanup;
} }
CLEARPGRES(res); CLEARPGRES(res);
command(table->create_pktype, 0, NULL); command(table->create_pktype, 0, NULL);
@ -1241,6 +1241,9 @@ repack_one_table(repack_table *table, const char *orderby)
*/ */
if (!(kill_ddl(connection, table->target_oid, true))) if (!(kill_ddl(connection, table->target_oid, true)))
{ {
if (no_kill_backend)
elog(INFO, "Skipping repack %s due to timeout.", table->target_name);
else
elog(WARNING, "kill_ddl() failed."); elog(WARNING, "kill_ddl() failed.");
goto cleanup; goto cleanup;
} }
@ -1250,7 +1253,7 @@ repack_one_table(repack_table *table, const char *orderby)
*/ */
command("COMMIT", 0, NULL); command("COMMIT", 0, NULL);
/* The main connection has now committed its z_repack_trigger, /* The main connection has now committed its repack_trigger,
* log table, and temp. table. If any error occurs from this point * log table, and temp. table. If any error occurs from this point
* on and we bail out, we should try to clean those up. * on and we bail out, we should try to clean those up.
*/ */
@ -1471,9 +1474,9 @@ cleanup:
} }
/* Kill off any concurrent DDL (or any transaction attempting to take /* Kill off any concurrent DDL (or any transaction attempting to take
* an AccessExclusive lock) trying to run against our table. Note, we're * an AccessExclusive lock) trying to run against our table if we want to
* killing these queries off *before* they are granted an AccessExclusive * do. Note, we're killing these queries off *before* they are granted
* lock on our table. * an AccessExclusive lock on our table.
* *
* Returns true if no problems encountered, false otherwise. * Returns true if no problems encountered, false otherwise.
*/ */
@ -1483,9 +1486,29 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate)
bool ret = true; bool ret = true;
PGresult *res; PGresult *res;
StringInfoData sql; StringInfoData sql;
int n_tuples;
initStringInfo(&sql); initStringInfo(&sql);
/* Check the number of backends competing AccessExclusiveLock */
printfStringInfo(&sql, COUNT_COMPETING_LOCKS, relid);
res = pgut_execute(conn, sql.data, 0, NULL);
n_tuples = PQntuples(res);
if (n_tuples != 0)
{
/* Competing backend is exsits, but if we do not want to calcel/terminate
* any backend, do nothing.
*/
if (no_kill_backend)
{
elog(WARNING, "%d unsafe queries remain but do not cancel them and skip to repack it",
n_tuples);
ret = false;
}
else
{
resetStringInfo(&sql);
printfStringInfo(&sql, CANCEL_COMPETING_LOCKS, relid); printfStringInfo(&sql, CANCEL_COMPETING_LOCKS, relid);
res = pgut_execute(conn, sql.data, 0, NULL); res = pgut_execute(conn, sql.data, 0, NULL);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
@ -1512,6 +1535,8 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate)
} }
else if (PQntuples(res) > 0) else if (PQntuples(res) > 0)
elog(NOTICE, "Canceled %d unsafe queries", PQntuples(res)); elog(NOTICE, "Canceled %d unsafe queries", PQntuples(res));
}
}
else else
elog(DEBUG2, "No competing DDL to cancel."); elog(DEBUG2, "No competing DDL to cancel.");
@ -1669,6 +1694,14 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta
duration = time(NULL) - start; duration = time(NULL) - start;
if (duration > wait_timeout) if (duration > wait_timeout)
{
if (no_kill_backend)
{
elog(WARNING, "timed out, do not cancel conflicting backends");
ret = false;
break;
}
else
{ {
const char *cancel_query; const char *cancel_query;
if (PQserverVersion(conn) >= 80400 && if (PQserverVersion(conn) >= 80400 &&
@ -1691,6 +1724,7 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta
pgut_command(conn, cancel_query, 1, &relid); pgut_command(conn, cancel_query, 1, &relid);
} }
}
/* wait for a while to lock the table. */ /* wait for a while to lock the table. */
wait_msec = Min(1000, i * 100); wait_msec = Min(1000, i * 100);
@ -2081,6 +2115,8 @@ pgut_help(bool details)
printf(" -i, --index=INDEX move only the specified index\n"); printf(" -i, --index=INDEX move only the specified index\n");
printf(" -x, --only-indexes move only indexes of the specified table\n"); printf(" -x, --only-indexes move only indexes of the specified table\n");
printf(" -T, --wait-timeout=SECS timeout to cancel other backends on conflict\n"); printf(" -T, --wait-timeout=SECS timeout to cancel other backends on conflict\n");
printf(" -D, --no-kill-backend don't kill other backends when timed out\n");
printf(" -Z, --no-analyze don't analyze at end\n"); printf(" -Z, --no-analyze don't analyze at end\n");
printf(" -k, --no-superuser-check skip superuser checks in client\n");
printf(" -C, --include-extensions repack tables which belong to extensions\n"); printf(" -C, --include-extensions repack tables which belong to extensions\n");
} }

View File

@ -1307,7 +1307,7 @@ pgut_malloc(size_t size)
if ((ret = malloc(size)) == NULL) if ((ret = malloc(size)) == NULL)
ereport(FATAL, ereport(FATAL,
(errcode_errno(), (errcode_errno(),
errmsg("could not allocate memory (%lu bytes): ", errmsg("could not allocate memory (" UINT64_FORMAT " bytes): ",
(unsigned long) size))); (unsigned long) size)));
return ret; return ret;
} }
@ -1320,7 +1320,7 @@ pgut_realloc(void *p, size_t size)
if ((ret = realloc(p, size)) == NULL) if ((ret = realloc(p, size)) == NULL)
ereport(FATAL, ereport(FATAL,
(errcode_errno(), (errcode_errno(),
errmsg("could not re-allocate memory (%lu bytes): ", errmsg("could not re-allocate memory (" UINT64_FORMAT " bytes): ",
(unsigned long) size))); (unsigned long) size)));
return ret; return ret;
} }

View File

@ -40,7 +40,7 @@ Requirements
------------ ------------
PostgreSQL versions PostgreSQL versions
PostgreSQL 8.3, 8.4, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5 PostgreSQL 8.3, 8.4, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6
Disks Disks
Performing a full-table repack requires free disk space about twice as Performing a full-table repack requires free disk space about twice as
@ -127,7 +127,9 @@ Options:
-i, --index=INDEX move only the specified index -i, --index=INDEX move only the specified index
-x, --only-indexes move only indexes of the specified table -x, --only-indexes move only indexes of the specified table
-T, --wait-timeout=SECS timeout to cancel other backends on conflict -T, --wait-timeout=SECS timeout to cancel other backends on conflict
-D, --no-kill-backend don't kill other backends when timed out
-Z, --no-analyze don't analyze at end -Z, --no-analyze don't analyze at end
-k, --no-superuser-check skip superuser checks in client
Connection options: Connection options:
-d, --dbname=DBNAME database to connect -d, --dbname=DBNAME database to connect
@ -200,16 +202,26 @@ Reorg Options
``-T SECS``, ``--wait-timeout=SECS`` ``-T SECS``, ``--wait-timeout=SECS``
pg_repack needs to take an exclusive lock at the end of the pg_repack needs to take an exclusive lock at the end of the
reorganization. This setting controls how many seconds pg_repack will reorganization. This setting controls how many seconds pg_repack will
wait to acquire this lock. If the lock cannot be taken after this duration, wait to acquire this lock. If the lock cannot be taken after this duration
pg_repack will forcibly cancel the conflicting queries. If you are using and ``--no-kill-backend`` option is not specified, pg_repack will forcibly
PostgreSQL version 8.4 or newer, pg_repack will fall back to using cancel the conflicting queries. If you are using PostgreSQL version 8.4
pg_terminate_backend() to disconnect any remaining backends after or newer, pg_repack will fall back to using pg_terminate_backend() to
twice this timeout has passed. The default is 60 seconds. disconnect any remaining backends after twice this timeout has passed.
The default is 60 seconds.
``-D``, ``--no-kill-backend``
Skip to repack table if the lock cannot be taken for duration specified
``--wait-timeout``, instead of cancelling conflicting queries. The default
is false.
``-Z``, ``--no-analyze`` ``-Z``, ``--no-analyze``
Disable ANALYZE after a full-table reorganization. If not specified, run Disable ANALYZE after a full-table reorganization. If not specified, run
ANALYZE after the reorganization. ANALYZE after the reorganization.
``-k``, ``--no-superuser-check``
Skip the superuser checks in the client. This setting is useful for using
pg_repack on platforms that support running it as non-superusers.
Connection Options Connection Options
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
@ -362,7 +374,7 @@ ERROR: query failed: ERROR: column "col" does not exist
Specify existing columns. Specify existing columns.
WARNING: the table "tbl" already has a trigger called z_repack_trigger WARNING: the table "tbl" already has a trigger called repack_trigger
The trigger was probably installed during a previous attempt to run The trigger was probably installed during a previous attempt to run
pg_repack on the table which was interrupted and for some reason failed pg_repack on the table which was interrupted and for some reason failed
to clean up the temporary objects. to clean up the temporary objects.
@ -370,16 +382,6 @@ WARNING: the table "tbl" already has a trigger called z_repack_trigger
You can remove all the temporary objects by dropping and re-creating the You can remove all the temporary objects by dropping and re-creating the
extension: see the installation_ section for the details. 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``
in alphabetical order.
The ``z_repack_trigger`` should be the last BEFORE 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;
ERROR: Another pg_repack command may be running on the table. Please try again ERROR: Another pg_repack command may be running on the table. Please try again
later. later.

View File

@ -62,7 +62,7 @@ pg_repackでは再編成する方法として次のものが選択できます
------------ ------------
PostgreSQL versions PostgreSQL versions
PostgreSQL 8.3, 8.4, 9.0, 9.1, 9.2, 9.3, 9.4 PostgreSQL 8.3, 8.4, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6
Disks Disks
Performing a full-table repack requires free disk space about twice as Performing a full-table repack requires free disk space about twice as
@ -206,6 +206,7 @@ pg_repackもしくはpg_reorgの古いバージョンからのアップグレー
-i, --index=INDEX move only the specified index -i, --index=INDEX move only the specified index
-x, --only-indexes move only indexes of the specified table -x, --only-indexes move only indexes of the specified table
-T, --wait-timeout=SECS timeout to cancel other backends on conflict -T, --wait-timeout=SECS timeout to cancel other backends on conflict
-D, --no-kill-backend don't kill other backends when timed out
-Z, --no-analyze don't analyze at end -Z, --no-analyze don't analyze at end
Connection options: Connection options:
@ -244,6 +245,7 @@ OPTIONには以下のものが指定できます。
-i, --index=INDEX 指定したインデックスのみ再編成します -i, --index=INDEX 指定したインデックスのみ再編成します
-x, --only-indexes 指定したテーブルに付与されたインデックスだけを再編成します -x, --only-indexes 指定したテーブルに付与されたインデックスだけを再編成します
-T, --wait-timeout=SECS ロック競合している他のトランザクションをキャンセルするまで待機する時間を指定します -T, --wait-timeout=SECS ロック競合している他のトランザクションをキャンセルするまで待機する時間を指定します
-D, --no-kill-backend タイムアウト時に他のバックエンドをキャンセルしません
-Z, --no-analyze 再編成後にANALYZEを行いません -Z, --no-analyze 再編成後にANALYZEを行いません
接続オプション: 接続オプション:
@ -351,15 +353,22 @@ OPTIONには以下のものが指定できます。
.. ``-T SECS``, ``--wait-timeout=SECS`` .. ``-T SECS``, ``--wait-timeout=SECS``
pg_repack needs to take an exclusive lock at the end of the pg_repack needs to take an exclusive lock at the end of the
reorganization. This setting controls how many seconds pg_repack will reorganization. This setting controls how many seconds pg_repack will
wait to acquire this lock. If the lock cannot be taken after this duration, wait to acquire this lock. If the lock cannot be taken after this duration
pg_repack will forcibly cancel the conflicting queries. If you are using and ``--no-kill-backend`` option is not specified, pg_repack will forcibly
PostgreSQL version 8.4 or newer, pg_repack will fall back to using cancel the conflicting queries. If you are using PostgreSQL version 8.4
pg_terminate_backend() to disconnect any remaining backends after or newer, pg_repack will fall back to using pg_terminate_backend() to
twice this timeout has passed. The default is 60 seconds. disconnect any remaining backends after twice this timeout has passed.
The default is 60 seconds.
``-T SECS``, ``--wait-timeout=SECS`` ``-T SECS``, ``--wait-timeout=SECS``
pg_repackは再編成の完了直前に排他ロックを利用します。このオプションは、このロック取得時に何秒間pg_repackが取得を待機するかを指定します。指定した時間経ってもロックが取得できない場合、pg_repackは競合するクエリを強制的にキャンセルさせます。PostgreSQL 8.4以上のバージョンを利用している場合、指定した時間の2倍以上経ってもロックが取得できない場合、pg_repackは競合するクエリを実行しているPostgreSQLバックエンドプロセスをpg_terminate_backend()関数により強制的に停止させます。このオプションのデフォルトは60秒です。 pg_repackは再編成の完了直前に排他ロックを利用します。このオプションは、このロック取得時に何秒間pg_repackが取得を待機するかを指定します。指定した時間経ってもロックが取得できないかつ、`no-kill-backend`オプションが指定されていない場合、pg_repackは競合するクエリを強制的にキャンセルさせます。PostgreSQL 8.4以上のバージョンを利用している場合、指定した時間の2倍以上経ってもロックが取得できない場合、pg_repackは競合するクエリを実行しているPostgreSQLバックエンドプロセスをpg_terminate_backend()関数により強制的に停止させます。このオプションのデフォルトは60秒です。
.. ``-D``, ``--no-kill-backend``
Skip to repack table if the lock cannot be taken for duration specified
``--wait-timeout``, instead of cancelling conflicting queries. The default
is false.
``-D``, ``--no-kill-backend``
``--wait-timeout``オプションで指定された時間が経過してもロックが取得できない場合、競合するクエリをキャンセルする代わりに対象テーブルの再編成をスキップします。
.. ``-Z``, ``--no-analyze`` .. ``-Z``, ``--no-analyze``
Disable ANALYZE after a full-table reorganization. If not specified, run Disable ANALYZE after a full-table reorganization. If not specified, run
@ -651,7 +660,7 @@ ERROR: query failed: ERROR: column "col" does not exist
対象のテーブルが ``--order-by`` オプションで指定したカラムを持っていない場合に表示されます。 対象のテーブルが ``--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 The trigger was probably installed during a previous attempt to run
pg_repack on the table which was interrupted and for some reason failed pg_repack on the table which was interrupted and for some reason failed
to clean up the temporary objects. to clean up the temporary objects.
@ -661,37 +670,21 @@ ERROR: query failed: ERROR: column "col" does not exist
.. class:: diag .. class:: diag
WARNING: the table "tbl" already has a trigger called z_repack_trigger WARNING: the table "tbl" already has a trigger called repack_trigger
以前に実行したが何らかの理由で中断したか、あるいは失敗したpg_repackコマンドにより、 以前に実行したが何らかの理由で中断したか、あるいは失敗したpg_repackコマンドにより、
対象テーブルにpg_repackが利用するトリガが残存している場合に表示されます。 対象テーブルにpg_repackが利用するトリガが残存している場合に表示されます。
pg_repackを一度削除して、再度登録することで、こうした一時オブジェクトを削除できます。 pg_repackを一度削除して、再度登録することで、こうした一時オブジェクトを削除できます。
`インストール`_ を参照してください。 `インストール`_ を参照してください。
.. WARNING: trigger "trg" conflicting on table "tbl" .. 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 ``repack_trigger``
in alphabetical order. in alphabetical order.
The ``z_repack_trigger`` should be the last BEFORE trigger to fire. The ``repack_trigger`` should be the first AFTER trigger to fire.
Please rename your trigger so that it sorts alphabetically before Please rename your trigger so that it sorts alphabetically before
pg_repack's one; you can use:: 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トリガになる必要があります。
該当のトリガ名称を変更してください。::
ALTER TRIGGER zzz_my_trigger ON sometable RENAME TO yyy_my_trigger;
.. ERROR: Another pg_repack command may be running on the table. Please try again
later.
There is a chance of deadlock when two concurrent pg_repack commands are run
on the same table. So, try to run the command after some time.
.. class:: diag .. class:: diag

View File

@ -68,8 +68,8 @@ LANGUAGE sql STABLE STRICT;
CREATE FUNCTION repack.get_create_trigger(relid oid, pkid oid) CREATE FUNCTION repack.get_create_trigger(relid oid, pkid oid)
RETURNS text AS RETURNS text AS
$$ $$
SELECT 'CREATE TRIGGER z_repack_trigger' || SELECT 'CREATE TRIGGER repack_trigger' ||
' BEFORE INSERT OR DELETE OR UPDATE ON ' || repack.oid2text($1) || ' AFTER INSERT OR DELETE OR UPDATE ON ' || repack.oid2text($1) ||
' FOR EACH ROW EXECUTE PROCEDURE repack.repack_trigger(' || ' FOR EACH ROW EXECUTE PROCEDURE repack.repack_trigger(' ||
'''INSERT INTO repack.log_' || $1 || '(pk, row) VALUES(' || '''INSERT INTO repack.log_' || $1 || '(pk, row) VALUES(' ||
' CASE WHEN $1 IS NULL THEN NULL ELSE (ROW($1.' || ' 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 RETURNS text AS
$$ $$
SELECT 'ALTER TABLE ' || repack.oid2text($1) || SELECT 'ALTER TABLE ' || repack.oid2text($1) ||
' ENABLE ALWAYS TRIGGER z_repack_trigger'; ' ENABLE ALWAYS TRIGGER repack_trigger';
$$ $$
LANGUAGE sql STABLE STRICT; LANGUAGE sql STABLE STRICT;
@ -223,8 +223,7 @@ LANGUAGE C VOLATILE STRICT SECURITY DEFINER;
CREATE FUNCTION repack.conflicted_triggers(oid) RETURNS SETOF name AS CREATE FUNCTION repack.conflicted_triggers(oid) RETURNS SETOF name AS
$$ $$
SELECT tgname FROM pg_trigger SELECT tgname FROM pg_trigger
WHERE tgrelid = $1 AND tgname >= 'z_repack_trigger' WHERE tgrelid = $1 AND tgname = 'repack_trigger'
AND (tgtype & 2) = 2 -- BEFORE trigger
ORDER BY tgname; ORDER BY tgname;
$$ $$
LANGUAGE sql STABLE STRICT; 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 */ /* make sure it's called as a trigger at all */
if (!CALLED_AS_TRIGGER(fcinfo) || if (!CALLED_AS_TRIGGER(fcinfo) ||
!TRIGGER_FIRED_BEFORE(trigdata->tg_event) || !TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event) || !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event) ||
trigdata->tg_trigger->tgnargs != 1) trigdata->tg_trigger->tgnargs != 1)
elog(ERROR, "repack_trigger: invalid trigger call"); elog(ERROR, "repack_trigger: invalid trigger call");
@ -921,7 +921,7 @@ repack_swap(PG_FUNCTION_ARGS)
/* drop repack trigger */ /* drop repack trigger */
execute_with_format( execute_with_format(
SPI_OK_UTILITY, SPI_OK_UTILITY,
"DROP TRIGGER IF EXISTS z_repack_trigger ON %s.%s CASCADE", "DROP TRIGGER IF EXISTS repack_trigger ON %s.%s CASCADE",
nspname, relname); nspname, relname);
SPI_finish(); SPI_finish();
@ -962,7 +962,7 @@ repack_drop(PG_FUNCTION_ARGS)
* To prevent concurrent lockers of the repack target table from causing * To prevent concurrent lockers of the repack target table from causing
* deadlocks, take an exclusive lock on it. Consider that the following * deadlocks, take an exclusive lock on it. Consider that the following
* commands take exclusive lock on tables log_xxx and the target table * 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 repack_trigger on it, while concurrent
* updaters require row exclusive lock on the target table and in * updaters require row exclusive lock on the target table and in
* addition, on the log_xxx table, because of the trigger. * addition, on the log_xxx table, because of the trigger.
* *
@ -1011,7 +1011,7 @@ repack_drop(PG_FUNCTION_ARGS)
{ {
execute_with_format( execute_with_format(
SPI_OK_UTILITY, SPI_OK_UTILITY,
"DROP TRIGGER IF EXISTS z_repack_trigger ON %s.%s CASCADE", "DROP TRIGGER IF EXISTS repack_trigger ON %s.%s CASCADE",
nspname, relname); nspname, relname);
--numobj; --numobj;
} }
@ -1306,8 +1306,8 @@ repack_index_swap(PG_FUNCTION_ARGS)
orig_idx_oid); orig_idx_oid);
execute(SPI_OK_SELECT, str.data); execute(SPI_OK_SELECT, str.data);
if (SPI_processed != 1) if (SPI_processed != 1)
elog(ERROR, "Could not find index 'index_%u', found %d matches", elog(ERROR, "Could not find index 'index_%u', found " UINT64_FORMAT " matches",
orig_idx_oid, SPI_processed); orig_idx_oid, (uint64) SPI_processed);
tuptable = SPI_tuptable; tuptable = SPI_tuptable;
desc = tuptable->tupdesc; desc = tuptable->tupdesc;

View File

@ -338,25 +338,19 @@ CREATE FUNCTION trgtest() RETURNS trigger AS
$$BEGIN RETURN NEW; END$$ $$BEGIN RETURN NEW; END$$
LANGUAGE plpgsql; LANGUAGE plpgsql;
CREATE TABLE trg1 (id integer PRIMARY KEY); CREATE TABLE trg1 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_triggeq BEFORE UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest(); CREATE TRIGGER repack_trigger_1 AFTER UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg1 \! pg_repack --dbname=contrib_regression --table=trg1
INFO: repacking table "trg1" INFO: repacking table "trg1"
CREATE TABLE trg2 (id integer PRIMARY KEY); CREATE TABLE trg2 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigger BEFORE UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest(); CREATE TRIGGER repack_trigger AFTER UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg2 \! pg_repack --dbname=contrib_regression --table=trg2
INFO: repacking 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 "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. 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 TABLE trg3 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigges BEFORE UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest(); CREATE TRIGGER repack_trigger_1 BEFORE UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg3 \! pg_repack --dbname=contrib_regression --table=trg3
INFO: repacking 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".
CREATE TABLE trg4 (id integer PRIMARY KEY);
CREATE TRIGGER zzzzzz AFTER UPDATE ON trg4 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg4
INFO: repacking table "trg4"
-- --
-- Dry run -- Dry run
-- --
@ -387,3 +381,25 @@ ERROR: cannot repack specific table(s) in schema, use schema.table notation inst
-- => ERROR -- => ERROR
\! pg_repack --dbname=contrib_regression --all --schema=test_schema1 \! pg_repack --dbname=contrib_regression --all --schema=test_schema1
ERROR: cannot repack specific schema(s) in all databases ERROR: cannot repack specific schema(s) in all databases
--
-- don't kill backend
--
\! pg_repack --dbname=contrib_regression --table=tbl_cluster --no-kill-backend
INFO: repacking table "tbl_cluster"
--
-- no superuser check
--
DROP ROLE IF EXISTS nosuper;
CREATE ROLE nosuper WITH LOGIN;
-- => OK
\! pg_repack --dbname=contrib_regression --table=tbl_cluster --no-superuser-check
INFO: repacking table "tbl_cluster"
-- => ERROR
\! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper
ERROR: pg_repack failed with error: You must be a superuser to use pg_repack
-- => ERROR
\! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper --no-superuser-check
ERROR: pg_repack failed with error: ERROR: permission denied for schema repack
LINE 1: select repack.version(), repack.version_sql()
^
DROP ROLE IF EXISTS nosuper;

View File

@ -0,0 +1,234 @@
SET client_min_messages = warning;
--
-- Tablespace features tests
--
-- Note: in order to pass this test you must create a tablespace called 'testts'
--
SELECT spcname FROM pg_tablespace WHERE spcname = 'testts';
spcname
---------
testts
(1 row)
-- If the query above failed you must create the 'testts' tablespace;
CREATE TABLE testts1 (id serial primary key, data text);
CREATE INDEX testts1_partial_idx on testts1 (id) where (id > 0);
CREATE INDEX testts1_with_idx on testts1 (id) with (fillfactor=80);
INSERT INTO testts1 (data) values ('a');
INSERT INTO testts1 (data) values ('b');
INSERT INTO testts1 (data) values ('c');
-- check the indexes definitions
SELECT regexp_replace(
repack.repack_indexdef(indexrelid, 'testts1'::regclass, NULL, false),
'_[0-9]+', '_OID', 'g')
FROM pg_index i join pg_class c ON c.oid = indexrelid
WHERE indrelid = 'testts1'::regclass ORDER BY relname;
regexp_replace
----------------------------------------------------------------------------------------------------------
CREATE INDEX index_OID ON repack.table_OID USING btree (id) TABLESPACE pg_default WHERE (id > 0)
CREATE UNIQUE INDEX index_OID ON repack.table_OID USING btree (id) TABLESPACE pg_default
CREATE INDEX index_OID ON repack.table_OID USING btree (id) WITH (fillfactor='80') TABLESPACE pg_default
(3 rows)
SELECT regexp_replace(
repack.repack_indexdef(indexrelid, 'testts1'::regclass, 'foo', false),
'_[0-9]+', '_OID', 'g')
FROM pg_index i join pg_class c ON c.oid = indexrelid
WHERE indrelid = 'testts1'::regclass ORDER BY relname;
regexp_replace
---------------------------------------------------------------------------------------------------
CREATE INDEX index_OID ON repack.table_OID USING btree (id) TABLESPACE foo WHERE (id > 0)
CREATE UNIQUE INDEX index_OID ON repack.table_OID USING btree (id) TABLESPACE foo
CREATE INDEX index_OID ON repack.table_OID USING btree (id) WITH (fillfactor='80') TABLESPACE foo
(3 rows)
SELECT regexp_replace(
repack.repack_indexdef(indexrelid, 'testts1'::regclass, NULL, true),
'_[0-9]+', '_OID', 'g')
FROM pg_index i join pg_class c ON c.oid = indexrelid
WHERE indrelid = 'testts1'::regclass ORDER BY relname;
regexp_replace
--------------------------------------------------------------------------------------------------------------
CREATE INDEX CONCURRENTLY index_OID ON testts1 USING btree (id) TABLESPACE pg_default WHERE (id > 0)
CREATE UNIQUE INDEX CONCURRENTLY index_OID ON testts1 USING btree (id) TABLESPACE pg_default
CREATE INDEX CONCURRENTLY index_OID ON testts1 USING btree (id) WITH (fillfactor='80') TABLESPACE pg_default
(3 rows)
SELECT regexp_replace(
repack.repack_indexdef(indexrelid, 'testts1'::regclass, 'foo', true),
'_[0-9]+', '_OID', 'g')
FROM pg_index i join pg_class c ON c.oid = indexrelid
WHERE indrelid = 'testts1'::regclass ORDER BY relname;
regexp_replace
-------------------------------------------------------------------------------------------------------
CREATE INDEX CONCURRENTLY index_OID ON testts1 USING btree (id) TABLESPACE foo WHERE (id > 0)
CREATE UNIQUE INDEX CONCURRENTLY index_OID ON testts1 USING btree (id) TABLESPACE foo
CREATE INDEX CONCURRENTLY index_OID ON testts1 USING btree (id) WITH (fillfactor='80') TABLESPACE foo
(3 rows)
-- can move the tablespace from default
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --tablespace testts
INFO: repacking table "testts1"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------+---------
testts1 | testts
(1 row)
SELECT * from testts1 order by id;
id | data
----+------
1 | a
2 | b
3 | c
(3 rows)
-- tablespace stays where it is
\! pg_repack --dbname=contrib_regression --no-order --table=testts1
INFO: repacking table "testts1"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------+---------
testts1 | testts
(1 row)
-- can move the ts back to default
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 -s pg_default
INFO: repacking table "testts1"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------+---------
(0 rows)
-- can move the table together with the indexes
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --tablespace testts --moveidx
INFO: repacking table "testts1"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------------------+---------
testts1 | testts
testts1_partial_idx | testts
testts1_pkey | testts
testts1_with_idx | testts
(4 rows)
-- can't specify --moveidx without --tablespace
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --moveidx
ERROR: cannot specify --moveidx (-S) without --tablespace (-s)
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 -S
ERROR: cannot specify --moveidx (-S) without --tablespace (-s)
-- not broken with order
\! pg_repack --dbname=contrib_regression -o id --table=testts1 --tablespace pg_default --moveidx
INFO: repacking table "testts1"
--move all indexes of the table to a tablespace
\! pg_repack --dbname=contrib_regression --table=testts1 --only-indexes --tablespace=testts
INFO: repacking indexes of "testts1"
INFO: repacking index "public"."testts1_partial_idx"
INFO: repacking index "public"."testts1_pkey"
INFO: repacking index "public"."testts1_with_idx"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------------------+---------
testts1_partial_idx | testts
testts1_pkey | testts
testts1_with_idx | testts
(3 rows)
--all indexes of tablespace remain in same tablespace
\! pg_repack --dbname=contrib_regression --table=testts1 --only-indexes
INFO: repacking indexes of "testts1"
INFO: repacking index "public"."testts1_partial_idx"
INFO: repacking index "public"."testts1_pkey"
INFO: repacking index "public"."testts1_with_idx"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------------------+---------
testts1_partial_idx | testts
testts1_pkey | testts
testts1_with_idx | testts
(3 rows)
--move all indexes of the table to pg_default
\! pg_repack --dbname=contrib_regression --table=testts1 --only-indexes --tablespace=pg_default
INFO: repacking indexes of "testts1"
INFO: repacking index "public"."testts1_partial_idx"
INFO: repacking index "public"."testts1_pkey"
INFO: repacking index "public"."testts1_with_idx"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------+---------
(0 rows)
--move one index to a tablespace
\! pg_repack --dbname=contrib_regression --index=testts1_pkey --tablespace=testts
INFO: repacking index "public"."testts1_pkey"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
--------------+---------
testts1_pkey | testts
(1 row)
--index tablespace stays as is
\! pg_repack --dbname=contrib_regression --index=testts1_pkey
INFO: repacking index "public"."testts1_pkey"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
--------------+---------
testts1_pkey | testts
(1 row)
--move index to pg_default
\! pg_repack --dbname=contrib_regression --index=testts1_pkey --tablespace=pg_default
INFO: repacking index "public"."testts1_pkey"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------+---------
(0 rows)
--using multiple --index option
\! pg_repack --dbname=contrib_regression --index=testts1_pkey --index=testts1_with_idx --tablespace=testts
INFO: repacking index "public"."testts1_pkey"
INFO: repacking index "public"."testts1_with_idx"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
------------------+---------
testts1_pkey | testts
testts1_with_idx | testts
(2 rows)
--using --indexes-only and --index option together
\! pg_repack --dbname=contrib_regression --table=testts1 --only-indexes --index=testts1_pkey
ERROR: cannot specify --index (-i) and --table (-t)

View File

@ -197,17 +197,14 @@ CREATE FUNCTION trgtest() RETURNS trigger AS
$$BEGIN RETURN NEW; END$$ $$BEGIN RETURN NEW; END$$
LANGUAGE plpgsql; LANGUAGE plpgsql;
CREATE TABLE trg1 (id integer PRIMARY KEY); CREATE TABLE trg1 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_triggeq BEFORE UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest(); CREATE TRIGGER repack_trigger_1 AFTER UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg1 \! pg_repack --dbname=contrib_regression --table=trg1
CREATE TABLE trg2 (id integer PRIMARY KEY); CREATE TABLE trg2 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigger BEFORE UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest(); CREATE TRIGGER repack_trigger AFTER UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg2 \! pg_repack --dbname=contrib_regression --table=trg2
CREATE TABLE trg3 (id integer PRIMARY KEY); CREATE TABLE trg3 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigges BEFORE UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest(); CREATE TRIGGER repack_trigger_1 BEFORE UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg3 \! 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();
\! pg_repack --dbname=contrib_regression --table=trg4
-- --
-- Dry run -- Dry run
@ -230,3 +227,21 @@ CREATE TABLE test_schema2.tbl2 (id INTEGER PRIMARY KEY);
\! pg_repack --dbname=contrib_regression --schema=test_schema1 --table=tbl1 \! pg_repack --dbname=contrib_regression --schema=test_schema1 --table=tbl1
-- => ERROR -- => ERROR
\! pg_repack --dbname=contrib_regression --all --schema=test_schema1 \! pg_repack --dbname=contrib_regression --all --schema=test_schema1
--
-- don't kill backend
--
\! pg_repack --dbname=contrib_regression --table=tbl_cluster --no-kill-backend
--
-- no superuser check
--
DROP ROLE IF EXISTS nosuper;
CREATE ROLE nosuper WITH LOGIN;
-- => OK
\! pg_repack --dbname=contrib_regression --table=tbl_cluster --no-superuser-check
-- => ERROR
\! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper
-- => ERROR
\! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper --no-superuser-check
DROP ROLE IF EXISTS nosuper;