On 6/15/17 12:23, Tom Lane wrote:
> It strikes me that you could rewrite psql's query to just do its own
> catalog search and not bother with the function at all.  It would have
> to know a bit more about the catalog structure than it does now, but not
> that much:
> 
> select pub.pubname from pg_catalog.pg_publication pub
> where puballtables or
>   exists(select 1 from pg_catalog.pg_publication_rel r
>          where r.prpubid = pub.oid and r.prrelid = '%s');

We used to do something like that, but then people complained that that
was not absolutely accurate, because it did not exclude catalog tables
and related things properly.  See commit
2d460179baa8744e9e2a183a5121306596c53fba.  To do this properly, you need
to filter pg_class using is_publishable_class() (hitherto internal C
function).

The way this was originally written was for use by subscriptioncmds.c
fetch_table_list(), which generally only deals with a small number of
publications as the search key and wants to find all the relations.  The
psql use case is exactly the opposite: We start with a relation and want
to find all the publications.  The third use case is that we document
the view pg_publication_tables for general use, so depending on which
search key you start with, you might get terrible performance if you
have a lot of tables.

An academically nice way to write a general query for this would be:

CREATE VIEW pg_publication_tables AS
     SELECT
         p.pubname AS pubname,
         n.nspname AS schemaname,
         c.relname AS tablename,
         c.oid AS relid
     FROM pg_publication p
          JOIN pg_publication_rel pr ON p.oid = pr.prpubid
          JOIN pg_class c ON pr.prrelid = c.oid
          JOIN pg_namespace n ON c.relnamespace = n.oid
     UNION
     SELECT
         p.pubname AS pubname,
         n.nspname AS schemaname,
         c.relname AS tablename,
         c.oid AS relid
     FROM pg_publication p
          JOIN pg_class c ON p.puballtables AND
pg_is_relation_publishable(c.oid)
          JOIN pg_namespace n ON c.relnamespace = n.oid;

But looking at the plans this generates, it will do a sequential scan of
pg_class even if you look for a publication that is not puballtables,
which would suck for the subscriptioncmds.c use case.

We could use the above definition for the documented view and the psql
use case.

We could then create second view that uses the existing definition

CREATE VIEW pg_publication_tables AS
    SELECT
        P.pubname AS pubname,
        N.nspname AS schemaname,
        C.relname AS tablename
    FROM pg_publication P, pg_class C
         JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.oid IN (SELECT relid FROM pg_get_publication_tables(P.pubname));

for use in subscriptioncmds.c.  Or don't use a view for that.  But the
view is useful because we should preserve this interface across versions.

Or we throw away all the views and use custom code everywhere.

Patch for experimentation attached.

Any ideas?


-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From 855aff09b862fab7bf8ca49b3b653b296a124484 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pete...@gmx.net>
Date: Thu, 15 Jun 2017 23:18:39 -0400
Subject: [PATCH] WIP Tweak publication fetching in psql

---
 src/backend/catalog/pg_publication.c | 16 ++++++++++++++++
 src/backend/catalog/system_views.sql | 23 +++++++++++++++++------
 src/bin/psql/describe.c              |  5 ++---
 src/include/catalog/pg_proc.h        |  2 ++
 4 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/src/backend/catalog/pg_publication.c 
b/src/backend/catalog/pg_publication.c
index 17105f4f2c..fccf642605 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -105,6 +105,22 @@ is_publishable_class(Oid relid, Form_pg_class reltuple)
                relid >= FirstNormalObjectId;
 }
 
+Datum
+pg_is_relation_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.
  */
diff --git a/src/backend/catalog/system_views.sql 
b/src/backend/catalog/system_views.sql
index 0fdad0c119..895b4001b7 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -255,12 +255,23 @@ CREATE VIEW pg_stats WITH (security_barrier) AS
 
 CREATE VIEW pg_publication_tables AS
     SELECT
-        P.pubname AS pubname,
-        N.nspname AS schemaname,
-        C.relname AS tablename
-    FROM pg_publication P, pg_class C
-         JOIN pg_namespace N ON (N.oid = C.relnamespace)
-    WHERE C.oid IN (SELECT relid FROM pg_get_publication_tables(P.pubname));
+        p.pubname AS pubname,
+        n.nspname AS schemaname,
+        c.relname AS tablename,
+        c.oid AS relid
+    FROM pg_publication p
+         JOIN pg_publication_rel pr ON p.oid = pr.prpubid
+         JOIN pg_class c ON pr.prrelid = c.oid
+         JOIN pg_namespace n ON c.relnamespace = n.oid
+    UNION
+    SELECT
+        p.pubname AS pubname,
+        n.nspname AS schemaname,
+        c.relname AS tablename,
+        c.oid AS relid
+    FROM pg_publication p
+         JOIN pg_class c ON p.puballtables AND 
pg_is_relation_publishable(c.oid)
+         JOIN pg_namespace n ON c.relnamespace = n.oid;
 
 CREATE VIEW pg_locks AS
     SELECT * FROM pg_lock_status() AS L;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 1c268f0b08..f2c6f1dd75 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2536,9 +2536,8 @@ 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"
+                                                         "SELECT pubname\n"
+                                                         "FROM 
pg_catalog.pg_publication_tables\n"
                                                          "WHERE relid = '%s'\n"
                                                          "ORDER BY 1;",
                                                          oid);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6c44def6e6..c3b461163e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -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 t 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_is_relation_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_is_relation_publishable _null_ _null_ _null_ ));
+DESCR("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_ ));
-- 
2.13.1

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to