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 : }
|