LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_publication.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 99.4 % 545 542
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 38 38
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 90.2 % 336 303

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pg_publication.c
       4                 :             :  *      publication C API manipulation
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  * IDENTIFICATION
      10                 :             :  *      src/backend/catalog/pg_publication.c
      11                 :             :  *
      12                 :             :  *-------------------------------------------------------------------------
      13                 :             :  */
      14                 :             : 
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include "access/genam.h"
      18                 :             : #include "access/heapam.h"
      19                 :             : #include "access/htup_details.h"
      20                 :             : #include "access/tableam.h"
      21                 :             : #include "catalog/catalog.h"
      22                 :             : #include "catalog/dependency.h"
      23                 :             : #include "catalog/indexing.h"
      24                 :             : #include "catalog/namespace.h"
      25                 :             : #include "catalog/objectaddress.h"
      26                 :             : #include "catalog/partition.h"
      27                 :             : #include "catalog/pg_inherits.h"
      28                 :             : #include "catalog/pg_namespace.h"
      29                 :             : #include "catalog/pg_publication.h"
      30                 :             : #include "catalog/pg_publication_namespace.h"
      31                 :             : #include "catalog/pg_publication_rel.h"
      32                 :             : #include "catalog/pg_type.h"
      33                 :             : #include "commands/publicationcmds.h"
      34                 :             : #include "funcapi.h"
      35                 :             : #include "utils/array.h"
      36                 :             : #include "utils/builtins.h"
      37                 :             : #include "utils/catcache.h"
      38                 :             : #include "utils/fmgroids.h"
      39                 :             : #include "utils/lsyscache.h"
      40                 :             : #include "utils/rel.h"
      41                 :             : #include "utils/syscache.h"
      42                 :             : 
      43                 :             : /* Records association between publication and published table */
      44                 :             : typedef struct
      45                 :             : {
      46                 :             :     Oid         relid;          /* OID of published table */
      47                 :             :     Oid         pubid;          /* OID of publication that publishes this
      48                 :             :                                  * table. */
      49                 :             : } published_rel;
      50                 :             : 
      51                 :             : /*
      52                 :             :  * Check if relation can be in given publication and throws appropriate
      53                 :             :  * error if not.
      54                 :             :  */
      55                 :             : static void
      56                 :         812 : check_publication_add_relation(PublicationRelInfo *pri)
      57                 :             : {
      58                 :         812 :     Relation    targetrel = pri->relation;
      59                 :             :     const char *relname;
      60                 :             :     const char *errormsg;
      61                 :             : 
      62         [ +  + ]:         812 :     if (pri->except)
      63                 :             :     {
      64                 :          81 :         relname = RelationGetQualifiedRelationName(targetrel);
      65                 :          81 :         errormsg = gettext_noop("cannot specify relation \"%s\" in the publication EXCEPT clause");
      66                 :             :     }
      67                 :             :     else
      68                 :             :     {
      69                 :         731 :         relname = RelationGetRelationName(targetrel);
      70                 :         731 :         errormsg = gettext_noop("cannot add relation \"%s\" to publication");
      71                 :             :     }
      72                 :             : 
      73                 :             :     /* If in EXCEPT clause, must be root partitioned table */
      74   [ +  +  +  + ]:         812 :     if (pri->except && targetrel->rd_rel->relispartition)
      75         [ +  - ]:           4 :         ereport(ERROR,
      76                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      77                 :             :                  errmsg(errormsg, relname),
      78                 :             :                  errdetail("This operation is not supported for individual partitions.")));
      79                 :             : 
      80                 :             :     /* Must be a regular or partitioned table */
      81         [ +  + ]:         808 :     if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
      82         [ +  + ]:         115 :         RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE)
      83         [ +  - ]:           9 :         ereport(ERROR,
      84                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      85                 :             :                  errmsg(errormsg, relname),
      86                 :             :                  errdetail_relkind_not_supported(RelationGetForm(targetrel)->relkind)));
      87                 :             : 
      88                 :             :     /* Can't be system table */
      89         [ +  + ]:         799 :     if (IsCatalogRelation(targetrel))
      90         [ +  - ]:           4 :         ereport(ERROR,
      91                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      92                 :             :                  errmsg(errormsg, relname),
      93                 :             :                  errdetail("This operation is not supported for system tables.")));
      94                 :             : 
      95                 :             :     /* Can't be conflict log table */
      96         [ +  + ]:         795 :     if (IsConflictLogTableNamespace(RelationGetNamespace(targetrel)))
      97         [ +  - ]:           4 :         ereport(ERROR,
      98                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      99                 :             :                  errmsg(errormsg, relname),
     100                 :             :                  errdetail("This operation is not supported for conflict log tables.")));
     101                 :             : 
     102                 :             :     /* UNLOGGED and TEMP relations cannot be part of publication. */
     103         [ +  + ]:         791 :     if (targetrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
     104         [ +  - ]:           4 :         ereport(ERROR,
     105                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     106                 :             :                  errmsg(errormsg, relname),
     107                 :             :                  errdetail("This operation is not supported for temporary tables.")));
     108         [ +  + ]:         787 :     else if (targetrel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
     109         [ +  - ]:           4 :         ereport(ERROR,
     110                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     111                 :             :                  errmsg(errormsg, relname),
     112                 :             :                  errdetail("This operation is not supported for unlogged tables.")));
     113                 :         783 : }
     114                 :             : 
     115                 :             : /*
     116                 :             :  * Check if schema can be in given publication and throw appropriate error if
     117                 :             :  * not.
     118                 :             :  */
     119                 :             : static void
     120                 :         163 : check_publication_add_schema(Oid schemaid)
     121                 :             : {
     122                 :             :     /* Can't be system namespace */
     123   [ +  +  +  -  :         322 :     if (IsCatalogNamespace(schemaid) || IsToastNamespace(schemaid) ||
                   -  + ]
     124                 :         159 :         IsConflictLogTableNamespace(schemaid))
     125         [ +  - ]:           4 :         ereport(ERROR,
     126                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     127                 :             :                  errmsg("cannot add schema \"%s\" to publication",
     128                 :             :                         get_namespace_name(schemaid)),
     129                 :             :                  errdetail("This operation is not supported for system schemas.")));
     130                 :             : 
     131                 :             :     /* Can't be temporary namespace */
     132         [ -  + ]:         159 :     if (isAnyTempNamespace(schemaid))
     133         [ #  # ]:           0 :         ereport(ERROR,
     134                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     135                 :             :                  errmsg("cannot add schema \"%s\" to publication",
     136                 :             :                         get_namespace_name(schemaid)),
     137                 :             :                  errdetail("Temporary schemas cannot be replicated.")));
     138                 :         159 : }
     139                 :             : 
     140                 :             : /*
     141                 :             :  * Returns if relation represented by oid and Form_pg_class entry
     142                 :             :  * is publishable.
     143                 :             :  *
     144                 :             :  * Does same checks as check_publication_add_relation() above except for
     145                 :             :  * RELKIND_SEQUENCE, but does not need relation to be opened and also does
     146                 :             :  * not throw errors. Here, the additional check is to support ALL SEQUENCES
     147                 :             :  * publication.
     148                 :             :  *
     149                 :             :  * XXX  This also excludes all tables with relid < FirstNormalObjectId,
     150                 :             :  * ie all tables created during initdb.  This mainly affects the preinstalled
     151                 :             :  * information_schema.  IsCatalogRelationOid() only excludes tables with
     152                 :             :  * relid < FirstUnpinnedObjectId, making that test rather redundant,
     153                 :             :  * but really we should get rid of the FirstNormalObjectId test not
     154                 :             :  * IsCatalogRelationOid.  We can't do so today because we don't want
     155                 :             :  * information_schema tables to be considered publishable; but this test
     156                 :             :  * is really inadequate for that, since the information_schema could be
     157                 :             :  * dropped and reloaded and then it'll be considered publishable.  The best
     158                 :             :  * long-term solution may be to add a "relispublishable" bool to pg_class,
     159                 :             :  * and depend on that instead of OID checks.  IsConflictLogTableClass()
     160                 :             :  * excludes tables in conflict schema.
     161                 :             :  */
     162                 :             : static bool
     163                 :      323470 : is_publishable_class(Oid relid, Form_pg_class reltuple)
     164                 :             : {
     165                 :      331740 :     return (reltuple->relkind == RELKIND_RELATION ||
     166         [ +  + ]:        8270 :             reltuple->relkind == RELKIND_PARTITIONED_TABLE ||
     167         [ +  + ]:        7264 :             reltuple->relkind == RELKIND_SEQUENCE) &&
     168         [ +  + ]:      317577 :         !IsCatalogRelationOid(relid) &&
     169         [ +  + ]:      312232 :         !IsConflictLogTableClass(reltuple) &&
     170   [ +  +  +  +  :      646940 :         reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
                   +  + ]
     171                 :             :         relid >= FirstNormalObjectId;
     172                 :             : }
     173                 :             : 
     174                 :             : /*
     175                 :             :  * Another variant of is_publishable_class(), taking a Relation.
     176                 :             :  */
     177                 :             : bool
     178                 :      297959 : is_publishable_relation(Relation rel)
     179                 :             : {
     180                 :      297959 :     return is_publishable_class(RelationGetRelid(rel), rel->rd_rel);
     181                 :             : }
     182                 :             : 
     183                 :             : /*
     184                 :             :  * Similar to is_publishable_class() but checks whether the given OID
     185                 :             :  * is a publishable "table" or not.
     186                 :             :  */
     187                 :             : static bool
     188                 :         546 : is_publishable_table(Oid tableoid)
     189                 :             : {
     190                 :             :     HeapTuple   tuple;
     191                 :             :     Form_pg_class relform;
     192                 :             : 
     193                 :         546 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(tableoid));
     194         [ +  + ]:         546 :     if (!HeapTupleIsValid(tuple))
     195                 :           4 :         return false;
     196                 :             : 
     197                 :         542 :     relform = (Form_pg_class) GETSTRUCT(tuple);
     198                 :             : 
     199                 :             :     /*
     200                 :             :      * is_publishable_class() includes sequences, so we need to explicitly
     201                 :             :      * check the relkind to filter them out here.
     202                 :             :      */
     203   [ +  -  +  + ]:        1084 :     if (relform->relkind != RELKIND_SEQUENCE &&
     204                 :         542 :         is_publishable_class(tableoid, relform))
     205                 :             :     {
     206                 :         538 :         ReleaseSysCache(tuple);
     207                 :         538 :         return true;
     208                 :             :     }
     209                 :             : 
     210                 :           4 :     ReleaseSysCache(tuple);
     211                 :           4 :     return false;
     212                 :             : }
     213                 :             : 
     214                 :             : /*
     215                 :             :  * SQL-callable variant of the above
     216                 :             :  *
     217                 :             :  * This returns null when the relation does not exist.  This is intended to be
     218                 :             :  * used for example in psql to avoid gratuitous errors when there are
     219                 :             :  * concurrent catalog changes.
     220                 :             :  */
     221                 :             : Datum
     222                 :        4292 : pg_relation_is_publishable(PG_FUNCTION_ARGS)
     223                 :             : {
     224                 :        4292 :     Oid         relid = PG_GETARG_OID(0);
     225                 :             :     HeapTuple   tuple;
     226                 :             :     bool        result;
     227                 :             : 
     228                 :        4292 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
     229         [ -  + ]:        4292 :     if (!HeapTupleIsValid(tuple))
     230                 :           0 :         PG_RETURN_NULL();
     231                 :        4292 :     result = is_publishable_class(relid, (Form_pg_class) GETSTRUCT(tuple));
     232                 :        4292 :     ReleaseSysCache(tuple);
     233                 :        4292 :     PG_RETURN_BOOL(result);
     234                 :             : }
     235                 :             : 
     236                 :             : /*
     237                 :             :  * Returns true if the ancestor is in the list of published relations.
     238                 :             :  * Otherwise, returns false.
     239                 :             :  */
     240                 :             : static bool
     241                 :          61 : is_ancestor_member_tableinfos(Oid ancestor, List *table_infos)
     242                 :             : {
     243                 :             :     ListCell   *lc;
     244                 :             : 
     245   [ +  -  +  +  :         273 :     foreach(lc, table_infos)
                   +  + ]
     246                 :             :     {
     247                 :         242 :         Oid         relid = ((published_rel *) lfirst(lc))->relid;
     248                 :             : 
     249         [ +  + ]:         242 :         if (relid == ancestor)
     250                 :          30 :             return true;
     251                 :             :     }
     252                 :             : 
     253                 :          31 :     return false;
     254                 :             : }
     255                 :             : 
     256                 :             : /*
     257                 :             :  * Filter out the partitions whose parent tables are also present in the list.
     258                 :             :  */
     259                 :             : static void
     260                 :         167 : filter_partitions(List *table_infos)
     261                 :             : {
     262                 :             :     ListCell   *lc;
     263                 :             : 
     264   [ +  +  +  +  :         395 :     foreach(lc, table_infos)
                   +  + ]
     265                 :             :     {
     266                 :         228 :         bool        skip = false;
     267                 :         228 :         List       *ancestors = NIL;
     268                 :             :         ListCell   *lc2;
     269                 :         228 :         published_rel *table_info = (published_rel *) lfirst(lc);
     270                 :             : 
     271         [ +  + ]:         228 :         if (get_rel_relispartition(table_info->relid))
     272                 :          61 :             ancestors = get_partition_ancestors(table_info->relid);
     273                 :             : 
     274   [ +  +  +  +  :         259 :         foreach(lc2, ancestors)
                   +  + ]
     275                 :             :         {
     276                 :          61 :             Oid         ancestor = lfirst_oid(lc2);
     277                 :             : 
     278         [ +  + ]:          61 :             if (is_ancestor_member_tableinfos(ancestor, table_infos))
     279                 :             :             {
     280                 :          30 :                 skip = true;
     281                 :          30 :                 break;
     282                 :             :             }
     283                 :             :         }
     284                 :             : 
     285         [ +  + ]:         228 :         if (skip)
     286                 :          30 :             table_infos = foreach_delete_current(table_infos, lc);
     287                 :             :     }
     288                 :         167 : }
     289                 :             : 
     290                 :             : /*
     291                 :             :  * Returns true if any schema is associated with the publication, false if no
     292                 :             :  * schema is associated with the publication.
     293                 :             :  */
     294                 :             : bool
     295                 :         215 : is_schema_publication(Oid pubid)
     296                 :             : {
     297                 :             :     Relation    pubschsrel;
     298                 :             :     ScanKeyData scankey;
     299                 :             :     SysScanDesc scan;
     300                 :             :     HeapTuple   tup;
     301                 :         215 :     bool        result = false;
     302                 :             : 
     303                 :         215 :     pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
     304                 :         215 :     ScanKeyInit(&scankey,
     305                 :             :                 Anum_pg_publication_namespace_pnpubid,
     306                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     307                 :             :                 ObjectIdGetDatum(pubid));
     308                 :             : 
     309                 :         215 :     scan = systable_beginscan(pubschsrel,
     310                 :             :                               PublicationNamespacePnnspidPnpubidIndexId,
     311                 :             :                               true, NULL, 1, &scankey);
     312                 :         215 :     tup = systable_getnext(scan);
     313                 :         215 :     result = HeapTupleIsValid(tup);
     314                 :             : 
     315                 :         215 :     systable_endscan(scan);
     316                 :         215 :     table_close(pubschsrel, AccessShareLock);
     317                 :             : 
     318                 :         215 :     return result;
     319                 :             : }
     320                 :             : 
     321                 :             : /*
     322                 :             :  * Returns true if the publication has explicitly included relation (i.e.,
     323                 :             :  * not marked as EXCEPT).
     324                 :             :  */
     325                 :             : bool
     326                 :          49 : is_table_publication(Oid pubid)
     327                 :             : {
     328                 :             :     Relation    pubrelsrel;
     329                 :             :     ScanKeyData scankey;
     330                 :             :     SysScanDesc scan;
     331                 :             :     HeapTuple   tup;
     332                 :          49 :     bool        result = false;
     333                 :             : 
     334                 :          49 :     pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
     335                 :          49 :     ScanKeyInit(&scankey,
     336                 :             :                 Anum_pg_publication_rel_prpubid,
     337                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     338                 :             :                 ObjectIdGetDatum(pubid));
     339                 :             : 
     340                 :          49 :     scan = systable_beginscan(pubrelsrel,
     341                 :             :                               PublicationRelPrpubidIndexId,
     342                 :             :                               true, NULL, 1, &scankey);
     343                 :          49 :     tup = systable_getnext(scan);
     344         [ +  + ]:          49 :     if (HeapTupleIsValid(tup))
     345                 :             :     {
     346                 :             :         Form_pg_publication_rel pubrel;
     347                 :             : 
     348                 :          25 :         pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
     349                 :             : 
     350                 :             :         /*
     351                 :             :          * For any publication, pg_publication_rel contains either only EXCEPT
     352                 :             :          * entries or only explicitly included tables. Therefore, examining
     353                 :             :          * the first tuple is sufficient to determine table inclusion.
     354                 :             :          */
     355                 :          25 :         result = !pubrel->prexcept;
     356                 :             :     }
     357                 :             : 
     358                 :          49 :     systable_endscan(scan);
     359                 :          49 :     table_close(pubrelsrel, AccessShareLock);
     360                 :             : 
     361                 :          49 :     return result;
     362                 :             : }
     363                 :             : 
     364                 :             : /*
     365                 :             :  * Returns true if the relation has column list associated with the
     366                 :             :  * publication, false otherwise.
     367                 :             :  *
     368                 :             :  * If a column list is found, the corresponding bitmap is returned through the
     369                 :             :  * cols parameter, if provided. The bitmap is constructed within the given
     370                 :             :  * memory context (mcxt).
     371                 :             :  */
     372                 :             : bool
     373                 :         911 : check_and_fetch_column_list(Publication *pub, Oid relid, MemoryContext mcxt,
     374                 :             :                             Bitmapset **cols)
     375                 :             : {
     376                 :             :     HeapTuple   cftuple;
     377                 :         911 :     bool        found = false;
     378                 :             : 
     379         [ +  + ]:         911 :     if (pub->alltables)
     380                 :         220 :         return false;
     381                 :             : 
     382                 :         691 :     cftuple = SearchSysCache2(PUBLICATIONRELMAP,
     383                 :             :                               ObjectIdGetDatum(relid),
     384                 :             :                               ObjectIdGetDatum(pub->oid));
     385         [ +  + ]:         691 :     if (HeapTupleIsValid(cftuple))
     386                 :             :     {
     387                 :             :         Datum       cfdatum;
     388                 :             :         bool        isnull;
     389                 :             : 
     390                 :             :         /* Lookup the column list attribute. */
     391                 :         633 :         cfdatum = SysCacheGetAttr(PUBLICATIONRELMAP, cftuple,
     392                 :             :                                   Anum_pg_publication_rel_prattrs, &isnull);
     393                 :             : 
     394                 :             :         /* Was a column list found? */
     395         [ +  + ]:         633 :         if (!isnull)
     396                 :             :         {
     397                 :             :             /* Build the column list bitmap in the given memory context. */
     398         [ +  + ]:         191 :             if (cols)
     399                 :         188 :                 *cols = pub_collist_to_bitmapset(*cols, cfdatum, mcxt);
     400                 :             : 
     401                 :         191 :             found = true;
     402                 :             :         }
     403                 :             : 
     404                 :         633 :         ReleaseSysCache(cftuple);
     405                 :             :     }
     406                 :             : 
     407                 :         691 :     return found;
     408                 :             : }
     409                 :             : 
     410                 :             : /*
     411                 :             :  * Gets the relations based on the publication partition option for a specified
     412                 :             :  * relation.
     413                 :             :  */
     414                 :             : List *
     415                 :        2499 : GetPubPartitionOptionRelations(List *result, PublicationPartOpt pub_partopt,
     416                 :             :                                Oid relid)
     417                 :             : {
     418   [ +  +  +  + ]:        2499 :     if (get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE &&
     419                 :             :         pub_partopt != PUBLICATION_PART_ROOT)
     420                 :         701 :     {
     421                 :         701 :         List       *all_parts = find_all_inheritors(relid, NoLock,
     422                 :             :                                                     NULL);
     423                 :             : 
     424         [ +  + ]:         701 :         if (pub_partopt == PUBLICATION_PART_ALL)
     425                 :         682 :             result = list_concat(result, all_parts);
     426         [ +  - ]:          19 :         else if (pub_partopt == PUBLICATION_PART_LEAF)
     427                 :             :         {
     428                 :             :             ListCell   *lc;
     429                 :             : 
     430   [ +  -  +  +  :          69 :             foreach(lc, all_parts)
                   +  + ]
     431                 :             :             {
     432                 :          50 :                 Oid         partOid = lfirst_oid(lc);
     433                 :             : 
     434         [ +  + ]:          50 :                 if (get_rel_relkind(partOid) != RELKIND_PARTITIONED_TABLE)
     435                 :          30 :                     result = lappend_oid(result, partOid);
     436                 :             :             }
     437                 :             :         }
     438                 :             :         else
     439                 :             :             Assert(false);
     440                 :             :     }
     441                 :             :     else
     442                 :        1798 :         result = lappend_oid(result, relid);
     443                 :             : 
     444                 :        2499 :     return result;
     445                 :             : }
     446                 :             : 
     447                 :             : /*
     448                 :             :  * Returns the relid of the topmost ancestor that is published via this
     449                 :             :  * publication if any and set its ancestor level to ancestor_level,
     450                 :             :  * otherwise returns InvalidOid.
     451                 :             :  *
     452                 :             :  * The ancestor_level value allows us to compare the results for multiple
     453                 :             :  * publications, and decide which value is higher up.
     454                 :             :  *
     455                 :             :  * Note that the list of ancestors should be ordered such that the topmost
     456                 :             :  * ancestor is at the end of the list.
     457                 :             :  */
     458                 :             : Oid
     459                 :         426 : GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level)
     460                 :             : {
     461                 :             :     ListCell   *lc;
     462                 :         426 :     Oid         topmost_relid = InvalidOid;
     463                 :         426 :     int         level = 0;
     464                 :             : 
     465                 :             :     /*
     466                 :             :      * Find the "topmost" ancestor that is in this publication.
     467                 :             :      */
     468   [ +  -  +  +  :         862 :     foreach(lc, ancestors)
                   +  + ]
     469                 :             :     {
     470                 :         436 :         Oid         ancestor = lfirst_oid(lc);
     471                 :         436 :         List       *apubids = GetRelationIncludedPublications(ancestor);
     472                 :         436 :         List       *aschemaPubids = NIL;
     473                 :             : 
     474                 :         436 :         level++;
     475                 :             : 
     476         [ +  + ]:         436 :         if (list_member_oid(apubids, puboid))
     477                 :             :         {
     478                 :         223 :             topmost_relid = ancestor;
     479                 :             : 
     480         [ +  + ]:         223 :             if (ancestor_level)
     481                 :          43 :                 *ancestor_level = level;
     482                 :             :         }
     483                 :             :         else
     484                 :             :         {
     485                 :         213 :             aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
     486         [ +  + ]:         213 :             if (list_member_oid(aschemaPubids, puboid))
     487                 :             :             {
     488                 :          13 :                 topmost_relid = ancestor;
     489                 :             : 
     490         [ +  + ]:          13 :                 if (ancestor_level)
     491                 :           5 :                     *ancestor_level = level;
     492                 :             :             }
     493                 :             :         }
     494                 :             : 
     495                 :         436 :         list_free(apubids);
     496                 :         436 :         list_free(aschemaPubids);
     497                 :             :     }
     498                 :             : 
     499                 :         426 :     return topmost_relid;
     500                 :             : }
     501                 :             : 
     502                 :             : /*
     503                 :             :  * attnumstoint2vector
     504                 :             :  *      Convert a Bitmapset of AttrNumbers into an int2vector.
     505                 :             :  *
     506                 :             :  * AttrNumber numbers are 0-based, i.e., not offset by
     507                 :             :  * FirstLowInvalidHeapAttributeNumber.
     508                 :             :  */
     509                 :             : static int2vector *
     510                 :         212 : attnumstoint2vector(Bitmapset *attrs)
     511                 :             : {
     512                 :             :     int2vector *result;
     513                 :         212 :     int         n = bms_num_members(attrs);
     514                 :         212 :     int         i = -1;
     515                 :         212 :     int         j = 0;
     516                 :             : 
     517                 :         212 :     result = buildint2vector(NULL, n);
     518                 :             : 
     519         [ +  + ]:         577 :     while ((i = bms_next_member(attrs, i)) >= 0)
     520                 :             :     {
     521                 :             :         Assert(i <= PG_INT16_MAX);
     522                 :             : 
     523                 :         365 :         result->values[j++] = (int16) i;
     524                 :             :     }
     525                 :             : 
     526                 :         212 :     return result;
     527                 :             : }
     528                 :             : 
     529                 :             : /*
     530                 :             :  * Insert new publication / relation mapping.
     531                 :             :  */
     532                 :             : ObjectAddress
     533                 :         834 : publication_add_relation(Oid pubid, PublicationRelInfo *pri,
     534                 :             :                          bool if_not_exists, AlterPublicationStmt *alter_stmt)
     535                 :             : {
     536                 :             :     Relation    rel;
     537                 :             :     HeapTuple   tup;
     538                 :             :     Datum       values[Natts_pg_publication_rel];
     539                 :             :     bool        nulls[Natts_pg_publication_rel];
     540                 :         834 :     Relation    targetrel = pri->relation;
     541                 :         834 :     Oid         relid = RelationGetRelid(targetrel);
     542                 :             :     Oid         pubreloid;
     543                 :             :     Bitmapset  *attnums;
     544                 :         834 :     Publication *pub = GetPublication(pubid);
     545                 :             :     ObjectAddress myself,
     546                 :             :                 referenced;
     547                 :         834 :     List       *relids = NIL;
     548                 :             :     int         i;
     549                 :             :     bool        inval_except_table;
     550                 :             : 
     551                 :         834 :     rel = table_open(PublicationRelRelationId, RowExclusiveLock);
     552                 :             : 
     553                 :             :     /*
     554                 :             :      * Check for duplicates. Note that this does not really prevent
     555                 :             :      * duplicates, it's here just to provide nicer error message in common
     556                 :             :      * case. The real protection is the unique key on the catalog.
     557                 :             :      */
     558         [ +  + ]:         834 :     if (SearchSysCacheExists2(PUBLICATIONRELMAP, ObjectIdGetDatum(relid),
     559                 :             :                               ObjectIdGetDatum(pubid)))
     560                 :             :     {
     561                 :          22 :         table_close(rel, RowExclusiveLock);
     562                 :             : 
     563         [ +  + ]:          22 :         if (if_not_exists)
     564                 :          18 :             return InvalidObjectAddress;
     565                 :             : 
     566         [ +  - ]:           4 :         ereport(ERROR,
     567                 :             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     568                 :             :                  errmsg("relation \"%s\" is already member of publication \"%s\"",
     569                 :             :                         RelationGetRelationName(targetrel), pub->name)));
     570                 :             :     }
     571                 :             : 
     572                 :         812 :     check_publication_add_relation(pri);
     573                 :             : 
     574                 :             :     /* Validate and translate column names into a Bitmapset of attnums. */
     575                 :         783 :     attnums = pub_collist_validate(pri->relation, pri->columns);
     576                 :             : 
     577                 :             :     /* Form a tuple. */
     578                 :         767 :     memset(values, 0, sizeof(values));
     579                 :         767 :     memset(nulls, false, sizeof(nulls));
     580                 :             : 
     581                 :         767 :     pubreloid = GetNewOidWithIndex(rel, PublicationRelObjectIndexId,
     582                 :             :                                    Anum_pg_publication_rel_oid);
     583                 :         767 :     values[Anum_pg_publication_rel_oid - 1] = ObjectIdGetDatum(pubreloid);
     584                 :         767 :     values[Anum_pg_publication_rel_prpubid - 1] =
     585                 :         767 :         ObjectIdGetDatum(pubid);
     586                 :         767 :     values[Anum_pg_publication_rel_prrelid - 1] =
     587                 :         767 :         ObjectIdGetDatum(relid);
     588                 :         767 :     values[Anum_pg_publication_rel_prexcept - 1] =
     589                 :         767 :         BoolGetDatum(pri->except);
     590                 :             : 
     591                 :             :     /* Add qualifications, if available */
     592         [ +  + ]:         767 :     if (pri->whereClause != NULL)
     593                 :         220 :         values[Anum_pg_publication_rel_prqual - 1] = CStringGetTextDatum(nodeToString(pri->whereClause));
     594                 :             :     else
     595                 :         547 :         nulls[Anum_pg_publication_rel_prqual - 1] = true;
     596                 :             : 
     597                 :             :     /* Add column list, if available */
     598         [ +  + ]:         767 :     if (pri->columns)
     599                 :         212 :         values[Anum_pg_publication_rel_prattrs - 1] = PointerGetDatum(attnumstoint2vector(attnums));
     600                 :             :     else
     601                 :         555 :         nulls[Anum_pg_publication_rel_prattrs - 1] = true;
     602                 :             : 
     603                 :         767 :     tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
     604                 :             : 
     605                 :             :     /* Insert tuple into catalog. */
     606                 :         767 :     CatalogTupleInsert(rel, tup);
     607                 :         767 :     heap_freetuple(tup);
     608                 :             : 
     609                 :             :     /* Register dependencies as needed */
     610                 :         767 :     ObjectAddressSet(myself, PublicationRelRelationId, pubreloid);
     611                 :             : 
     612                 :             :     /* Add dependency on the publication */
     613                 :         767 :     ObjectAddressSet(referenced, PublicationRelationId, pubid);
     614                 :         767 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     615                 :             : 
     616                 :             :     /* Add dependency on the relation */
     617                 :         767 :     ObjectAddressSet(referenced, RelationRelationId, relid);
     618                 :         767 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     619                 :             : 
     620                 :             :     /* Add dependency on the objects mentioned in the qualifications */
     621         [ +  + ]:         767 :     if (pri->whereClause)
     622                 :         220 :         recordDependencyOnSingleRelExpr(&myself, pri->whereClause, relid,
     623                 :             :                                         DEPENDENCY_NORMAL, DEPENDENCY_NORMAL,
     624                 :             :                                         false);
     625                 :             : 
     626                 :             :     /* Add dependency on the columns, if any are listed */
     627                 :         767 :     i = -1;
     628         [ +  + ]:        1132 :     while ((i = bms_next_member(attnums, i)) >= 0)
     629                 :             :     {
     630                 :         365 :         ObjectAddressSubSet(referenced, RelationRelationId, relid, i);
     631                 :         365 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     632                 :             :     }
     633                 :             : 
     634                 :             :     /* Close the table. */
     635                 :         767 :     table_close(rel, RowExclusiveLock);
     636                 :             : 
     637                 :             :     /*
     638                 :             :      * Determine whether EXCEPT tables require explicit relcache invalidation.
     639                 :             :      *
     640                 :             :      * For CREATE PUBLICATION with EXCEPT tables, invalidation is skipped
     641                 :             :      * here, as CreatePublication() function invalidates all relations as part
     642                 :             :      * of defining a FOR ALL TABLES publication.
     643                 :             :      *
     644                 :             :      * For ALTER PUBLICATION, invalidation is needed only when adding an
     645                 :             :      * EXCEPT table to a publication already marked as ALL TABLES. For
     646                 :             :      * publications that were originally empty or defined as ALL SEQUENCES and
     647                 :             :      * are being converted to ALL TABLES, invalidation is skipped here, as
     648                 :             :      * AlterPublicationAllFlags() function invalidates all relations while
     649                 :             :      * marking the publication as ALL TABLES publication.
     650                 :             :      */
     651   [ +  +  +  + ]:         776 :     inval_except_table = (alter_stmt != NULL) && pub->alltables &&
     652   [ +  -  +  - ]:           9 :         (alter_stmt->for_all_tables && pri->except);
     653                 :             : 
     654   [ +  +  +  + ]:         767 :     if (!pri->except || inval_except_table)
     655                 :             :     {
     656                 :             :         /*
     657                 :             :          * Invalidate relcache so that publication info is rebuilt.
     658                 :             :          *
     659                 :             :          * For the partitioned tables, we must invalidate all partitions
     660                 :             :          * contained in the respective partition hierarchies, not just the one
     661                 :             :          * explicitly mentioned in the publication. This is required because
     662                 :             :          * we implicitly publish the child tables when the parent table is
     663                 :             :          * published.
     664                 :             :          */
     665                 :         699 :         relids = GetPubPartitionOptionRelations(relids, PUBLICATION_PART_ALL,
     666                 :             :                                                 relid);
     667                 :             : 
     668                 :         699 :         InvalidatePublicationRels(relids);
     669                 :             :     }
     670                 :             : 
     671                 :         767 :     return myself;
     672                 :             : }
     673                 :             : 
     674                 :             : /*
     675                 :             :  * pub_collist_validate
     676                 :             :  *      Process and validate the 'columns' list and ensure the columns are all
     677                 :             :  *      valid to use for a publication.  Checks for and raises an ERROR for
     678                 :             :  *      any unknown columns, system columns, duplicate columns, or virtual
     679                 :             :  *      generated columns.
     680                 :             :  *
     681                 :             :  * Looks up each column's attnum and returns a 0-based Bitmapset of the
     682                 :             :  * corresponding attnums.
     683                 :             :  */
     684                 :             : Bitmapset *
     685                 :        1073 : pub_collist_validate(Relation targetrel, List *columns)
     686                 :             : {
     687                 :        1073 :     Bitmapset  *set = NULL;
     688                 :             :     ListCell   *lc;
     689                 :        1073 :     TupleDesc   tupdesc = RelationGetDescr(targetrel);
     690                 :             : 
     691   [ +  +  +  +  :        1618 :     foreach(lc, columns)
                   +  + ]
     692                 :             :     {
     693                 :         569 :         char       *colname = strVal(lfirst(lc));
     694                 :         569 :         AttrNumber  attnum = get_attnum(RelationGetRelid(targetrel), colname);
     695                 :             : 
     696         [ +  + ]:         569 :         if (attnum == InvalidAttrNumber)
     697         [ +  - ]:           4 :             ereport(ERROR,
     698                 :             :                     errcode(ERRCODE_UNDEFINED_COLUMN),
     699                 :             :                     errmsg("column \"%s\" of relation \"%s\" does not exist",
     700                 :             :                            colname, RelationGetRelationName(targetrel)));
     701                 :             : 
     702         [ +  + ]:         565 :         if (!AttrNumberIsForUserDefinedAttr(attnum))
     703         [ +  - ]:           8 :             ereport(ERROR,
     704                 :             :                     errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
     705                 :             :                     errmsg("cannot use system column \"%s\" in publication column list",
     706                 :             :                            colname));
     707                 :             : 
     708         [ +  + ]:         557 :         if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
     709         [ +  - ]:           4 :             ereport(ERROR,
     710                 :             :                     errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
     711                 :             :                     errmsg("cannot use virtual generated column \"%s\" in publication column list",
     712                 :             :                            colname));
     713                 :             : 
     714         [ +  + ]:         553 :         if (bms_is_member(attnum, set))
     715         [ +  - ]:           8 :             ereport(ERROR,
     716                 :             :                     errcode(ERRCODE_DUPLICATE_OBJECT),
     717                 :             :                     errmsg("duplicate column \"%s\" in publication column list",
     718                 :             :                            colname));
     719                 :             : 
     720                 :         545 :         set = bms_add_member(set, attnum);
     721                 :             :     }
     722                 :             : 
     723                 :        1049 :     return set;
     724                 :             : }
     725                 :             : 
     726                 :             : /*
     727                 :             :  * Transform a column list (represented by an array Datum) to a bitmapset.
     728                 :             :  *
     729                 :             :  * If columns isn't NULL, add the column numbers to that set.
     730                 :             :  *
     731                 :             :  * If mcxt isn't NULL, build the bitmapset in that context.
     732                 :             :  */
     733                 :             : Bitmapset *
     734                 :         278 : pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, MemoryContext mcxt)
     735                 :             : {
     736                 :         278 :     Bitmapset  *result = columns;
     737                 :             :     ArrayType  *arr;
     738                 :             :     int         nelems;
     739                 :             :     int16      *elems;
     740                 :         278 :     MemoryContext oldcxt = NULL;
     741                 :             : 
     742                 :         278 :     arr = DatumGetArrayTypeP(pubcols);
     743                 :         278 :     nelems = ARR_DIMS(arr)[0];
     744         [ -  + ]:         278 :     elems = (int16 *) ARR_DATA_PTR(arr);
     745                 :             : 
     746                 :             :     /* If a memory context was specified, switch to it. */
     747         [ +  + ]:         278 :     if (mcxt)
     748                 :          39 :         oldcxt = MemoryContextSwitchTo(mcxt);
     749                 :             : 
     750         [ +  + ]:         764 :     for (int i = 0; i < nelems; i++)
     751                 :         486 :         result = bms_add_member(result, elems[i]);
     752                 :             : 
     753         [ +  + ]:         278 :     if (mcxt)
     754                 :          39 :         MemoryContextSwitchTo(oldcxt);
     755                 :             : 
     756                 :         278 :     return result;
     757                 :             : }
     758                 :             : 
     759                 :             : /*
     760                 :             :  * Returns a bitmap representing the columns of the specified table.
     761                 :             :  *
     762                 :             :  * Generated columns are included if include_gencols_type is
     763                 :             :  * PUBLISH_GENCOLS_STORED.
     764                 :             :  */
     765                 :             : Bitmapset *
     766                 :           9 : pub_form_cols_map(Relation relation, PublishGencolsType include_gencols_type)
     767                 :             : {
     768                 :           9 :     Bitmapset  *result = NULL;
     769                 :           9 :     TupleDesc   desc = RelationGetDescr(relation);
     770                 :             : 
     771         [ +  + ]:          30 :     for (int i = 0; i < desc->natts; i++)
     772                 :             :     {
     773                 :          21 :         Form_pg_attribute att = TupleDescAttr(desc, i);
     774                 :             : 
     775         [ +  + ]:          21 :         if (att->attisdropped)
     776                 :           1 :             continue;
     777                 :             : 
     778         [ +  + ]:          20 :         if (att->attgenerated)
     779                 :             :         {
     780                 :             :             /* We only support replication of STORED generated cols. */
     781         [ +  + ]:           2 :             if (att->attgenerated != ATTRIBUTE_GENERATED_STORED)
     782                 :           1 :                 continue;
     783                 :             : 
     784                 :             :             /* User hasn't requested to replicate STORED generated cols. */
     785         [ +  - ]:           1 :             if (include_gencols_type != PUBLISH_GENCOLS_STORED)
     786                 :           1 :                 continue;
     787                 :             :         }
     788                 :             : 
     789                 :          18 :         result = bms_add_member(result, att->attnum);
     790                 :             :     }
     791                 :             : 
     792                 :           9 :     return result;
     793                 :             : }
     794                 :             : 
     795                 :             : /*
     796                 :             :  * Insert new publication / schema mapping.
     797                 :             :  */
     798                 :             : ObjectAddress
     799                 :         175 : publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
     800                 :             : {
     801                 :             :     Relation    rel;
     802                 :             :     HeapTuple   tup;
     803                 :             :     Datum       values[Natts_pg_publication_namespace];
     804                 :             :     bool        nulls[Natts_pg_publication_namespace];
     805                 :             :     Oid         psschid;
     806                 :         175 :     Publication *pub = GetPublication(pubid);
     807                 :         175 :     List       *schemaRels = NIL;
     808                 :             :     ObjectAddress myself,
     809                 :             :                 referenced;
     810                 :             : 
     811                 :         175 :     rel = table_open(PublicationNamespaceRelationId, RowExclusiveLock);
     812                 :             : 
     813                 :             :     /*
     814                 :             :      * Check for duplicates. Note that this does not really prevent
     815                 :             :      * duplicates, it's here just to provide nicer error message in common
     816                 :             :      * case. The real protection is the unique key on the catalog.
     817                 :             :      */
     818         [ +  + ]:         175 :     if (SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
     819                 :             :                               ObjectIdGetDatum(schemaid),
     820                 :             :                               ObjectIdGetDatum(pubid)))
     821                 :             :     {
     822                 :          12 :         table_close(rel, RowExclusiveLock);
     823                 :             : 
     824         [ +  + ]:          12 :         if (if_not_exists)
     825                 :           8 :             return InvalidObjectAddress;
     826                 :             : 
     827         [ +  - ]:           4 :         ereport(ERROR,
     828                 :             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     829                 :             :                  errmsg("schema \"%s\" is already member of publication \"%s\"",
     830                 :             :                         get_namespace_name(schemaid), pub->name)));
     831                 :             :     }
     832                 :             : 
     833                 :         163 :     check_publication_add_schema(schemaid);
     834                 :             : 
     835                 :             :     /* Form a tuple */
     836                 :         159 :     memset(values, 0, sizeof(values));
     837                 :         159 :     memset(nulls, false, sizeof(nulls));
     838                 :             : 
     839                 :         159 :     psschid = GetNewOidWithIndex(rel, PublicationNamespaceObjectIndexId,
     840                 :             :                                  Anum_pg_publication_namespace_oid);
     841                 :         159 :     values[Anum_pg_publication_namespace_oid - 1] = ObjectIdGetDatum(psschid);
     842                 :         159 :     values[Anum_pg_publication_namespace_pnpubid - 1] =
     843                 :         159 :         ObjectIdGetDatum(pubid);
     844                 :         159 :     values[Anum_pg_publication_namespace_pnnspid - 1] =
     845                 :         159 :         ObjectIdGetDatum(schemaid);
     846                 :             : 
     847                 :         159 :     tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
     848                 :             : 
     849                 :             :     /* Insert tuple into catalog */
     850                 :         159 :     CatalogTupleInsert(rel, tup);
     851                 :         159 :     heap_freetuple(tup);
     852                 :             : 
     853                 :         159 :     ObjectAddressSet(myself, PublicationNamespaceRelationId, psschid);
     854                 :             : 
     855                 :             :     /* Add dependency on the publication */
     856                 :         159 :     ObjectAddressSet(referenced, PublicationRelationId, pubid);
     857                 :         159 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     858                 :             : 
     859                 :             :     /* Add dependency on the schema */
     860                 :         159 :     ObjectAddressSet(referenced, NamespaceRelationId, schemaid);
     861                 :         159 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     862                 :             : 
     863                 :             :     /* Close the table */
     864                 :         159 :     table_close(rel, RowExclusiveLock);
     865                 :             : 
     866                 :             :     /*
     867                 :             :      * Invalidate relcache so that publication info is rebuilt. See
     868                 :             :      * publication_add_relation for why we need to consider all the
     869                 :             :      * partitions.
     870                 :             :      */
     871                 :         159 :     schemaRels = GetSchemaPublicationRelations(schemaid,
     872                 :             :                                                PUBLICATION_PART_ALL);
     873                 :         159 :     InvalidatePublicationRels(schemaRels);
     874                 :             : 
     875                 :         159 :     return myself;
     876                 :             : }
     877                 :             : 
     878                 :             : /*
     879                 :             :  * Internal function to get the list of publication oids for a relation.
     880                 :             :  *
     881                 :             :  * If except_flag is true, returns the list of publication that specified the
     882                 :             :  * relation in the EXCEPT clause; otherwise, returns the list of publications
     883                 :             :  * in which relation is included.
     884                 :             :  */
     885                 :             : static List *
     886                 :       16780 : get_relation_publications(Oid relid, bool except_flag)
     887                 :             : {
     888                 :       16780 :     List       *result = NIL;
     889                 :             :     CatCList   *pubrellist;
     890                 :             : 
     891                 :             :     /* Find all publications associated with the relation. */
     892                 :       16780 :     pubrellist = SearchSysCacheList1(PUBLICATIONRELMAP,
     893                 :             :                                      ObjectIdGetDatum(relid));
     894         [ +  + ]:       18396 :     for (int i = 0; i < pubrellist->n_members; i++)
     895                 :             :     {
     896                 :        1616 :         HeapTuple   tup = &pubrellist->members[i]->tuple;
     897                 :        1616 :         Form_pg_publication_rel pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
     898                 :        1616 :         Oid         pubid = pubrel->prpubid;
     899                 :             : 
     900         [ +  + ]:        1616 :         if (pubrel->prexcept == except_flag)
     901                 :        1144 :             result = lappend_oid(result, pubid);
     902                 :             :     }
     903                 :             : 
     904                 :       16780 :     ReleaseSysCacheList(pubrellist);
     905                 :             : 
     906                 :       16780 :     return result;
     907                 :             : }
     908                 :             : 
     909                 :             : /*
     910                 :             :  * Gets list of publication oids for a relation.
     911                 :             :  */
     912                 :             : List *
     913                 :        9027 : GetRelationIncludedPublications(Oid relid)
     914                 :             : {
     915                 :        9027 :     return get_relation_publications(relid, false);
     916                 :             : }
     917                 :             : 
     918                 :             : /*
     919                 :             :  * Gets list of publication oids which has relation in the EXCEPT clause.
     920                 :             :  */
     921                 :             : List *
     922                 :        7753 : GetRelationExcludedPublications(Oid relid)
     923                 :             : {
     924                 :        7753 :     return get_relation_publications(relid, true);
     925                 :             : }
     926                 :             : 
     927                 :             : /*
     928                 :             :  * Internal function to get the list of relation oids for a publication.
     929                 :             :  *
     930                 :             :  * If except_flag is true, returns the list of relations specified in the
     931                 :             :  * EXCEPT clause of the publication; otherwise, returns the list of relations
     932                 :             :  * included in the publication.
     933                 :             :  */
     934                 :             : static List *
     935                 :         680 : get_publication_relations(Oid pubid, PublicationPartOpt pub_partopt,
     936                 :             :                           bool except_flag)
     937                 :             : {
     938                 :             :     List       *result;
     939                 :             :     Relation    pubrelsrel;
     940                 :             :     ScanKeyData scankey;
     941                 :             :     SysScanDesc scan;
     942                 :             :     HeapTuple   tup;
     943                 :             : 
     944                 :             :     /* Find all relations associated with the publication. */
     945                 :         680 :     pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
     946                 :             : 
     947                 :         680 :     ScanKeyInit(&scankey,
     948                 :             :                 Anum_pg_publication_rel_prpubid,
     949                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     950                 :             :                 ObjectIdGetDatum(pubid));
     951                 :             : 
     952                 :         680 :     scan = systable_beginscan(pubrelsrel, PublicationRelPrpubidIndexId,
     953                 :             :                               true, NULL, 1, &scankey);
     954                 :             : 
     955                 :         680 :     result = NIL;
     956         [ +  + ]:        2000 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     957                 :             :     {
     958                 :             :         Form_pg_publication_rel pubrel;
     959                 :             : 
     960                 :         640 :         pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
     961                 :             : 
     962         [ -  + ]:         640 :         if (except_flag == pubrel->prexcept)
     963                 :         640 :             result = GetPubPartitionOptionRelations(result, pub_partopt,
     964                 :             :                                                     pubrel->prrelid);
     965                 :             :     }
     966                 :             : 
     967                 :         680 :     systable_endscan(scan);
     968                 :         680 :     table_close(pubrelsrel, AccessShareLock);
     969                 :             : 
     970                 :             :     /* Now sort and de-duplicate the result list */
     971                 :         680 :     list_sort(result, list_oid_cmp);
     972                 :         680 :     list_deduplicate_oid(result);
     973                 :             : 
     974                 :         680 :     return result;
     975                 :             : }
     976                 :             : 
     977                 :             : /*
     978                 :             :  * Gets list of relation oids that are associated with a publication.
     979                 :             :  *
     980                 :             :  * This should only be used FOR TABLE publications, the FOR ALL TABLES/SEQUENCES
     981                 :             :  * should use GetAllPublicationRelations().
     982                 :             :  */
     983                 :             : List *
     984                 :         610 : GetIncludedPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
     985                 :             : {
     986                 :             :     Assert(!GetPublication(pubid)->alltables);
     987                 :             : 
     988                 :         610 :     return get_publication_relations(pubid, pub_partopt, false);
     989                 :             : }
     990                 :             : 
     991                 :             : /*
     992                 :             :  * Gets list of table oids that were specified in the EXCEPT clause for a
     993                 :             :  * publication.
     994                 :             :  *
     995                 :             :  * This should only be used FOR ALL TABLES publications.
     996                 :             :  */
     997                 :             : List *
     998                 :          70 : GetExcludedPublicationTables(Oid pubid, PublicationPartOpt pub_partopt)
     999                 :             : {
    1000                 :             :     Assert(GetPublication(pubid)->alltables);
    1001                 :             : 
    1002                 :          70 :     return get_publication_relations(pubid, pub_partopt, true);
    1003                 :             : }
    1004                 :             : 
    1005                 :             : /*
    1006                 :             :  * Gets list of publication oids for publications marked as FOR ALL TABLES.
    1007                 :             :  */
    1008                 :             : List *
    1009                 :        6056 : GetAllTablesPublications(void)
    1010                 :             : {
    1011                 :             :     List       *result;
    1012                 :             :     Relation    rel;
    1013                 :             :     ScanKeyData scankey;
    1014                 :             :     SysScanDesc scan;
    1015                 :             :     HeapTuple   tup;
    1016                 :             : 
    1017                 :             :     /* Find all publications that are marked as for all tables. */
    1018                 :        6056 :     rel = table_open(PublicationRelationId, AccessShareLock);
    1019                 :             : 
    1020                 :        6056 :     ScanKeyInit(&scankey,
    1021                 :             :                 Anum_pg_publication_puballtables,
    1022                 :             :                 BTEqualStrategyNumber, F_BOOLEQ,
    1023                 :             :                 BoolGetDatum(true));
    1024                 :             : 
    1025                 :        6056 :     scan = systable_beginscan(rel, InvalidOid, false,
    1026                 :             :                               NULL, 1, &scankey);
    1027                 :             : 
    1028                 :        6056 :     result = NIL;
    1029         [ +  + ]:        6186 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
    1030                 :             :     {
    1031                 :         130 :         Oid         oid = ((Form_pg_publication) GETSTRUCT(tup))->oid;
    1032                 :             : 
    1033                 :         130 :         result = lappend_oid(result, oid);
    1034                 :             :     }
    1035                 :             : 
    1036                 :        6056 :     systable_endscan(scan);
    1037                 :        6056 :     table_close(rel, AccessShareLock);
    1038                 :             : 
    1039                 :        6056 :     return result;
    1040                 :             : }
    1041                 :             : 
    1042                 :             : /*
    1043                 :             :  * Gets list of all relations published by FOR ALL TABLES/SEQUENCES
    1044                 :             :  * publication.
    1045                 :             :  *
    1046                 :             :  * If the publication publishes partition changes via their respective root
    1047                 :             :  * partitioned tables, we must exclude partitions in favor of including the
    1048                 :             :  * root partitioned tables. This is not applicable to FOR ALL SEQUENCES
    1049                 :             :  * publication.
    1050                 :             :  *
    1051                 :             :  * For a FOR ALL TABLES publication, the returned list excludes tables mentioned
    1052                 :             :  * in the EXCEPT clause.
    1053                 :             :  */
    1054                 :             : List *
    1055                 :          55 : GetAllPublicationRelations(Oid pubid, char relkind, bool pubviaroot)
    1056                 :             : {
    1057                 :             :     Relation    classRel;
    1058                 :             :     ScanKeyData key[1];
    1059                 :             :     TableScanDesc scan;
    1060                 :             :     HeapTuple   tuple;
    1061                 :          55 :     List       *result = NIL;
    1062                 :          55 :     List       *exceptlist = NIL;
    1063                 :             : 
    1064                 :             :     Assert(!(relkind == RELKIND_SEQUENCE && pubviaroot));
    1065                 :             : 
    1066                 :             :     /* EXCEPT filtering applies only to relations, not sequences */
    1067         [ +  + ]:          55 :     if (relkind == RELKIND_RELATION)
    1068                 :          49 :         exceptlist = GetExcludedPublicationTables(pubid, pubviaroot ?
    1069                 :          49 :                                                   PUBLICATION_PART_ROOT :
    1070                 :             :                                                   PUBLICATION_PART_LEAF);
    1071                 :             : 
    1072                 :          55 :     classRel = table_open(RelationRelationId, AccessShareLock);
    1073                 :             : 
    1074                 :          55 :     ScanKeyInit(&key[0],
    1075                 :             :                 Anum_pg_class_relkind,
    1076                 :             :                 BTEqualStrategyNumber, F_CHAREQ,
    1077                 :             :                 CharGetDatum(relkind));
    1078                 :             : 
    1079                 :          55 :     scan = table_beginscan_catalog(classRel, 1, key);
    1080                 :             : 
    1081         [ +  + ]:        3769 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    1082                 :             :     {
    1083                 :        3714 :         Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
    1084                 :        3714 :         Oid         relid = relForm->oid;
    1085                 :             : 
    1086         [ +  + ]:        3714 :         if (is_publishable_class(relid, relForm) &&
    1087   [ +  +  +  + ]:         135 :             !(relForm->relispartition && pubviaroot) &&
    1088         [ +  + ]:         112 :             !list_member_oid(exceptlist, relid))
    1089                 :         104 :             result = lappend_oid(result, relid);
    1090                 :             :     }
    1091                 :             : 
    1092                 :          55 :     table_endscan(scan);
    1093                 :             : 
    1094         [ +  + ]:          55 :     if (pubviaroot)
    1095                 :             :     {
    1096                 :           4 :         ScanKeyInit(&key[0],
    1097                 :             :                     Anum_pg_class_relkind,
    1098                 :             :                     BTEqualStrategyNumber, F_CHAREQ,
    1099                 :             :                     CharGetDatum(RELKIND_PARTITIONED_TABLE));
    1100                 :             : 
    1101                 :           4 :         scan = table_beginscan_catalog(classRel, 1, key);
    1102                 :             : 
    1103         [ +  + ]:          21 :         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    1104                 :             :         {
    1105                 :          17 :             Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
    1106                 :          17 :             Oid         relid = relForm->oid;
    1107                 :             : 
    1108         [ +  - ]:          17 :             if (is_publishable_class(relid, relForm) &&
    1109         [ +  + ]:          17 :                 !relForm->relispartition &&
    1110         [ +  + ]:          13 :                 !list_member_oid(exceptlist, relid))
    1111                 :          12 :                 result = lappend_oid(result, relid);
    1112                 :             :         }
    1113                 :             : 
    1114                 :           4 :         table_endscan(scan);
    1115                 :             :     }
    1116                 :             : 
    1117                 :          55 :     table_close(classRel, AccessShareLock);
    1118                 :          55 :     return result;
    1119                 :             : }
    1120                 :             : 
    1121                 :             : /*
    1122                 :             :  * Gets the list of schema oids for a publication.
    1123                 :             :  *
    1124                 :             :  * This should only be used FOR TABLES IN SCHEMA publications.
    1125                 :             :  */
    1126                 :             : List *
    1127                 :         576 : GetPublicationSchemas(Oid pubid)
    1128                 :             : {
    1129                 :         576 :     List       *result = NIL;
    1130                 :             :     Relation    pubschsrel;
    1131                 :             :     ScanKeyData scankey;
    1132                 :             :     SysScanDesc scan;
    1133                 :             :     HeapTuple   tup;
    1134                 :             : 
    1135                 :             :     /* Find all schemas associated with the publication */
    1136                 :         576 :     pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
    1137                 :             : 
    1138                 :         576 :     ScanKeyInit(&scankey,
    1139                 :             :                 Anum_pg_publication_namespace_pnpubid,
    1140                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
    1141                 :             :                 ObjectIdGetDatum(pubid));
    1142                 :             : 
    1143                 :         576 :     scan = systable_beginscan(pubschsrel,
    1144                 :             :                               PublicationNamespacePnnspidPnpubidIndexId,
    1145                 :             :                               true, NULL, 1, &scankey);
    1146         [ +  + ]:         616 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
    1147                 :             :     {
    1148                 :             :         Form_pg_publication_namespace pubsch;
    1149                 :             : 
    1150                 :          40 :         pubsch = (Form_pg_publication_namespace) GETSTRUCT(tup);
    1151                 :             : 
    1152                 :          40 :         result = lappend_oid(result, pubsch->pnnspid);
    1153                 :             :     }
    1154                 :             : 
    1155                 :         576 :     systable_endscan(scan);
    1156                 :         576 :     table_close(pubschsrel, AccessShareLock);
    1157                 :             : 
    1158                 :         576 :     return result;
    1159                 :             : }
    1160                 :             : 
    1161                 :             : /*
    1162                 :             :  * Gets the list of publication oids associated with a specified schema.
    1163                 :             :  */
    1164                 :             : List *
    1165                 :        8695 : GetSchemaPublications(Oid schemaid)
    1166                 :             : {
    1167                 :        8695 :     List       *result = NIL;
    1168                 :             :     CatCList   *pubschlist;
    1169                 :             :     int         i;
    1170                 :             : 
    1171                 :             :     /* Find all publications associated with the schema */
    1172                 :        8695 :     pubschlist = SearchSysCacheList1(PUBLICATIONNAMESPACEMAP,
    1173                 :             :                                      ObjectIdGetDatum(schemaid));
    1174         [ +  + ]:        8774 :     for (i = 0; i < pubschlist->n_members; i++)
    1175                 :             :     {
    1176                 :          79 :         HeapTuple   tup = &pubschlist->members[i]->tuple;
    1177                 :          79 :         Oid         pubid = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pnpubid;
    1178                 :             : 
    1179                 :          79 :         result = lappend_oid(result, pubid);
    1180                 :             :     }
    1181                 :             : 
    1182                 :        8695 :     ReleaseSysCacheList(pubschlist);
    1183                 :             : 
    1184                 :        8695 :     return result;
    1185                 :             : }
    1186                 :             : 
    1187                 :             : /*
    1188                 :             :  * Get the list of publishable relation oids for a specified schema.
    1189                 :             :  */
    1190                 :             : List *
    1191                 :         310 : GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
    1192                 :             : {
    1193                 :             :     Relation    classRel;
    1194                 :             :     ScanKeyData key[1];
    1195                 :             :     TableScanDesc scan;
    1196                 :             :     HeapTuple   tuple;
    1197                 :         310 :     List       *result = NIL;
    1198                 :             : 
    1199                 :             :     Assert(OidIsValid(schemaid));
    1200                 :             : 
    1201                 :         310 :     classRel = table_open(RelationRelationId, AccessShareLock);
    1202                 :             : 
    1203                 :         310 :     ScanKeyInit(&key[0],
    1204                 :             :                 Anum_pg_class_relnamespace,
    1205                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
    1206                 :             :                 ObjectIdGetDatum(schemaid));
    1207                 :             : 
    1208                 :             :     /* get all the relations present in the specified schema */
    1209                 :         310 :     scan = table_beginscan_catalog(classRel, 1, key);
    1210         [ +  + ]:       17256 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    1211                 :             :     {
    1212                 :       16946 :         Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
    1213                 :       16946 :         Oid         relid = relForm->oid;
    1214                 :             :         char        relkind;
    1215                 :             : 
    1216         [ +  + ]:       16946 :         if (!is_publishable_class(relid, relForm))
    1217                 :        5839 :             continue;
    1218                 :             : 
    1219                 :       11107 :         relkind = get_rel_relkind(relid);
    1220         [ +  + ]:       11107 :         if (relkind == RELKIND_RELATION)
    1221                 :        9554 :             result = lappend_oid(result, relid);
    1222         [ +  + ]:        1553 :         else if (relkind == RELKIND_PARTITIONED_TABLE)
    1223                 :             :         {
    1224                 :         495 :             List       *partitionrels = NIL;
    1225                 :             : 
    1226                 :             :             /*
    1227                 :             :              * It is quite possible that some of the partitions are in a
    1228                 :             :              * different schema than the parent table, so we need to get such
    1229                 :             :              * partitions separately.
    1230                 :             :              */
    1231                 :         495 :             partitionrels = GetPubPartitionOptionRelations(partitionrels,
    1232                 :             :                                                            pub_partopt,
    1233                 :             :                                                            relForm->oid);
    1234                 :         495 :             result = list_concat_unique_oid(result, partitionrels);
    1235                 :             :         }
    1236                 :             :     }
    1237                 :             : 
    1238                 :         310 :     table_endscan(scan);
    1239                 :         310 :     table_close(classRel, AccessShareLock);
    1240                 :         310 :     return result;
    1241                 :             : }
    1242                 :             : 
    1243                 :             : /*
    1244                 :             :  * Gets the list of all relations published by FOR TABLES IN SCHEMA
    1245                 :             :  * publication.
    1246                 :             :  */
    1247                 :             : List *
    1248                 :         283 : GetAllSchemaPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
    1249                 :             : {
    1250                 :         283 :     List       *result = NIL;
    1251                 :         283 :     List       *pubschemalist = GetPublicationSchemas(pubid);
    1252                 :             :     ListCell   *cell;
    1253                 :             : 
    1254   [ +  +  +  +  :         303 :     foreach(cell, pubschemalist)
                   +  + ]
    1255                 :             :     {
    1256                 :          20 :         Oid         schemaid = lfirst_oid(cell);
    1257                 :          20 :         List       *schemaRels = NIL;
    1258                 :             : 
    1259                 :          20 :         schemaRels = GetSchemaPublicationRelations(schemaid, pub_partopt);
    1260                 :          20 :         result = list_concat(result, schemaRels);
    1261                 :             :     }
    1262                 :             : 
    1263                 :         283 :     return result;
    1264                 :             : }
    1265                 :             : 
    1266                 :             : /*
    1267                 :             :  * Get publication using oid
    1268                 :             :  *
    1269                 :             :  * The Publication struct and its data are palloc'ed here.
    1270                 :             :  */
    1271                 :             : Publication *
    1272                 :        4171 : GetPublication(Oid pubid)
    1273                 :             : {
    1274                 :             :     HeapTuple   tup;
    1275                 :             :     Publication *pub;
    1276                 :             :     Form_pg_publication pubform;
    1277                 :             : 
    1278                 :        4171 :     tup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid));
    1279         [ -  + ]:        4171 :     if (!HeapTupleIsValid(tup))
    1280         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for publication %u", pubid);
    1281                 :             : 
    1282                 :        4171 :     pubform = (Form_pg_publication) GETSTRUCT(tup);
    1283                 :             : 
    1284                 :        4171 :     pub = palloc_object(Publication);
    1285                 :        4171 :     pub->oid = pubid;
    1286                 :        4171 :     pub->name = pstrdup(NameStr(pubform->pubname));
    1287                 :        4171 :     pub->alltables = pubform->puballtables;
    1288                 :        4171 :     pub->allsequences = pubform->puballsequences;
    1289                 :        4171 :     pub->pubactions.pubinsert = pubform->pubinsert;
    1290                 :        4171 :     pub->pubactions.pubupdate = pubform->pubupdate;
    1291                 :        4171 :     pub->pubactions.pubdelete = pubform->pubdelete;
    1292                 :        4171 :     pub->pubactions.pubtruncate = pubform->pubtruncate;
    1293                 :        4171 :     pub->pubviaroot = pubform->pubviaroot;
    1294                 :        4171 :     pub->pubgencols_type = pubform->pubgencols;
    1295                 :             : 
    1296                 :        4171 :     ReleaseSysCache(tup);
    1297                 :             : 
    1298                 :        4171 :     return pub;
    1299                 :             : }
    1300                 :             : 
    1301                 :             : /*
    1302                 :             :  * Get Publication using name.
    1303                 :             :  */
    1304                 :             : Publication *
    1305                 :        1765 : GetPublicationByName(const char *pubname, bool missing_ok)
    1306                 :             : {
    1307                 :             :     Oid         oid;
    1308                 :             : 
    1309                 :        1765 :     oid = get_publication_oid(pubname, missing_ok);
    1310                 :             : 
    1311         [ +  + ]:        1765 :     return OidIsValid(oid) ? GetPublication(oid) : NULL;
    1312                 :             : }
    1313                 :             : 
    1314                 :             : /*
    1315                 :             :  * A helper function for pg_get_publication_tables() to check whether the
    1316                 :             :  * table with the given relid is published in the specified publication.
    1317                 :             :  *
    1318                 :             :  * This function evaluates the effective published OID based on the
    1319                 :             :  * publish_via_partition_root setting, rather than just checking catalog entries
    1320                 :             :  * (e.g., pg_publication_rel). For instance, when publish_via_partition_root is
    1321                 :             :  * false, it returns false for a parent partitioned table and returns true
    1322                 :             :  * for its leaf partitions, even if the parent is the one explicitly added
    1323                 :             :  * to the publication.
    1324                 :             :  *
    1325                 :             :  * For performance reasons, this function avoids the overhead of constructing
    1326                 :             :  * the complete list of published tables during the evaluation. It can execute
    1327                 :             :  * quickly even when the publication contains a large number of relations.
    1328                 :             :  *
    1329                 :             :  * Note: this leaks memory for the ancestors list into the current memory
    1330                 :             :  * context.
    1331                 :             :  */
    1332                 :             : static bool
    1333                 :         982 : is_table_publishable_in_publication(Oid relid, Publication *pub)
    1334                 :             : {
    1335                 :             :     bool        relispartition;
    1336                 :         982 :     List       *ancestors = NIL;
    1337                 :             : 
    1338                 :             :     /*
    1339                 :             :      * For non-pubviaroot publications, a partitioned table is never the
    1340                 :             :      * effective published OID; only its leaf partitions can be.
    1341                 :             :      */
    1342   [ +  +  +  + ]:         982 :     if (!pub->pubviaroot && get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE)
    1343                 :          86 :         return false;
    1344                 :             : 
    1345                 :         896 :     relispartition = get_rel_relispartition(relid);
    1346                 :             : 
    1347         [ +  + ]:         896 :     if (relispartition)
    1348                 :         170 :         ancestors = get_partition_ancestors(relid);
    1349                 :             : 
    1350         [ +  + ]:         896 :     if (pub->alltables)
    1351                 :             :     {
    1352                 :             :         /*
    1353                 :             :          * ALL TABLES with pubviaroot includes only regular tables or top-most
    1354                 :             :          * partitioned tables -- never child partitions.
    1355                 :             :          */
    1356   [ +  +  +  + ]:         200 :         if (pub->pubviaroot && relispartition)
    1357                 :          12 :             return false;
    1358                 :             : 
    1359                 :             :         /*
    1360                 :             :          * For ALL TABLES publications, the table is published unless it
    1361                 :             :          * appears in the EXCEPT clause. Only the top-most can appear in the
    1362                 :             :          * EXCEPT clause, so exclusion must be evaluated at the top-most
    1363                 :             :          * ancestor if it has. These publications store only EXCEPT'ed tables
    1364                 :             :          * in pg_publication_rel, so checking existence is sufficient.
    1365                 :             :          *
    1366                 :             :          * Note that this existence check below would incorrectly return true
    1367                 :             :          * (published) for partitions when pubviaroot is enabled; however,
    1368                 :             :          * that case is already caught and returned false by the above check.
    1369                 :             :          */
    1370         [ +  + ]:         188 :         return !SearchSysCacheExists2(PUBLICATIONRELMAP,
    1371                 :             :                                       ObjectIdGetDatum(ancestors
    1372                 :             :                                                        ? llast_oid(ancestors) : relid),
    1373                 :             :                                       ObjectIdGetDatum(pub->oid));
    1374                 :             :     }
    1375                 :             : 
    1376                 :             :     /*
    1377                 :             :      * Non-ALL-TABLE publication cases.
    1378                 :             :      *
    1379                 :             :      * A table is published if it (or a containing schema) was explicitly
    1380                 :             :      * added, or if it is a partition whose ancestor was added.
    1381                 :             :      */
    1382                 :             : 
    1383                 :             :     /*
    1384                 :             :      * If an ancestor is published, the partition's status depends on
    1385                 :             :      * publish_via_partition_root value.
    1386                 :             :      *
    1387                 :             :      * If it's true, the ancestor's relation OID is the effective published
    1388                 :             :      * OID, so the partition itself should be excluded (return false).
    1389                 :             :      *
    1390                 :             :      * If it's false, the partition is covered by its ancestor's presence in
    1391                 :             :      * the publication, it should be included (return true).
    1392                 :             :      */
    1393   [ +  +  +  + ]:         836 :     if (relispartition &&
    1394                 :         140 :         OidIsValid(GetTopMostAncestorInPublication(pub->oid, ancestors, NULL)))
    1395                 :          44 :         return !pub->pubviaroot;
    1396                 :             : 
    1397                 :             :     /*
    1398                 :             :      * Check whether the table is explicitly published via pg_publication_rel
    1399                 :             :      * or pg_publication_namespace.
    1400                 :             :      */
    1401                 :         652 :     return (SearchSysCacheExists2(PUBLICATIONRELMAP,
    1402                 :             :                                   ObjectIdGetDatum(relid),
    1403   [ +  +  +  + ]:        1022 :                                   ObjectIdGetDatum(pub->oid)) ||
    1404                 :         370 :             SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
    1405                 :             :                                   ObjectIdGetDatum(get_rel_namespace(relid)),
    1406                 :             :                                   ObjectIdGetDatum(pub->oid)));
    1407                 :             : }
    1408                 :             : 
    1409                 :             : /*
    1410                 :             :  * Helper function to get information of the tables in the given
    1411                 :             :  * publication(s).
    1412                 :             :  *
    1413                 :             :  * If filter_by_relid is true, only the row(s) for target_relid is returned;
    1414                 :             :  * if target_relid does not exist or is not part of the publications, zero
    1415                 :             :  * rows are returned.  If filter_by_relid is false, rows for all tables
    1416                 :             :  * within the specified publications are returned and target_relid is
    1417                 :             :  * ignored.
    1418                 :             :  *
    1419                 :             :  * Returns pubid, relid, column list, and row filter for each table.
    1420                 :             :  */
    1421                 :             : static Datum
    1422                 :        1592 : pg_get_publication_tables(FunctionCallInfo fcinfo, ArrayType *pubnames,
    1423                 :             :                           Oid target_relid, bool filter_by_relid,
    1424                 :             :                           bool pub_missing_ok)
    1425                 :             : {
    1426                 :             : #define NUM_PUBLICATION_TABLES_ELEM 4
    1427                 :             :     FuncCallContext *funcctx;
    1428                 :        1592 :     List       *table_infos = NIL;
    1429                 :             : 
    1430                 :             :     /* stuff done only on the first call of the function */
    1431         [ +  + ]:        1592 :     if (SRF_IS_FIRSTCALL())
    1432                 :             :     {
    1433                 :             :         TupleDesc   tupdesc;
    1434                 :             :         MemoryContext oldcontext;
    1435                 :             :         Datum      *elems;
    1436                 :             :         int         nelems,
    1437                 :             :                     i;
    1438                 :         750 :         bool        viaroot = false;
    1439                 :             : 
    1440                 :             :         /* create a function context for cross-call persistence */
    1441                 :         750 :         funcctx = SRF_FIRSTCALL_INIT();
    1442                 :             : 
    1443                 :             :         /*
    1444                 :             :          * Preliminary check if the specified table can be published in the
    1445                 :             :          * first place. If not, we can return early without checking the given
    1446                 :             :          * publications and the table.
    1447                 :             :          */
    1448   [ +  +  +  + ]:         750 :         if (filter_by_relid && !is_publishable_table(target_relid))
    1449                 :           8 :             SRF_RETURN_DONE(funcctx);
    1450                 :             : 
    1451                 :             :         /* switch to memory context appropriate for multiple function calls */
    1452                 :         742 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    1453                 :             : 
    1454                 :             :         /*
    1455                 :             :          * Deconstruct the parameter into elements where each element is a
    1456                 :             :          * publication name.
    1457                 :             :          */
    1458                 :         742 :         deconstruct_array_builtin(pubnames, TEXTOID, &elems, NULL, &nelems);
    1459                 :             : 
    1460                 :             :         /* Get Oids of tables from each publication. */
    1461         [ +  + ]:        1982 :         for (i = 0; i < nelems; i++)
    1462                 :             :         {
    1463                 :             :             Publication *pub_elem;
    1464                 :        1240 :             List       *pub_elem_tables = NIL;
    1465                 :             :             ListCell   *lc;
    1466                 :             : 
    1467                 :        1240 :             pub_elem = GetPublicationByName(TextDatumGetCString(elems[i]),
    1468                 :             :                                             pub_missing_ok);
    1469                 :             : 
    1470         [ +  + ]:        1240 :             if (pub_elem == NULL)
    1471                 :           6 :                 continue;
    1472                 :             : 
    1473         [ +  + ]:        1234 :             if (filter_by_relid)
    1474                 :             :             {
    1475                 :             :                 /* Check if the given table is published for the publication */
    1476         [ +  + ]:         982 :                 if (is_table_publishable_in_publication(target_relid, pub_elem))
    1477                 :             :                 {
    1478                 :         496 :                     pub_elem_tables = list_make1_oid(target_relid);
    1479                 :             :                 }
    1480                 :             :             }
    1481                 :             :             else
    1482                 :             :             {
    1483                 :             :                 /*
    1484                 :             :                  * Publications support partitioned tables. If
    1485                 :             :                  * publish_via_partition_root is false, all changes are
    1486                 :             :                  * replicated using leaf partition identity and schema, so we
    1487                 :             :                  * only need those. Otherwise, get the partitioned table
    1488                 :             :                  * itself.
    1489                 :             :                  */
    1490         [ +  + ]:         252 :                 if (pub_elem->alltables)
    1491                 :          49 :                     pub_elem_tables = GetAllPublicationRelations(pub_elem->oid,
    1492                 :             :                                                                  RELKIND_RELATION,
    1493                 :          49 :                                                                  pub_elem->pubviaroot);
    1494                 :             :                 else
    1495                 :             :                 {
    1496                 :             :                     List       *relids,
    1497                 :             :                                *schemarelids;
    1498                 :             : 
    1499                 :         203 :                     relids = GetIncludedPublicationRelations(pub_elem->oid,
    1500                 :         203 :                                                              pub_elem->pubviaroot ?
    1501                 :         203 :                                                              PUBLICATION_PART_ROOT :
    1502                 :             :                                                              PUBLICATION_PART_LEAF);
    1503                 :         203 :                     schemarelids = GetAllSchemaPublicationRelations(pub_elem->oid,
    1504                 :         203 :                                                                     pub_elem->pubviaroot ?
    1505                 :         203 :                                                                     PUBLICATION_PART_ROOT :
    1506                 :             :                                                                     PUBLICATION_PART_LEAF);
    1507                 :         203 :                     pub_elem_tables = list_concat_unique_oid(relids, schemarelids);
    1508                 :             :                 }
    1509                 :             :             }
    1510                 :             : 
    1511                 :             :             /*
    1512                 :             :              * Record the published table and the corresponding publication so
    1513                 :             :              * that we can get row filters and column lists later.
    1514                 :             :              *
    1515                 :             :              * When a table is published by multiple publications, to obtain
    1516                 :             :              * all row filters and column lists, the structure related to this
    1517                 :             :              * table will be recorded multiple times.
    1518                 :             :              */
    1519   [ +  +  +  +  :        2106 :             foreach(lc, pub_elem_tables)
                   +  + ]
    1520                 :             :             {
    1521                 :         872 :                 published_rel *table_info = palloc_object(published_rel);
    1522                 :             : 
    1523                 :         872 :                 table_info->relid = lfirst_oid(lc);
    1524                 :         872 :                 table_info->pubid = pub_elem->oid;
    1525                 :         872 :                 table_infos = lappend(table_infos, table_info);
    1526                 :             :             }
    1527                 :             : 
    1528                 :             :             /* At least one publication is using publish_via_partition_root. */
    1529         [ +  + ]:        1234 :             if (pub_elem->pubviaroot)
    1530                 :         247 :                 viaroot = true;
    1531                 :             :         }
    1532                 :             : 
    1533                 :             :         /*
    1534                 :             :          * If the publication publishes partition changes via their respective
    1535                 :             :          * root partitioned tables, we must exclude partitions in favor of
    1536                 :             :          * including the root partitioned tables. Otherwise, the function
    1537                 :             :          * could return both the child and parent tables which could cause
    1538                 :             :          * data of the child table to be double-published on the subscriber
    1539                 :             :          * side.
    1540                 :             :          */
    1541         [ +  + ]:         742 :         if (viaroot)
    1542                 :         167 :             filter_partitions(table_infos);
    1543                 :             : 
    1544                 :             :         /* Construct a tuple descriptor for the result rows. */
    1545                 :         742 :         tupdesc = CreateTemplateTupleDesc(NUM_PUBLICATION_TABLES_ELEM);
    1546                 :         742 :         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pubid",
    1547                 :             :                            OIDOID, -1, 0);
    1548                 :         742 :         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "relid",
    1549                 :             :                            OIDOID, -1, 0);
    1550                 :         742 :         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "attrs",
    1551                 :             :                            INT2VECTOROID, -1, 0);
    1552                 :         742 :         TupleDescInitEntry(tupdesc, (AttrNumber) 4, "qual",
    1553                 :             :                            PG_NODE_TREEOID, -1, 0);
    1554                 :             : 
    1555                 :         742 :         TupleDescFinalize(tupdesc);
    1556                 :         742 :         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
    1557                 :         742 :         funcctx->user_fctx = table_infos;
    1558                 :             : 
    1559                 :         742 :         MemoryContextSwitchTo(oldcontext);
    1560                 :             :     }
    1561                 :             : 
    1562                 :             :     /* stuff done on every call of the function */
    1563                 :        1584 :     funcctx = SRF_PERCALL_SETUP();
    1564                 :        1584 :     table_infos = (List *) funcctx->user_fctx;
    1565                 :             : 
    1566         [ +  + ]:        1584 :     if (funcctx->call_cntr < list_length(table_infos))
    1567                 :             :     {
    1568                 :         842 :         HeapTuple   pubtuple = NULL;
    1569                 :             :         HeapTuple   rettuple;
    1570                 :             :         Publication *pub;
    1571                 :         842 :         published_rel *table_info = (published_rel *) list_nth(table_infos, funcctx->call_cntr);
    1572                 :         842 :         Oid         relid = table_info->relid;
    1573                 :         842 :         Oid         schemaid = get_rel_namespace(relid);
    1574                 :         842 :         Datum       values[NUM_PUBLICATION_TABLES_ELEM] = {0};
    1575                 :         842 :         bool        nulls[NUM_PUBLICATION_TABLES_ELEM] = {0};
    1576                 :             : 
    1577                 :             :         /*
    1578                 :             :          * Form tuple with appropriate data.
    1579                 :             :          */
    1580                 :             : 
    1581                 :         842 :         pub = GetPublication(table_info->pubid);
    1582                 :             : 
    1583                 :         842 :         values[0] = ObjectIdGetDatum(pub->oid);
    1584                 :         842 :         values[1] = ObjectIdGetDatum(relid);
    1585                 :             : 
    1586                 :             :         /*
    1587                 :             :          * We don't consider row filters or column lists for FOR ALL TABLES or
    1588                 :             :          * FOR TABLES IN SCHEMA publications.
    1589                 :             :          */
    1590         [ +  + ]:         842 :         if (!pub->alltables &&
    1591         [ +  + ]:         573 :             !SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
    1592                 :             :                                    ObjectIdGetDatum(schemaid),
    1593                 :             :                                    ObjectIdGetDatum(pub->oid)))
    1594                 :         537 :             pubtuple = SearchSysCacheCopy2(PUBLICATIONRELMAP,
    1595                 :             :                                            ObjectIdGetDatum(relid),
    1596                 :             :                                            ObjectIdGetDatum(pub->oid));
    1597                 :             : 
    1598         [ +  + ]:         842 :         if (HeapTupleIsValid(pubtuple))
    1599                 :             :         {
    1600                 :             :             /* Lookup the column list attribute. */
    1601                 :         498 :             values[2] = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
    1602                 :             :                                         Anum_pg_publication_rel_prattrs,
    1603                 :             :                                         &(nulls[2]));
    1604                 :             : 
    1605                 :             :             /* Null indicates no filter. */
    1606                 :         498 :             values[3] = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
    1607                 :             :                                         Anum_pg_publication_rel_prqual,
    1608                 :             :                                         &(nulls[3]));
    1609                 :             :         }
    1610                 :             :         else
    1611                 :             :         {
    1612                 :         344 :             nulls[2] = true;
    1613                 :         344 :             nulls[3] = true;
    1614                 :             :         }
    1615                 :             : 
    1616                 :             :         /* Show all columns when the column list is not specified. */
    1617         [ +  + ]:         842 :         if (nulls[2])
    1618                 :             :         {
    1619                 :         746 :             Relation    rel = table_open(relid, AccessShareLock);
    1620                 :         746 :             int         nattnums = 0;
    1621                 :             :             int16      *attnums;
    1622                 :         746 :             TupleDesc   desc = RelationGetDescr(rel);
    1623                 :             :             int         i;
    1624                 :             : 
    1625                 :         746 :             attnums = palloc_array(int16, desc->natts);
    1626                 :             : 
    1627         [ +  + ]:        1974 :             for (i = 0; i < desc->natts; i++)
    1628                 :             :             {
    1629                 :        1228 :                 Form_pg_attribute att = TupleDescAttr(desc, i);
    1630                 :             : 
    1631         [ +  + ]:        1228 :                 if (att->attisdropped)
    1632                 :           4 :                     continue;
    1633                 :             : 
    1634         [ +  + ]:        1224 :                 if (att->attgenerated)
    1635                 :             :                 {
    1636                 :             :                     /* We only support replication of STORED generated cols. */
    1637         [ +  + ]:          22 :                     if (att->attgenerated != ATTRIBUTE_GENERATED_STORED)
    1638                 :          10 :                         continue;
    1639                 :             : 
    1640                 :             :                     /*
    1641                 :             :                      * User hasn't requested to replicate STORED generated
    1642                 :             :                      * cols.
    1643                 :             :                      */
    1644         [ +  + ]:          12 :                     if (pub->pubgencols_type != PUBLISH_GENCOLS_STORED)
    1645                 :           9 :                         continue;
    1646                 :             :                 }
    1647                 :             : 
    1648                 :        1205 :                 attnums[nattnums++] = att->attnum;
    1649                 :             :             }
    1650                 :             : 
    1651         [ +  + ]:         746 :             if (nattnums > 0)
    1652                 :             :             {
    1653                 :         742 :                 values[2] = PointerGetDatum(buildint2vector(attnums, nattnums));
    1654                 :         742 :                 nulls[2] = false;
    1655                 :             :             }
    1656                 :             : 
    1657                 :         746 :             table_close(rel, AccessShareLock);
    1658                 :             :         }
    1659                 :             : 
    1660                 :         842 :         rettuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
    1661                 :             : 
    1662                 :         842 :         SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(rettuple));
    1663                 :             :     }
    1664                 :             : 
    1665                 :         742 :     SRF_RETURN_DONE(funcctx);
    1666                 :             : }
    1667                 :             : 
    1668                 :             : Datum
    1669                 :         550 : pg_get_publication_tables_a(PG_FUNCTION_ARGS)
    1670                 :             : {
    1671                 :             :     /*
    1672                 :             :      * Get information for all tables in the given publications.
    1673                 :             :      * filter_by_relid is false so all tables are returned; pub_missing_ok is
    1674                 :             :      * false for backward compatibility.
    1675                 :             :      */
    1676                 :         550 :     return pg_get_publication_tables(fcinfo, PG_GETARG_ARRAYTYPE_P(0),
    1677                 :             :                                      InvalidOid, false, false);
    1678                 :             : }
    1679                 :             : 
    1680                 :             : Datum
    1681                 :        1042 : pg_get_publication_tables_b(PG_FUNCTION_ARGS)
    1682                 :             : {
    1683                 :             :     /*
    1684                 :             :      * Get information for the specified table in the given publications. The
    1685                 :             :      * SQL-level function is declared STRICT, so target_relid is guaranteed to
    1686                 :             :      * be non-NULL here.
    1687                 :             :      */
    1688                 :        1042 :     return pg_get_publication_tables(fcinfo, PG_GETARG_ARRAYTYPE_P(0),
    1689                 :             :                                      PG_GETARG_OID(1), true, true);
    1690                 :             : }
    1691                 :             : 
    1692                 :             : /*
    1693                 :             :  * Returns Oids of sequences in a publication.
    1694                 :             :  */
    1695                 :             : Datum
    1696                 :         234 : pg_get_publication_sequences(PG_FUNCTION_ARGS)
    1697                 :             : {
    1698                 :             :     FuncCallContext *funcctx;
    1699                 :         234 :     List       *sequences = NIL;
    1700                 :             : 
    1701                 :             :     /* stuff done only on the first call of the function */
    1702         [ +  + ]:         234 :     if (SRF_IS_FIRSTCALL())
    1703                 :             :     {
    1704                 :         217 :         char       *pubname = text_to_cstring(PG_GETARG_TEXT_PP(0));
    1705                 :             :         Publication *publication;
    1706                 :             :         MemoryContext oldcontext;
    1707                 :             : 
    1708                 :             :         /* create a function context for cross-call persistence */
    1709                 :         217 :         funcctx = SRF_FIRSTCALL_INIT();
    1710                 :             : 
    1711                 :             :         /* switch to memory context appropriate for multiple function calls */
    1712                 :         217 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    1713                 :             : 
    1714                 :         217 :         publication = GetPublicationByName(pubname, false);
    1715                 :             : 
    1716         [ +  + ]:         217 :         if (publication->allsequences)
    1717                 :           6 :             sequences = GetAllPublicationRelations(publication->oid,
    1718                 :             :                                                    RELKIND_SEQUENCE,
    1719                 :             :                                                    false);
    1720                 :             : 
    1721                 :         217 :         funcctx->user_fctx = sequences;
    1722                 :             : 
    1723                 :         217 :         MemoryContextSwitchTo(oldcontext);
    1724                 :             :     }
    1725                 :             : 
    1726                 :             :     /* stuff done on every call of the function */
    1727                 :         234 :     funcctx = SRF_PERCALL_SETUP();
    1728                 :         234 :     sequences = (List *) funcctx->user_fctx;
    1729                 :             : 
    1730         [ +  + ]:         234 :     if (funcctx->call_cntr < list_length(sequences))
    1731                 :             :     {
    1732                 :          17 :         Oid         relid = list_nth_oid(sequences, funcctx->call_cntr);
    1733                 :             : 
    1734                 :          17 :         SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
    1735                 :             :     }
    1736                 :             : 
    1737                 :         217 :     SRF_RETURN_DONE(funcctx);
    1738                 :             : }
        

Generated by: LCOV version 2.0-1