@@ -57,17 +57,17 @@ static void send_relation_and_attrs(Relation relation, LogicalDecodingContext *c
57
57
/*
58
58
* Entry in the map used to remember which relation schemas we sent.
59
59
*
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
+ *
60
64
* For partitions, 'pubactions' considers not only the table's own
61
65
* publications, but also those of all of its ancestors.
62
66
*/
63
67
typedef struct RelationSyncEntry
64
68
{
65
69
Oid relid ; /* relation oid */
66
70
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
- */
71
71
bool schema_sent ;
72
72
73
73
bool replicate_valid ;
@@ -292,10 +292,17 @@ static void
292
292
maybe_send_schema (LogicalDecodingContext * ctx ,
293
293
Relation relation , RelationSyncEntry * relentry )
294
294
{
295
+ /* Nothing to do if we already sent the schema. */
295
296
if (relentry -> schema_sent )
296
297
return ;
297
298
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
+ */
299
306
if (relentry -> publish_as_relid != RelationGetRelid (relation ))
300
307
{
301
308
Relation ancestor = RelationIdGetRelation (relentry -> publish_as_relid );
@@ -305,8 +312,21 @@ maybe_send_schema(LogicalDecodingContext *ctx,
305
312
306
313
/* Map must live as long as the session does. */
307
314
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
+
310
330
MemoryContextSwitchTo (oldctx );
311
331
send_relation_and_attrs (ancestor , ctx );
312
332
RelationClose (ancestor );
@@ -759,6 +779,7 @@ get_rel_sync_entry(PGOutputData *data, Oid relid)
759
779
list_free (pubids );
760
780
761
781
entry -> publish_as_relid = publish_as_relid ;
782
+ entry -> map = NULL ; /* will be set by maybe_send_schema() if needed */
762
783
entry -> replicate_valid = true;
763
784
}
764
785
@@ -801,9 +822,23 @@ rel_sync_cache_relation_cb(Datum arg, Oid relid)
801
822
802
823
/*
803
824
* Reset schema sent status as the relation definition may have changed.
825
+ * Also, free any objects that depended on the earlier definition.
804
826
*/
805
827
if (entry != NULL )
828
+ {
806
829
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
+ }
807
842
}
808
843
809
844
/*
0 commit comments