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

Generated by: LCOV version 1.14