diff --git a/bin/pg_repack.c b/bin/pg_repack.c index a64383c..b63281b 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -189,6 +189,7 @@ typedef struct repack_table Oid target_tidx; /* target: toast index OID */ Oid pkid; /* target: PK OID */ Oid ckid; /* target: CK OID */ + Oid temp_oid; /* temp: OID */ const char *create_pktype; /* CREATE TYPE pk */ const char *create_log; /* CREATE TABLE log */ const char *create_trigger; /* CREATE TRIGGER repack_trigger */ @@ -910,6 +911,7 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) c++; // Skip schemaname table.pkid = getoid(res, i, c++); table.ckid = getoid(res, i, c++); + table.temp_oid = InvalidOid; /* filled after creating the temp table */ if (table.pkid == 0) { ereport(WARNING, @@ -1537,6 +1539,14 @@ repack_one_table(repack_table *table, const char *orderby) command(sql.data, 0, NULL); command("COMMIT", 0, NULL); + /* Get OID of the temp table */ + printfStringInfo(&sql, "SELECT 'repack.table_%u'::regclass::oid", + table->target_oid); + res = execute(sql.data, 0, NULL); + table->temp_oid = getoid(res, 0, 0); + Assert(OidIsValid(table->temp_oid)); + CLEARPGRES(res); + /* * 3. Create indexes on temp table. */ @@ -1612,6 +1622,20 @@ repack_one_table(repack_table *table, const char *orderby) goto cleanup; } + /* + * Acquire AccessExclusiveLock on the temp table to prevent concurrent + * operations during swapping relations. + */ + printfStringInfo(&sql, "LOCK TABLE repack.table_%u IN ACCESS EXCLUSIVE MODE", + table->target_oid); + if (!(lock_exclusive(conn2, utoa(table->temp_oid, buffer), + sql.data, false))) + { + elog(WARNING, "lock_exclusive() failed in conn2 for table_%u", + table->target_oid); + goto cleanup; + } + apply_log(conn2, table, 0); params[0] = utoa(table->target_oid, buffer); pgut_command(conn2, "SELECT repack.repack_swap($1)", 1, params); diff --git a/lib/repack.c b/lib/repack.c index 26a937d..32ccc6c 100644 --- a/lib/repack.c +++ b/lib/repack.c @@ -901,6 +901,24 @@ repack_swap(PG_FUNCTION_ARGS) CommandCounterIncrement(); } + /* + * Sanity check if both relations are locked in access exclusive mode + * before swapping these files. + */ +#if PG_VERSION_NUM >= 120000 + { + LOCKTAG tag; + + SET_LOCKTAG_RELATION(tag, MyDatabaseId, oid); + if (!LockHeldByMe(&tag, AccessExclusiveLock)) + elog(ERROR, "must hold access exclusive lock on table \"%s\"", relname); + + SET_LOCKTAG_RELATION(tag, MyDatabaseId, oid2); + if (!LockHeldByMe(&tag, AccessExclusiveLock)) + elog(ERROR, "must hold access exclusive lock on table \"table_%u\"", oid); + } +#endif + /* swap tables. */ swap_heap_or_index_files(oid, oid2); CommandCounterIncrement();