diff --git a/bin/pg_repack.c b/bin/pg_repack.c index ca75429..308fc36 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -152,6 +152,11 @@ const char *PROGRAM_VERSION = "unknown"; " AND granted = false AND relation = %u"\ " 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. */ #define REPACK_LOCK_PREFIX_STR "16185446" @@ -186,8 +191,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 repack_trigger */ + const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER 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 */ @@ -244,6 +249,8 @@ static int wait_timeout = 60; /* in seconds */ static int jobs = 0; /* number of concurrent worker conns. */ static bool dryrun = false; 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 */ /* buffer should have at least 11 bytes */ @@ -270,6 +277,8 @@ static pgut_option options[] = { 'i', 'T', "wait-timeout", &wait_timeout }, { 'B', 'Z', "no-analyze", &analyze }, { '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 }, { 0 }, }; @@ -373,6 +382,9 @@ is_superuser(void) { const char *val; + if (no_superuser_check) + return true; + if (!connection) return false; @@ -1042,7 +1054,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 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. @@ -1092,7 +1104,10 @@ repack_one_table(repack_table *table, const char *orderby) if (!(lock_exclusive(connection, buffer, table->lock_table, TRUE))) { - elog(WARNING, "lock_exclusive() failed for %s", table->target_name); + 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); goto cleanup; } @@ -1144,43 +1159,28 @@ 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 if repack_trigger is not conflict with existing trigger. We can + * 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); if (PQntuples(res) > 0) { - if (0 == strcmp("z_repack_trigger", PQgetvalue(res, 0, 0))) - { - ereport(WARNING, + ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("the table \"%s\" already has a trigger called \"%s\"", - table->target_name, PQgetvalue(res, 0, 0)), + table->target_name, "repack_trigger"), errdetail( - "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" + "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."))); - } - 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))); - } - + " to remove all the temporary objects left over."))); goto cleanup; } + CLEARPGRES(res); command(table->create_pktype, 0, NULL); @@ -1241,7 +1241,10 @@ repack_one_table(repack_table *table, const char *orderby) */ if (!(kill_ddl(connection, table->target_oid, true))) { - elog(WARNING, "kill_ddl() failed."); + if (no_kill_backend) + elog(INFO, "Skipping repack %s due to timeout.", table->target_name); + else + elog(WARNING, "kill_ddl() failed."); goto cleanup; } @@ -1250,7 +1253,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 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. */ @@ -1471,9 +1474,9 @@ cleanup: } /* Kill off any concurrent DDL (or any transaction attempting to take - * an AccessExclusive lock) trying to run against our table. Note, we're - * killing these queries off *before* they are granted an AccessExclusive - * lock on our table. + * an AccessExclusive lock) trying to run against our table if we want to + * do. Note, we're killing these queries off *before* they are granted + * an AccessExclusive lock on our table. * * Returns true if no problems encountered, false otherwise. */ @@ -1483,35 +1486,57 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) bool ret = true; PGresult *res; StringInfoData sql; + int n_tuples; initStringInfo(&sql); - printfStringInfo(&sql, CANCEL_COMPETING_LOCKS, relid); + /* Check the number of backends competing AccessExclusiveLock */ + printfStringInfo(&sql, COUNT_COMPETING_LOCKS, relid); res = pgut_execute(conn, sql.data, 0, NULL); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - { - elog(WARNING, "Error canceling unsafe queries: %s", - PQerrorMessage(conn)); - ret = false; - } - else if (PQntuples(res) > 0 && terminate && PQserverVersion(conn) >= 80400) - { - elog(WARNING, - "Canceled %d unsafe queries. Terminating any remaining PIDs.", - PQntuples(res)); + n_tuples = PQntuples(res); - CLEARPGRES(res); - printfStringInfo(&sql, KILL_COMPETING_LOCKS, relid); - res = pgut_execute(conn, sql.data, 0, NULL); - if (PQresultStatus(res) != PGRES_TUPLES_OK) + 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, "Error killing unsafe queries: %s", - PQerrorMessage(conn)); + 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); + res = pgut_execute(conn, sql.data, 0, NULL); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + elog(WARNING, "Error canceling unsafe queries: %s", + PQerrorMessage(conn)); + ret = false; + } + else if (PQntuples(res) > 0 && terminate && PQserverVersion(conn) >= 80400) + { + elog(WARNING, + "Canceled %d unsafe queries. Terminating any remaining PIDs.", + PQntuples(res)); + + CLEARPGRES(res); + printfStringInfo(&sql, KILL_COMPETING_LOCKS, relid); + res = pgut_execute(conn, sql.data, 0, NULL); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + elog(WARNING, "Error killing unsafe queries: %s", + PQerrorMessage(conn)); + ret = false; + } + } + else if (PQntuples(res) > 0) + elog(NOTICE, "Canceled %d unsafe queries", PQntuples(res)); + } } - else if (PQntuples(res) > 0) - elog(NOTICE, "Canceled %d unsafe queries", PQntuples(res)); else elog(DEBUG2, "No competing DDL to cancel."); @@ -1670,26 +1695,35 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta duration = time(NULL) - start; if (duration > wait_timeout) { - const char *cancel_query; - if (PQserverVersion(conn) >= 80400 && - duration > wait_timeout * 2) + if (no_kill_backend) { - elog(WARNING, "terminating conflicted backends"); - cancel_query = - "SELECT pg_terminate_backend(pid) FROM pg_locks" - " WHERE locktype = 'relation'" - " AND relation = $1 AND pid <> pg_backend_pid()"; + elog(WARNING, "timed out, do not cancel conflicting backends"); + ret = false; + break; } else { - elog(WARNING, "canceling conflicted backends"); - cancel_query = - "SELECT pg_cancel_backend(pid) FROM pg_locks" - " WHERE locktype = 'relation'" - " AND relation = $1 AND pid <> pg_backend_pid()"; - } + const char *cancel_query; + if (PQserverVersion(conn) >= 80400 && + duration > wait_timeout * 2) + { + elog(WARNING, "terminating conflicted backends"); + cancel_query = + "SELECT pg_terminate_backend(pid) FROM pg_locks" + " WHERE locktype = 'relation'" + " AND relation = $1 AND pid <> pg_backend_pid()"; + } + else + { + elog(WARNING, "canceling conflicted backends"); + cancel_query = + "SELECT pg_cancel_backend(pid) FROM pg_locks" + " WHERE locktype = 'relation'" + " AND relation = $1 AND pid <> pg_backend_pid()"; + } - pgut_command(conn, cancel_query, 1, &relid); + pgut_command(conn, cancel_query, 1, &relid); + } } /* wait for a while to lock the table. */ @@ -2081,6 +2115,8 @@ pgut_help(bool details) printf(" -i, --index=INDEX move only the specified index\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(" -D, --no-kill-backend don't kill other backends when timed out\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"); } diff --git a/bin/pgut/pgut.c b/bin/pgut/pgut.c index 3261ac4..90ed8cb 100644 --- a/bin/pgut/pgut.c +++ b/bin/pgut/pgut.c @@ -1307,7 +1307,7 @@ pgut_malloc(size_t size) if ((ret = malloc(size)) == NULL) ereport(FATAL, (errcode_errno(), - errmsg("could not allocate memory (%lu bytes): ", + errmsg("could not allocate memory (" UINT64_FORMAT " bytes): ", (unsigned long) size))); return ret; } @@ -1320,7 +1320,7 @@ pgut_realloc(void *p, size_t size) if ((ret = realloc(p, size)) == NULL) ereport(FATAL, (errcode_errno(), - errmsg("could not re-allocate memory (%lu bytes): ", + errmsg("could not re-allocate memory (" UINT64_FORMAT " bytes): ", (unsigned long) size))); return ret; } diff --git a/doc/pg_repack.rst b/doc/pg_repack.rst index fef2477..2205cbb 100644 --- a/doc/pg_repack.rst +++ b/doc/pg_repack.rst @@ -40,7 +40,7 @@ Requirements ------------ 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 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 -x, --only-indexes move only indexes of the specified table -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 + -k, --no-superuser-check skip superuser checks in client Connection options: -d, --dbname=DBNAME database to connect @@ -200,16 +202,26 @@ Reorg Options ``-T SECS``, ``--wait-timeout=SECS`` pg_repack needs to take an exclusive lock at the end of the reorganization. This setting controls how many seconds pg_repack will - 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 - PostgreSQL version 8.4 or newer, pg_repack will fall back to using - pg_terminate_backend() to disconnect any remaining backends after - twice this timeout has passed. The default is 60 seconds. + wait to acquire this lock. If the lock cannot be taken after this duration + and ``--no-kill-backend`` option is not specified, pg_repack will forcibly + cancel the conflicting queries. If you are using PostgreSQL version 8.4 + or newer, pg_repack will fall back to using pg_terminate_backend() to + 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`` Disable ANALYZE after a full-table reorganization. If not specified, run 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 ^^^^^^^^^^^^^^^^^^ @@ -362,7 +374,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 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. @@ -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 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 later. diff --git a/doc/pg_repack_jp.rst b/doc/pg_repack_jp.rst index 21ac723..87b79e6 100644 --- a/doc/pg_repack_jp.rst +++ b/doc/pg_repack_jp.rst @@ -62,7 +62,7 @@ pg_repackでは再編成する方法として次のものが選択できます ------------ 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 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 -x, --only-indexes move only indexes of the specified table -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 Connection options: @@ -244,6 +245,7 @@ OPTIONには以下のものが指定できます。 -i, --index=INDEX 指定したインデックスのみ再編成します -x, --only-indexes 指定したテーブルに付与されたインデックスだけを再編成します -T, --wait-timeout=SECS ロック競合している他のトランザクションをキャンセルするまで待機する時間を指定します + -D, --no-kill-backend タイムアウト時に他のバックエンドをキャンセルしません -Z, --no-analyze 再編成後にANALYZEを行いません 接続オプション: @@ -351,15 +353,22 @@ OPTIONには以下のものが指定できます。 .. ``-T SECS``, ``--wait-timeout=SECS`` pg_repack needs to take an exclusive lock at the end of the reorganization. This setting controls how many seconds pg_repack will - 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 - PostgreSQL version 8.4 or newer, pg_repack will fall back to using - pg_terminate_backend() to disconnect any remaining backends after - twice this timeout has passed. The default is 60 seconds. + wait to acquire this lock. If the lock cannot be taken after this duration + and ``--no-kill-backend`` option is not specified, pg_repack will forcibly + cancel the conflicting queries. If you are using PostgreSQL version 8.4 + or newer, pg_repack will fall back to using pg_terminate_backend() to + disconnect any remaining backends after twice this timeout has passed. + The default is 60 seconds. ``-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`` 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`` オプションで指定したカラムを持っていない場合に表示されます。 存在しているカラムを指定してください。 -.. 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,37 +670,21 @@ 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 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 ``repack_trigger`` 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 pg_repack's one; you can use:: - ALTER TRIGGER zzz_my_trigger ON sometable RENAME TO yyy_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. + ALTER TRIGGER aaa_my_trigger ON sometable RENAME TO bbb_my_trigger; .. class:: diag diff --git a/lib/pg_repack.sql.in b/lib/pg_repack.sql.in index 72942d2..d294548 100644 --- a/lib/pg_repack.sql.in +++ b/lib/pg_repack.sql.in @@ -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 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 repack_trigger'; $$ LANGUAGE sql STABLE STRICT; @@ -223,8 +223,7 @@ 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 = 'repack_trigger' ORDER BY tgname; $$ LANGUAGE sql STABLE STRICT; diff --git a/lib/repack.c b/lib/repack.c index a79449c..4e6711b 100644 --- a/lib/repack.c +++ b/lib/repack.c @@ -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 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 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 repack_trigger ON %s.%s CASCADE", nspname, relname); --numobj; } @@ -1306,8 +1306,8 @@ repack_index_swap(PG_FUNCTION_ARGS) orig_idx_oid); execute(SPI_OK_SELECT, str.data); if (SPI_processed != 1) - elog(ERROR, "Could not find index 'index_%u', found %d matches", - orig_idx_oid, SPI_processed); + elog(ERROR, "Could not find index 'index_%u', found " UINT64_FORMAT " matches", + orig_idx_oid, (uint64) SPI_processed); tuptable = SPI_tuptable; desc = tuptable->tupdesc; diff --git a/regress/expected/repack.out b/regress/expected/repack.out index 4c5635b..17e3666 100644 --- a/regress/expected/repack.out +++ b/regress/expected/repack.out @@ -338,25 +338,19 @@ 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 repack_trigger_1 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 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 "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 repack_trigger_1 BEFORE 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". -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 -- @@ -387,3 +381,25 @@ ERROR: cannot repack specific table(s) in schema, use schema.table notation inst -- => ERROR \! pg_repack --dbname=contrib_regression --all --schema=test_schema1 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; diff --git a/regress/expected/tablespace_2.out b/regress/expected/tablespace_2.out new file mode 100644 index 0000000..d0e7e43 --- /dev/null +++ b/regress/expected/tablespace_2.out @@ -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) diff --git a/regress/sql/repack.sql b/regress/sql/repack.sql index 03e72cd..e613d63 100644 --- a/regress/sql/repack.sql +++ b/regress/sql/repack.sql @@ -197,17 +197,14 @@ 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 repack_trigger_1 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 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 repack_trigger_1 BEFORE 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(); -\! pg_repack --dbname=contrib_regression --table=trg4 -- -- 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 -- => ERROR \! 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;