PostgreSQL Source Code  git master
pg_publication.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pg_publication.c
4  * publication C API manipulation
5  *
6  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * src/backend/catalog/pg_publication.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 
15 #include "postgres.h"
16 
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "access/htup_details.h"
20 #include "access/tableam.h"
21 #include "catalog/catalog.h"
22 #include "catalog/dependency.h"
23 #include "catalog/indexing.h"
24 #include "catalog/namespace.h"
25 #include "catalog/objectaddress.h"
26 #include "catalog/partition.h"
27 #include "catalog/pg_inherits.h"
28 #include "catalog/pg_namespace.h"
29 #include "catalog/pg_publication.h"
32 #include "catalog/pg_type.h"
34 #include "funcapi.h"
35 #include "utils/array.h"
36 #include "utils/builtins.h"
37 #include "utils/catcache.h"
38 #include "utils/fmgroids.h"
39 #include "utils/lsyscache.h"
40 #include "utils/rel.h"
41 #include "utils/syscache.h"
42 
43 /* Records association between publication and published table */
44 typedef struct
45 {
46  Oid relid; /* OID of published table */
47  Oid pubid; /* OID of publication that publishes this
48  * table. */
50 
51 /*
52  * Check if relation can be in given publication and throws appropriate
53  * error if not.
54  */
55 static void
57 {
58  /* Must be a regular or partitioned table */
59  if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
60  RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE)
61  ereport(ERROR,
62  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
63  errmsg("cannot add relation \"%s\" to publication",
64  RelationGetRelationName(targetrel)),
65  errdetail_relkind_not_supported(RelationGetForm(targetrel)->relkind)));
66 
67  /* Can't be system table */
68  if (IsCatalogRelation(targetrel))
69  ereport(ERROR,
70  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
71  errmsg("cannot add relation \"%s\" to publication",
72  RelationGetRelationName(targetrel)),
73  errdetail("This operation is not supported for system tables.")));
74 
75  /* UNLOGGED and TEMP relations cannot be part of publication. */
76  if (targetrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
77  ereport(ERROR,
78  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
79  errmsg("cannot add relation \"%s\" to publication",
80  RelationGetRelationName(targetrel)),
81  errdetail("This operation is not supported for temporary tables.")));
82  else if (targetrel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
83  ereport(ERROR,
84  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
85  errmsg("cannot add relation \"%s\" to publication",
86  RelationGetRelationName(targetrel)),
87  errdetail("This operation is not supported for unlogged tables.")));
88 }
89 
90 /*
91  * Check if schema can be in given publication and throw appropriate error if
92  * not.
93  */
94 static void
96 {
97  /* Can't be system namespace */
98  if (IsCatalogNamespace(schemaid) || IsToastNamespace(schemaid))
99  ereport(ERROR,
100  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
101  errmsg("cannot add schema \"%s\" to publication",
102  get_namespace_name(schemaid)),
103  errdetail("This operation is not supported for system schemas.")));
104 
105  /* Can't be temporary namespace */
106  if (isAnyTempNamespace(schemaid))
107  ereport(ERROR,
108  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
109  errmsg("cannot add schema \"%s\" to publication",
110  get_namespace_name(schemaid)),
111  errdetail("Temporary schemas cannot be replicated.")));
112 }
113 
114 /*
115  * Returns if relation represented by oid and Form_pg_class entry
116  * is publishable.
117  *
118  * Does same checks as check_publication_add_relation() above, but does not
119  * need relation to be opened and also does not throw errors.
120  *
121  * XXX This also excludes all tables with relid < FirstNormalObjectId,
122  * ie all tables created during initdb. This mainly affects the preinstalled
123  * information_schema. IsCatalogRelationOid() only excludes tables with
124  * relid < FirstUnpinnedObjectId, making that test rather redundant,
125  * but really we should get rid of the FirstNormalObjectId test not
126  * IsCatalogRelationOid. We can't do so today because we don't want
127  * information_schema tables to be considered publishable; but this test
128  * is really inadequate for that, since the information_schema could be
129  * dropped and reloaded and then it'll be considered publishable. The best
130  * long-term solution may be to add a "relispublishable" bool to pg_class,
131  * and depend on that instead of OID checks.
132  */
133 static bool
135 {
136  return (reltuple->relkind == RELKIND_RELATION ||
137  reltuple->relkind == RELKIND_PARTITIONED_TABLE) &&
138  !IsCatalogRelationOid(relid) &&
139  reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
140  relid >= FirstNormalObjectId;
141 }
142 
143 /*
144  * Another variant of is_publishable_class(), taking a Relation.
145  */
146 bool
148 {
149  return is_publishable_class(RelationGetRelid(rel), rel->rd_rel);
150 }
151 
152 /*
153  * SQL-callable variant of the above
154  *
155  * This returns null when the relation does not exist. This is intended to be
156  * used for example in psql to avoid gratuitous errors when there are
157  * concurrent catalog changes.
158  */
159 Datum
161 {
162  Oid relid = PG_GETARG_OID(0);
163  HeapTuple tuple;
164  bool result;
165 
166  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
167  if (!HeapTupleIsValid(tuple))
168  PG_RETURN_NULL();
169  result = is_publishable_class(relid, (Form_pg_class) GETSTRUCT(tuple));
170  ReleaseSysCache(tuple);
171  PG_RETURN_BOOL(result);
172 }
173 
174 /*
175  * Returns true if the ancestor is in the list of published relations.
176  * Otherwise, returns false.
177  */
178 static bool
179 is_ancestor_member_tableinfos(Oid ancestor, List *table_infos)
180 {
181  ListCell *lc;
182 
183  foreach(lc, table_infos)
184  {
185  Oid relid = ((published_rel *) lfirst(lc))->relid;
186 
187  if (relid == ancestor)
188  return true;
189  }
190 
191  return false;
192 }
193 
194 /*
195  * Filter out the partitions whose parent tables are also present in the list.
196  */
197 static void
198 filter_partitions(List *table_infos)
199 {
200  ListCell *lc;
201 
202  foreach(lc, table_infos)
203  {
204  bool skip = false;
205  List *ancestors = NIL;
206  ListCell *lc2;
207  published_rel *table_info = (published_rel *) lfirst(lc);
208 
209  if (get_rel_relispartition(table_info->relid))
210  ancestors = get_partition_ancestors(table_info->relid);
211 
212  foreach(lc2, ancestors)
213  {
214  Oid ancestor = lfirst_oid(lc2);
215 
216  if (is_ancestor_member_tableinfos(ancestor, table_infos))
217  {
218  skip = true;
219  break;
220  }
221  }
222 
223  if (skip)
224  table_infos = foreach_delete_current(table_infos, lc);
225  }
226 }
227 
228 /*
229  * Returns true if any schema is associated with the publication, false if no
230  * schema is associated with the publication.
231  */
232 bool
234 {
235  Relation pubschsrel;
236  ScanKeyData scankey;
237  SysScanDesc scan;
238  HeapTuple tup;
239  bool result = false;
240 
241  pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
242  ScanKeyInit(&scankey,
243  Anum_pg_publication_namespace_pnpubid,
244  BTEqualStrategyNumber, F_OIDEQ,
245  ObjectIdGetDatum(pubid));
246 
247  scan = systable_beginscan(pubschsrel,
248  PublicationNamespacePnnspidPnpubidIndexId,
249  true, NULL, 1, &scankey);
250  tup = systable_getnext(scan);
251  result = HeapTupleIsValid(tup);
252 
253  systable_endscan(scan);
254  table_close(pubschsrel, AccessShareLock);
255 
256  return result;
257 }
258 
259 /*
260  * Returns true if the relation has column list associated with the
261  * publication, false otherwise.
262  *
263  * If a column list is found, the corresponding bitmap is returned through the
264  * cols parameter, if provided. The bitmap is constructed within the given
265  * memory context (mcxt).
266  */
267 bool
269  Bitmapset **cols)
270 {
271  HeapTuple cftuple;
272  bool found = false;
273 
274  if (pub->alltables)
275  return false;
276 
277  cftuple = SearchSysCache2(PUBLICATIONRELMAP,
278  ObjectIdGetDatum(relid),
279  ObjectIdGetDatum(pub->oid));
280  if (HeapTupleIsValid(cftuple))
281  {
282  Datum cfdatum;
283  bool isnull;
284 
285  /* Lookup the column list attribute. */
286  cfdatum = SysCacheGetAttr(PUBLICATIONRELMAP, cftuple,
287  Anum_pg_publication_rel_prattrs, &isnull);
288 
289  /* Was a column list found? */
290  if (!isnull)
291  {
292  /* Build the column list bitmap in the given memory context. */
293  if (cols)
294  *cols = pub_collist_to_bitmapset(*cols, cfdatum, mcxt);
295 
296  found = true;
297  }
298 
299  ReleaseSysCache(cftuple);
300  }
301 
302  return found;
303 }
304 
305 /*
306  * Gets the relations based on the publication partition option for a specified
307  * relation.
308  */
309 List *
311  Oid relid)
312 {
313  if (get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE &&
314  pub_partopt != PUBLICATION_PART_ROOT)
315  {
316  List *all_parts = find_all_inheritors(relid, NoLock,
317  NULL);
318 
319  if (pub_partopt == PUBLICATION_PART_ALL)
320  result = list_concat(result, all_parts);
321  else if (pub_partopt == PUBLICATION_PART_LEAF)
322  {
323  ListCell *lc;
324 
325  foreach(lc, all_parts)
326  {
327  Oid partOid = lfirst_oid(lc);
328 
329  if (get_rel_relkind(partOid) != RELKIND_PARTITIONED_TABLE)
330  result = lappend_oid(result, partOid);
331  }
332  }
333  else
334  Assert(false);
335  }
336  else
337  result = lappend_oid(result, relid);
338 
339  return result;
340 }
341 
342 /*
343  * Returns the relid of the topmost ancestor that is published via this
344  * publication if any and set its ancestor level to ancestor_level,
345  * otherwise returns InvalidOid.
346  *
347  * The ancestor_level value allows us to compare the results for multiple
348  * publications, and decide which value is higher up.
349  *
350  * Note that the list of ancestors should be ordered such that the topmost
351  * ancestor is at the end of the list.
352  */
353 Oid
354 GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level)
355 {
356  ListCell *lc;
357  Oid topmost_relid = InvalidOid;
358  int level = 0;
359 
360  /*
361  * Find the "topmost" ancestor that is in this publication.
362  */
363  foreach(lc, ancestors)
364  {
365  Oid ancestor = lfirst_oid(lc);
366  List *apubids = GetRelationPublications(ancestor);
367  List *aschemaPubids = NIL;
368 
369  level++;
370 
371  if (list_member_oid(apubids, puboid))
372  {
373  topmost_relid = ancestor;
374 
375  if (ancestor_level)
376  *ancestor_level = level;
377  }
378  else
379  {
380  aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
381  if (list_member_oid(aschemaPubids, puboid))
382  {
383  topmost_relid = ancestor;
384 
385  if (ancestor_level)
386  *ancestor_level = level;
387  }
388  }
389 
390  list_free(apubids);
391  list_free(aschemaPubids);
392  }
393 
394  return topmost_relid;
395 }
396 
397 /*
398  * attnumstoint2vector
399  * Convert a Bitmapset of AttrNumbers into an int2vector.
400  *
401  * AttrNumber numbers are 0-based, i.e., not offset by
402  * FirstLowInvalidHeapAttributeNumber.
403  */
404 static int2vector *
406 {
407  int2vector *result;
408  int n = bms_num_members(attrs);
409  int i = -1;
410  int j = 0;
411 
412  result = buildint2vector(NULL, n);
413 
414  while ((i = bms_next_member(attrs, i)) >= 0)
415  {
416  Assert(i <= PG_INT16_MAX);
417 
418  result->values[j++] = (int16) i;
419  }
420 
421  return result;
422 }
423 
424 /*
425  * Insert new publication / relation mapping.
426  */
429  bool if_not_exists)
430 {
431  Relation rel;
432  HeapTuple tup;
433  Datum values[Natts_pg_publication_rel];
434  bool nulls[Natts_pg_publication_rel];
435  Relation targetrel = pri->relation;
436  Oid relid = RelationGetRelid(targetrel);
437  Oid pubreloid;
438  Bitmapset *attnums;
439  Publication *pub = GetPublication(pubid);
440  ObjectAddress myself,
441  referenced;
442  List *relids = NIL;
443  int i;
444 
445  rel = table_open(PublicationRelRelationId, RowExclusiveLock);
446 
447  /*
448  * Check for duplicates. Note that this does not really prevent
449  * duplicates, it's here just to provide nicer error message in common
450  * case. The real protection is the unique key on the catalog.
451  */
452  if (SearchSysCacheExists2(PUBLICATIONRELMAP, ObjectIdGetDatum(relid),
453  ObjectIdGetDatum(pubid)))
454  {
456 
457  if (if_not_exists)
458  return InvalidObjectAddress;
459 
460  ereport(ERROR,
462  errmsg("relation \"%s\" is already member of publication \"%s\"",
463  RelationGetRelationName(targetrel), pub->name)));
464  }
465 
467 
468  /* Validate and translate column names into a Bitmapset of attnums. */
469  attnums = pub_collist_validate(pri->relation, pri->columns);
470 
471  /* Form a tuple. */
472  memset(values, 0, sizeof(values));
473  memset(nulls, false, sizeof(nulls));
474 
475  pubreloid = GetNewOidWithIndex(rel, PublicationRelObjectIndexId,
476  Anum_pg_publication_rel_oid);
477  values[Anum_pg_publication_rel_oid - 1] = ObjectIdGetDatum(pubreloid);
478  values[Anum_pg_publication_rel_prpubid - 1] =
479  ObjectIdGetDatum(pubid);
480  values[Anum_pg_publication_rel_prrelid - 1] =
481  ObjectIdGetDatum(relid);
482 
483  /* Add qualifications, if available */
484  if (pri->whereClause != NULL)
485  values[Anum_pg_publication_rel_prqual - 1] = CStringGetTextDatum(nodeToString(pri->whereClause));
486  else
487  nulls[Anum_pg_publication_rel_prqual - 1] = true;
488 
489  /* Add column list, if available */
490  if (pri->columns)
491  values[Anum_pg_publication_rel_prattrs - 1] = PointerGetDatum(attnumstoint2vector(attnums));
492  else
493  nulls[Anum_pg_publication_rel_prattrs - 1] = true;
494 
495  tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
496 
497  /* Insert tuple into catalog. */
498  CatalogTupleInsert(rel, tup);
499  heap_freetuple(tup);
500 
501  /* Register dependencies as needed */
502  ObjectAddressSet(myself, PublicationRelRelationId, pubreloid);
503 
504  /* Add dependency on the publication */
505  ObjectAddressSet(referenced, PublicationRelationId, pubid);
506  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
507 
508  /* Add dependency on the relation */
509  ObjectAddressSet(referenced, RelationRelationId, relid);
510  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
511 
512  /* Add dependency on the objects mentioned in the qualifications */
513  if (pri->whereClause)
514  recordDependencyOnSingleRelExpr(&myself, pri->whereClause, relid,
516  false);
517 
518  /* Add dependency on the columns, if any are listed */
519  i = -1;
520  while ((i = bms_next_member(attnums, i)) >= 0)
521  {
522  ObjectAddressSubSet(referenced, RelationRelationId, relid, i);
523  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
524  }
525 
526  /* Close the table. */
528 
529  /*
530  * Invalidate relcache so that publication info is rebuilt.
531  *
532  * For the partitioned tables, we must invalidate all partitions contained
533  * in the respective partition hierarchies, not just the one explicitly
534  * mentioned in the publication. This is required because we implicitly
535  * publish the child tables when the parent table is published.
536  */
538  relid);
539 
541 
542  return myself;
543 }
544 
545 /*
546  * pub_collist_validate
547  * Process and validate the 'columns' list and ensure the columns are all
548  * valid to use for a publication. Checks for and raises an ERROR for
549  * any unknown columns, system columns, or duplicate columns.
550  *
551  * Looks up each column's attnum and returns a 0-based Bitmapset of the
552  * corresponding attnums.
553  */
554 Bitmapset *
555 pub_collist_validate(Relation targetrel, List *columns)
556 {
557  Bitmapset *set = NULL;
558  ListCell *lc;
559 
560  foreach(lc, columns)
561  {
562  char *colname = strVal(lfirst(lc));
563  AttrNumber attnum = get_attnum(RelationGetRelid(targetrel), colname);
564 
565  if (attnum == InvalidAttrNumber)
566  ereport(ERROR,
567  errcode(ERRCODE_UNDEFINED_COLUMN),
568  errmsg("column \"%s\" of relation \"%s\" does not exist",
569  colname, RelationGetRelationName(targetrel)));
570 
572  ereport(ERROR,
573  errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
574  errmsg("cannot use system column \"%s\" in publication column list",
575  colname));
576 
577  if (bms_is_member(attnum, set))
578  ereport(ERROR,
580  errmsg("duplicate column \"%s\" in publication column list",
581  colname));
582 
583  set = bms_add_member(set, attnum);
584  }
585 
586  return set;
587 }
588 
589 /*
590  * Transform a column list (represented by an array Datum) to a bitmapset.
591  *
592  * If columns isn't NULL, add the column numbers to that set.
593  *
594  * If mcxt isn't NULL, build the bitmapset in that context.
595  */
596 Bitmapset *
598 {
599  Bitmapset *result = columns;
600  ArrayType *arr;
601  int nelems;
602  int16 *elems;
603  MemoryContext oldcxt = NULL;
604 
605  arr = DatumGetArrayTypeP(pubcols);
606  nelems = ARR_DIMS(arr)[0];
607  elems = (int16 *) ARR_DATA_PTR(arr);
608 
609  /* If a memory context was specified, switch to it. */
610  if (mcxt)
611  oldcxt = MemoryContextSwitchTo(mcxt);
612 
613  for (int i = 0; i < nelems; i++)
614  result = bms_add_member(result, elems[i]);
615 
616  if (mcxt)
617  MemoryContextSwitchTo(oldcxt);
618 
619  return result;
620 }
621 
622 /*
623  * Returns a bitmap representing the columns of the specified table.
624  *
625  * Generated columns are included if include_gencols is true.
626  */
627 Bitmapset *
628 pub_form_cols_map(Relation relation, bool include_gencols)
629 {
630  Bitmapset *result = NULL;
631  TupleDesc desc = RelationGetDescr(relation);
632 
633  for (int i = 0; i < desc->natts; i++)
634  {
635  Form_pg_attribute att = TupleDescAttr(desc, i);
636 
637  if (att->attisdropped || (att->attgenerated && !include_gencols))
638  continue;
639 
640  result = bms_add_member(result, att->attnum);
641  }
642 
643  return result;
644 }
645 
646 /*
647  * Insert new publication / schema mapping.
648  */
650 publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
651 {
652  Relation rel;
653  HeapTuple tup;
654  Datum values[Natts_pg_publication_namespace];
655  bool nulls[Natts_pg_publication_namespace];
656  Oid psschid;
657  Publication *pub = GetPublication(pubid);
658  List *schemaRels = NIL;
659  ObjectAddress myself,
660  referenced;
661 
662  rel = table_open(PublicationNamespaceRelationId, RowExclusiveLock);
663 
664  /*
665  * Check for duplicates. Note that this does not really prevent
666  * duplicates, it's here just to provide nicer error message in common
667  * case. The real protection is the unique key on the catalog.
668  */
669  if (SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
670  ObjectIdGetDatum(schemaid),
671  ObjectIdGetDatum(pubid)))
672  {
674 
675  if (if_not_exists)
676  return InvalidObjectAddress;
677 
678  ereport(ERROR,
680  errmsg("schema \"%s\" is already member of publication \"%s\"",
681  get_namespace_name(schemaid), pub->name)));
682  }
683 
685 
686  /* Form a tuple */
687  memset(values, 0, sizeof(values));
688  memset(nulls, false, sizeof(nulls));
689 
690  psschid = GetNewOidWithIndex(rel, PublicationNamespaceObjectIndexId,
691  Anum_pg_publication_namespace_oid);
692  values[Anum_pg_publication_namespace_oid - 1] = ObjectIdGetDatum(psschid);
693  values[Anum_pg_publication_namespace_pnpubid - 1] =
694  ObjectIdGetDatum(pubid);
695  values[Anum_pg_publication_namespace_pnnspid - 1] =
696  ObjectIdGetDatum(schemaid);
697 
698  tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
699 
700  /* Insert tuple into catalog */
701  CatalogTupleInsert(rel, tup);
702  heap_freetuple(tup);
703 
704  ObjectAddressSet(myself, PublicationNamespaceRelationId, psschid);
705 
706  /* Add dependency on the publication */
707  ObjectAddressSet(referenced, PublicationRelationId, pubid);
708  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
709 
710  /* Add dependency on the schema */
711  ObjectAddressSet(referenced, NamespaceRelationId, schemaid);
712  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
713 
714  /* Close the table */
716 
717  /*
718  * Invalidate relcache so that publication info is rebuilt. See
719  * publication_add_relation for why we need to consider all the
720  * partitions.
721  */
722  schemaRels = GetSchemaPublicationRelations(schemaid,
724  InvalidatePublicationRels(schemaRels);
725 
726  return myself;
727 }
728 
729 /* Gets list of publication oids for a relation */
730 List *
732 {
733  List *result = NIL;
734  CatCList *pubrellist;
735  int i;
736 
737  /* Find all publications associated with the relation. */
738  pubrellist = SearchSysCacheList1(PUBLICATIONRELMAP,
739  ObjectIdGetDatum(relid));
740  for (i = 0; i < pubrellist->n_members; i++)
741  {
742  HeapTuple tup = &pubrellist->members[i]->tuple;
743  Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid;
744 
745  result = lappend_oid(result, pubid);
746  }
747 
748  ReleaseSysCacheList(pubrellist);
749 
750  return result;
751 }
752 
753 /*
754  * Gets list of relation oids for a publication.
755  *
756  * This should only be used FOR TABLE publications, the FOR ALL TABLES
757  * should use GetAllTablesPublicationRelations().
758  */
759 List *
761 {
762  List *result;
763  Relation pubrelsrel;
764  ScanKeyData scankey;
765  SysScanDesc scan;
766  HeapTuple tup;
767 
768  /* Find all publications associated with the relation. */
769  pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
770 
771  ScanKeyInit(&scankey,
772  Anum_pg_publication_rel_prpubid,
773  BTEqualStrategyNumber, F_OIDEQ,
774  ObjectIdGetDatum(pubid));
775 
776  scan = systable_beginscan(pubrelsrel, PublicationRelPrpubidIndexId,
777  true, NULL, 1, &scankey);
778 
779  result = NIL;
780  while (HeapTupleIsValid(tup = systable_getnext(scan)))
781  {
783 
784  pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
785  result = GetPubPartitionOptionRelations(result, pub_partopt,
786  pubrel->prrelid);
787  }
788 
789  systable_endscan(scan);
790  table_close(pubrelsrel, AccessShareLock);
791 
792  /* Now sort and de-duplicate the result list */
793  list_sort(result, list_oid_cmp);
794  list_deduplicate_oid(result);
795 
796  return result;
797 }
798 
799 /*
800  * Gets list of publication oids for publications marked as FOR ALL TABLES.
801  */
802 List *
804 {
805  List *result;
806  Relation rel;
807  ScanKeyData scankey;
808  SysScanDesc scan;
809  HeapTuple tup;
810 
811  /* Find all publications that are marked as for all tables. */
812  rel = table_open(PublicationRelationId, AccessShareLock);
813 
814  ScanKeyInit(&scankey,
815  Anum_pg_publication_puballtables,
816  BTEqualStrategyNumber, F_BOOLEQ,
817  BoolGetDatum(true));
818 
819  scan = systable_beginscan(rel, InvalidOid, false,
820  NULL, 1, &scankey);
821 
822  result = NIL;
823  while (HeapTupleIsValid(tup = systable_getnext(scan)))
824  {
825  Oid oid = ((Form_pg_publication) GETSTRUCT(tup))->oid;
826 
827  result = lappend_oid(result, oid);
828  }
829 
830  systable_endscan(scan);
832 
833  return result;
834 }
835 
836 /*
837  * Gets list of all relation published by FOR ALL TABLES publication(s).
838  *
839  * If the publication publishes partition changes via their respective root
840  * partitioned tables, we must exclude partitions in favor of including the
841  * root partitioned tables.
842  */
843 List *
845 {
846  Relation classRel;
847  ScanKeyData key[1];
848  TableScanDesc scan;
849  HeapTuple tuple;
850  List *result = NIL;
851 
852  classRel = table_open(RelationRelationId, AccessShareLock);
853 
854  ScanKeyInit(&key[0],
855  Anum_pg_class_relkind,
856  BTEqualStrategyNumber, F_CHAREQ,
857  CharGetDatum(RELKIND_RELATION));
858 
859  scan = table_beginscan_catalog(classRel, 1, key);
860 
861  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
862  {
863  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
864  Oid relid = relForm->oid;
865 
866  if (is_publishable_class(relid, relForm) &&
867  !(relForm->relispartition && pubviaroot))
868  result = lappend_oid(result, relid);
869  }
870 
871  table_endscan(scan);
872 
873  if (pubviaroot)
874  {
875  ScanKeyInit(&key[0],
876  Anum_pg_class_relkind,
877  BTEqualStrategyNumber, F_CHAREQ,
878  CharGetDatum(RELKIND_PARTITIONED_TABLE));
879 
880  scan = table_beginscan_catalog(classRel, 1, key);
881 
882  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
883  {
884  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
885  Oid relid = relForm->oid;
886 
887  if (is_publishable_class(relid, relForm) &&
888  !relForm->relispartition)
889  result = lappend_oid(result, relid);
890  }
891 
892  table_endscan(scan);
893  }
894 
895  table_close(classRel, AccessShareLock);
896  return result;
897 }
898 
899 /*
900  * Gets the list of schema oids for a publication.
901  *
902  * This should only be used FOR TABLES IN SCHEMA publications.
903  */
904 List *
906 {
907  List *result = NIL;
908  Relation pubschsrel;
909  ScanKeyData scankey;
910  SysScanDesc scan;
911  HeapTuple tup;
912 
913  /* Find all schemas associated with the publication */
914  pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
915 
916  ScanKeyInit(&scankey,
917  Anum_pg_publication_namespace_pnpubid,
918  BTEqualStrategyNumber, F_OIDEQ,
919  ObjectIdGetDatum(pubid));
920 
921  scan = systable_beginscan(pubschsrel,
922  PublicationNamespacePnnspidPnpubidIndexId,
923  true, NULL, 1, &scankey);
924  while (HeapTupleIsValid(tup = systable_getnext(scan)))
925  {
927 
929 
930  result = lappend_oid(result, pubsch->pnnspid);
931  }
932 
933  systable_endscan(scan);
934  table_close(pubschsrel, AccessShareLock);
935 
936  return result;
937 }
938 
939 /*
940  * Gets the list of publication oids associated with a specified schema.
941  */
942 List *
944 {
945  List *result = NIL;
946  CatCList *pubschlist;
947  int i;
948 
949  /* Find all publications associated with the schema */
950  pubschlist = SearchSysCacheList1(PUBLICATIONNAMESPACEMAP,
951  ObjectIdGetDatum(schemaid));
952  for (i = 0; i < pubschlist->n_members; i++)
953  {
954  HeapTuple tup = &pubschlist->members[i]->tuple;
955  Oid pubid = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pnpubid;
956 
957  result = lappend_oid(result, pubid);
958  }
959 
960  ReleaseSysCacheList(pubschlist);
961 
962  return result;
963 }
964 
965 /*
966  * Get the list of publishable relation oids for a specified schema.
967  */
968 List *
970 {
971  Relation classRel;
972  ScanKeyData key[1];
973  TableScanDesc scan;
974  HeapTuple tuple;
975  List *result = NIL;
976 
977  Assert(OidIsValid(schemaid));
978 
979  classRel = table_open(RelationRelationId, AccessShareLock);
980 
981  ScanKeyInit(&key[0],
982  Anum_pg_class_relnamespace,
983  BTEqualStrategyNumber, F_OIDEQ,
984  schemaid);
985 
986  /* get all the relations present in the specified schema */
987  scan = table_beginscan_catalog(classRel, 1, key);
988  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
989  {
990  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
991  Oid relid = relForm->oid;
992  char relkind;
993 
994  if (!is_publishable_class(relid, relForm))
995  continue;
996 
997  relkind = get_rel_relkind(relid);
998  if (relkind == RELKIND_RELATION)
999  result = lappend_oid(result, relid);
1000  else if (relkind == RELKIND_PARTITIONED_TABLE)
1001  {
1002  List *partitionrels = NIL;
1003 
1004  /*
1005  * It is quite possible that some of the partitions are in a
1006  * different schema than the parent table, so we need to get such
1007  * partitions separately.
1008  */
1009  partitionrels = GetPubPartitionOptionRelations(partitionrels,
1010  pub_partopt,
1011  relForm->oid);
1012  result = list_concat_unique_oid(result, partitionrels);
1013  }
1014  }
1015 
1016  table_endscan(scan);
1017  table_close(classRel, AccessShareLock);
1018  return result;
1019 }
1020 
1021 /*
1022  * Gets the list of all relations published by FOR TABLES IN SCHEMA
1023  * publication.
1024  */
1025 List *
1027 {
1028  List *result = NIL;
1029  List *pubschemalist = GetPublicationSchemas(pubid);
1030  ListCell *cell;
1031 
1032  foreach(cell, pubschemalist)
1033  {
1034  Oid schemaid = lfirst_oid(cell);
1035  List *schemaRels = NIL;
1036 
1037  schemaRels = GetSchemaPublicationRelations(schemaid, pub_partopt);
1038  result = list_concat(result, schemaRels);
1039  }
1040 
1041  return result;
1042 }
1043 
1044 /*
1045  * Get publication using oid
1046  *
1047  * The Publication struct and its data are palloc'ed here.
1048  */
1049 Publication *
1051 {
1052  HeapTuple tup;
1053  Publication *pub;
1054  Form_pg_publication pubform;
1055 
1056  tup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid));
1057  if (!HeapTupleIsValid(tup))
1058  elog(ERROR, "cache lookup failed for publication %u", pubid);
1059 
1060  pubform = (Form_pg_publication) GETSTRUCT(tup);
1061 
1062  pub = (Publication *) palloc(sizeof(Publication));
1063  pub->oid = pubid;
1064  pub->name = pstrdup(NameStr(pubform->pubname));
1065  pub->alltables = pubform->puballtables;
1066  pub->pubactions.pubinsert = pubform->pubinsert;
1067  pub->pubactions.pubupdate = pubform->pubupdate;
1068  pub->pubactions.pubdelete = pubform->pubdelete;
1069  pub->pubactions.pubtruncate = pubform->pubtruncate;
1070  pub->pubviaroot = pubform->pubviaroot;
1071  pub->pubgencols = pubform->pubgencols;
1072 
1073  ReleaseSysCache(tup);
1074 
1075  return pub;
1076 }
1077 
1078 /*
1079  * Get Publication using name.
1080  */
1081 Publication *
1082 GetPublicationByName(const char *pubname, bool missing_ok)
1083 {
1084  Oid oid;
1085 
1086  oid = get_publication_oid(pubname, missing_ok);
1087 
1088  return OidIsValid(oid) ? GetPublication(oid) : NULL;
1089 }
1090 
1091 /*
1092  * Get information of the tables in the given publication array.
1093  *
1094  * Returns pubid, relid, column list, row filter for each table.
1095  */
1096 Datum
1098 {
1099 #define NUM_PUBLICATION_TABLES_ELEM 4
1100  FuncCallContext *funcctx;
1101  List *table_infos = NIL;
1102 
1103  /* stuff done only on the first call of the function */
1104  if (SRF_IS_FIRSTCALL())
1105  {
1106  TupleDesc tupdesc;
1107  MemoryContext oldcontext;
1108  ArrayType *arr;
1109  Datum *elems;
1110  int nelems,
1111  i;
1112  bool viaroot = false;
1113 
1114  /* create a function context for cross-call persistence */
1115  funcctx = SRF_FIRSTCALL_INIT();
1116 
1117  /* switch to memory context appropriate for multiple function calls */
1118  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1119 
1120  /*
1121  * Deconstruct the parameter into elements where each element is a
1122  * publication name.
1123  */
1124  arr = PG_GETARG_ARRAYTYPE_P(0);
1125  deconstruct_array_builtin(arr, TEXTOID, &elems, NULL, &nelems);
1126 
1127  /* Get Oids of tables from each publication. */
1128  for (i = 0; i < nelems; i++)
1129  {
1130  Publication *pub_elem;
1131  List *pub_elem_tables = NIL;
1132  ListCell *lc;
1133 
1134  pub_elem = GetPublicationByName(TextDatumGetCString(elems[i]), false);
1135 
1136  /*
1137  * Publications support partitioned tables. If
1138  * publish_via_partition_root is false, all changes are replicated
1139  * using leaf partition identity and schema, so we only need
1140  * those. Otherwise, get the partitioned table itself.
1141  */
1142  if (pub_elem->alltables)
1143  pub_elem_tables = GetAllTablesPublicationRelations(pub_elem->pubviaroot);
1144  else
1145  {
1146  List *relids,
1147  *schemarelids;
1148 
1149  relids = GetPublicationRelations(pub_elem->oid,
1150  pub_elem->pubviaroot ?
1153  schemarelids = GetAllSchemaPublicationRelations(pub_elem->oid,
1154  pub_elem->pubviaroot ?
1157  pub_elem_tables = list_concat_unique_oid(relids, schemarelids);
1158  }
1159 
1160  /*
1161  * Record the published table and the corresponding publication so
1162  * that we can get row filters and column lists later.
1163  *
1164  * When a table is published by multiple publications, to obtain
1165  * all row filters and column lists, the structure related to this
1166  * table will be recorded multiple times.
1167  */
1168  foreach(lc, pub_elem_tables)
1169  {
1170  published_rel *table_info = (published_rel *) palloc(sizeof(published_rel));
1171 
1172  table_info->relid = lfirst_oid(lc);
1173  table_info->pubid = pub_elem->oid;
1174  table_infos = lappend(table_infos, table_info);
1175  }
1176 
1177  /* At least one publication is using publish_via_partition_root. */
1178  if (pub_elem->pubviaroot)
1179  viaroot = true;
1180  }
1181 
1182  /*
1183  * If the publication publishes partition changes via their respective
1184  * root partitioned tables, we must exclude partitions in favor of
1185  * including the root partitioned tables. Otherwise, the function
1186  * could return both the child and parent tables which could cause
1187  * data of the child table to be double-published on the subscriber
1188  * side.
1189  */
1190  if (viaroot)
1191  filter_partitions(table_infos);
1192 
1193  /* Construct a tuple descriptor for the result rows. */
1195  TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pubid",
1196  OIDOID, -1, 0);
1197  TupleDescInitEntry(tupdesc, (AttrNumber) 2, "relid",
1198  OIDOID, -1, 0);
1199  TupleDescInitEntry(tupdesc, (AttrNumber) 3, "attrs",
1200  INT2VECTOROID, -1, 0);
1201  TupleDescInitEntry(tupdesc, (AttrNumber) 4, "qual",
1202  PG_NODE_TREEOID, -1, 0);
1203 
1204  funcctx->tuple_desc = BlessTupleDesc(tupdesc);
1205  funcctx->user_fctx = table_infos;
1206 
1207  MemoryContextSwitchTo(oldcontext);
1208  }
1209 
1210  /* stuff done on every call of the function */
1211  funcctx = SRF_PERCALL_SETUP();
1212  table_infos = (List *) funcctx->user_fctx;
1213 
1214  if (funcctx->call_cntr < list_length(table_infos))
1215  {
1216  HeapTuple pubtuple = NULL;
1217  HeapTuple rettuple;
1218  Publication *pub;
1219  published_rel *table_info = (published_rel *) list_nth(table_infos, funcctx->call_cntr);
1220  Oid relid = table_info->relid;
1221  Oid schemaid = get_rel_namespace(relid);
1223  bool nulls[NUM_PUBLICATION_TABLES_ELEM] = {0};
1224 
1225  /*
1226  * Form tuple with appropriate data.
1227  */
1228 
1229  pub = GetPublication(table_info->pubid);
1230 
1231  values[0] = ObjectIdGetDatum(pub->oid);
1232  values[1] = ObjectIdGetDatum(relid);
1233 
1234  /*
1235  * We don't consider row filters or column lists for FOR ALL TABLES or
1236  * FOR TABLES IN SCHEMA publications.
1237  */
1238  if (!pub->alltables &&
1239  !SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
1240  ObjectIdGetDatum(schemaid),
1241  ObjectIdGetDatum(pub->oid)))
1242  pubtuple = SearchSysCacheCopy2(PUBLICATIONRELMAP,
1243  ObjectIdGetDatum(relid),
1244  ObjectIdGetDatum(pub->oid));
1245 
1246  if (HeapTupleIsValid(pubtuple))
1247  {
1248  /* Lookup the column list attribute. */
1249  values[2] = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
1250  Anum_pg_publication_rel_prattrs,
1251  &(nulls[2]));
1252 
1253  /* Null indicates no filter. */
1254  values[3] = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
1255  Anum_pg_publication_rel_prqual,
1256  &(nulls[3]));
1257  }
1258  else
1259  {
1260  nulls[2] = true;
1261  nulls[3] = true;
1262  }
1263 
1264  /* Show all columns when the column list is not specified. */
1265  if (nulls[2])
1266  {
1267  Relation rel = table_open(relid, AccessShareLock);
1268  int nattnums = 0;
1269  int16 *attnums;
1270  TupleDesc desc = RelationGetDescr(rel);
1271  int i;
1272 
1273  attnums = (int16 *) palloc(desc->natts * sizeof(int16));
1274 
1275  for (i = 0; i < desc->natts; i++)
1276  {
1277  Form_pg_attribute att = TupleDescAttr(desc, i);
1278 
1279  if (att->attisdropped || (att->attgenerated && !pub->pubgencols))
1280  continue;
1281 
1282  attnums[nattnums++] = att->attnum;
1283  }
1284 
1285  if (nattnums > 0)
1286  {
1287  values[2] = PointerGetDatum(buildint2vector(attnums, nattnums));
1288  nulls[2] = false;
1289  }
1290 
1292  }
1293 
1294  rettuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
1295 
1296  SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(rettuple));
1297  }
1298 
1299  SRF_RETURN_DONE(funcctx);
1300 }
#define PG_GETARG_ARRAYTYPE_P(n)
Definition: array.h:263
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_DIMS(a)
Definition: array.h:294
void deconstruct_array_builtin(ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3697
int16 AttrNumber
Definition: attnum.h:21
#define AttrNumberIsForUserDefinedAttr(attributeNumber)
Definition: attnum.h:41
#define InvalidAttrNumber
Definition: attnum.h:23
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:751
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define NameStr(name)
Definition: c.h:700
#define Assert(condition)
Definition: c.h:812
int16_t int16
Definition: c.h:480
#define PG_INT16_MAX
Definition: c.h:540
#define OidIsValid(objectId)
Definition: c.h:729
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:230
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:419
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:212
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:103
bool IsCatalogRelationOid(Oid relid)
Definition: catalog.c:120
void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, bool reverse_self)
Definition: dependency.c:1596
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2158
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:308
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:306
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:328
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:606
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:513
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:387
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1243
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
int2vector * buildint2vector(const int16 *int2s, int n)
Definition: int.c:114
int j
Definition: isn.c:73
int i
Definition: isn.c:72
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
void list_sort(List *list, list_sort_comparator cmp)
Definition: list.c:1674
List * list_concat_unique_oid(List *list1, const List *list2)
Definition: list.c:1469
List * lappend(List *list, void *datum)
Definition: list.c:339
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
void list_deduplicate_oid(List *list)
Definition: list.c:1495
int list_oid_cmp(const ListCell *p1, const ListCell *p2)
Definition: list.c:1703
void list_free(List *list)
Definition: list.c:1546
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
#define NoLock
Definition: lockdefs.h:34
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
bool get_rel_relispartition(Oid relid)
Definition: lsyscache.c:2027
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:858
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3366
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2003
Oid get_publication_oid(const char *pubname, bool missing_ok)
Definition: lsyscache.c:3625
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1952
char * pstrdup(const char *in)
Definition: mcxt.c:1696
void * palloc(Size size)
Definition: mcxt.c:1317
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3687
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Definition: objectaddress.h:33
char * nodeToString(const void *obj)
Definition: outfuncs.c:794
List * get_partition_ancestors(Oid relid)
Definition: partition.c:134
int16 attnum
Definition: pg_attribute.h:74
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
static const struct exclude_list_item skip[]
Definition: pg_checksums.c:107
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define lfirst_oid(lc)
Definition: pg_list.h:174
static int2vector * attnumstoint2vector(Bitmapset *attrs)
bool is_schema_publication(Oid pubid)
List * GetPublicationSchemas(Oid pubid)
Publication * GetPublicationByName(const char *pubname, bool missing_ok)
List * GetSchemaPublications(Oid schemaid)
Publication * GetPublication(Oid pubid)
List * GetRelationPublications(Oid relid)
ObjectAddress publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri, bool if_not_exists)
List * GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
Bitmapset * pub_collist_validate(Relation targetrel, List *columns)
Datum pg_get_publication_tables(PG_FUNCTION_ARGS)
List * GetAllTablesPublications(void)
static void check_publication_add_relation(Relation targetrel)
static void filter_partitions(List *table_infos)
List * GetAllTablesPublicationRelations(bool pubviaroot)
Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level)
List * GetPubPartitionOptionRelations(List *result, PublicationPartOpt pub_partopt, Oid relid)
static bool is_publishable_class(Oid relid, Form_pg_class reltuple)
#define NUM_PUBLICATION_TABLES_ELEM
static bool is_ancestor_member_tableinfos(Oid ancestor, List *table_infos)
bool check_and_fetch_column_list(Publication *pub, Oid relid, MemoryContext mcxt, Bitmapset **cols)
static void check_publication_add_schema(Oid schemaid)
Bitmapset * pub_form_cols_map(Relation relation, bool include_gencols)
List * GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
Bitmapset * pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, MemoryContext mcxt)
Datum pg_relation_is_publishable(PG_FUNCTION_ARGS)
bool is_publishable_relation(Relation rel)
List * GetAllSchemaPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
FormData_pg_publication * Form_pg_publication
PublicationPartOpt
@ PUBLICATION_PART_LEAF
@ PUBLICATION_PART_ROOT
@ PUBLICATION_PART_ALL
FormData_pg_publication_namespace * Form_pg_publication_namespace
FormData_pg_publication_rel * Form_pg_publication_rel
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum CharGetDatum(char X)
Definition: postgres.h:122
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
void InvalidatePublicationRels(List *relids)
MemoryContextSwitchTo(old_ctx)
#define RelationGetForm(relation)
Definition: rel.h:499
#define RelationGetRelid(relation)
Definition: rel.h:505
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
@ ForwardScanDirection
Definition: sdir.h:28
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
void * user_fctx
Definition: funcapi.h:82
uint64 call_cntr
Definition: funcapi.h:65
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
TupleDesc tuple_desc
Definition: funcapi.h:112
Definition: pg_list.h:54
PublicationActions pubactions
Form_pg_class rd_rel
Definition: rel.h:111
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:180
int n_members
Definition: catcache.h:178
HeapTupleData tuple
Definition: catcache.h:123
Definition: c.h:669
int16 values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:676
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:600
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:232
#define SearchSysCacheCopy2(cacheId, key1, key2)
Definition: syscache.h:93
#define ReleaseSysCacheList(x)
Definition: syscache.h:134
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:102
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:127
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:112
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1028
#define FirstNormalObjectId
Definition: transam.h:197
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:67
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:651
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define strVal(v)
Definition: value.h:82