LCOV - code coverage report
Current view: top level - src/backend/replication/logical - relation.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 173 201 86.1 %
Date: 2021-12-09 04:09:06 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  * relation.c
       3             :  *     PostgreSQL logical replication relation mapping cache
       4             :  *
       5             :  * Copyright (c) 2016-2021, PostgreSQL Global Development Group
       6             :  *
       7             :  * IDENTIFICATION
       8             :  *    src/backend/replication/logical/relation.c
       9             :  *
      10             :  * NOTES
      11             :  *    Routines in this file mainly have to do with mapping the properties
      12             :  *    of local replication target relations to the properties of their
      13             :  *    remote counterpart.
      14             :  *
      15             :  *-------------------------------------------------------------------------
      16             :  */
      17             : 
      18             : #include "postgres.h"
      19             : 
      20             : #include "access/table.h"
      21             : #include "catalog/namespace.h"
      22             : #include "catalog/pg_subscription_rel.h"
      23             : #include "executor/executor.h"
      24             : #include "nodes/makefuncs.h"
      25             : #include "replication/logicalrelation.h"
      26             : #include "replication/worker_internal.h"
      27             : #include "utils/inval.h"
      28             : 
      29             : 
      30             : static MemoryContext LogicalRepRelMapContext = NULL;
      31             : 
      32             : static HTAB *LogicalRepRelMap = NULL;
      33             : 
      34             : /*
      35             :  * Partition map (LogicalRepPartMap)
      36             :  *
      37             :  * When a partitioned table is used as replication target, replicated
      38             :  * operations are actually performed on its leaf partitions, which requires
      39             :  * the partitions to also be mapped to the remote relation.  Parent's entry
      40             :  * (LogicalRepRelMapEntry) cannot be used as-is for all partitions, because
      41             :  * individual partitions may have different attribute numbers, which means
      42             :  * attribute mappings to remote relation's attributes must be maintained
      43             :  * separately for each partition.
      44             :  */
      45             : static MemoryContext LogicalRepPartMapContext = NULL;
      46             : static HTAB *LogicalRepPartMap = NULL;
      47             : typedef struct LogicalRepPartMapEntry
      48             : {
      49             :     Oid         partoid;        /* LogicalRepPartMap's key */
      50             :     LogicalRepRelMapEntry relmapentry;
      51             : } LogicalRepPartMapEntry;
      52             : 
      53             : /*
      54             :  * Relcache invalidation callback for our relation map cache.
      55             :  */
      56             : static void
      57         788 : logicalrep_relmap_invalidate_cb(Datum arg, Oid reloid)
      58             : {
      59             :     LogicalRepRelMapEntry *entry;
      60             : 
      61             :     /* Just to be sure. */
      62         788 :     if (LogicalRepRelMap == NULL)
      63           0 :         return;
      64             : 
      65         788 :     if (reloid != InvalidOid)
      66             :     {
      67             :         HASH_SEQ_STATUS status;
      68             : 
      69         788 :         hash_seq_init(&status, LogicalRepRelMap);
      70             : 
      71             :         /* TODO, use inverse lookup hashtable? */
      72        3424 :         while ((entry = (LogicalRepRelMapEntry *) hash_seq_search(&status)) != NULL)
      73             :         {
      74        2796 :             if (entry->localreloid == reloid)
      75             :             {
      76         160 :                 entry->localrelvalid = false;
      77         160 :                 hash_seq_term(&status);
      78         160 :                 break;
      79             :             }
      80             :         }
      81             :     }
      82             :     else
      83             :     {
      84             :         /* invalidate all cache entries */
      85             :         HASH_SEQ_STATUS status;
      86             : 
      87           0 :         hash_seq_init(&status, LogicalRepRelMap);
      88             : 
      89           0 :         while ((entry = (LogicalRepRelMapEntry *) hash_seq_search(&status)) != NULL)
      90           0 :             entry->localrelvalid = false;
      91             :     }
      92             : }
      93             : 
      94             : /*
      95             :  * Initialize the relation map cache.
      96             :  */
      97             : static void
      98         274 : logicalrep_relmap_init(void)
      99             : {
     100             :     HASHCTL     ctl;
     101             : 
     102         274 :     if (!LogicalRepRelMapContext)
     103         274 :         LogicalRepRelMapContext =
     104         274 :             AllocSetContextCreate(CacheMemoryContext,
     105             :                                   "LogicalRepRelMapContext",
     106             :                                   ALLOCSET_DEFAULT_SIZES);
     107             : 
     108             :     /* Initialize the relation hash table. */
     109         274 :     ctl.keysize = sizeof(LogicalRepRelId);
     110         274 :     ctl.entrysize = sizeof(LogicalRepRelMapEntry);
     111         274 :     ctl.hcxt = LogicalRepRelMapContext;
     112             : 
     113         274 :     LogicalRepRelMap = hash_create("logicalrep relation map cache", 128, &ctl,
     114             :                                    HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     115             : 
     116             :     /* Watch for invalidation events. */
     117         274 :     CacheRegisterRelcacheCallback(logicalrep_relmap_invalidate_cb,
     118             :                                   (Datum) 0);
     119         274 : }
     120             : 
     121             : /*
     122             :  * Free the entry of a relation map cache.
     123             :  */
     124             : static void
     125         134 : logicalrep_relmap_free_entry(LogicalRepRelMapEntry *entry)
     126             : {
     127             :     LogicalRepRelation *remoterel;
     128             : 
     129         134 :     remoterel = &entry->remoterel;
     130             : 
     131         134 :     pfree(remoterel->nspname);
     132         134 :     pfree(remoterel->relname);
     133             : 
     134         134 :     if (remoterel->natts > 0)
     135             :     {
     136             :         int         i;
     137             : 
     138         432 :         for (i = 0; i < remoterel->natts; i++)
     139         298 :             pfree(remoterel->attnames[i]);
     140             : 
     141         134 :         pfree(remoterel->attnames);
     142         134 :         pfree(remoterel->atttyps);
     143             :     }
     144         134 :     bms_free(remoterel->attkeys);
     145             : 
     146         134 :     if (entry->attrmap)
     147         126 :         pfree(entry->attrmap);
     148         134 : }
     149             : 
     150             : /*
     151             :  * Add new entry or update existing entry in the relation map cache.
     152             :  *
     153             :  * Called when new relation mapping is sent by the publisher to update
     154             :  * our expected view of incoming data from said publisher.
     155             :  */
     156             : void
     157         506 : logicalrep_relmap_update(LogicalRepRelation *remoterel)
     158             : {
     159             :     MemoryContext oldctx;
     160             :     LogicalRepRelMapEntry *entry;
     161             :     bool        found;
     162             :     int         i;
     163             : 
     164         506 :     if (LogicalRepRelMap == NULL)
     165         274 :         logicalrep_relmap_init();
     166             : 
     167             :     /*
     168             :      * HASH_ENTER returns the existing entry if present or creates a new one.
     169             :      */
     170         506 :     entry = hash_search(LogicalRepRelMap, (void *) &remoterel->remoteid,
     171             :                         HASH_ENTER, &found);
     172             : 
     173         506 :     if (found)
     174         134 :         logicalrep_relmap_free_entry(entry);
     175             : 
     176         506 :     memset(entry, 0, sizeof(LogicalRepRelMapEntry));
     177             : 
     178             :     /* Make cached copy of the data */
     179         506 :     oldctx = MemoryContextSwitchTo(LogicalRepRelMapContext);
     180         506 :     entry->remoterel.remoteid = remoterel->remoteid;
     181         506 :     entry->remoterel.nspname = pstrdup(remoterel->nspname);
     182         506 :     entry->remoterel.relname = pstrdup(remoterel->relname);
     183         506 :     entry->remoterel.natts = remoterel->natts;
     184         506 :     entry->remoterel.attnames = palloc(remoterel->natts * sizeof(char *));
     185         506 :     entry->remoterel.atttyps = palloc(remoterel->natts * sizeof(Oid));
     186        1468 :     for (i = 0; i < remoterel->natts; i++)
     187             :     {
     188         962 :         entry->remoterel.attnames[i] = pstrdup(remoterel->attnames[i]);
     189         962 :         entry->remoterel.atttyps[i] = remoterel->atttyps[i];
     190             :     }
     191         506 :     entry->remoterel.replident = remoterel->replident;
     192         506 :     entry->remoterel.attkeys = bms_copy(remoterel->attkeys);
     193         506 :     MemoryContextSwitchTo(oldctx);
     194         506 : }
     195             : 
     196             : /*
     197             :  * Find attribute index in TupleDesc struct by attribute name.
     198             :  *
     199             :  * Returns -1 if not found.
     200             :  */
     201             : static int
     202        1162 : logicalrep_rel_att_by_name(LogicalRepRelation *remoterel, const char *attname)
     203             : {
     204             :     int         i;
     205             : 
     206        2364 :     for (i = 0; i < remoterel->natts; i++)
     207             :     {
     208        2088 :         if (strcmp(remoterel->attnames[i], attname) == 0)
     209         886 :             return i;
     210             :     }
     211             : 
     212         276 :     return -1;
     213             : }
     214             : 
     215             : /*
     216             :  * Report error with names of the missing local relation column(s), if any.
     217             :  */
     218             : static void
     219         470 : logicalrep_report_missing_attrs(LogicalRepRelation *remoterel,
     220             :                                 Bitmapset *missingatts)
     221             : {
     222         470 :     if (!bms_is_empty(missingatts))
     223             :     {
     224             :         StringInfoData missingattsbuf;
     225           0 :         int         missingattcnt = 0;
     226             :         int         i;
     227             : 
     228           0 :         initStringInfo(&missingattsbuf);
     229             : 
     230           0 :         while ((i = bms_first_member(missingatts)) >= 0)
     231             :         {
     232           0 :             missingattcnt++;
     233           0 :             if (missingattcnt == 1)
     234           0 :                 appendStringInfo(&missingattsbuf, _("\"%s\""),
     235           0 :                                  remoterel->attnames[i]);
     236             :             else
     237           0 :                 appendStringInfo(&missingattsbuf, _(", \"%s\""),
     238           0 :                                  remoterel->attnames[i]);
     239             :         }
     240             : 
     241           0 :         ereport(ERROR,
     242             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     243             :                  errmsg_plural("logical replication target relation \"%s.%s\" is missing replicated column: %s",
     244             :                                "logical replication target relation \"%s.%s\" is missing replicated columns: %s",
     245             :                                missingattcnt,
     246             :                                remoterel->nspname,
     247             :                                remoterel->relname,
     248             :                                missingattsbuf.data)));
     249             :     }
     250         470 : }
     251             : 
     252             : /*
     253             :  * Open the local relation associated with the remote one.
     254             :  *
     255             :  * Rebuilds the Relcache mapping if it was invalidated by local DDL.
     256             :  */
     257             : LogicalRepRelMapEntry *
     258      216488 : logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE lockmode)
     259             : {
     260             :     LogicalRepRelMapEntry *entry;
     261             :     bool        found;
     262             :     LogicalRepRelation *remoterel;
     263             : 
     264      216488 :     if (LogicalRepRelMap == NULL)
     265           0 :         logicalrep_relmap_init();
     266             : 
     267             :     /* Search for existing entry. */
     268      216488 :     entry = hash_search(LogicalRepRelMap, (void *) &remoteid,
     269             :                         HASH_FIND, &found);
     270             : 
     271      216488 :     if (!found)
     272           0 :         elog(ERROR, "no relation map entry for remote relation ID %u",
     273             :              remoteid);
     274             : 
     275      216488 :     remoterel = &entry->remoterel;
     276             : 
     277             :     /* Ensure we don't leak a relcache refcount. */
     278      216488 :     if (entry->localrel)
     279           0 :         elog(ERROR, "remote relation ID %u is already open", remoteid);
     280             : 
     281             :     /*
     282             :      * When opening and locking a relation, pending invalidation messages are
     283             :      * processed which can invalidate the relation.  Hence, if the entry is
     284             :      * currently considered valid, try to open the local relation by OID and
     285             :      * see if invalidation ensues.
     286             :      */
     287      216488 :     if (entry->localrelvalid)
     288             :     {
     289      216008 :         entry->localrel = try_table_open(entry->localreloid, lockmode);
     290      216008 :         if (!entry->localrel)
     291             :         {
     292             :             /* Table was renamed or dropped. */
     293           0 :             entry->localrelvalid = false;
     294             :         }
     295      216008 :         else if (!entry->localrelvalid)
     296             :         {
     297             :             /* Note we release the no-longer-useful lock here. */
     298           0 :             table_close(entry->localrel, lockmode);
     299           0 :             entry->localrel = NULL;
     300             :         }
     301             :     }
     302             : 
     303             :     /*
     304             :      * If the entry has been marked invalid since we last had lock on it,
     305             :      * re-open the local relation by name and rebuild all derived data.
     306             :      */
     307      216488 :     if (!entry->localrelvalid)
     308             :     {
     309             :         Oid         relid;
     310             :         Bitmapset  *idkey;
     311             :         TupleDesc   desc;
     312             :         MemoryContext oldctx;
     313             :         int         i;
     314             :         Bitmapset  *missingatts;
     315             : 
     316             :         /* Try to find and lock the relation by name. */
     317         480 :         relid = RangeVarGetRelid(makeRangeVar(remoterel->nspname,
     318             :                                               remoterel->relname, -1),
     319             :                                  lockmode, true);
     320         480 :         if (!OidIsValid(relid))
     321          10 :             ereport(ERROR,
     322             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     323             :                      errmsg("logical replication target relation \"%s.%s\" does not exist",
     324             :                             remoterel->nspname, remoterel->relname)));
     325         470 :         entry->localrel = table_open(relid, NoLock);
     326         470 :         entry->localreloid = relid;
     327             : 
     328             :         /* Check for supported relkind. */
     329         470 :         CheckSubscriptionRelkind(entry->localrel->rd_rel->relkind,
     330         470 :                                  remoterel->nspname, remoterel->relname);
     331             : 
     332             :         /*
     333             :          * Build the mapping of local attribute numbers to remote attribute
     334             :          * numbers and validate that we don't miss any replicated columns as
     335             :          * that would result in potentially unwanted data loss.
     336             :          */
     337         470 :         desc = RelationGetDescr(entry->localrel);
     338         470 :         oldctx = MemoryContextSwitchTo(LogicalRepRelMapContext);
     339         470 :         entry->attrmap = make_attrmap(desc->natts);
     340         470 :         MemoryContextSwitchTo(oldctx);
     341             : 
     342             :         /* check and report missing attrs, if any */
     343         470 :         missingatts = bms_add_range(NULL, 0, remoterel->natts - 1);
     344        1638 :         for (i = 0; i < desc->natts; i++)
     345             :         {
     346             :             int         attnum;
     347        1168 :             Form_pg_attribute attr = TupleDescAttr(desc, i);
     348             : 
     349        1168 :             if (attr->attisdropped || attr->attgenerated)
     350             :             {
     351           6 :                 entry->attrmap->attnums[i] = -1;
     352           6 :                 continue;
     353             :             }
     354             : 
     355        1162 :             attnum = logicalrep_rel_att_by_name(remoterel,
     356        1162 :                                                 NameStr(attr->attname));
     357             : 
     358        1162 :             entry->attrmap->attnums[i] = attnum;
     359        1162 :             if (attnum >= 0)
     360         886 :                 missingatts = bms_del_member(missingatts, attnum);
     361             :         }
     362             : 
     363         470 :         logicalrep_report_missing_attrs(remoterel, missingatts);
     364             : 
     365             :         /* be tidy */
     366         470 :         bms_free(missingatts);
     367             : 
     368             :         /*
     369             :          * Check that replica identity matches. We allow for stricter replica
     370             :          * identity (fewer columns) on subscriber as that will not stop us
     371             :          * from finding unique tuple. IE, if publisher has identity
     372             :          * (id,timestamp) and subscriber just (id) this will not be a problem,
     373             :          * but in the opposite scenario it will.
     374             :          *
     375             :          * Don't throw any error here just mark the relation entry as not
     376             :          * updatable, as replica identity is only for updates and deletes but
     377             :          * inserts can be replicated even without it.
     378             :          */
     379         470 :         entry->updatable = true;
     380         470 :         idkey = RelationGetIndexAttrBitmap(entry->localrel,
     381             :                                            INDEX_ATTR_BITMAP_IDENTITY_KEY);
     382             :         /* fallback to PK if no replica identity */
     383         470 :         if (idkey == NULL)
     384             :         {
     385         106 :             idkey = RelationGetIndexAttrBitmap(entry->localrel,
     386             :                                                INDEX_ATTR_BITMAP_PRIMARY_KEY);
     387             : 
     388             :             /*
     389             :              * If no replica identity index and no PK, the published table
     390             :              * must have replica identity FULL.
     391             :              */
     392         106 :             if (idkey == NULL && remoterel->replident != REPLICA_IDENTITY_FULL)
     393          92 :                 entry->updatable = false;
     394             :         }
     395             : 
     396         470 :         i = -1;
     397         822 :         while ((i = bms_next_member(idkey, i)) >= 0)
     398             :         {
     399         368 :             int         attnum = i + FirstLowInvalidHeapAttributeNumber;
     400             : 
     401         368 :             if (!AttrNumberIsForUserDefinedAttr(attnum))
     402           0 :                 ereport(ERROR,
     403             :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     404             :                          errmsg("logical replication target relation \"%s.%s\" uses "
     405             :                                 "system columns in REPLICA IDENTITY index",
     406             :                                 remoterel->nspname, remoterel->relname)));
     407             : 
     408         368 :             attnum = AttrNumberGetAttrOffset(attnum);
     409             : 
     410         368 :             if (entry->attrmap->attnums[attnum] < 0 ||
     411         366 :                 !bms_is_member(entry->attrmap->attnums[attnum], remoterel->attkeys))
     412             :             {
     413          16 :                 entry->updatable = false;
     414          16 :                 break;
     415             :             }
     416             :         }
     417             : 
     418         470 :         entry->localrelvalid = true;
     419             :     }
     420             : 
     421      216478 :     if (entry->state != SUBREL_STATE_READY)
     422         524 :         entry->state = GetSubscriptionRelState(MySubscription->oid,
     423             :                                                entry->localreloid,
     424             :                                                &entry->statelsn);
     425             : 
     426      216478 :     return entry;
     427             : }
     428             : 
     429             : /*
     430             :  * Close the previously opened logical relation.
     431             :  */
     432             : void
     433      216466 : logicalrep_rel_close(LogicalRepRelMapEntry *rel, LOCKMODE lockmode)
     434             : {
     435      216466 :     table_close(rel->localrel, lockmode);
     436      216466 :     rel->localrel = NULL;
     437      216466 : }
     438             : 
     439             : /*
     440             :  * Partition cache: look up partition LogicalRepRelMapEntry's
     441             :  *
     442             :  * Unlike relation map cache, this is keyed by partition OID, not remote
     443             :  * relation OID, because we only have to use this cache in the case where
     444             :  * partitions are not directly mapped to any remote relation, such as when
     445             :  * replication is occurring with one of their ancestors as target.
     446             :  */
     447             : 
     448             : /*
     449             :  * Relcache invalidation callback
     450             :  */
     451             : static void
     452         416 : logicalrep_partmap_invalidate_cb(Datum arg, Oid reloid)
     453             : {
     454             :     LogicalRepRelMapEntry *entry;
     455             : 
     456             :     /* Just to be sure. */
     457         416 :     if (LogicalRepPartMap == NULL)
     458           0 :         return;
     459             : 
     460         416 :     if (reloid != InvalidOid)
     461             :     {
     462             :         HASH_SEQ_STATUS status;
     463             : 
     464         416 :         hash_seq_init(&status, LogicalRepPartMap);
     465             : 
     466             :         /* TODO, use inverse lookup hashtable? */
     467        1024 :         while ((entry = (LogicalRepRelMapEntry *) hash_seq_search(&status)) != NULL)
     468             :         {
     469         608 :             if (entry->localreloid == reloid)
     470             :             {
     471           0 :                 entry->localrelvalid = false;
     472           0 :                 hash_seq_term(&status);
     473           0 :                 break;
     474             :             }
     475             :         }
     476             :     }
     477             :     else
     478             :     {
     479             :         /* invalidate all cache entries */
     480             :         HASH_SEQ_STATUS status;
     481             : 
     482           0 :         hash_seq_init(&status, LogicalRepPartMap);
     483             : 
     484           0 :         while ((entry = (LogicalRepRelMapEntry *) hash_seq_search(&status)) != NULL)
     485           0 :             entry->localrelvalid = false;
     486             :     }
     487             : }
     488             : 
     489             : /*
     490             :  * Initialize the partition map cache.
     491             :  */
     492             : static void
     493           6 : logicalrep_partmap_init(void)
     494             : {
     495             :     HASHCTL     ctl;
     496             : 
     497           6 :     if (!LogicalRepPartMapContext)
     498           6 :         LogicalRepPartMapContext =
     499           6 :             AllocSetContextCreate(CacheMemoryContext,
     500             :                                   "LogicalRepPartMapContext",
     501             :                                   ALLOCSET_DEFAULT_SIZES);
     502             : 
     503             :     /* Initialize the relation hash table. */
     504           6 :     ctl.keysize = sizeof(Oid);  /* partition OID */
     505           6 :     ctl.entrysize = sizeof(LogicalRepPartMapEntry);
     506           6 :     ctl.hcxt = LogicalRepPartMapContext;
     507             : 
     508           6 :     LogicalRepPartMap = hash_create("logicalrep partition map cache", 64, &ctl,
     509             :                                     HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     510             : 
     511             :     /* Watch for invalidation events. */
     512           6 :     CacheRegisterRelcacheCallback(logicalrep_partmap_invalidate_cb,
     513             :                                   (Datum) 0);
     514           6 : }
     515             : 
     516             : /*
     517             :  * logicalrep_partition_open
     518             :  *
     519             :  * Returned entry reuses most of the values of the root table's entry, save
     520             :  * the attribute map, which can be different for the partition.  However,
     521             :  * we must physically copy all the data, in case the root table's entry
     522             :  * gets freed/rebuilt.
     523             :  *
     524             :  * Note there's no logicalrep_partition_close, because the caller closes the
     525             :  * component relation.
     526             :  */
     527             : LogicalRepRelMapEntry *
     528          14 : logicalrep_partition_open(LogicalRepRelMapEntry *root,
     529             :                           Relation partrel, AttrMap *map)
     530             : {
     531             :     LogicalRepRelMapEntry *entry;
     532             :     LogicalRepPartMapEntry *part_entry;
     533          14 :     LogicalRepRelation *remoterel = &root->remoterel;
     534          14 :     Oid         partOid = RelationGetRelid(partrel);
     535          14 :     AttrMap    *attrmap = root->attrmap;
     536             :     bool        found;
     537             :     int         i;
     538             :     MemoryContext oldctx;
     539             : 
     540          14 :     if (LogicalRepPartMap == NULL)
     541           6 :         logicalrep_partmap_init();
     542             : 
     543             :     /* Search for existing entry. */
     544          14 :     part_entry = (LogicalRepPartMapEntry *) hash_search(LogicalRepPartMap,
     545             :                                                         (void *) &partOid,
     546             :                                                         HASH_ENTER, &found);
     547             : 
     548          14 :     if (found)
     549           6 :         return &part_entry->relmapentry;
     550             : 
     551           8 :     memset(part_entry, 0, sizeof(LogicalRepPartMapEntry));
     552             : 
     553             :     /* Switch to longer-lived context. */
     554           8 :     oldctx = MemoryContextSwitchTo(LogicalRepPartMapContext);
     555             : 
     556           8 :     part_entry->partoid = partOid;
     557             : 
     558             :     /* Remote relation is copied as-is from the root entry. */
     559           8 :     entry = &part_entry->relmapentry;
     560           8 :     entry->remoterel.remoteid = remoterel->remoteid;
     561           8 :     entry->remoterel.nspname = pstrdup(remoterel->nspname);
     562           8 :     entry->remoterel.relname = pstrdup(remoterel->relname);
     563           8 :     entry->remoterel.natts = remoterel->natts;
     564           8 :     entry->remoterel.attnames = palloc(remoterel->natts * sizeof(char *));
     565           8 :     entry->remoterel.atttyps = palloc(remoterel->natts * sizeof(Oid));
     566          24 :     for (i = 0; i < remoterel->natts; i++)
     567             :     {
     568          16 :         entry->remoterel.attnames[i] = pstrdup(remoterel->attnames[i]);
     569          16 :         entry->remoterel.atttyps[i] = remoterel->atttyps[i];
     570             :     }
     571           8 :     entry->remoterel.replident = remoterel->replident;
     572           8 :     entry->remoterel.attkeys = bms_copy(remoterel->attkeys);
     573             : 
     574           8 :     entry->localrel = partrel;
     575           8 :     entry->localreloid = partOid;
     576             : 
     577             :     /*
     578             :      * If the partition's attributes don't match the root relation's, we'll
     579             :      * need to make a new attrmap which maps partition attribute numbers to
     580             :      * remoterel's, instead of the original which maps root relation's
     581             :      * attribute numbers to remoterel's.
     582             :      *
     583             :      * Note that 'map' which comes from the tuple routing data structure
     584             :      * contains 1-based attribute numbers (of the parent relation).  However,
     585             :      * the map in 'entry', a logical replication data structure, contains
     586             :      * 0-based attribute numbers (of the remote relation).
     587             :      */
     588           8 :     if (map)
     589             :     {
     590             :         AttrNumber  attno;
     591             : 
     592           4 :         entry->attrmap = make_attrmap(map->maplen);
     593          16 :         for (attno = 0; attno < entry->attrmap->maplen; attno++)
     594             :         {
     595          12 :             AttrNumber  root_attno = map->attnums[attno];
     596             : 
     597          12 :             entry->attrmap->attnums[attno] = attrmap->attnums[root_attno - 1];
     598             :         }
     599             :     }
     600             :     else
     601             :     {
     602             :         /* Lacking copy_attmap, do this the hard way. */
     603           4 :         entry->attrmap = make_attrmap(attrmap->maplen);
     604           4 :         memcpy(entry->attrmap->attnums, attrmap->attnums,
     605           4 :                attrmap->maplen * sizeof(AttrNumber));
     606             :     }
     607             : 
     608           8 :     entry->updatable = root->updatable;
     609             : 
     610           8 :     entry->localrelvalid = true;
     611             : 
     612             :     /* state and statelsn are left set to 0. */
     613           8 :     MemoryContextSwitchTo(oldctx);
     614             : 
     615           8 :     return entry;
     616             : }

Generated by: LCOV version 1.14