@@ -57,17 +57,17 @@ static void send_relation_and_attrs(Relation relation, LogicalDecodingContext *c
5757/*
5858 * Entry in the map used to remember which relation schemas we sent.
5959 *
60+ * The schema_sent flag determines if the current schema record for the
61+ * relation (and for its ancestor if publish_as_relid is set) was already sent
62+ * to the subscriber (in which case we don't need to send it again).
63+ *
6064 * For partitions, 'pubactions' considers not only the table's own
6165 * publications, but also those of all of its ancestors.
6266 */
6367typedef struct RelationSyncEntry
6468{
6569 Oid relid ; /* relation oid */
6670
67- /*
68- * Did we send the schema? If ancestor relid is set, its schema must also
69- * have been sent for this to be true.
70- */
7171 bool schema_sent ;
7272
7373 bool replicate_valid ;
@@ -292,10 +292,17 @@ static void
292292maybe_send_schema (LogicalDecodingContext * ctx ,
293293 Relation relation , RelationSyncEntry * relentry )
294294{
295+ /* Nothing to do if we already sent the schema. */
295296 if (relentry -> schema_sent )
296297 return ;
297298
298- /* If needed, send the ancestor's schema first. */
299+ /*
300+ * Nope, so send the schema. If the changes will be published using an
301+ * ancestor's schema, not the relation's own, send that ancestor's schema
302+ * before sending relation's own (XXX - maybe sending only the former
303+ * suffices?). This is also a good place to set the map that will be used
304+ * to convert the relation's tuples into the ancestor's format, if needed.
305+ */
299306 if (relentry -> publish_as_relid != RelationGetRelid (relation ))
300307 {
301308 Relation ancestor = RelationIdGetRelation (relentry -> publish_as_relid );
@@ -305,8 +312,21 @@ maybe_send_schema(LogicalDecodingContext *ctx,
305312
306313 /* Map must live as long as the session does. */
307314 oldctx = MemoryContextSwitchTo (CacheMemoryContext );
308- relentry -> map = convert_tuples_by_name (CreateTupleDescCopy (indesc ),
309- CreateTupleDescCopy (outdesc ));
315+
316+ /*
317+ * Make copies of the TupleDescs that will live as long as the map
318+ * does before putting into the map.
319+ */
320+ indesc = CreateTupleDescCopy (indesc );
321+ outdesc = CreateTupleDescCopy (outdesc );
322+ relentry -> map = convert_tuples_by_name (indesc , outdesc );
323+ if (relentry -> map == NULL )
324+ {
325+ /* Map not necessary, so free the TupleDescs too. */
326+ FreeTupleDesc (indesc );
327+ FreeTupleDesc (outdesc );
328+ }
329+
310330 MemoryContextSwitchTo (oldctx );
311331 send_relation_and_attrs (ancestor , ctx );
312332 RelationClose (ancestor );
@@ -759,6 +779,7 @@ get_rel_sync_entry(PGOutputData *data, Oid relid)
759779 list_free (pubids );
760780
761781 entry -> publish_as_relid = publish_as_relid ;
782+ entry -> map = NULL ; /* will be set by maybe_send_schema() if needed */
762783 entry -> replicate_valid = true;
763784 }
764785
@@ -801,9 +822,23 @@ rel_sync_cache_relation_cb(Datum arg, Oid relid)
801822
802823 /*
803824 * Reset schema sent status as the relation definition may have changed.
825+ * Also, free any objects that depended on the earlier definition.
804826 */
805827 if (entry != NULL )
828+ {
806829 entry -> schema_sent = false;
830+ if (entry -> map )
831+ {
832+ /*
833+ * Must free the TupleDescs contained in the map explicitly,
834+ * because free_conversion_map() doesn't.
835+ */
836+ FreeTupleDesc (entry -> map -> indesc );
837+ FreeTupleDesc (entry -> map -> outdesc );
838+ free_conversion_map (entry -> map );
839+ }
840+ entry -> map = NULL ;
841+ }
807842}
808843
809844/*
0 commit comments