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-2018, 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/relscan.h"
18 #include "access/transam.h"
19 #include "access/xact.h"
20 #include "commands/trigger.h"
21 #include "executor/executor.h"
22 #include "nodes/nodeFuncs.h"
23 #include "parser/parse_relation.h"
24 #include "parser/parsetree.h"
25 #include "storage/bufmgr.h"
26 #include "storage/lmgr.h"
27 #include "utils/builtins.h"
28 #include "utils/datum.h"
29 #include "utils/lsyscache.h"
30 #include "utils/memutils.h"
31 #include "utils/rel.h"
32 #include "utils/snapmgr.h"
33 #include "utils/syscache.h"
34 #include "utils/typcache.h"
35 #include "utils/tqual.h"
36 
37 
38 /*
39  * Setup a ScanKey for a search in the relation 'rel' for a tuple 'key' that
40  * is setup to match 'rel' (*NOT* idxrel!).
41  *
42  * Returns whether any column contains NULLs.
43  *
44  * This is not generic routine, it expects the idxrel to be replication
45  * identity of a rel and meet all limitations associated with that.
46  */
47 static bool
49  TupleTableSlot *searchslot)
50 {
51  int attoff;
52  bool isnull;
53  Datum indclassDatum;
54  oidvector *opclass;
55  int2vector *indkey = &idxrel->rd_index->indkey;
56  bool hasnulls = false;
57 
59 
60  indclassDatum = SysCacheGetAttr(INDEXRELID, idxrel->rd_indextuple,
61  Anum_pg_index_indclass, &isnull);
62  Assert(!isnull);
63  opclass = (oidvector *) DatumGetPointer(indclassDatum);
64 
65  /* Build scankey for every attribute in the index. */
66  for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(idxrel); attoff++)
67  {
68  Oid operator;
69  Oid opfamily;
70  RegProcedure regop;
71  int pkattno = attoff + 1;
72  int mainattno = indkey->values[attoff];
73  Oid optype = get_opclass_input_type(opclass->values[attoff]);
74 
75  /*
76  * Load the operator info. We need this to get the equality operator
77  * function for the scan key.
78  */
79  opfamily = get_opclass_family(opclass->values[attoff]);
80 
81  operator = get_opfamily_member(opfamily, optype,
82  optype,
84  if (!OidIsValid(operator))
85  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
86  BTEqualStrategyNumber, optype, optype, opfamily);
87 
88  regop = get_opcode(operator);
89 
90  /* Initialize the scankey. */
91  ScanKeyInit(&skey[attoff],
92  pkattno,
94  regop,
95  searchslot->tts_values[mainattno - 1]);
96 
97  /* Check for null value. */
98  if (searchslot->tts_isnull[mainattno - 1])
99  {
100  hasnulls = true;
101  skey[attoff].sk_flags |= SK_ISNULL;
102  }
103  }
104 
105  return hasnulls;
106 }
107 
108 /*
109  * Search the relation 'rel' for tuple using the index.
110  *
111  * If a matching tuple is found, lock it with lockmode, fill the slot with its
112  * contents, and return true. Return false otherwise.
113  */
114 bool
116  LockTupleMode lockmode,
117  TupleTableSlot *searchslot,
118  TupleTableSlot *outslot)
119 {
120  HeapTuple scantuple;
122  IndexScanDesc scan;
123  SnapshotData snap;
124  TransactionId xwait;
125  Relation idxrel;
126  bool found;
127 
128  /* Open the index. */
129  idxrel = index_open(idxoid, RowExclusiveLock);
130 
131  /* Start an index scan. */
132  InitDirtySnapshot(snap);
133  scan = index_beginscan(rel, idxrel, &snap,
135  0);
136 
137  /* Build scan key. */
138  build_replindex_scan_key(skey, rel, idxrel, searchslot);
139 
140 retry:
141  found = false;
142 
143  index_rescan(scan, skey, IndexRelationGetNumberOfKeyAttributes(idxrel), NULL, 0);
144 
145  /* Try to find the tuple */
146  if ((scantuple = index_getnext(scan, ForwardScanDirection)) != NULL)
147  {
148  found = true;
149  ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
150  ExecMaterializeSlot(outslot);
151 
152  xwait = TransactionIdIsValid(snap.xmin) ?
153  snap.xmin : snap.xmax;
154 
155  /*
156  * If the tuple is locked, wait for locking transaction to finish and
157  * retry.
158  */
159  if (TransactionIdIsValid(xwait))
160  {
161  XactLockTableWait(xwait, NULL, NULL, XLTW_None);
162  goto retry;
163  }
164  }
165 
166  /* Found tuple, try to lock it in the lockmode. */
167  if (found)
168  {
169  Buffer buf;
171  HTSU_Result res;
172  HeapTupleData locktup;
173 
174  ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
175 
177 
178  res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
179  lockmode,
181  false /* don't follow updates */ ,
182  &buf, &hufd);
183  /* the tuple slot already has the buffer pinned */
184  ReleaseBuffer(buf);
185 
187 
188  switch (res)
189  {
191  break;
192  case HeapTupleUpdated:
193  /* XXX: Improve handling here */
195  ereport(LOG,
196  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
197  errmsg("tuple to be locked was already moved to another partition due to concurrent update, retrying")));
198  else
199  ereport(LOG,
200  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
201  errmsg("concurrent update, retrying")));
202  goto retry;
203  case HeapTupleInvisible:
204  elog(ERROR, "attempted to lock invisible tuple");
205  default:
206  elog(ERROR, "unexpected heap_lock_tuple status: %u", res);
207  break;
208  }
209  }
210 
211  index_endscan(scan);
212 
213  /* Don't release lock until commit. */
214  index_close(idxrel, NoLock);
215 
216  return found;
217 }
218 
219 /*
220  * Compare the tuple and slot and check if they have equal values.
221  *
222  * We use binary datum comparison which might return false negatives but
223  * that's the best we can do here as there may be multiple notions of
224  * equality for the data types and table columns don't specify which one
225  * to use.
226  */
227 static bool
229 {
231  bool isnull[MaxTupleAttributeNumber];
232  int attrnum;
233 
234  heap_deform_tuple(tup, desc, values, isnull);
235 
236  /* Check equality of the attributes. */
237  for (attrnum = 0; attrnum < desc->natts; attrnum++)
238  {
239  Form_pg_attribute att;
240  TypeCacheEntry *typentry;
241 
242  /*
243  * If one value is NULL and other is not, then they are certainly not
244  * equal
245  */
246  if (isnull[attrnum] != slot->tts_isnull[attrnum])
247  return false;
248 
249  /*
250  * If both are NULL, they can be considered equal.
251  */
252  if (isnull[attrnum])
253  continue;
254 
255  att = TupleDescAttr(desc, attrnum);
256 
257  typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
258  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
259  ereport(ERROR,
260  (errcode(ERRCODE_UNDEFINED_FUNCTION),
261  errmsg("could not identify an equality operator for type %s",
262  format_type_be(att->atttypid))));
263 
264  if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
265  values[attrnum],
266  slot->tts_values[attrnum])))
267  return false;
268  }
269 
270  return true;
271 }
272 
273 /*
274  * Search the relation 'rel' for tuple using the sequential scan.
275  *
276  * If a matching tuple is found, lock it with lockmode, fill the slot with its
277  * contents, and return true. Return false otherwise.
278  *
279  * Note that this stops on the first matching tuple.
280  *
281  * This can obviously be quite slow on tables that have more than few rows.
282  */
283 bool
285  TupleTableSlot *searchslot, TupleTableSlot *outslot)
286 {
287  HeapTuple scantuple;
288  HeapScanDesc scan;
289  SnapshotData snap;
290  TransactionId xwait;
291  bool found;
292  TupleDesc desc = RelationGetDescr(rel);
293 
294  Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
295 
296  /* Start a heap scan. */
297  InitDirtySnapshot(snap);
298  scan = heap_beginscan(rel, &snap, 0, NULL);
299 
300 retry:
301  found = false;
302 
303  heap_rescan(scan, NULL);
304 
305  /* Try to find the tuple */
306  while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
307  {
308  if (!tuple_equals_slot(desc, scantuple, searchslot))
309  continue;
310 
311  found = true;
312  ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
313  ExecMaterializeSlot(outslot);
314 
315  xwait = TransactionIdIsValid(snap.xmin) ?
316  snap.xmin : snap.xmax;
317 
318  /*
319  * If the tuple is locked, wait for locking transaction to finish and
320  * retry.
321  */
322  if (TransactionIdIsValid(xwait))
323  {
324  XactLockTableWait(xwait, NULL, NULL, XLTW_None);
325  goto retry;
326  }
327  }
328 
329  /* Found tuple, try to lock it in the lockmode. */
330  if (found)
331  {
332  Buffer buf;
334  HTSU_Result res;
335  HeapTupleData locktup;
336 
337  ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
338 
340 
341  res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
342  lockmode,
344  false /* don't follow updates */ ,
345  &buf, &hufd);
346  /* the tuple slot already has the buffer pinned */
347  ReleaseBuffer(buf);
348 
350 
351  switch (res)
352  {
354  break;
355  case HeapTupleUpdated:
356  /* XXX: Improve handling here */
358  ereport(LOG,
359  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
360  errmsg("tuple to be locked was already moved to another partition due to concurrent update, retrying")));
361  else
362  ereport(LOG,
363  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
364  errmsg("concurrent update, retrying")));
365  goto retry;
366  case HeapTupleInvisible:
367  elog(ERROR, "attempted to lock invisible tuple");
368  default:
369  elog(ERROR, "unexpected heap_lock_tuple status: %u", res);
370  break;
371  }
372  }
373 
374  heap_endscan(scan);
375 
376  return found;
377 }
378 
379 /*
380  * Insert tuple represented in the slot to the relation, update the indexes,
381  * and execute any constraints and per-row triggers.
382  *
383  * Caller is responsible for opening the indexes.
384  */
385 void
387 {
388  bool skip_tuple = false;
389  HeapTuple tuple;
390  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
391  Relation rel = resultRelInfo->ri_RelationDesc;
392 
393  /* For now we support only tables. */
394  Assert(rel->rd_rel->relkind == RELKIND_RELATION);
395 
397 
398  /* BEFORE ROW INSERT Triggers */
399  if (resultRelInfo->ri_TrigDesc &&
400  resultRelInfo->ri_TrigDesc->trig_insert_before_row)
401  {
402  slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
403 
404  if (slot == NULL) /* "do nothing" */
405  skip_tuple = true;
406  }
407 
408  if (!skip_tuple)
409  {
410  List *recheckIndexes = NIL;
411 
412  /* Check the constraints of the tuple */
413  if (rel->rd_att->constr)
414  ExecConstraints(resultRelInfo, slot, estate, true);
415 
416  /* Store the slot into tuple that we can inspect. */
417  tuple = ExecMaterializeSlot(slot);
418 
419  /* OK, store the tuple and create index entries for it */
420  simple_heap_insert(rel, tuple);
421 
422  if (resultRelInfo->ri_NumIndices > 0)
423  recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
424  estate, false, NULL,
425  NIL);
426 
427  /* AFTER ROW INSERT Triggers */
428  ExecARInsertTriggers(estate, resultRelInfo, tuple,
429  recheckIndexes, NULL);
430 
431  /*
432  * XXX we should in theory pass a TransitionCaptureState object to the
433  * above to capture transition tuples, but after statement triggers
434  * don't actually get fired by replication yet anyway
435  */
436 
437  list_free(recheckIndexes);
438  }
439 }
440 
441 /*
442  * Find the searchslot tuple and update it with data in the slot,
443  * update the indexes, and execute any constraints and per-row triggers.
444  *
445  * Caller is responsible for opening the indexes.
446  */
447 void
449  TupleTableSlot *searchslot, TupleTableSlot *slot)
450 {
451  bool skip_tuple = false;
452  HeapTuple tuple;
453  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
454  Relation rel = resultRelInfo->ri_RelationDesc;
455 
456  /* For now we support only tables. */
457  Assert(rel->rd_rel->relkind == RELKIND_RELATION);
458 
460 
461  /* BEFORE ROW UPDATE Triggers */
462  if (resultRelInfo->ri_TrigDesc &&
463  resultRelInfo->ri_TrigDesc->trig_update_before_row)
464  {
465  slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
466  &searchslot->tts_tuple->t_self,
467  NULL, slot);
468 
469  if (slot == NULL) /* "do nothing" */
470  skip_tuple = true;
471  }
472 
473  if (!skip_tuple)
474  {
475  List *recheckIndexes = NIL;
476 
477  /* Check the constraints of the tuple */
478  if (rel->rd_att->constr)
479  ExecConstraints(resultRelInfo, slot, estate, true);
480 
481  /* Store the slot into tuple that we can write. */
482  tuple = ExecMaterializeSlot(slot);
483 
484  /* OK, update the tuple and index entries for it */
485  simple_heap_update(rel, &searchslot->tts_tuple->t_self,
486  slot->tts_tuple);
487 
488  if (resultRelInfo->ri_NumIndices > 0 &&
490  recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
491  estate, false, NULL,
492  NIL);
493 
494  /* AFTER ROW UPDATE Triggers */
495  ExecARUpdateTriggers(estate, resultRelInfo,
496  &searchslot->tts_tuple->t_self,
497  NULL, tuple, recheckIndexes, NULL);
498 
499  list_free(recheckIndexes);
500  }
501 }
502 
503 /*
504  * Find the searchslot tuple and delete it, and execute any constraints
505  * and per-row triggers.
506  *
507  * Caller is responsible for opening the indexes.
508  */
509 void
511  TupleTableSlot *searchslot)
512 {
513  bool skip_tuple = false;
514  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
515  Relation rel = resultRelInfo->ri_RelationDesc;
516 
517  /* For now we support only tables. */
518  Assert(rel->rd_rel->relkind == RELKIND_RELATION);
519 
521 
522  /* BEFORE ROW DELETE Triggers */
523  if (resultRelInfo->ri_TrigDesc &&
524  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
525  {
526  skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
527  &searchslot->tts_tuple->t_self,
528  NULL);
529  }
530 
531  if (!skip_tuple)
532  {
533  List *recheckIndexes = NIL;
534 
535  /* OK, delete the tuple */
536  simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
537 
538  /* AFTER ROW DELETE Triggers */
539  ExecARDeleteTriggers(estate, resultRelInfo,
540  &searchslot->tts_tuple->t_self, NULL, NULL);
541 
542  list_free(recheckIndexes);
543  }
544 }
545 
546 /*
547  * Check if command can be executed with current replica identity.
548  */
549 void
551 {
552  PublicationActions *pubactions;
553 
554  /* We only need to do checks for UPDATE and DELETE. */
555  if (cmd != CMD_UPDATE && cmd != CMD_DELETE)
556  return;
557 
558  /* If relation has replica identity we are always good. */
559  if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL ||
561  return;
562 
563  /*
564  * This is either UPDATE OR DELETE and there is no replica identity.
565  *
566  * Check if the table publishes UPDATES or DELETES.
567  */
568  pubactions = GetRelationPublicationActions(rel);
569  if (cmd == CMD_UPDATE && pubactions->pubupdate)
570  ereport(ERROR,
571  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
572  errmsg("cannot update table \"%s\" because it does not have a replica identity and publishes updates",
574  errhint("To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.")));
575  else if (cmd == CMD_DELETE && pubactions->pubdelete)
576  ereport(ERROR,
577  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
578  errmsg("cannot delete from table \"%s\" because it does not have a replica identity and publishes deletes",
580  errhint("To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.")));
581 }
582 
583 
584 /*
585  * Check if we support writing into specific relkind.
586  *
587  * The nspname and relname are only needed for error reporting.
588  */
589 void
590 CheckSubscriptionRelkind(char relkind, const char *nspname,
591  const char *relname)
592 {
593  /*
594  * We currently only support writing to regular tables.
595  */
596  if (relkind != RELKIND_RELATION)
597  ereport(ERROR,
598  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
599  errmsg("logical replication target relation \"%s.%s\" is not a table",
600  nspname, relname)));
601 }
void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
int ri_NumIndices
Definition: execnodes.h:400
#define NIL
Definition: pg_list.h:69
Definition: c.h:555
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:356
Relation ri_RelationDesc
Definition: execnodes.h:397
int errhint(const char *fmt,...)
Definition: elog.c:987
void heap_endscan(HeapScanDesc scan)
Definition: heapam.c:1572
Oid RelationGetReplicaIndex(Relation relation)
Definition: relcache.c:4521
List * ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes)
Definition: execIndexing.c:271
uint32 TransactionId
Definition: c.h:474
#define MaxTupleAttributeNumber
Definition: htup_details.h:33
#define RelationGetDescr(relation)
Definition: rel.h:433
HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
Definition: heapam.c:4685
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:132
regproc RegProcedure
Definition: c.h:472
TupleTableSlot * ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *slot)
Definition: trigger.c:2951
#define FunctionCall2(flinfo, arg1, arg2)
Definition: fmgr.h:610
#define InvalidBuffer
Definition: buf.h:25
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition: indexam.c:310
int errcode(int sqlerrcode)
Definition: elog.c:575
char * format_type_be(Oid type_oid)
Definition: format_type.c:328
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2581
Datum * tts_values
Definition: tuptable.h:130
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
void PopActiveSnapshot(void)
Definition: snapmgr.c:812
#define LOG
Definition: elog.h:26
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool check_partition_constraint)
Definition: execMain.c:1961
#define OidIsValid(objectId)
Definition: c.h:605
bool RelationFindReplTupleByIndex(Relation rel, Oid idxoid, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot)
int natts
Definition: tupdesc.h:82
char relkind
Definition: pg_class.h:51
struct HeapTupleData * rd_indextuple
Definition: rel.h:133
static bool tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
Definition: trigger.c:2799
LockTupleMode
Definition: heapam.h:38
Form_pg_index rd_index
Definition: rel.h:131
#define ERROR
Definition: elog.h:43
Definition: lmgr.h:26
#define InitDirtySnapshot(snapshotdata)
Definition: tqual.h:103
ItemPointerData t_self
Definition: htup.h:65
#define NoLock
Definition: lockdefs.h:34
static char * buf
Definition: pg_test_fsync.c:67
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:733
bool * tts_isnull
Definition: tuptable.h:132
void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot)
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:563
#define RowExclusiveLock
Definition: lockdefs.h:38
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:163
#define DatumGetBool(X)
Definition: postgres.h:376
bool trig_update_before_row
Definition: reltrigger.h:60
void heap_rescan(HeapScanDesc scan, ScanKey key)
Definition: heapam.c:1528
HTSU_Result
Definition: snapshot.h:121
#define RelationGetRelationName(relation)
Definition: rel.h:441
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:197
TransactionId xmax
Definition: snapshot.h:69
void CheckSubscriptionRelkind(char relkind, const char *nspname, const char *relname)
TransactionId xmin
Definition: snapshot.h:68
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:426
void CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
void index_endscan(IndexScanDesc scan)
Definition: indexam.c:340
#define SK_ISNULL
Definition: skey.h:115
#define ereport(elevel, rest)
Definition: elog.h:122
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:409
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, HeapTuple newtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:3068
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
Definition: c.h:544
bool trig_insert_before_row
Definition: reltrigger.h:55
uintptr_t Datum
Definition: postgres.h:365
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
Oid simple_heap_insert(Relation relation, HeapTuple tup)
Definition: heapam.c:2986
HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction)
Definition: heapam.c:1835
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:162
TupleDesc rd_att
Definition: rel.h:85
FmgrInfo eq_opr_finfo
Definition: typcache.h:71
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:321
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1079
Oid fn_oid
Definition: fmgr.h:59
struct PublicationActions * GetRelationPublicationActions(Relation relation)
Definition: relcache.c:5120
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple)
Definition: trigger.c:2730
int sk_flags
Definition: skey.h:66
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:554
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:703
#define Assert(condition)
Definition: c.h:699
TupleConstr * constr
Definition: tupdesc.h:87
#define INDEX_MAX_KEYS
void simple_heap_delete(Relation relation, ItemPointer tid)
Definition: heapam.c:3455
void simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
Definition: heapam.c:4596
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:781
int16 values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:552
Oid get_opclass_family(Oid opclass)
Definition: lsyscache.c:1032
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:379
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:176
#define DatumGetPointer(X)
Definition: postgres.h:532
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1315
static Datum values[MAXATTR]
Definition: bootstrap.c:164
int errmsg(const char *fmt,...)
Definition: elog.c:797
void list_free(List *list)
Definition: list.c:1133
bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
Definition: tupdesc.c:409
TupleTableSlot * ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2515
ItemPointerData ctid
Definition: heapam.h:70
HeapTuple tts_tuple
Definition: tuptable.h:122
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:679
#define elog
Definition: elog.h:219
#define TransactionIdIsValid(xid)
Definition: transam.h:41
Definition: pg_list.h:45
HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: heapam.c:1404
int Buffer
Definition: buf.h:23
static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot)
#define RelationGetRelid(relation)
Definition: rel.h:407
CmdType
Definition: nodes.h:657
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:150
#define BTEqualStrategyNumber
Definition: stratnum.h:31
void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot, TupleTableSlot *slot)
HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction)
Definition: indexam.c:659
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, int norderbys)
Definition: indexam.c:221
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:139
bool trig_delete_before_row
Definition: reltrigger.h:65
Oid get_opclass_input_type(Oid opclass)
Definition: lsyscache.c:1054
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:492