PostgreSQL Source Code  git master
execReplication.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * execReplication.c
4  * miscellaneous executor routines for logical replication
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  * src/backend/executor/execReplication.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 
15 #include "postgres.h"
16 
17 #include "access/genam.h"
18 #include "access/relscan.h"
19 #include "access/tableam.h"
20 #include "access/transam.h"
21 #include "access/xact.h"
22 #include "commands/trigger.h"
23 #include "executor/executor.h"
25 #include "nodes/nodeFuncs.h"
26 #include "parser/parse_relation.h"
27 #include "parser/parsetree.h"
28 #include "storage/bufmgr.h"
29 #include "storage/lmgr.h"
30 #include "utils/builtins.h"
31 #include "utils/datum.h"
32 #include "utils/lsyscache.h"
33 #include "utils/memutils.h"
34 #include "utils/rel.h"
35 #include "utils/snapmgr.h"
36 #include "utils/syscache.h"
37 #include "utils/typcache.h"
38 
39 
40 /*
41  * Setup a ScanKey for a search in the relation 'rel' for a tuple 'key' that
42  * is setup to match 'rel' (*NOT* idxrel!).
43  *
44  * Returns whether any column contains NULLs.
45  *
46  * This is not generic routine, it expects the idxrel to be replication
47  * identity of a rel and meet all limitations associated with that.
48  */
49 static bool
51  TupleTableSlot *searchslot)
52 {
53  int attoff;
54  bool isnull;
55  Datum indclassDatum;
56  oidvector *opclass;
57  int2vector *indkey = &idxrel->rd_index->indkey;
58  bool hasnulls = false;
59 
62 
63  indclassDatum = SysCacheGetAttr(INDEXRELID, idxrel->rd_indextuple,
64  Anum_pg_index_indclass, &isnull);
65  Assert(!isnull);
66  opclass = (oidvector *) DatumGetPointer(indclassDatum);
67 
68  /* Build scankey for every attribute in the index. */
69  for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(idxrel); attoff++)
70  {
71  Oid operator;
72  Oid opfamily;
73  RegProcedure regop;
74  int pkattno = attoff + 1;
75  int mainattno = indkey->values[attoff];
76  Oid optype = get_opclass_input_type(opclass->values[attoff]);
77 
78  /*
79  * Load the operator info. We need this to get the equality operator
80  * function for the scan key.
81  */
82  opfamily = get_opclass_family(opclass->values[attoff]);
83 
84  operator = get_opfamily_member(opfamily, optype,
85  optype,
87  if (!OidIsValid(operator))
88  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
89  BTEqualStrategyNumber, optype, optype, opfamily);
90 
91  regop = get_opcode(operator);
92 
93  /* Initialize the scankey. */
94  ScanKeyInit(&skey[attoff],
95  pkattno,
97  regop,
98  searchslot->tts_values[mainattno - 1]);
99 
100  skey[attoff].sk_collation = idxrel->rd_indcollation[attoff];
101 
102  /* Check for null value. */
103  if (searchslot->tts_isnull[mainattno - 1])
104  {
105  hasnulls = true;
106  skey[attoff].sk_flags |= SK_ISNULL;
107  }
108  }
109 
110  return hasnulls;
111 }
112 
113 /*
114  * Search the relation 'rel' for tuple using the index.
115  *
116  * If a matching tuple is found, lock it with lockmode, fill the slot with its
117  * contents, and return true. Return false otherwise.
118  */
119 bool
121  LockTupleMode lockmode,
122  TupleTableSlot *searchslot,
123  TupleTableSlot *outslot)
124 {
126  IndexScanDesc scan;
127  SnapshotData snap;
128  TransactionId xwait;
129  Relation idxrel;
130  bool found;
131 
132  /* Open the index. */
133  idxrel = index_open(idxoid, RowExclusiveLock);
134 
135  /* Start an index scan. */
136  InitDirtySnapshot(snap);
137  scan = index_beginscan(rel, idxrel, &snap,
139  0);
140 
141  /* Build scan key. */
142  build_replindex_scan_key(skey, rel, idxrel, searchslot);
143 
144 retry:
145  found = false;
146 
147  index_rescan(scan, skey, IndexRelationGetNumberOfKeyAttributes(idxrel), NULL, 0);
148 
149  /* Try to find the tuple */
150  if (index_getnext_slot(scan, ForwardScanDirection, outslot))
151  {
152  found = true;
153  ExecMaterializeSlot(outslot);
154 
155  xwait = TransactionIdIsValid(snap.xmin) ?
156  snap.xmin : snap.xmax;
157 
158  /*
159  * If the tuple is locked, wait for locking transaction to finish and
160  * retry.
161  */
162  if (TransactionIdIsValid(xwait))
163  {
164  XactLockTableWait(xwait, NULL, NULL, XLTW_None);
165  goto retry;
166  }
167  }
168 
169  /* Found tuple, try to lock it in the lockmode. */
170  if (found)
171  {
172  TM_FailureData tmfd;
173  TM_Result res;
174 
176 
177  res = table_tuple_lock(rel, &(outslot->tts_tid), GetLatestSnapshot(),
178  outslot,
179  GetCurrentCommandId(false),
180  lockmode,
182  0 /* don't follow updates */ ,
183  &tmfd);
184 
186 
187  switch (res)
188  {
189  case TM_Ok:
190  break;
191  case TM_Updated:
192  /* XXX: Improve handling here */
194  ereport(LOG,
195  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
196  errmsg("tuple to be locked was already moved to another partition due to concurrent update, retrying")));
197  else
198  ereport(LOG,
199  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
200  errmsg("concurrent update, retrying")));
201  goto retry;
202  case TM_Deleted:
203  /* XXX: Improve handling here */
204  ereport(LOG,
205  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
206  errmsg("concurrent delete, retrying")));
207  goto retry;
208  case TM_Invisible:
209  elog(ERROR, "attempted to lock invisible tuple");
210  break;
211  default:
212  elog(ERROR, "unexpected table_tuple_lock status: %u", res);
213  break;
214  }
215  }
216 
217  index_endscan(scan);
218 
219  /* Don't release lock until commit. */
220  index_close(idxrel, NoLock);
221 
222  return found;
223 }
224 
225 /*
226  * Compare the tuples in the slots by checking if they have equal values.
227  */
228 static bool
230  TypeCacheEntry **eq)
231 {
232  int attrnum;
233 
234  Assert(slot1->tts_tupleDescriptor->natts ==
235  slot2->tts_tupleDescriptor->natts);
236 
237  slot_getallattrs(slot1);
238  slot_getallattrs(slot2);
239 
240  /* Check equality of the attributes. */
241  for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
242  {
243  Form_pg_attribute att;
244  TypeCacheEntry *typentry;
245 
246  /*
247  * If one value is NULL and other is not, then they are certainly not
248  * equal
249  */
250  if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
251  return false;
252 
253  /*
254  * If both are NULL, they can be considered equal.
255  */
256  if (slot1->tts_isnull[attrnum] || slot2->tts_isnull[attrnum])
257  continue;
258 
259  att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
260 
261  typentry = eq[attrnum];
262  if (typentry == NULL)
263  {
264  typentry = lookup_type_cache(att->atttypid,
266  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
267  ereport(ERROR,
268  (errcode(ERRCODE_UNDEFINED_FUNCTION),
269  errmsg("could not identify an equality operator for type %s",
270  format_type_be(att->atttypid))));
271  eq[attrnum] = typentry;
272  }
273 
275  att->attcollation,
276  slot1->tts_values[attrnum],
277  slot2->tts_values[attrnum])))
278  return false;
279  }
280 
281  return true;
282 }
283 
284 /*
285  * Search the relation 'rel' for tuple using the sequential scan.
286  *
287  * If a matching tuple is found, lock it with lockmode, fill the slot with its
288  * contents, and return true. Return false otherwise.
289  *
290  * Note that this stops on the first matching tuple.
291  *
292  * This can obviously be quite slow on tables that have more than few rows.
293  */
294 bool
296  TupleTableSlot *searchslot, TupleTableSlot *outslot)
297 {
298  TupleTableSlot *scanslot;
299  TableScanDesc scan;
300  SnapshotData snap;
301  TypeCacheEntry **eq;
302  TransactionId xwait;
303  bool found;
305 
306  Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
307 
308  eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
309 
310  /* Start a heap scan. */
311  InitDirtySnapshot(snap);
312  scan = table_beginscan(rel, &snap, 0, NULL);
313  scanslot = table_slot_create(rel, NULL);
314 
315 retry:
316  found = false;
317 
318  table_rescan(scan, NULL);
319 
320  /* Try to find the tuple */
321  while (table_scan_getnextslot(scan, ForwardScanDirection, scanslot))
322  {
323  if (!tuples_equal(scanslot, searchslot, eq))
324  continue;
325 
326  found = true;
327  ExecCopySlot(outslot, scanslot);
328 
329  xwait = TransactionIdIsValid(snap.xmin) ?
330  snap.xmin : snap.xmax;
331 
332  /*
333  * If the tuple is locked, wait for locking transaction to finish and
334  * retry.
335  */
336  if (TransactionIdIsValid(xwait))
337  {
338  XactLockTableWait(xwait, NULL, NULL, XLTW_None);
339  goto retry;
340  }
341 
342  /* Found our tuple and it's not locked */
343  break;
344  }
345 
346  /* Found tuple, try to lock it in the lockmode. */
347  if (found)
348  {
349  TM_FailureData tmfd;
350  TM_Result res;
351 
353 
354  res = table_tuple_lock(rel, &(outslot->tts_tid), GetLatestSnapshot(),
355  outslot,
356  GetCurrentCommandId(false),
357  lockmode,
359  0 /* don't follow updates */ ,
360  &tmfd);
361 
363 
364  switch (res)
365  {
366  case TM_Ok:
367  break;
368  case TM_Updated:
369  /* XXX: Improve handling here */
371  ereport(LOG,
372  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
373  errmsg("tuple to be locked was already moved to another partition due to concurrent update, retrying")));
374  else
375  ereport(LOG,
376  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
377  errmsg("concurrent update, retrying")));
378  goto retry;
379  case TM_Deleted:
380  /* XXX: Improve handling here */
381  ereport(LOG,
382  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
383  errmsg("concurrent delete, retrying")));
384  goto retry;
385  case TM_Invisible:
386  elog(ERROR, "attempted to lock invisible tuple");
387  break;
388  default:
389  elog(ERROR, "unexpected table_tuple_lock status: %u", res);
390  break;
391  }
392  }
393 
394  table_endscan(scan);
396 
397  return found;
398 }
399 
400 /*
401  * Insert tuple represented in the slot to the relation, update the indexes,
402  * and execute any constraints and per-row triggers.
403  *
404  * Caller is responsible for opening the indexes.
405  */
406 void
408  EState *estate, TupleTableSlot *slot)
409 {
410  bool skip_tuple = false;
411  Relation rel = resultRelInfo->ri_RelationDesc;
412 
413  /* For now we support only tables. */
414  Assert(rel->rd_rel->relkind == RELKIND_RELATION);
415 
417 
418  /* BEFORE ROW INSERT Triggers */
419  if (resultRelInfo->ri_TrigDesc &&
420  resultRelInfo->ri_TrigDesc->trig_insert_before_row)
421  {
422  if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
423  skip_tuple = true; /* "do nothing" */
424  }
425 
426  if (!skip_tuple)
427  {
428  List *recheckIndexes = NIL;
429 
430  /* Compute stored generated columns */
431  if (rel->rd_att->constr &&
433  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
434  CMD_INSERT);
435 
436  /* Check the constraints of the tuple */
437  if (rel->rd_att->constr)
438  ExecConstraints(resultRelInfo, slot, estate);
439  if (rel->rd_rel->relispartition)
440  ExecPartitionCheck(resultRelInfo, slot, estate, true);
441 
442  /* OK, store the tuple and create index entries for it */
443  simple_table_tuple_insert(resultRelInfo->ri_RelationDesc, slot);
444 
445  if (resultRelInfo->ri_NumIndices > 0)
446  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
447  slot, estate, false, NULL,
448  NIL);
449 
450  /* AFTER ROW INSERT Triggers */
451  ExecARInsertTriggers(estate, resultRelInfo, slot,
452  recheckIndexes, NULL);
453 
454  /*
455  * XXX we should in theory pass a TransitionCaptureState object to the
456  * above to capture transition tuples, but after statement triggers
457  * don't actually get fired by replication yet anyway
458  */
459 
460  list_free(recheckIndexes);
461  }
462 }
463 
464 /*
465  * Find the searchslot tuple and update it with data in the slot,
466  * update the indexes, and execute any constraints and per-row triggers.
467  *
468  * Caller is responsible for opening the indexes.
469  */
470 void
472  EState *estate, EPQState *epqstate,
473  TupleTableSlot *searchslot, TupleTableSlot *slot)
474 {
475  bool skip_tuple = false;
476  Relation rel = resultRelInfo->ri_RelationDesc;
477  ItemPointer tid = &(searchslot->tts_tid);
478 
479  /* For now we support only tables. */
480  Assert(rel->rd_rel->relkind == RELKIND_RELATION);
481 
483 
484  /* BEFORE ROW UPDATE Triggers */
485  if (resultRelInfo->ri_TrigDesc &&
486  resultRelInfo->ri_TrigDesc->trig_update_before_row)
487  {
488  if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
489  tid, NULL, slot))
490  skip_tuple = true; /* "do nothing" */
491  }
492 
493  if (!skip_tuple)
494  {
495  List *recheckIndexes = NIL;
496  bool update_indexes;
497 
498  /* Compute stored generated columns */
499  if (rel->rd_att->constr &&
501  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
502  CMD_UPDATE);
503 
504  /* Check the constraints of the tuple */
505  if (rel->rd_att->constr)
506  ExecConstraints(resultRelInfo, slot, estate);
507  if (rel->rd_rel->relispartition)
508  ExecPartitionCheck(resultRelInfo, slot, estate, true);
509 
510  simple_table_tuple_update(rel, tid, slot, estate->es_snapshot,
511  &update_indexes);
512 
513  if (resultRelInfo->ri_NumIndices > 0 && update_indexes)
514  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
515  slot, estate, false, NULL,
516  NIL);
517 
518  /* AFTER ROW UPDATE Triggers */
519  ExecARUpdateTriggers(estate, resultRelInfo,
520  tid, NULL, slot,
521  recheckIndexes, NULL);
522 
523  list_free(recheckIndexes);
524  }
525 }
526 
527 /*
528  * Find the searchslot tuple and delete it, and execute any constraints
529  * and per-row triggers.
530  *
531  * Caller is responsible for opening the indexes.
532  */
533 void
535  EState *estate, EPQState *epqstate,
536  TupleTableSlot *searchslot)
537 {
538  bool skip_tuple = false;
539  Relation rel = resultRelInfo->ri_RelationDesc;
540  ItemPointer tid = &searchslot->tts_tid;
541 
543 
544  /* BEFORE ROW DELETE Triggers */
545  if (resultRelInfo->ri_TrigDesc &&
546  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
547  {
548  skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
549  tid, NULL, NULL);
550 
551  }
552 
553  if (!skip_tuple)
554  {
555  /* OK, delete the tuple */
556  simple_table_tuple_delete(rel, tid, estate->es_snapshot);
557 
558  /* AFTER ROW DELETE Triggers */
559  ExecARDeleteTriggers(estate, resultRelInfo,
560  tid, NULL, NULL);
561  }
562 }
563 
564 /*
565  * Check if command can be executed with current replica identity.
566  */
567 void
569 {
570  PublicationActions *pubactions;
571 
572  /* We only need to do checks for UPDATE and DELETE. */
573  if (cmd != CMD_UPDATE && cmd != CMD_DELETE)
574  return;
575 
576  /* If relation has replica identity we are always good. */
577  if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL ||
579  return;
580 
581  /*
582  * This is either UPDATE OR DELETE and there is no replica identity.
583  *
584  * Check if the table publishes UPDATES or DELETES.
585  */
586  pubactions = GetRelationPublicationActions(rel);
587  if (cmd == CMD_UPDATE && pubactions->pubupdate)
588  ereport(ERROR,
589  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
590  errmsg("cannot update table \"%s\" because it does not have a replica identity and publishes updates",
592  errhint("To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.")));
593  else if (cmd == CMD_DELETE && pubactions->pubdelete)
594  ereport(ERROR,
595  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
596  errmsg("cannot delete from table \"%s\" because it does not have a replica identity and publishes deletes",
598  errhint("To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.")));
599 }
600 
601 
602 /*
603  * Check if we support writing into specific relkind.
604  *
605  * The nspname and relname are only needed for error reporting.
606  */
607 void
608 CheckSubscriptionRelkind(char relkind, const char *nspname,
609  const char *relname)
610 {
611  /*
612  * Give a more specific error for foreign tables.
613  */
614  if (relkind == RELKIND_FOREIGN_TABLE)
615  ereport(ERROR,
616  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
617  errmsg("cannot use relation \"%s.%s\" as logical replication target",
618  nspname, relname),
619  errdetail("\"%s.%s\" is a foreign table.",
620  nspname, relname)));
621 
622  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
623  ereport(ERROR,
624  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
625  errmsg("cannot use relation \"%s.%s\" as logical replication target",
626  nspname, relname),
627  errdetail("\"%s.%s\" is not a table.",
628  nspname, relname)));
629 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
int ri_NumIndices
Definition: execnodes.h:415
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475
#define NIL
Definition: pg_list.h:65
ItemPointerData ctid
Definition: tableam.h:125
Definition: c.h:602
Relation ri_RelationDesc
Definition: execnodes.h:412
LockTupleMode
Definition: lockoptions.h:49
#define InitDirtySnapshot(snapshotdata)
Definition: snapmgr.h:75
int errhint(const char *fmt,...)
Definition: elog.c:1068
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2248
Oid RelationGetReplicaIndex(Relation relation)
Definition: relcache.c:4723
uint32 TransactionId
Definition: c.h:521
#define RelationGetDescr(relation)
Definition: rel.h:482
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:135
regproc RegProcedure
Definition: c.h:519
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1801
void simple_table_tuple_update(Relation rel, ItemPointer otid, TupleTableSlot *slot, Snapshot snapshot, bool *update_indexes)
Definition: tableam.c:346
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition: indexam.c:295
int errcode(int sqlerrcode)
Definition: elog.c:610
void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
Snapshot es_snapshot
Definition: execnodes.h:517
static bool tuples_equal(TupleTableSlot *slot1, TupleTableSlot *slot2, TypeCacheEntry **eq)
Datum * tts_values
Definition: tuptable.h:126
void PopActiveSnapshot(void)
Definition: snapmgr.c:759
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1152
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:904
#define LOG
Definition: elog.h:26
Form_pg_class rd_rel
Definition: rel.h:109
NameData relname
Definition: pg_class.h:38
unsigned int Oid
Definition: postgres_ext.h:31
void ExecSimpleRelationUpdate(ResultRelInfo *resultRelInfo, EState *estate, EPQState *epqstate, TupleTableSlot *searchslot, TupleTableSlot *slot)
#define OidIsValid(objectId)
Definition: c.h:652
static void table_rescan(TableScanDesc scan, struct ScanKeyData *key)
Definition: tableam.h:872
bool RelationFindReplTupleByIndex(Relation rel, Oid idxoid, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot)
struct HeapTupleData * rd_indextuple
Definition: rel.h:176
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot)
Definition: trigger.c:2393
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
Definition: trigger.c:2478
bool has_generated_stored
Definition: tupdesc.h:45
Form_pg_index rd_index
Definition: rel.h:174
Oid * rd_indcollation
Definition: rel.h:199
#define ERROR
Definition: elog.h:43
Definition: lmgr.h:26
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:755
bool ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2172
static TM_Result table_tuple_lock(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
Definition: tableam.h:1356
List * ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes)
Definition: execIndexing.c:273
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:354
#define NoLock
Definition: lockdefs.h:34
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
bool * tts_isnull
Definition: tuptable.h:128
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:610
TupleConstr * constr
Definition: tupdesc.h:85
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:954
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:164
#define DatumGetBool(X)
Definition: postgres.h:393
bool trig_update_before_row
Definition: reltrigger.h:61
#define RelationGetRelationName(relation)
Definition: rel.h:490
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
TransactionId xmax
Definition: snapshot.h:158
void CheckSubscriptionRelkind(char relkind, const char *nspname, const char *relname)
TransactionId xmin
Definition: snapshot.h:157
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:475
void CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
void index_endscan(IndexScanDesc scan)
Definition: indexam.c:321
#define SK_ISNULL
Definition: skey.h:115
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:424
void simple_table_tuple_delete(Relation rel, ItemPointer tid, Snapshot snapshot)
Definition: tableam.c:301
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2763
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
void ExecSimpleRelationDelete(ResultRelInfo *resultRelInfo, EState *estate, EPQState *epqstate, TupleTableSlot *searchslot)
TM_Result
Definition: tableam.h:70
Definition: c.h:591
bool trig_insert_before_row
Definition: reltrigger.h:56
void * palloc0(Size size)
Definition: mcxt.c:981
uintptr_t Datum
Definition: postgres.h:367
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1377
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:184
TupleDesc rd_att
Definition: rel.h:110
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
FmgrInfo eq_opr_finfo
Definition: typcache.h:74
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:331
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1202
Oid fn_oid
Definition: fmgr.h:59
#define ereport(elevel,...)
Definition: elog.h:144
struct PublicationActions * GetRelationPublicationActions(Relation relation)
Definition: relcache.c:5306
int sk_flags
Definition: skey.h:66
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:639
#define Assert(condition)
Definition: c.h:746
void simple_table_tuple_insert(Relation rel, TupleTableSlot *slot)
Definition: tableam.c:287
Definition: tableam.h:76
#define INDEX_MAX_KEYS
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
Definition: indexam.c:614
int16 values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:599
Oid get_opclass_family(Oid opclass)
Definition: lsyscache.c:1129
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:325
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
#define DatumGetPointer(X)
Definition: postgres.h:549
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:863
int errmsg(const char *fmt,...)
Definition: elog.c:821
Oid sk_collation
Definition: skey.h:70
void list_free(List *list)
Definition: list.c:1376
bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot)
#define elog(elevel,...)
Definition: elog.h:214
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:2623
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1679
bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
Definition: tupdesc.c:411
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:761
#define TransactionIdIsValid(xid)
Definition: transam.h:41
Definition: pg_list.h:50
ItemPointerData tts_tid
Definition: tuptable.h:130
static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot)
#define RelationGetRelid(relation)
Definition: rel.h:456
CmdType
Definition: nodes.h:671
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
Oid RelationGetPrimaryKeyIndex(Relation relation)
Definition: relcache.c:4702
#define BTEqualStrategyNumber
Definition: stratnum.h:31
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, int norderbys)
Definition: indexam.c:203
bool trig_delete_before_row
Definition: reltrigger.h:66
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:122
void ExecSimpleRelationInsert(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot)
Oid get_opclass_input_type(Oid opclass)
Definition: lsyscache.c:1151