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-2020, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * 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_publication.h"
33 #include "catalog/pg_type.h"
34 #include "funcapi.h"
35 #include "miscadmin.h"
36 #include "utils/array.h"
37 #include "utils/builtins.h"
38 #include "utils/catcache.h"
39 #include "utils/fmgroids.h"
40 #include "utils/inval.h"
41 #include "utils/lsyscache.h"
42 #include "utils/rel.h"
43 #include "utils/syscache.h"
44 
45 /*
46  * Check if relation can be in given publication and throws appropriate
47  * error if not.
48  */
49 static void
51 {
52  /* Must be a regular or partitioned table */
53  if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
54  RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE)
55  ereport(ERROR,
56  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
57  errmsg("\"%s\" is not a table",
58  RelationGetRelationName(targetrel)),
59  errdetail("Only tables can be added to publications.")));
60 
61  /* Can't be system table */
62  if (IsCatalogRelation(targetrel))
63  ereport(ERROR,
64  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
65  errmsg("\"%s\" is a system table",
66  RelationGetRelationName(targetrel)),
67  errdetail("System tables cannot be added to publications.")));
68 
69  /* UNLOGGED and TEMP relations cannot be part of publication. */
70  if (!RelationNeedsWAL(targetrel))
71  ereport(ERROR,
72  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
73  errmsg("table \"%s\" cannot be replicated",
74  RelationGetRelationName(targetrel)),
75  errdetail("Temporary and unlogged relations cannot be replicated.")));
76 }
77 
78 /*
79  * Returns if relation represented by oid and Form_pg_class entry
80  * is publishable.
81  *
82  * Does same checks as the above, but does not need relation to be opened
83  * and also does not throw errors.
84  *
85  * XXX This also excludes all tables with relid < FirstNormalObjectId,
86  * ie all tables created during initdb. This mainly affects the preinstalled
87  * information_schema. IsCatalogRelationOid() only excludes tables with
88  * relid < FirstBootstrapObjectId, making that test rather redundant,
89  * but really we should get rid of the FirstNormalObjectId test not
90  * IsCatalogRelationOid. We can't do so today because we don't want
91  * information_schema tables to be considered publishable; but this test
92  * is really inadequate for that, since the information_schema could be
93  * dropped and reloaded and then it'll be considered publishable. The best
94  * long-term solution may be to add a "relispublishable" bool to pg_class,
95  * and depend on that instead of OID checks.
96  */
97 static bool
99 {
100  return (reltuple->relkind == RELKIND_RELATION ||
101  reltuple->relkind == RELKIND_PARTITIONED_TABLE) &&
102  !IsCatalogRelationOid(relid) &&
103  reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
104  relid >= FirstNormalObjectId;
105 }
106 
107 /*
108  * Another variant of this, taking a Relation.
109  */
110 bool
112 {
113  return is_publishable_class(RelationGetRelid(rel), rel->rd_rel);
114 }
115 
116 
117 /*
118  * SQL-callable variant of the above
119  *
120  * This returns null when the relation does not exist. This is intended to be
121  * used for example in psql to avoid gratuitous errors when there are
122  * concurrent catalog changes.
123  */
124 Datum
126 {
127  Oid relid = PG_GETARG_OID(0);
128  HeapTuple tuple;
129  bool result;
130 
131  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
132  if (!HeapTupleIsValid(tuple))
133  PG_RETURN_NULL();
134  result = is_publishable_class(relid, (Form_pg_class) GETSTRUCT(tuple));
135  ReleaseSysCache(tuple);
136  PG_RETURN_BOOL(result);
137 }
138 
139 
140 /*
141  * Insert new publication / relation mapping.
142  */
145  bool if_not_exists)
146 {
147  Relation rel;
148  HeapTuple tup;
149  Datum values[Natts_pg_publication_rel];
150  bool nulls[Natts_pg_publication_rel];
151  Oid relid = RelationGetRelid(targetrel);
152  Oid prrelid;
153  Publication *pub = GetPublication(pubid);
154  ObjectAddress myself,
155  referenced;
156 
157  rel = table_open(PublicationRelRelationId, RowExclusiveLock);
158 
159  /*
160  * Check for duplicates. Note that this does not really prevent
161  * duplicates, it's here just to provide nicer error message in common
162  * case. The real protection is the unique key on the catalog.
163  */
165  ObjectIdGetDatum(pubid)))
166  {
168 
169  if (if_not_exists)
170  return InvalidObjectAddress;
171 
172  ereport(ERROR,
174  errmsg("relation \"%s\" is already member of publication \"%s\"",
175  RelationGetRelationName(targetrel), pub->name)));
176  }
177 
179 
180  /* Form a tuple. */
181  memset(values, 0, sizeof(values));
182  memset(nulls, false, sizeof(nulls));
183 
185  Anum_pg_publication_rel_oid);
186  values[Anum_pg_publication_rel_oid - 1] = ObjectIdGetDatum(prrelid);
187  values[Anum_pg_publication_rel_prpubid - 1] =
188  ObjectIdGetDatum(pubid);
189  values[Anum_pg_publication_rel_prrelid - 1] =
190  ObjectIdGetDatum(relid);
191 
192  tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
193 
194  /* Insert tuple into catalog. */
195  CatalogTupleInsert(rel, tup);
196  heap_freetuple(tup);
197 
198  ObjectAddressSet(myself, PublicationRelRelationId, prrelid);
199 
200  /* Add dependency on the publication */
201  ObjectAddressSet(referenced, PublicationRelationId, pubid);
202  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
203 
204  /* Add dependency on the relation */
205  ObjectAddressSet(referenced, RelationRelationId, relid);
206  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
207 
208  /* Close the table. */
210 
211  /* Invalidate relcache so that publication info is rebuilt. */
212  CacheInvalidateRelcache(targetrel);
213 
214  return myself;
215 }
216 
217 /* Gets list of publication oids for a relation */
218 List *
220 {
221  List *result = NIL;
222  CatCList *pubrellist;
223  int i;
224 
225  /* Find all publications associated with the relation. */
227  ObjectIdGetDatum(relid));
228  for (i = 0; i < pubrellist->n_members; i++)
229  {
230  HeapTuple tup = &pubrellist->members[i]->tuple;
231  Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid;
232 
233  result = lappend_oid(result, pubid);
234  }
235 
236  ReleaseSysCacheList(pubrellist);
237 
238  return result;
239 }
240 
241 /*
242  * Gets list of relation oids for a publication.
243  *
244  * This should only be used for normal publications, the FOR ALL TABLES
245  * should use GetAllTablesPublicationRelations().
246  */
247 List *
249 {
250  List *result;
251  Relation pubrelsrel;
252  ScanKeyData scankey;
253  SysScanDesc scan;
254  HeapTuple tup;
255 
256  /* Find all publications associated with the relation. */
257  pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
258 
259  ScanKeyInit(&scankey,
260  Anum_pg_publication_rel_prpubid,
261  BTEqualStrategyNumber, F_OIDEQ,
262  ObjectIdGetDatum(pubid));
263 
265  true, NULL, 1, &scankey);
266 
267  result = NIL;
268  while (HeapTupleIsValid(tup = systable_getnext(scan)))
269  {
271 
272  pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
273 
274  if (get_rel_relkind(pubrel->prrelid) == RELKIND_PARTITIONED_TABLE &&
275  pub_partopt != PUBLICATION_PART_ROOT)
276  {
277  List *all_parts = find_all_inheritors(pubrel->prrelid, NoLock,
278  NULL);
279 
280  if (pub_partopt == PUBLICATION_PART_ALL)
281  result = list_concat(result, all_parts);
282  else if (pub_partopt == PUBLICATION_PART_LEAF)
283  {
284  ListCell *lc;
285 
286  foreach(lc, all_parts)
287  {
288  Oid partOid = lfirst_oid(lc);
289 
290  if (get_rel_relkind(partOid) != RELKIND_PARTITIONED_TABLE)
291  result = lappend_oid(result, partOid);
292  }
293  }
294  else
295  Assert(false);
296  }
297  else
298  result = lappend_oid(result, pubrel->prrelid);
299  }
300 
301  systable_endscan(scan);
302  table_close(pubrelsrel, AccessShareLock);
303 
304  return result;
305 }
306 
307 /*
308  * Gets list of publication oids for publications marked as FOR ALL TABLES.
309  */
310 List *
312 {
313  List *result;
314  Relation rel;
315  ScanKeyData scankey;
316  SysScanDesc scan;
317  HeapTuple tup;
318 
319  /* Find all publications that are marked as for all tables. */
320  rel = table_open(PublicationRelationId, AccessShareLock);
321 
322  ScanKeyInit(&scankey,
323  Anum_pg_publication_puballtables,
324  BTEqualStrategyNumber, F_BOOLEQ,
325  BoolGetDatum(true));
326 
327  scan = systable_beginscan(rel, InvalidOid, false,
328  NULL, 1, &scankey);
329 
330  result = NIL;
331  while (HeapTupleIsValid(tup = systable_getnext(scan)))
332  {
333  Oid oid = ((Form_pg_publication) GETSTRUCT(tup))->oid;
334 
335  result = lappend_oid(result, oid);
336  }
337 
338  systable_endscan(scan);
340 
341  return result;
342 }
343 
344 /*
345  * Gets list of all relation published by FOR ALL TABLES publication(s).
346  *
347  * If the publication publishes partition changes via their respective root
348  * partitioned tables, we must exclude partitions in favor of including the
349  * root partitioned tables.
350  */
351 List *
353 {
354  Relation classRel;
355  ScanKeyData key[1];
356  TableScanDesc scan;
357  HeapTuple tuple;
358  List *result = NIL;
359 
360  classRel = table_open(RelationRelationId, AccessShareLock);
361 
362  ScanKeyInit(&key[0],
363  Anum_pg_class_relkind,
364  BTEqualStrategyNumber, F_CHAREQ,
365  CharGetDatum(RELKIND_RELATION));
366 
367  scan = table_beginscan_catalog(classRel, 1, key);
368 
369  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
370  {
371  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
372  Oid relid = relForm->oid;
373 
374  if (is_publishable_class(relid, relForm) &&
375  !(relForm->relispartition && pubviaroot))
376  result = lappend_oid(result, relid);
377  }
378 
379  table_endscan(scan);
380 
381  if (pubviaroot)
382  {
383  ScanKeyInit(&key[0],
384  Anum_pg_class_relkind,
385  BTEqualStrategyNumber, F_CHAREQ,
386  CharGetDatum(RELKIND_PARTITIONED_TABLE));
387 
388  scan = table_beginscan_catalog(classRel, 1, key);
389 
390  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
391  {
392  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
393  Oid relid = relForm->oid;
394 
395  if (is_publishable_class(relid, relForm) &&
396  !relForm->relispartition)
397  result = lappend_oid(result, relid);
398  }
399 
400  table_endscan(scan);
401  }
402 
403  table_close(classRel, AccessShareLock);
404  return result;
405 }
406 
407 /*
408  * Get publication using oid
409  *
410  * The Publication struct and its data are palloc'ed here.
411  */
412 Publication *
414 {
415  HeapTuple tup;
416  Publication *pub;
417  Form_pg_publication pubform;
418 
420  if (!HeapTupleIsValid(tup))
421  elog(ERROR, "cache lookup failed for publication %u", pubid);
422 
423  pubform = (Form_pg_publication) GETSTRUCT(tup);
424 
425  pub = (Publication *) palloc(sizeof(Publication));
426  pub->oid = pubid;
427  pub->name = pstrdup(NameStr(pubform->pubname));
428  pub->alltables = pubform->puballtables;
429  pub->pubactions.pubinsert = pubform->pubinsert;
430  pub->pubactions.pubupdate = pubform->pubupdate;
431  pub->pubactions.pubdelete = pubform->pubdelete;
432  pub->pubactions.pubtruncate = pubform->pubtruncate;
433  pub->pubviaroot = pubform->pubviaroot;
434 
435  ReleaseSysCache(tup);
436 
437  return pub;
438 }
439 
440 
441 /*
442  * Get Publication using name.
443  */
444 Publication *
445 GetPublicationByName(const char *pubname, bool missing_ok)
446 {
447  Oid oid;
448 
449  oid = get_publication_oid(pubname, missing_ok);
450 
451  return OidIsValid(oid) ? GetPublication(oid) : NULL;
452 }
453 
454 /*
455  * get_publication_oid - given a publication name, look up the OID
456  *
457  * If missing_ok is false, throw an error if name not found. If true, just
458  * return InvalidOid.
459  */
460 Oid
461 get_publication_oid(const char *pubname, bool missing_ok)
462 {
463  Oid oid;
464 
465  oid = GetSysCacheOid1(PUBLICATIONNAME, Anum_pg_publication_oid,
466  CStringGetDatum(pubname));
467  if (!OidIsValid(oid) && !missing_ok)
468  ereport(ERROR,
469  (errcode(ERRCODE_UNDEFINED_OBJECT),
470  errmsg("publication \"%s\" does not exist", pubname)));
471  return oid;
472 }
473 
474 /*
475  * get_publication_name - given a publication Oid, look up the name
476  *
477  * If missing_ok is false, throw an error if name not found. If true, just
478  * return NULL.
479  */
480 char *
481 get_publication_name(Oid pubid, bool missing_ok)
482 {
483  HeapTuple tup;
484  char *pubname;
485  Form_pg_publication pubform;
486 
488 
489  if (!HeapTupleIsValid(tup))
490  {
491  if (!missing_ok)
492  elog(ERROR, "cache lookup failed for publication %u", pubid);
493  return NULL;
494  }
495 
496  pubform = (Form_pg_publication) GETSTRUCT(tup);
497  pubname = pstrdup(NameStr(pubform->pubname));
498 
499  ReleaseSysCache(tup);
500 
501  return pubname;
502 }
503 
504 /*
505  * Returns Oids of tables in a publication.
506  */
507 Datum
509 {
510  FuncCallContext *funcctx;
511  char *pubname = text_to_cstring(PG_GETARG_TEXT_PP(0));
512  Publication *publication;
513  List *tables;
514 
515  /* stuff done only on the first call of the function */
516  if (SRF_IS_FIRSTCALL())
517  {
518  MemoryContext oldcontext;
519 
520  /* create a function context for cross-call persistence */
521  funcctx = SRF_FIRSTCALL_INIT();
522 
523  /* switch to memory context appropriate for multiple function calls */
524  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
525 
526  publication = GetPublicationByName(pubname, false);
527 
528  /*
529  * Publications support partitioned tables, although all changes are
530  * replicated using leaf partition identity and schema, so we only
531  * need those.
532  */
533  if (publication->alltables)
534  tables = GetAllTablesPublicationRelations(publication->pubviaroot);
535  else
536  tables = GetPublicationRelations(publication->oid,
537  publication->pubviaroot ?
540  funcctx->user_fctx = (void *) tables;
541 
542  MemoryContextSwitchTo(oldcontext);
543  }
544 
545  /* stuff done on every call of the function */
546  funcctx = SRF_PERCALL_SETUP();
547  tables = (List *) funcctx->user_fctx;
548 
549  if (funcctx->call_cntr < list_length(tables))
550  {
551  Oid relid = list_nth_oid(tables, funcctx->call_cntr);
552 
553  SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
554  }
555 
556  SRF_RETURN_DONE(funcctx);
557 }
uint64 call_cntr
Definition: funcapi.h:65
#define NIL
Definition: pg_list.h:65
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:317
PublicationActions pubactions
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:98
int n_members
Definition: catcache.h:176
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:529
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:112
List * GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
#define RelationGetDescr(relation)
Definition: rel.h:482
PublicationPartOpt
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:293
#define GetSysCacheOid1(cacheId, oidcol, key1)
Definition: syscache.h:192
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1915
char * pstrdup(const char *in)
Definition: mcxt.c:1186
#define RelationGetForm(relation)
Definition: rel.h:450
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define AccessShareLock
Definition: lockdefs.h:36
List * list_concat(List *list1, const List *list2)
Definition: list.c:515
int errcode(int sqlerrcode)
Definition: elog.c:610
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:43
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
Form_pg_class rd_rel
Definition: rel.h:109
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:357
#define OidIsValid(objectId)
Definition: c.h:651
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:297
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:356
Publication * GetPublicationByName(const char *pubname, bool missing_ok)
char * get_publication_name(Oid pubid, bool missing_ok)
#define FirstNormalObjectId
Definition: transam.h:155
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:308
static Oid list_nth_oid(const List *list, int n)
Definition: pg_list.h:299
bool IsCatalogRelationOid(Oid relid)
Definition: catalog.c:115
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:299
List * GetRelationPublications(Oid relid)
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:448
Publication * GetPublication(Oid pubid)
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
static void check_publication_add_relation(Relation targetrel)
#define NoLock
Definition: lockdefs.h:34
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1286
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
bool is_publishable_relation(Relation rel)
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:957
Datum pg_relation_is_publishable(PG_FUNCTION_ARGS)
#define CStringGetDatum(X)
Definition: postgres.h:578
#define RelationGetRelationName(relation)
Definition: rel.h:490
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:210
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
#define PublicationRelPrrelidPrpubidIndexId
Definition: indexing.h:360
#define ReleaseSysCacheList(x)
Definition: syscache.h:217
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:358
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
#define BoolGetDatum(X)
Definition: postgres.h:402
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:144
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:745
List * GetAllTablesPublications(void)
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
static int list_length(const List *l)
Definition: pg_list.h:169
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:185
#define RelationNeedsWAL(relation)
Definition: rel.h:562
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define CharGetDatum(X)
Definition: postgres.h:416
#define PublicationRelObjectIndexId
Definition: indexing.h:357
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1278
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:862
static Datum values[MAXATTR]
Definition: bootstrap.c:167
char * text_to_cstring(const text *t)
Definition: varlena.c:205
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:165
void * user_fctx
Definition: funcapi.h:82
const ObjectAddress InvalidObjectAddress
Datum pg_get_publication_tables(PG_FUNCTION_ARGS)
void * palloc(Size size)
Definition: mcxt.c:949
int errmsg(const char *fmt,...)
Definition: elog.c:824
#define elog(elevel,...)
Definition: elog.h:214
FormData_pg_publication_rel * Form_pg_publication_rel
int i
#define NameStr(name)
Definition: c.h:622
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
static bool is_publishable_class(Oid relid, Form_pg_class reltuple)
Oid get_publication_oid(const char *pubname, bool missing_ok)
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
HeapTupleData tuple
Definition: catcache.h:121
ObjectAddress publication_add_relation(Oid pubid, Relation targetrel, bool if_not_exists)
List * GetAllTablesPublicationRelations(bool pubviaroot)
FormData_pg_publication * Form_pg_publication
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:31
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:456
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:221
#define PG_RETURN_NULL()
Definition: fmgr.h:344
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:192
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:317
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:295