Attachments.
>From 7d94ac6e86670e11fdde20cdf8cebb008bbf8901 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@xxxxxxxxxxxxxx> Date: Tue, 7 Jul 2020 18:45:39 -0400 Subject: [PATCH v3 1/2] accept any relkind in LOCK TABLE --- doc/src/sgml/ref/lock.sgml | 15 +++++++++------ src/backend/commands/lockcmds.c | 18 ++++-------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/doc/src/sgml/ref/lock.sgml b/doc/src/sgml/ref/lock.sgml index 4cdfae2279..37881f25ac 100644 --- a/doc/src/sgml/ref/lock.sgml +++ b/doc/src/sgml/ref/lock.sgml @@ -16,7 +16,7 @@ PostgreSQL documentation <refnamediv> <refname>LOCK</refname> - <refpurpose>lock a table</refpurpose> + <refpurpose>lock a named relation (table, etc)</refpurpose> </refnamediv> <refsynopsisdiv> @@ -34,7 +34,9 @@ LOCK [ TABLE ] [ ONLY ] <replaceable class="parameter">name</replaceable> [ * ] <title>Description</title> <para> - <command>LOCK TABLE</command> obtains a table-level lock, waiting + <command>LOCK TABLE</command> obtains a table-level lock on a + relation (table, partitioned table, foreign table, view, + materialized view, index, composite type, sequence), waiting if necessary for any conflicting locks to be released. If <literal>NOWAIT</literal> is specified, <command>LOCK TABLE</command> does not wait to acquire the desired lock: if it @@ -115,17 +117,18 @@ LOCK [ TABLE ] [ ONLY ] <replaceable class="parameter">name</replaceable> [ * ] <term><replaceable class="parameter">name</replaceable></term> <listitem> <para> - The name (optionally schema-qualified) of an existing table to - lock. If <literal>ONLY</literal> is specified before the table name, only that + The name (optionally schema-qualified) of an existing relation to + lock. If <literal>ONLY</literal> is specified before a table name, only that table is locked. If <literal>ONLY</literal> is not specified, the table and all its descendant tables (if any) are locked. Optionally, <literal>*</literal> can be specified after the table name to explicitly indicate that - descendant tables are included. + descendant tables are included. When locking a view, all relations appearing + in the view definition are locked, regardless of <literal>ONLY</literal>. </para> <para> The command <literal>LOCK TABLE a, b;</literal> is equivalent to - <literal>LOCK TABLE a; LOCK TABLE b;</literal>. The tables are locked + <literal>LOCK TABLE a; LOCK TABLE b;</literal>. The relations are locked one-by-one in the order specified in the <command>LOCK TABLE</command> command. </para> diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index d8cafc42bb..215c1a2bd5 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -83,14 +83,6 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, return; /* woops, concurrently dropped; no permissions * check */ - /* Currently, we only allow plain tables or views to be locked */ - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE && - relkind != RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", - rv->relname))); - /* * Make note if a temporary relation has been accessed in this * transaction. @@ -187,11 +179,14 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context) { RangeTblEntry *rte = lfirst(rtable); AclResult aclresult; - Oid relid = rte->relid; char relkind = rte->relkind; char *relname = get_rel_name(relid); + /* ignore all non-relation RTEs */ + if (rte->rtekind != RTE_RELATION) + continue; + /* * The OLD and NEW placeholder entries in the view's rtable are * skipped. @@ -201,11 +196,6 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context) strcmp(rte->eref->aliasname, "new") == 0)) continue; - /* Currently, we only allow plain tables or views to be locked. */ - if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE && - relkind != RELKIND_VIEW) - continue; - /* Check infinite recursion in the view definition. */ if (list_member_oid(context->ancestor_views, relid)) ereport(ERROR, -- 2.20.1
>From 7cd08ce21b4396165b3e878bb1d4009b1285a46b Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@xxxxxxxxxxxxxx> Date: Tue, 7 Jul 2020 19:14:21 -0400 Subject: [PATCH v3 2/2] pg_dump: lock all rels, not just RELKIND_RELATION --- src/bin/pg_dump/pg_backup.h | 2 ++ src/bin/pg_dump/pg_backup_db.c | 64 ++++++++++++++++++++++++++++++++++ src/bin/pg_dump/pg_backup_db.h | 2 ++ src/bin/pg_dump/pg_dump.c | 17 +++++---- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index a6a8e6f2fd..6c79319164 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -198,6 +198,8 @@ typedef struct Archive int minRemoteVersion; /* allowable range */ int maxRemoteVersion; + bool hasGenericLockTable; /* can LOCK TABLE do non-table rels */ + int numWorkers; /* number of parallel processes */ char *sync_snapshot_id; /* sync snapshot id for parallel operation */ diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c index 5ba43441f5..28b2892538 100644 --- a/src/bin/pg_dump/pg_backup_db.c +++ b/src/bin/pg_dump/pg_backup_db.c @@ -531,6 +531,70 @@ EndDBCopyMode(Archive *AHX, const char *tocEntryTag) } } +/* + * Does LOCK TABLE work on non-table relations on this server? + * + * Note: assumes it is called out of any transaction + */ +bool +IsLockTableGeneric(Archive *AHX) +{ + ArchiveHandle *AH = (ArchiveHandle *) AHX; + PGresult *res; + char *sqlstate; + bool retval; + + if (AHX->remoteVersion >= 140000) + return true; + else if (AHX->remoteVersion < 90500) + return false; + + StartTransaction(AHX); + + /* + * Try a LOCK TABLE on a well-known non-table catalog; WRONG_OBJECT_TYPE + * tells us that this server doesn't support locking non-table rels, while + * LOCK_NOT_AVAILABLE and INSUFFICIENT_PRIVILEGE tell us that it does. + * Report anything else as a fatal problem. + */ +#define ERRCODE_INSUFFICIENT_PRIVILEGE "42501" +#define ERRCODE_WRONG_OBJECT_TYPE "42809" +#define ERRCODE_LOCK_NOT_AVAILABLE "55P03" + res = PQexec(AH->connection, + "LOCK TABLE pg_catalog.pg_class_tblspc_relfilenode_index IN ACCESS SHARE MODE NOWAIT"); + switch (PQresultStatus(res)) + { + case PGRES_COMMAND_OK: + retval = true; + break; + case PGRES_FATAL_ERROR: + sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); + if (strcmp(sqlstate, ERRCODE_WRONG_OBJECT_TYPE) == 0) + { + retval = false; + break; + } + else if (strcmp(sqlstate, ERRCODE_LOCK_NOT_AVAILABLE) == 0 || + strcmp(sqlstate, ERRCODE_INSUFFICIENT_PRIVILEGE) == 0) + { + retval = true; + break; + } + /* else, falls through */ + default: + warn_or_exit_horribly(AH, "LOCK TABLE failed for \"%s\": %s", + "pg_catalog.pg_class_tblspc_relfilenode_index", + PQerrorMessage(AH->connection)); + retval = false; /* not reached */ + break; + } + PQclear(res); + + CommitTransaction(AHX); + + return retval; +} + void StartTransaction(Archive *AHX) { diff --git a/src/bin/pg_dump/pg_backup_db.h b/src/bin/pg_dump/pg_backup_db.h index 8888dd34b9..355beec1f7 100644 --- a/src/bin/pg_dump/pg_backup_db.h +++ b/src/bin/pg_dump/pg_backup_db.h @@ -20,6 +20,8 @@ extern PGresult *ExecuteSqlQueryForSingleRow(Archive *fout, const char *query); extern void EndDBCopyMode(Archive *AHX, const char *tocEntryTag); +extern bool IsLockTableGeneric(Archive *AHX); + extern void StartTransaction(Archive *AHX); extern void CommitTransaction(Archive *AHX); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ff45e3fb8c..03f9d4d9e8 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1176,6 +1176,9 @@ setup_connection(Archive *AH, const char *dumpencoding, ExecuteSqlStatement(AH, "SET row_security = off"); } + /* Detect whether LOCK TABLE can handle non-table relations */ + AH->hasGenericLockTable = IsLockTableGeneric(AH); + /* * Start transaction-snapshot mode transaction to dump consistent data. */ @@ -6843,16 +6846,16 @@ getTables(Archive *fout, int *numTables) * assume our lock on the child is enough to prevent schema * alterations to parent tables. * - * NOTE: it'd be kinda nice to lock other relations too, not only - * plain or partitioned tables, but the backend doesn't presently - * allow that. - * - * We only need to lock the table for certain components; see + * We only need to lock the relation for certain components; see * pg_dump.h + * + * On server versions that support it, we lock all relations not just + * plain tables. */ if (tblinfo[i].dobj.dump && - (tblinfo[i].relkind == RELKIND_RELATION || - tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && + (fout->hasGenericLockTable || + tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE || + tblinfo[i].relkind == RELKIND_RELATION) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); -- 2.20.1