Tweak publication fetching in psql

Viewing a table with \d in psql also shows the publications at table is
in.  If a publication is concurrently dropped, this shows an error,
because the view pg_publication_tables internally uses
pg_get_publication_tables(), which uses a catalog snapshot.  This can be
particularly annoying if a for-all-tables publication is concurrently
dropped.

To avoid that, write the query in psql differently.  Expose the function
pg_relation_is_publishable() to SQL and write the query using that.
That still has a risk of being affected by concurrent catalog changes,
but in this case it would be a table drop that causes problems, and then
the psql \d command wouldn't be interesting anymore anyway.

Reported-by: Tom Lane <tgl@sss.pgh.pa.us>
This commit is contained in:
Peter Eisentraut 2017-06-20 12:25:07 -04:00
parent 20d7d68b09
commit a2141c42f9
4 changed files with 36 additions and 6 deletions

View File

@ -105,6 +105,30 @@ is_publishable_class(Oid relid, Form_pg_class reltuple)
relid >= FirstNormalObjectId;
}
/*
* SQL-callable variant of the above
*
* This returns null when the relation does not exist. This is intended to be
* used for example in psql to avoid gratuitous errors when there are
* concurrent catalog changes.
*/
Datum
pg_relation_is_publishable(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
HeapTuple tuple;
bool result;
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!tuple)
PG_RETURN_NULL();
result = is_publishable_class(relid, (Form_pg_class) GETSTRUCT(tuple));
ReleaseSysCache(tuple);
PG_RETURN_BOOL(result);
}
/*
* Insert new publication / relation mapping.
*/

View File

@ -2536,12 +2536,16 @@ describeOneTableDetails(const char *schemaname,
if (pset.sversion >= 100000)
{
printfPQExpBuffer(&buf,
"SELECT pub.pubname\n"
" FROM pg_catalog.pg_publication pub,\n"
" pg_catalog.pg_get_publication_tables(pub.pubname)\n"
"WHERE relid = '%s'\n"
"SELECT pubname\n"
"FROM pg_catalog.pg_publication p\n"
"JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
"WHERE pr.prrelid = '%s'\n"
"UNION ALL\n"
"SELECT pubname\n"
"FROM pg_catalog.pg_publication p\n"
"WHERE p.puballtables AND pg_relation_is_publishable('%s')\n"
"ORDER BY 1;",
oid);
oid, oid);
result = PSQLexec(buf.data);
if (!result)

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201706201
#define CATALOG_VERSION_NO 201706202
#endif

View File

@ -5436,6 +5436,8 @@ DESCR("get progress for all replication origins");
/* publications */
DATA(insert OID = 6119 ( pg_get_publication_tables PGNSP PGUID 12 1 1000 0 0 f f f f t t s s 1 0 26 "25" "{25,26}" "{i,o}" "{pubname,relid}" _null_ _null_ pg_get_publication_tables _null_ _null_ _null_ ));
DESCR("get OIDs of tables in a publication");
DATA(insert OID = 6121 ( pg_relation_is_publishable PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 16 "2205" _null_ _null_ _null_ _null_ _null_ pg_relation_is_publishable _null_ _null_ _null_ ));
DESCR("returns whether a relation can be part of a publication");
/* rls */
DATA(insert OID = 3298 ( row_security_active PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 16 "26" _null_ _null_ _null_ _null_ _null_ row_security_active _null_ _null_ _null_ ));