45
45
#include "utils/rel.h"
46
46
#include "utils/syscache.h"
47
47
48
+ /* Records association between publication and published table */
49
+ typedef struct
50
+ {
51
+ Oid relid ; /* OID of published table */
52
+ Oid pubid ; /* OID of publication that publishes this
53
+ * table. */
54
+ } published_rel ;
55
+
48
56
static void publication_translate_columns (Relation targetrel , List * columns ,
49
57
int * natts , AttrNumber * * attrs );
50
58
@@ -172,42 +180,57 @@ pg_relation_is_publishable(PG_FUNCTION_ARGS)
172
180
}
173
181
174
182
/*
175
- * Filter out the partitions whose parent tables were also specified in
176
- * the publication .
183
+ * Returns true if the ancestor is in the list of published relations.
184
+ * Otherwise, returns false .
177
185
*/
178
- static List *
179
- filter_partitions (List * relids )
186
+ static bool
187
+ is_ancestor_member_tableinfos (Oid ancestor , List * table_infos )
188
+ {
189
+ ListCell * lc ;
190
+
191
+ foreach (lc , table_infos )
192
+ {
193
+ Oid relid = ((published_rel * ) lfirst (lc ))-> relid ;
194
+
195
+ if (relid == ancestor )
196
+ return true;
197
+ }
198
+
199
+ return false;
200
+ }
201
+
202
+ /*
203
+ * Filter out the partitions whose parent tables are also present in the list.
204
+ */
205
+ static void
206
+ filter_partitions (List * table_infos )
180
207
{
181
- List * result = NIL ;
182
208
ListCell * lc ;
183
- ListCell * lc2 ;
184
209
185
- foreach (lc , relids )
210
+ foreach (lc , table_infos )
186
211
{
187
212
bool skip = false;
188
213
List * ancestors = NIL ;
189
- Oid relid = lfirst_oid (lc );
214
+ ListCell * lc2 ;
215
+ published_rel * table_info = (published_rel * ) lfirst (lc );
190
216
191
- if (get_rel_relispartition (relid ))
192
- ancestors = get_partition_ancestors (relid );
217
+ if (get_rel_relispartition (table_info -> relid ))
218
+ ancestors = get_partition_ancestors (table_info -> relid );
193
219
194
220
foreach (lc2 , ancestors )
195
221
{
196
222
Oid ancestor = lfirst_oid (lc2 );
197
223
198
- /* Check if the parent table exists in the published table list. */
199
- if (list_member_oid (relids , ancestor ))
224
+ if (is_ancestor_member_tableinfos (ancestor , table_infos ))
200
225
{
201
226
skip = true;
202
227
break ;
203
228
}
204
229
}
205
230
206
- if (! skip )
207
- result = lappend_oid ( result , relid );
231
+ if (skip )
232
+ table_infos = foreach_delete_current ( table_infos , lc );
208
233
}
209
-
210
- return result ;
211
234
}
212
235
213
236
/*
@@ -1026,91 +1049,136 @@ GetPublicationByName(const char *pubname, bool missing_ok)
1026
1049
}
1027
1050
1028
1051
/*
1029
- * Returns information of tables in a publication.
1052
+ * Get information of the tables in the given publication array.
1053
+ *
1054
+ * Returns pubid, relid, column list, row filter for each table.
1030
1055
*/
1031
1056
Datum
1032
1057
pg_get_publication_tables (PG_FUNCTION_ARGS )
1033
1058
{
1034
- #define NUM_PUBLICATION_TABLES_ELEM 3
1059
+ #define NUM_PUBLICATION_TABLES_ELEM 4
1035
1060
FuncCallContext * funcctx ;
1036
- char * pubname = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
1037
- Publication * publication ;
1038
- List * tables ;
1061
+ List * table_infos = NIL ;
1039
1062
1040
1063
/* stuff done only on the first call of the function */
1041
1064
if (SRF_IS_FIRSTCALL ())
1042
1065
{
1043
1066
TupleDesc tupdesc ;
1044
1067
MemoryContext oldcontext ;
1068
+ ArrayType * arr ;
1069
+ Datum * elems ;
1070
+ int nelems ,
1071
+ i ;
1072
+ bool viaroot = false;
1045
1073
1046
1074
/* create a function context for cross-call persistence */
1047
1075
funcctx = SRF_FIRSTCALL_INIT ();
1048
1076
1049
1077
/* switch to memory context appropriate for multiple function calls */
1050
1078
oldcontext = MemoryContextSwitchTo (funcctx -> multi_call_memory_ctx );
1051
1079
1052
- publication = GetPublicationByName (pubname , false);
1053
-
1054
1080
/*
1055
- * Publications support partitioned tables, although all changes are
1056
- * replicated using leaf partition identity and schema, so we only
1057
- * need those.
1081
+ * Deconstruct the parameter into elements where each element is a
1082
+ * publication name.
1058
1083
*/
1059
- if (publication -> alltables )
1060
- {
1061
- tables = GetAllTablesPublicationRelations (publication -> pubviaroot );
1062
- }
1063
- else
1084
+ arr = PG_GETARG_ARRAYTYPE_P (0 );
1085
+ deconstruct_array (arr , TEXTOID , -1 , false, TYPALIGN_INT ,
1086
+ & elems , NULL , & nelems );
1087
+
1088
+ /* Get Oids of tables from each publication. */
1089
+ for (i = 0 ; i < nelems ; i ++ )
1064
1090
{
1065
- List * relids ,
1066
- * schemarelids ;
1067
-
1068
- relids = GetPublicationRelations (publication -> oid ,
1069
- publication -> pubviaroot ?
1070
- PUBLICATION_PART_ROOT :
1071
- PUBLICATION_PART_LEAF );
1072
- schemarelids = GetAllSchemaPublicationRelations (publication -> oid ,
1073
- publication -> pubviaroot ?
1074
- PUBLICATION_PART_ROOT :
1075
- PUBLICATION_PART_LEAF );
1076
- tables = list_concat_unique_oid (relids , schemarelids );
1091
+ Publication * pub_elem ;
1092
+ List * pub_elem_tables = NIL ;
1093
+ ListCell * lc ;
1094
+
1095
+ pub_elem = GetPublicationByName (TextDatumGetCString (elems [i ]), false);
1077
1096
1078
1097
/*
1079
- * If the publication publishes partition changes via their
1080
- * respective root partitioned tables, we must exclude partitions
1081
- * in favor of including the root partitioned tables. Otherwise,
1082
- * the function could return both the child and parent tables
1083
- * which could cause data of the child table to be
1084
- * double-published on the subscriber side.
1098
+ * Publications support partitioned tables. If
1099
+ * publish_via_partition_root is false, all changes are replicated
1100
+ * using leaf partition identity and schema, so we only need
1101
+ * those. Otherwise, get the partitioned table itself.
1085
1102
*/
1086
- if (publication -> pubviaroot )
1087
- tables = filter_partitions (tables );
1103
+ if (pub_elem -> alltables )
1104
+ pub_elem_tables = GetAllTablesPublicationRelations (pub_elem -> pubviaroot );
1105
+ else
1106
+ {
1107
+ List * relids ,
1108
+ * schemarelids ;
1109
+
1110
+ relids = GetPublicationRelations (pub_elem -> oid ,
1111
+ pub_elem -> pubviaroot ?
1112
+ PUBLICATION_PART_ROOT :
1113
+ PUBLICATION_PART_LEAF );
1114
+ schemarelids = GetAllSchemaPublicationRelations (pub_elem -> oid ,
1115
+ pub_elem -> pubviaroot ?
1116
+ PUBLICATION_PART_ROOT :
1117
+ PUBLICATION_PART_LEAF );
1118
+ pub_elem_tables = list_concat_unique_oid (relids , schemarelids );
1119
+ }
1120
+
1121
+ /*
1122
+ * Record the published table and the corresponding publication so
1123
+ * that we can get row filters and column lists later.
1124
+ *
1125
+ * When a table is published by multiple publications, to obtain
1126
+ * all row filters and column lists, the structure related to this
1127
+ * table will be recorded multiple times.
1128
+ */
1129
+ foreach (lc , pub_elem_tables )
1130
+ {
1131
+ published_rel * table_info = (published_rel * ) palloc (sizeof (published_rel ));
1132
+
1133
+ table_info -> relid = lfirst_oid (lc );
1134
+ table_info -> pubid = pub_elem -> oid ;
1135
+ table_infos = lappend (table_infos , table_info );
1136
+ }
1137
+
1138
+ /* At least one publication is using publish_via_partition_root. */
1139
+ if (pub_elem -> pubviaroot )
1140
+ viaroot = true;
1088
1141
}
1089
1142
1143
+ /*
1144
+ * If the publication publishes partition changes via their respective
1145
+ * root partitioned tables, we must exclude partitions in favor of
1146
+ * including the root partitioned tables. Otherwise, the function
1147
+ * could return both the child and parent tables which could cause
1148
+ * data of the child table to be double-published on the subscriber
1149
+ * side.
1150
+ */
1151
+ if (viaroot )
1152
+ filter_partitions (table_infos );
1153
+
1090
1154
/* Construct a tuple descriptor for the result rows. */
1091
1155
tupdesc = CreateTemplateTupleDesc (NUM_PUBLICATION_TABLES_ELEM );
1092
- TupleDescInitEntry (tupdesc , (AttrNumber ) 1 , "relid " ,
1156
+ TupleDescInitEntry (tupdesc , (AttrNumber ) 1 , "pubid " ,
1093
1157
OIDOID , -1 , 0 );
1094
- TupleDescInitEntry (tupdesc , (AttrNumber ) 2 , "attrs" ,
1158
+ TupleDescInitEntry (tupdesc , (AttrNumber ) 2 , "relid" ,
1159
+ OIDOID , -1 , 0 );
1160
+ TupleDescInitEntry (tupdesc , (AttrNumber ) 3 , "attrs" ,
1095
1161
INT2VECTOROID , -1 , 0 );
1096
- TupleDescInitEntry (tupdesc , (AttrNumber ) 3 , "qual" ,
1162
+ TupleDescInitEntry (tupdesc , (AttrNumber ) 4 , "qual" ,
1097
1163
PG_NODE_TREEOID , -1 , 0 );
1098
1164
1099
1165
funcctx -> tuple_desc = BlessTupleDesc (tupdesc );
1100
- funcctx -> user_fctx = (void * ) tables ;
1166
+ funcctx -> user_fctx = (void * ) table_infos ;
1101
1167
1102
1168
MemoryContextSwitchTo (oldcontext );
1103
1169
}
1104
1170
1105
1171
/* stuff done on every call of the function */
1106
1172
funcctx = SRF_PERCALL_SETUP ();
1107
- tables = (List * ) funcctx -> user_fctx ;
1173
+ table_infos = (List * ) funcctx -> user_fctx ;
1108
1174
1109
- if (funcctx -> call_cntr < list_length (tables ))
1175
+ if (funcctx -> call_cntr < list_length (table_infos ))
1110
1176
{
1111
1177
HeapTuple pubtuple = NULL ;
1112
1178
HeapTuple rettuple ;
1113
- Oid relid = list_nth_oid (tables , funcctx -> call_cntr );
1179
+ Publication * pub ;
1180
+ published_rel * table_info = (published_rel * ) list_nth (table_infos , funcctx -> call_cntr );
1181
+ Oid relid = table_info -> relid ;
1114
1182
Oid schemaid = get_rel_namespace (relid );
1115
1183
Datum values [NUM_PUBLICATION_TABLES_ELEM ] = {0 };
1116
1184
bool nulls [NUM_PUBLICATION_TABLES_ELEM ] = {0 };
@@ -1119,42 +1187,43 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
1119
1187
* Form tuple with appropriate data.
1120
1188
*/
1121
1189
1122
- publication = GetPublicationByName ( pubname , false );
1190
+ pub = GetPublication ( table_info -> pubid );
1123
1191
1124
- values [0 ] = ObjectIdGetDatum (relid );
1192
+ values [0 ] = ObjectIdGetDatum (pub -> oid );
1193
+ values [1 ] = ObjectIdGetDatum (relid );
1125
1194
1126
1195
/*
1127
1196
* We don't consider row filters or column lists for FOR ALL TABLES or
1128
1197
* FOR TABLES IN SCHEMA publications.
1129
1198
*/
1130
- if (!publication -> alltables &&
1199
+ if (!pub -> alltables &&
1131
1200
!SearchSysCacheExists2 (PUBLICATIONNAMESPACEMAP ,
1132
1201
ObjectIdGetDatum (schemaid ),
1133
- ObjectIdGetDatum (publication -> oid )))
1202
+ ObjectIdGetDatum (pub -> oid )))
1134
1203
pubtuple = SearchSysCacheCopy2 (PUBLICATIONRELMAP ,
1135
1204
ObjectIdGetDatum (relid ),
1136
- ObjectIdGetDatum (publication -> oid ));
1205
+ ObjectIdGetDatum (pub -> oid ));
1137
1206
1138
1207
if (HeapTupleIsValid (pubtuple ))
1139
1208
{
1140
1209
/* Lookup the column list attribute. */
1141
- values [1 ] = SysCacheGetAttr (PUBLICATIONRELMAP , pubtuple ,
1210
+ values [2 ] = SysCacheGetAttr (PUBLICATIONRELMAP , pubtuple ,
1142
1211
Anum_pg_publication_rel_prattrs ,
1143
- & (nulls [1 ]));
1212
+ & (nulls [2 ]));
1144
1213
1145
1214
/* Null indicates no filter. */
1146
- values [2 ] = SysCacheGetAttr (PUBLICATIONRELMAP , pubtuple ,
1215
+ values [3 ] = SysCacheGetAttr (PUBLICATIONRELMAP , pubtuple ,
1147
1216
Anum_pg_publication_rel_prqual ,
1148
- & (nulls [2 ]));
1217
+ & (nulls [3 ]));
1149
1218
}
1150
1219
else
1151
1220
{
1152
- nulls [1 ] = true;
1153
1221
nulls [2 ] = true;
1222
+ nulls [3 ] = true;
1154
1223
}
1155
1224
1156
1225
/* Show all columns when the column list is not specified. */
1157
- if (nulls [1 ] == true )
1226
+ if (nulls [2 ] )
1158
1227
{
1159
1228
Relation rel = table_open (relid , AccessShareLock );
1160
1229
int nattnums = 0 ;
@@ -1176,8 +1245,8 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
1176
1245
1177
1246
if (nattnums > 0 )
1178
1247
{
1179
- values [1 ] = PointerGetDatum (buildint2vector (attnums , nattnums ));
1180
- nulls [1 ] = false;
1248
+ values [2 ] = PointerGetDatum (buildint2vector (attnums , nattnums ));
1249
+ nulls [2 ] = false;
1181
1250
}
1182
1251
1183
1252
table_close (rel , AccessShareLock );
0 commit comments