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-2023, 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 "access/xact.h"
22 #include "catalog/catalog.h"
23 #include "catalog/dependency.h"
24 #include "catalog/index.h"
25 #include "catalog/indexing.h"
26 #include "catalog/namespace.h"
27 #include "catalog/partition.h"
28 #include "catalog/objectaccess.h"
29 #include "catalog/objectaddress.h"
30 #include "catalog/pg_inherits.h"
31 #include "catalog/pg_namespace.h"
32 #include "catalog/pg_publication.h"
35 #include "catalog/pg_type.h"
37 #include "funcapi.h"
38 #include "miscadmin.h"
39 #include "utils/array.h"
40 #include "utils/builtins.h"
41 #include "utils/catcache.h"
42 #include "utils/fmgroids.h"
43 #include "utils/inval.h"
44 #include "utils/lsyscache.h"
45 #include "utils/rel.h"
46 #include "utils/syscache.h"
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. */
55 
56 static void publication_translate_columns(Relation targetrel, List *columns,
57  int *natts, AttrNumber **attrs);
58 
59 /*
60  * Check if relation can be in given publication and throws appropriate
61  * error if not.
62  */
63 static void
65 {
66  /* Must be a regular or partitioned table */
67  if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
68  RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE)
69  ereport(ERROR,
70  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
71  errmsg("cannot add relation \"%s\" to publication",
72  RelationGetRelationName(targetrel)),
73  errdetail_relkind_not_supported(RelationGetForm(targetrel)->relkind)));
74 
75  /* Can't be system table */
76  if (IsCatalogRelation(targetrel))
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 system tables.")));
82 
83  /* UNLOGGED and TEMP relations cannot be part of publication. */
84  if (targetrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
85  ereport(ERROR,
86  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
87  errmsg("cannot add relation \"%s\" to publication",
88  RelationGetRelationName(targetrel)),
89  errdetail("This operation is not supported for temporary tables.")));
90  else if (targetrel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
91  ereport(ERROR,
92  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
93  errmsg("cannot add relation \"%s\" to publication",
94  RelationGetRelationName(targetrel)),
95  errdetail("This operation is not supported for unlogged tables.")));
96 }
97 
98 /*
99  * Check if schema can be in given publication and throw appropriate error if
100  * not.
101  */
102 static void
104 {
105  /* Can't be system namespace */
106  if (IsCatalogNamespace(schemaid) || IsToastNamespace(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("This operation is not supported for system schemas.")));
112 
113  /* Can't be temporary namespace */
114  if (isAnyTempNamespace(schemaid))
115  ereport(ERROR,
116  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
117  errmsg("cannot add schema \"%s\" to publication",
118  get_namespace_name(schemaid)),
119  errdetail("Temporary schemas cannot be replicated.")));
120 }
121 
122 /*
123  * Returns if relation represented by oid and Form_pg_class entry
124  * is publishable.
125  *
126  * Does same checks as check_publication_add_relation() above, but does not
127  * need relation to be opened and also does not throw errors.
128  *
129  * XXX This also excludes all tables with relid < FirstNormalObjectId,
130  * ie all tables created during initdb. This mainly affects the preinstalled
131  * information_schema. IsCatalogRelationOid() only excludes tables with
132  * relid < FirstUnpinnedObjectId, making that test rather redundant,
133  * but really we should get rid of the FirstNormalObjectId test not
134  * IsCatalogRelationOid. We can't do so today because we don't want
135  * information_schema tables to be considered publishable; but this test
136  * is really inadequate for that, since the information_schema could be
137  * dropped and reloaded and then it'll be considered publishable. The best
138  * long-term solution may be to add a "relispublishable" bool to pg_class,
139  * and depend on that instead of OID checks.
140  */
141 static bool
143 {
144  return (reltuple->relkind == RELKIND_RELATION ||
145  reltuple->relkind == RELKIND_PARTITIONED_TABLE) &&
146  !IsCatalogRelationOid(relid) &&
147  reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
148  relid >= FirstNormalObjectId;
149 }
150 
151 /*
152  * Another variant of is_publishable_class(), taking a Relation.
153  */
154 bool
156 {
157  return is_publishable_class(RelationGetRelid(rel), rel->rd_rel);
158 }
159 
160 /*
161  * SQL-callable variant of the above
162  *
163  * This returns null when the relation does not exist. This is intended to be
164  * used for example in psql to avoid gratuitous errors when there are
165  * concurrent catalog changes.
166  */
167 Datum
169 {
170  Oid relid = PG_GETARG_OID(0);
171  HeapTuple tuple;
172  bool result;
173 
174  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
175  if (!HeapTupleIsValid(tuple))
176  PG_RETURN_NULL();
177  result = is_publishable_class(relid, (Form_pg_class) GETSTRUCT(tuple));
178  ReleaseSysCache(tuple);
179  PG_RETURN_BOOL(result);
180 }
181 
182 /*
183  * Returns true if the ancestor is in the list of published relations.
184  * Otherwise, returns false.
185  */
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)
207 {
208  ListCell *lc;
209 
210  foreach(lc, table_infos)
211  {
212  bool skip = false;
213  List *ancestors = NIL;
214  ListCell *lc2;
215  published_rel *table_info = (published_rel *) lfirst(lc);
216 
217  if (get_rel_relispartition(table_info->relid))
218  ancestors = get_partition_ancestors(table_info->relid);
219 
220  foreach(lc2, ancestors)
221  {
222  Oid ancestor = lfirst_oid(lc2);
223 
224  if (is_ancestor_member_tableinfos(ancestor, table_infos))
225  {
226  skip = true;
227  break;
228  }
229  }
230 
231  if (skip)
232  table_infos = foreach_delete_current(table_infos, lc);
233  }
234 }
235 
236 /*
237  * Returns true if any schema is associated with the publication, false if no
238  * schema is associated with the publication.
239  */
240 bool
242 {
243  Relation pubschsrel;
244  ScanKeyData scankey;
245  SysScanDesc scan;
246  HeapTuple tup;
247  bool result = false;
248 
249  pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
250  ScanKeyInit(&scankey,
251  Anum_pg_publication_namespace_pnpubid,
252  BTEqualStrategyNumber, F_OIDEQ,
253  ObjectIdGetDatum(pubid));
254 
255  scan = systable_beginscan(pubschsrel,
256  PublicationNamespacePnnspidPnpubidIndexId,
257  true, NULL, 1, &scankey);
258  tup = systable_getnext(scan);
259  result = HeapTupleIsValid(tup);
260 
261  systable_endscan(scan);
262  table_close(pubschsrel, AccessShareLock);
263 
264  return result;
265 }
266 
267 /*
268  * Gets the relations based on the publication partition option for a specified
269  * relation.
270  */
271 List *
273  Oid relid)
274 {
275  if (get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE &&
276  pub_partopt != PUBLICATION_PART_ROOT)
277  {
278  List *all_parts = find_all_inheritors(relid, NoLock,
279  NULL);
280 
281  if (pub_partopt == PUBLICATION_PART_ALL)
282  result = list_concat(result, all_parts);
283  else if (pub_partopt == PUBLICATION_PART_LEAF)
284  {
285  ListCell *lc;
286 
287  foreach(lc, all_parts)
288  {
289  Oid partOid = lfirst_oid(lc);
290 
291  if (get_rel_relkind(partOid) != RELKIND_PARTITIONED_TABLE)
292  result = lappend_oid(result, partOid);
293  }
294  }
295  else
296  Assert(false);
297  }
298  else
299  result = lappend_oid(result, relid);
300 
301  return result;
302 }
303 
304 /*
305  * Returns the relid of the topmost ancestor that is published via this
306  * publication if any and set its ancestor level to ancestor_level,
307  * otherwise returns InvalidOid.
308  *
309  * The ancestor_level value allows us to compare the results for multiple
310  * publications, and decide which value is higher up.
311  *
312  * Note that the list of ancestors should be ordered such that the topmost
313  * ancestor is at the end of the list.
314  */
315 Oid
316 GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level)
317 {
318  ListCell *lc;
319  Oid topmost_relid = InvalidOid;
320  int level = 0;
321 
322  /*
323  * Find the "topmost" ancestor that is in this publication.
324  */
325  foreach(lc, ancestors)
326  {
327  Oid ancestor = lfirst_oid(lc);
328  List *apubids = GetRelationPublications(ancestor);
329  List *aschemaPubids = NIL;
330 
331  level++;
332 
333  if (list_member_oid(apubids, puboid))
334  {
335  topmost_relid = ancestor;
336 
337  if (ancestor_level)
338  *ancestor_level = level;
339  }
340  else
341  {
342  aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
343  if (list_member_oid(aschemaPubids, puboid))
344  {
345  topmost_relid = ancestor;
346 
347  if (ancestor_level)
348  *ancestor_level = level;
349  }
350  }
351 
352  list_free(apubids);
353  list_free(aschemaPubids);
354  }
355 
356  return topmost_relid;
357 }
358 
359 /*
360  * Insert new publication / relation mapping.
361  */
364  bool if_not_exists)
365 {
366  Relation rel;
367  HeapTuple tup;
368  Datum values[Natts_pg_publication_rel];
369  bool nulls[Natts_pg_publication_rel];
370  Relation targetrel = pri->relation;
371  Oid relid = RelationGetRelid(targetrel);
372  Oid pubreloid;
373  Publication *pub = GetPublication(pubid);
374  AttrNumber *attarray = NULL;
375  int natts = 0;
376  ObjectAddress myself,
377  referenced;
378  List *relids = NIL;
379 
380  rel = table_open(PublicationRelRelationId, RowExclusiveLock);
381 
382  /*
383  * Check for duplicates. Note that this does not really prevent
384  * duplicates, it's here just to provide nicer error message in common
385  * case. The real protection is the unique key on the catalog.
386  */
388  ObjectIdGetDatum(pubid)))
389  {
391 
392  if (if_not_exists)
393  return InvalidObjectAddress;
394 
395  ereport(ERROR,
397  errmsg("relation \"%s\" is already member of publication \"%s\"",
398  RelationGetRelationName(targetrel), pub->name)));
399  }
400 
402 
403  /*
404  * Translate column names to attnums and make sure the column list
405  * contains only allowed elements (no system or generated columns etc.).
406  * Also build an array of attnums, for storing in the catalog.
407  */
409  &natts, &attarray);
410 
411  /* Form a tuple. */
412  memset(values, 0, sizeof(values));
413  memset(nulls, false, sizeof(nulls));
414 
415  pubreloid = GetNewOidWithIndex(rel, PublicationRelObjectIndexId,
416  Anum_pg_publication_rel_oid);
417  values[Anum_pg_publication_rel_oid - 1] = ObjectIdGetDatum(pubreloid);
418  values[Anum_pg_publication_rel_prpubid - 1] =
419  ObjectIdGetDatum(pubid);
420  values[Anum_pg_publication_rel_prrelid - 1] =
421  ObjectIdGetDatum(relid);
422 
423  /* Add qualifications, if available */
424  if (pri->whereClause != NULL)
425  values[Anum_pg_publication_rel_prqual - 1] = CStringGetTextDatum(nodeToString(pri->whereClause));
426  else
427  nulls[Anum_pg_publication_rel_prqual - 1] = true;
428 
429  /* Add column list, if available */
430  if (pri->columns)
431  values[Anum_pg_publication_rel_prattrs - 1] = PointerGetDatum(buildint2vector(attarray, natts));
432  else
433  nulls[Anum_pg_publication_rel_prattrs - 1] = true;
434 
435  tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
436 
437  /* Insert tuple into catalog. */
438  CatalogTupleInsert(rel, tup);
439  heap_freetuple(tup);
440 
441  /* Register dependencies as needed */
442  ObjectAddressSet(myself, PublicationRelRelationId, pubreloid);
443 
444  /* Add dependency on the publication */
445  ObjectAddressSet(referenced, PublicationRelationId, pubid);
446  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
447 
448  /* Add dependency on the relation */
449  ObjectAddressSet(referenced, RelationRelationId, relid);
450  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
451 
452  /* Add dependency on the objects mentioned in the qualifications */
453  if (pri->whereClause)
454  recordDependencyOnSingleRelExpr(&myself, pri->whereClause, relid,
456  false);
457 
458  /* Add dependency on the columns, if any are listed */
459  for (int i = 0; i < natts; i++)
460  {
461  ObjectAddressSubSet(referenced, RelationRelationId, relid, attarray[i]);
462  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
463  }
464 
465  /* Close the table. */
467 
468  /*
469  * Invalidate relcache so that publication info is rebuilt.
470  *
471  * For the partitioned tables, we must invalidate all partitions contained
472  * in the respective partition hierarchies, not just the one explicitly
473  * mentioned in the publication. This is required because we implicitly
474  * publish the child tables when the parent table is published.
475  */
477  relid);
478 
480 
481  return myself;
482 }
483 
484 /* qsort comparator for attnums */
485 static int
486 compare_int16(const void *a, const void *b)
487 {
488  int av = *(const int16 *) a;
489  int bv = *(const int16 *) b;
490 
491  /* this can't overflow if int is wider than int16 */
492  return (av - bv);
493 }
494 
495 /*
496  * Translate a list of column names to an array of attribute numbers
497  * and a Bitmapset with them; verify that each attribute is appropriate
498  * to have in a publication column list (no system or generated attributes,
499  * no duplicates). Additional checks with replica identity are done later;
500  * see pub_collist_contains_invalid_column.
501  *
502  * Note that the attribute numbers are *not* offset by
503  * FirstLowInvalidHeapAttributeNumber; system columns are forbidden so this
504  * is okay.
505  */
506 static void
508  int *natts, AttrNumber **attrs)
509 {
510  AttrNumber *attarray = NULL;
511  Bitmapset *set = NULL;
512  ListCell *lc;
513  int n = 0;
514  TupleDesc tupdesc = RelationGetDescr(targetrel);
515 
516  /* Bail out when no column list defined. */
517  if (!columns)
518  return;
519 
520  /*
521  * Translate list of columns to attnums. We prohibit system attributes and
522  * make sure there are no duplicate columns.
523  */
524  attarray = palloc(sizeof(AttrNumber) * list_length(columns));
525  foreach(lc, columns)
526  {
527  char *colname = strVal(lfirst(lc));
528  AttrNumber attnum = get_attnum(RelationGetRelid(targetrel), colname);
529 
530  if (attnum == InvalidAttrNumber)
531  ereport(ERROR,
532  errcode(ERRCODE_UNDEFINED_COLUMN),
533  errmsg("column \"%s\" of relation \"%s\" does not exist",
534  colname, RelationGetRelationName(targetrel)));
535 
537  ereport(ERROR,
538  errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
539  errmsg("cannot use system column \"%s\" in publication column list",
540  colname));
541 
542  if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
543  ereport(ERROR,
544  errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
545  errmsg("cannot use generated column \"%s\" in publication column list",
546  colname));
547 
548  if (bms_is_member(attnum, set))
549  ereport(ERROR,
551  errmsg("duplicate column \"%s\" in publication column list",
552  colname));
553 
554  set = bms_add_member(set, attnum);
555  attarray[n++] = attnum;
556  }
557 
558  /* Be tidy, so that the catalog representation is always sorted */
559  qsort(attarray, n, sizeof(AttrNumber), compare_int16);
560 
561  *natts = n;
562  *attrs = attarray;
563 
564  bms_free(set);
565 }
566 
567 /*
568  * Transform a column list (represented by an array Datum) to a bitmapset.
569  *
570  * If columns isn't NULL, add the column numbers to that set.
571  *
572  * If mcxt isn't NULL, build the bitmapset in that context.
573  */
574 Bitmapset *
576 {
577  Bitmapset *result = NULL;
578  ArrayType *arr;
579  int nelems;
580  int16 *elems;
581  MemoryContext oldcxt = NULL;
582 
583  /*
584  * If an existing bitmap was provided, use it. Otherwise just use NULL and
585  * build a new bitmap.
586  */
587  if (columns)
588  result = columns;
589 
590  arr = DatumGetArrayTypeP(pubcols);
591  nelems = ARR_DIMS(arr)[0];
592  elems = (int16 *) ARR_DATA_PTR(arr);
593 
594  /* If a memory context was specified, switch to it. */
595  if (mcxt)
596  oldcxt = MemoryContextSwitchTo(mcxt);
597 
598  for (int i = 0; i < nelems; i++)
599  result = bms_add_member(result, elems[i]);
600 
601  if (mcxt)
602  MemoryContextSwitchTo(oldcxt);
603 
604  return result;
605 }
606 
607 /*
608  * Insert new publication / schema mapping.
609  */
611 publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
612 {
613  Relation rel;
614  HeapTuple tup;
615  Datum values[Natts_pg_publication_namespace];
616  bool nulls[Natts_pg_publication_namespace];
617  Oid psschid;
618  Publication *pub = GetPublication(pubid);
619  List *schemaRels = NIL;
620  ObjectAddress myself,
621  referenced;
622 
623  rel = table_open(PublicationNamespaceRelationId, RowExclusiveLock);
624 
625  /*
626  * Check for duplicates. Note that this does not really prevent
627  * duplicates, it's here just to provide nicer error message in common
628  * case. The real protection is the unique key on the catalog.
629  */
631  ObjectIdGetDatum(schemaid),
632  ObjectIdGetDatum(pubid)))
633  {
635 
636  if (if_not_exists)
637  return InvalidObjectAddress;
638 
639  ereport(ERROR,
641  errmsg("schema \"%s\" is already member of publication \"%s\"",
642  get_namespace_name(schemaid), pub->name)));
643  }
644 
646 
647  /* Form a tuple */
648  memset(values, 0, sizeof(values));
649  memset(nulls, false, sizeof(nulls));
650 
651  psschid = GetNewOidWithIndex(rel, PublicationNamespaceObjectIndexId,
652  Anum_pg_publication_namespace_oid);
653  values[Anum_pg_publication_namespace_oid - 1] = ObjectIdGetDatum(psschid);
654  values[Anum_pg_publication_namespace_pnpubid - 1] =
655  ObjectIdGetDatum(pubid);
656  values[Anum_pg_publication_namespace_pnnspid - 1] =
657  ObjectIdGetDatum(schemaid);
658 
659  tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
660 
661  /* Insert tuple into catalog */
662  CatalogTupleInsert(rel, tup);
663  heap_freetuple(tup);
664 
665  ObjectAddressSet(myself, PublicationNamespaceRelationId, psschid);
666 
667  /* Add dependency on the publication */
668  ObjectAddressSet(referenced, PublicationRelationId, pubid);
669  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
670 
671  /* Add dependency on the schema */
672  ObjectAddressSet(referenced, NamespaceRelationId, schemaid);
673  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
674 
675  /* Close the table */
677 
678  /*
679  * Invalidate relcache so that publication info is rebuilt. See
680  * publication_add_relation for why we need to consider all the
681  * partitions.
682  */
683  schemaRels = GetSchemaPublicationRelations(schemaid,
685  InvalidatePublicationRels(schemaRels);
686 
687  return myself;
688 }
689 
690 /* Gets list of publication oids for a relation */
691 List *
693 {
694  List *result = NIL;
695  CatCList *pubrellist;
696  int i;
697 
698  /* Find all publications associated with the relation. */
700  ObjectIdGetDatum(relid));
701  for (i = 0; i < pubrellist->n_members; i++)
702  {
703  HeapTuple tup = &pubrellist->members[i]->tuple;
704  Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid;
705 
706  result = lappend_oid(result, pubid);
707  }
708 
709  ReleaseSysCacheList(pubrellist);
710 
711  return result;
712 }
713 
714 /*
715  * Gets list of relation oids for a publication.
716  *
717  * This should only be used FOR TABLE publications, the FOR ALL TABLES
718  * should use GetAllTablesPublicationRelations().
719  */
720 List *
722 {
723  List *result;
724  Relation pubrelsrel;
725  ScanKeyData scankey;
726  SysScanDesc scan;
727  HeapTuple tup;
728 
729  /* Find all publications associated with the relation. */
730  pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
731 
732  ScanKeyInit(&scankey,
733  Anum_pg_publication_rel_prpubid,
734  BTEqualStrategyNumber, F_OIDEQ,
735  ObjectIdGetDatum(pubid));
736 
737  scan = systable_beginscan(pubrelsrel, PublicationRelPrpubidIndexId,
738  true, NULL, 1, &scankey);
739 
740  result = NIL;
741  while (HeapTupleIsValid(tup = systable_getnext(scan)))
742  {
744 
745  pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
746  result = GetPubPartitionOptionRelations(result, pub_partopt,
747  pubrel->prrelid);
748  }
749 
750  systable_endscan(scan);
751  table_close(pubrelsrel, AccessShareLock);
752 
753  /* Now sort and de-duplicate the result list */
754  list_sort(result, list_oid_cmp);
755  list_deduplicate_oid(result);
756 
757  return result;
758 }
759 
760 /*
761  * Gets list of publication oids for publications marked as FOR ALL TABLES.
762  */
763 List *
765 {
766  List *result;
767  Relation rel;
768  ScanKeyData scankey;
769  SysScanDesc scan;
770  HeapTuple tup;
771 
772  /* Find all publications that are marked as for all tables. */
773  rel = table_open(PublicationRelationId, AccessShareLock);
774 
775  ScanKeyInit(&scankey,
776  Anum_pg_publication_puballtables,
777  BTEqualStrategyNumber, F_BOOLEQ,
778  BoolGetDatum(true));
779 
780  scan = systable_beginscan(rel, InvalidOid, false,
781  NULL, 1, &scankey);
782 
783  result = NIL;
784  while (HeapTupleIsValid(tup = systable_getnext(scan)))
785  {
786  Oid oid = ((Form_pg_publication) GETSTRUCT(tup))->oid;
787 
788  result = lappend_oid(result, oid);
789  }
790 
791  systable_endscan(scan);
793 
794  return result;
795 }
796 
797 /*
798  * Gets list of all relation published by FOR ALL TABLES publication(s).
799  *
800  * If the publication publishes partition changes via their respective root
801  * partitioned tables, we must exclude partitions in favor of including the
802  * root partitioned tables.
803  */
804 List *
806 {
807  Relation classRel;
808  ScanKeyData key[1];
809  TableScanDesc scan;
810  HeapTuple tuple;
811  List *result = NIL;
812 
813  classRel = table_open(RelationRelationId, AccessShareLock);
814 
815  ScanKeyInit(&key[0],
816  Anum_pg_class_relkind,
817  BTEqualStrategyNumber, F_CHAREQ,
818  CharGetDatum(RELKIND_RELATION));
819 
820  scan = table_beginscan_catalog(classRel, 1, key);
821 
822  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
823  {
824  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
825  Oid relid = relForm->oid;
826 
827  if (is_publishable_class(relid, relForm) &&
828  !(relForm->relispartition && pubviaroot))
829  result = lappend_oid(result, relid);
830  }
831 
832  table_endscan(scan);
833 
834  if (pubviaroot)
835  {
836  ScanKeyInit(&key[0],
837  Anum_pg_class_relkind,
838  BTEqualStrategyNumber, F_CHAREQ,
839  CharGetDatum(RELKIND_PARTITIONED_TABLE));
840 
841  scan = table_beginscan_catalog(classRel, 1, key);
842 
843  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
844  {
845  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
846  Oid relid = relForm->oid;
847 
848  if (is_publishable_class(relid, relForm) &&
849  !relForm->relispartition)
850  result = lappend_oid(result, relid);
851  }
852 
853  table_endscan(scan);
854  }
855 
856  table_close(classRel, AccessShareLock);
857  return result;
858 }
859 
860 /*
861  * Gets the list of schema oids for a publication.
862  *
863  * This should only be used FOR TABLES IN SCHEMA publications.
864  */
865 List *
867 {
868  List *result = NIL;
869  Relation pubschsrel;
870  ScanKeyData scankey;
871  SysScanDesc scan;
872  HeapTuple tup;
873 
874  /* Find all schemas associated with the publication */
875  pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
876 
877  ScanKeyInit(&scankey,
878  Anum_pg_publication_namespace_pnpubid,
879  BTEqualStrategyNumber, F_OIDEQ,
880  ObjectIdGetDatum(pubid));
881 
882  scan = systable_beginscan(pubschsrel,
883  PublicationNamespacePnnspidPnpubidIndexId,
884  true, NULL, 1, &scankey);
885  while (HeapTupleIsValid(tup = systable_getnext(scan)))
886  {
888 
890 
891  result = lappend_oid(result, pubsch->pnnspid);
892  }
893 
894  systable_endscan(scan);
895  table_close(pubschsrel, AccessShareLock);
896 
897  return result;
898 }
899 
900 /*
901  * Gets the list of publication oids associated with a specified schema.
902  */
903 List *
905 {
906  List *result = NIL;
907  CatCList *pubschlist;
908  int i;
909 
910  /* Find all publications associated with the schema */
912  ObjectIdGetDatum(schemaid));
913  for (i = 0; i < pubschlist->n_members; i++)
914  {
915  HeapTuple tup = &pubschlist->members[i]->tuple;
916  Oid pubid = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pnpubid;
917 
918  result = lappend_oid(result, pubid);
919  }
920 
921  ReleaseSysCacheList(pubschlist);
922 
923  return result;
924 }
925 
926 /*
927  * Get the list of publishable relation oids for a specified schema.
928  */
929 List *
931 {
932  Relation classRel;
933  ScanKeyData key[1];
934  TableScanDesc scan;
935  HeapTuple tuple;
936  List *result = NIL;
937 
938  Assert(OidIsValid(schemaid));
939 
940  classRel = table_open(RelationRelationId, AccessShareLock);
941 
942  ScanKeyInit(&key[0],
943  Anum_pg_class_relnamespace,
944  BTEqualStrategyNumber, F_OIDEQ,
945  schemaid);
946 
947  /* get all the relations present in the specified schema */
948  scan = table_beginscan_catalog(classRel, 1, key);
949  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
950  {
951  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
952  Oid relid = relForm->oid;
953  char relkind;
954 
955  if (!is_publishable_class(relid, relForm))
956  continue;
957 
958  relkind = get_rel_relkind(relid);
959  if (relkind == RELKIND_RELATION)
960  result = lappend_oid(result, relid);
961  else if (relkind == RELKIND_PARTITIONED_TABLE)
962  {
963  List *partitionrels = NIL;
964 
965  /*
966  * It is quite possible that some of the partitions are in a
967  * different schema than the parent table, so we need to get such
968  * partitions separately.
969  */
970  partitionrels = GetPubPartitionOptionRelations(partitionrels,
971  pub_partopt,
972  relForm->oid);
973  result = list_concat_unique_oid(result, partitionrels);
974  }
975  }
976 
977  table_endscan(scan);
978  table_close(classRel, AccessShareLock);
979  return result;
980 }
981 
982 /*
983  * Gets the list of all relations published by FOR TABLES IN SCHEMA
984  * publication.
985  */
986 List *
988 {
989  List *result = NIL;
990  List *pubschemalist = GetPublicationSchemas(pubid);
991  ListCell *cell;
992 
993  foreach(cell, pubschemalist)
994  {
995  Oid schemaid = lfirst_oid(cell);
996  List *schemaRels = NIL;
997 
998  schemaRels = GetSchemaPublicationRelations(schemaid, pub_partopt);
999  result = list_concat(result, schemaRels);
1000  }
1001 
1002  return result;
1003 }
1004 
1005 /*
1006  * Get publication using oid
1007  *
1008  * The Publication struct and its data are palloc'ed here.
1009  */
1010 Publication *
1012 {
1013  HeapTuple tup;
1014  Publication *pub;
1015  Form_pg_publication pubform;
1016 
1018  if (!HeapTupleIsValid(tup))
1019  elog(ERROR, "cache lookup failed for publication %u", pubid);
1020 
1021  pubform = (Form_pg_publication) GETSTRUCT(tup);
1022 
1023  pub = (Publication *) palloc(sizeof(Publication));
1024  pub->oid = pubid;
1025  pub->name = pstrdup(NameStr(pubform->pubname));
1026  pub->alltables = pubform->puballtables;
1027  pub->pubactions.pubinsert = pubform->pubinsert;
1028  pub->pubactions.pubupdate = pubform->pubupdate;
1029  pub->pubactions.pubdelete = pubform->pubdelete;
1030  pub->pubactions.pubtruncate = pubform->pubtruncate;
1031  pub->pubviaroot = pubform->pubviaroot;
1032 
1033  ReleaseSysCache(tup);
1034 
1035  return pub;
1036 }
1037 
1038 /*
1039  * Get Publication using name.
1040  */
1041 Publication *
1042 GetPublicationByName(const char *pubname, bool missing_ok)
1043 {
1044  Oid oid;
1045 
1046  oid = get_publication_oid(pubname, missing_ok);
1047 
1048  return OidIsValid(oid) ? GetPublication(oid) : NULL;
1049 }
1050 
1051 /*
1052  * Get information of the tables in the given publication array.
1053  *
1054  * Returns pubid, relid, column list, row filter for each table.
1055  */
1056 Datum
1058 {
1059 #define NUM_PUBLICATION_TABLES_ELEM 4
1060  FuncCallContext *funcctx;
1061  List *table_infos = NIL;
1062 
1063  /* stuff done only on the first call of the function */
1064  if (SRF_IS_FIRSTCALL())
1065  {
1066  TupleDesc tupdesc;
1067  MemoryContext oldcontext;
1068  ArrayType *arr;
1069  Datum *elems;
1070  int nelems,
1071  i;
1072  bool viaroot = false;
1073 
1074  /* create a function context for cross-call persistence */
1075  funcctx = SRF_FIRSTCALL_INIT();
1076 
1077  /* switch to memory context appropriate for multiple function calls */
1078  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1079 
1080  /*
1081  * Deconstruct the parameter into elements where each element is a
1082  * publication name.
1083  */
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++)
1090  {
1091  Publication *pub_elem;
1092  List *pub_elem_tables = NIL;
1093  ListCell *lc;
1094 
1095  pub_elem = GetPublicationByName(TextDatumGetCString(elems[i]), false);
1096 
1097  /*
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.
1102  */
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 ?
1114  schemarelids = GetAllSchemaPublicationRelations(pub_elem->oid,
1115  pub_elem->pubviaroot ?
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;
1141  }
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 
1154  /* Construct a tuple descriptor for the result rows. */
1156  TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pubid",
1157  OIDOID, -1, 0);
1158  TupleDescInitEntry(tupdesc, (AttrNumber) 2, "relid",
1159  OIDOID, -1, 0);
1160  TupleDescInitEntry(tupdesc, (AttrNumber) 3, "attrs",
1161  INT2VECTOROID, -1, 0);
1162  TupleDescInitEntry(tupdesc, (AttrNumber) 4, "qual",
1163  PG_NODE_TREEOID, -1, 0);
1164 
1165  funcctx->tuple_desc = BlessTupleDesc(tupdesc);
1166  funcctx->user_fctx = (void *) table_infos;
1167 
1168  MemoryContextSwitchTo(oldcontext);
1169  }
1170 
1171  /* stuff done on every call of the function */
1172  funcctx = SRF_PERCALL_SETUP();
1173  table_infos = (List *) funcctx->user_fctx;
1174 
1175  if (funcctx->call_cntr < list_length(table_infos))
1176  {
1177  HeapTuple pubtuple = NULL;
1178  HeapTuple rettuple;
1179  Publication *pub;
1180  published_rel *table_info = (published_rel *) list_nth(table_infos, funcctx->call_cntr);
1181  Oid relid = table_info->relid;
1182  Oid schemaid = get_rel_namespace(relid);
1184  bool nulls[NUM_PUBLICATION_TABLES_ELEM] = {0};
1185 
1186  /*
1187  * Form tuple with appropriate data.
1188  */
1189 
1190  pub = GetPublication(table_info->pubid);
1191 
1192  values[0] = ObjectIdGetDatum(pub->oid);
1193  values[1] = ObjectIdGetDatum(relid);
1194 
1195  /*
1196  * We don't consider row filters or column lists for FOR ALL TABLES or
1197  * FOR TABLES IN SCHEMA publications.
1198  */
1199  if (!pub->alltables &&
1201  ObjectIdGetDatum(schemaid),
1202  ObjectIdGetDatum(pub->oid)))
1204  ObjectIdGetDatum(relid),
1205  ObjectIdGetDatum(pub->oid));
1206 
1207  if (HeapTupleIsValid(pubtuple))
1208  {
1209  /* Lookup the column list attribute. */
1210  values[2] = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
1211  Anum_pg_publication_rel_prattrs,
1212  &(nulls[2]));
1213 
1214  /* Null indicates no filter. */
1215  values[3] = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
1216  Anum_pg_publication_rel_prqual,
1217  &(nulls[3]));
1218  }
1219  else
1220  {
1221  nulls[2] = true;
1222  nulls[3] = true;
1223  }
1224 
1225  /* Show all columns when the column list is not specified. */
1226  if (nulls[2])
1227  {
1228  Relation rel = table_open(relid, AccessShareLock);
1229  int nattnums = 0;
1230  int16 *attnums;
1231  TupleDesc desc = RelationGetDescr(rel);
1232  int i;
1233 
1234  attnums = (int16 *) palloc(desc->natts * sizeof(int16));
1235 
1236  for (i = 0; i < desc->natts; i++)
1237  {
1238  Form_pg_attribute att = TupleDescAttr(desc, i);
1239 
1240  if (att->attisdropped || att->attgenerated)
1241  continue;
1242 
1243  attnums[nattnums++] = att->attnum;
1244  }
1245 
1246  if (nattnums > 0)
1247  {
1248  values[2] = PointerGetDatum(buildint2vector(attnums, nattnums));
1249  nulls[2] = false;
1250  }
1251 
1253  }
1254 
1255  rettuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
1256 
1257  SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(rettuple));
1258  }
1259 
1260  SRF_RETURN_DONE(funcctx);
1261 }
#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(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3613
int16 AttrNumber
Definition: attnum.h:21
#define AttrNumberIsForUserDefinedAttr(attributeNumber)
Definition: attnum.h:41
#define InvalidAttrNumber
Definition: attnum.h:23
void bms_free(Bitmapset *a)
Definition: bitmapset.c:194
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:460
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:753
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define CStringGetTextDatum(s)
Definition: builtins.h:94
#define TextDatumGetCString(d)
Definition: builtins.h:95
#define NameStr(name)
Definition: c.h:735
signed short int16
Definition: c.h:482
#define OidIsValid(objectId)
Definition: c.h:764
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:202
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:393
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:184
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:105
bool IsCatalogRelationOid(Oid relid)
Definition: catalog.c:122
void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, bool reverse_self)
Definition: dependency.c:1645
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2072
#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:599
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:506
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:1086
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1117
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
#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 b
Definition: isn.c:70
int a
Definition: isn.c:69
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
Assert(fmt[strlen(fmt) - 1] !='\n')
void list_sort(List *list, list_sort_comparator cmp)
Definition: list.c:1673
List * list_concat_unique_oid(List *list1, const List *list2)
Definition: list.c:1468
List * lappend(List *list, void *datum)
Definition: list.c:338
List * lappend_oid(List *list, Oid datum)
Definition: list.c:374
void list_deduplicate_oid(List *list)
Definition: list.c:1494
int list_oid_cmp(const ListCell *p1, const ListCell *p2)
Definition: list.c:1706
void list_free(List *list)
Definition: list.c:1545
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:721
List * list_concat(List *list1, const List *list2)
Definition: list.c:560
#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:2031
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:857
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3348
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2007
Oid get_publication_oid(const char *pubname, bool missing_ok)
Definition: lsyscache.c:3607
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1956
char * pstrdup(const char *in)
Definition: mcxt.c:1644
void * palloc(Size size)
Definition: mcxt.c:1226
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3628
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:883
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
List * get_partition_ancestors(Oid relid)
Definition: partition.c:133
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:108
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:44
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:256
#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
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define foreach_delete_current(lst, cell)
Definition: pg_list.h:390
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)
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)
static void check_publication_add_schema(Oid schemaid)
static int compare_int16(const void *a, const void *b)
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)
static void publication_translate_columns(Relation targetrel, List *columns, int *natts, AttrNumber **attrs)
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
#define qsort(a, b, c, d)
Definition: port.h:445
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)
#define RelationGetForm(relation)
Definition: rel.h:498
#define RelationGetRelid(relation)
Definition: rel.h:504
#define RelationGetDescr(relation)
Definition: rel.h:530
#define RelationGetRelationName(relation)
Definition: rel.h:538
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
@ ForwardScanDirection
Definition: sdir.h:28
struct @10::@11 av[32]
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
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:178
int n_members
Definition: catcache.h:176
HeapTupleData tuple
Definition: catcache.h:121
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:868
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:820
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1081
#define SearchSysCacheCopy2(cacheId, key1, key2)
Definition: syscache.h:184
@ PUBLICATIONOID
Definition: syscache.h:83
@ RELOID
Definition: syscache.h:89
@ PUBLICATIONNAMESPACEMAP
Definition: syscache.h:82
@ PUBLICATIONRELMAP
Definition: syscache.h:85
#define ReleaseSysCacheList(x)
Definition: syscache.h:225
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:193
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:218
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:1009
#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:605
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define strVal(v)
Definition: value.h:82