PostgreSQL Source Code  git master
refint.c
Go to the documentation of this file.
1 /*
2  * contrib/spi/refint.c
3  *
4  *
5  * refint.c -- set of functions to define referential integrity
6  * constraints using general triggers.
7  */
8 #include "postgres.h"
9 
10 #include <ctype.h>
11 
12 #include "commands/trigger.h"
13 #include "executor/spi.h"
14 #include "utils/builtins.h"
15 #include "utils/memutils.h"
16 #include "utils/rel.h"
17 
19 
20 typedef struct
21 {
22  char *ident;
23  int nplans;
25 } EPlan;
26 
27 static EPlan *FPlans = NULL;
28 static int nFPlans = 0;
29 static EPlan *PPlans = NULL;
30 static int nPPlans = 0;
31 
32 static EPlan *find_plan(char *ident, EPlan **eplan, int *nplans);
33 
34 /*
35  * check_primary_key () -- check that key in tuple being inserted/updated
36  * references existing tuple in "primary" table.
37  * Though it's called without args You have to specify referenced
38  * table/keys while creating trigger: key field names in triggered table,
39  * referenced table name, referenced key field names:
40  * EXECUTE PROCEDURE
41  * check_primary_key ('Fkey1', 'Fkey2', 'Ptable', 'Pkey1', 'Pkey2').
42  */
43 
45 
46 Datum
48 {
49  TriggerData *trigdata = (TriggerData *) fcinfo->context;
50  Trigger *trigger; /* to get trigger name */
51  int nargs; /* # of args specified in CREATE TRIGGER */
52  char **args; /* arguments: column names and table name */
53  int nkeys; /* # of key columns (= nargs / 2) */
54  Datum *kvals; /* key values */
55  char *relname; /* referenced relation name */
56  Relation rel; /* triggered relation */
57  HeapTuple tuple = NULL; /* tuple to return */
58  TupleDesc tupdesc; /* tuple description */
59  EPlan *plan; /* prepared plan */
60  Oid *argtypes = NULL; /* key types to prepare execution plan */
61  bool isnull; /* to know is some column NULL or not */
62  char ident[2 * NAMEDATALEN]; /* to identify myself */
63  int ret;
64  int i;
65 
66 #ifdef DEBUG_QUERY
67  elog(DEBUG4, "check_primary_key: Enter Function");
68 #endif
69 
70  /*
71  * Some checks first...
72  */
73 
74  /* Called by trigger manager ? */
75  if (!CALLED_AS_TRIGGER(fcinfo))
76  /* internal error */
77  elog(ERROR, "check_primary_key: not fired by trigger manager");
78 
79  /* Should be called for ROW trigger */
80  if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
81  /* internal error */
82  elog(ERROR, "check_primary_key: must be fired for row");
83 
84  /* If INSERTion then must check Tuple to being inserted */
85  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
86  tuple = trigdata->tg_trigtuple;
87 
88  /* Not should be called for DELETE */
89  else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
90  /* internal error */
91  elog(ERROR, "check_primary_key: cannot process DELETE events");
92 
93  /* If UPDATE, then must check new Tuple, not old one */
94  else
95  tuple = trigdata->tg_newtuple;
96 
97  trigger = trigdata->tg_trigger;
98  nargs = trigger->tgnargs;
99  args = trigger->tgargs;
100 
101  if (nargs % 2 != 1) /* odd number of arguments! */
102  /* internal error */
103  elog(ERROR, "check_primary_key: odd number of arguments should be specified");
104 
105  nkeys = nargs / 2;
106  relname = args[nkeys];
107  rel = trigdata->tg_relation;
108  tupdesc = rel->rd_att;
109 
110  /* Connect to SPI manager */
111  SPI_connect();
112 
113  /*
114  * We use SPI plan preparation feature, so allocate space to place key
115  * values.
116  */
117  kvals = (Datum *) palloc(nkeys * sizeof(Datum));
118 
119  /*
120  * Construct ident string as TriggerName $ TriggeredRelationId and try to
121  * find prepared execution plan.
122  */
123  snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
125 
126  /* if there is no plan then allocate argtypes for preparation */
127  if (plan->nplans <= 0)
128  argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
129 
130  /* For each column in key ... */
131  for (i = 0; i < nkeys; i++)
132  {
133  /* get index of column in tuple */
134  int fnumber = SPI_fnumber(tupdesc, args[i]);
135 
136  /* Bad guys may give us un-existing column in CREATE TRIGGER */
137  if (fnumber <= 0)
138  ereport(ERROR,
139  (errcode(ERRCODE_UNDEFINED_COLUMN),
140  errmsg("there is no attribute \"%s\" in relation \"%s\"",
141  args[i], SPI_getrelname(rel))));
142 
143  /* Well, get binary (in internal format) value of column */
144  kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
145 
146  /*
147  * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
148  * DON'T FORGET return tuple! Executor inserts tuple you're returning!
149  * If you return NULL then nothing will be inserted!
150  */
151  if (isnull)
152  {
153  SPI_finish();
154  return PointerGetDatum(tuple);
155  }
156 
157  if (plan->nplans <= 0) /* Get typeId of column */
158  argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
159  }
160 
161  /*
162  * If we have to prepare plan ...
163  */
164  if (plan->nplans <= 0)
165  {
166  SPIPlanPtr pplan;
167  char sql[8192];
168 
169  /*
170  * Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 =
171  * $1 [AND Pkey2 = $2 [...]]
172  */
173  snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
174  for (i = 0; i < nkeys; i++)
175  {
176  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
177  args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
178  }
179 
180  /* Prepare plan for query */
181  pplan = SPI_prepare(sql, nkeys, argtypes);
182  if (pplan == NULL)
183  /* internal error */
184  elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
185 
186  /*
187  * Remember that SPI_prepare places plan in current memory context -
188  * so, we have to save plan in TopMemoryContext for later use.
189  */
190  if (SPI_keepplan(pplan))
191  /* internal error */
192  elog(ERROR, "check_primary_key: SPI_keepplan failed");
194  sizeof(SPIPlanPtr));
195  *(plan->splan) = pplan;
196  plan->nplans = 1;
197  }
198 
199  /*
200  * Ok, execute prepared plan.
201  */
202  ret = SPI_execp(*(plan->splan), kvals, NULL, 1);
203  /* we have no NULLs - so we pass ^^^^ here */
204 
205  if (ret < 0)
206  /* internal error */
207  elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
208 
209  /*
210  * If there are no tuples returned by SELECT then ...
211  */
212  if (SPI_processed == 0)
213  ereport(ERROR,
214  (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
215  errmsg("tuple references non-existent key"),
216  errdetail("Trigger \"%s\" found tuple referencing non-existent key in \"%s\".", trigger->tgname, relname)));
217 
218  SPI_finish();
219 
220  return PointerGetDatum(tuple);
221 }
222 
223 /*
224  * check_foreign_key () -- check that key in tuple being deleted/updated
225  * is not referenced by tuples in "foreign" table(s).
226  * Though it's called without args You have to specify (while creating trigger):
227  * number of references, action to do if key referenced
228  * ('restrict' | 'setnull' | 'cascade'), key field names in triggered
229  * ("primary") table and referencing table(s)/keys:
230  * EXECUTE PROCEDURE
231  * check_foreign_key (2, 'restrict', 'Pkey1', 'Pkey2',
232  * 'Ftable1', 'Fkey11', 'Fkey12', 'Ftable2', 'Fkey21', 'Fkey22').
233  */
234 
236 
237 Datum
239 {
240  TriggerData *trigdata = (TriggerData *) fcinfo->context;
241  Trigger *trigger; /* to get trigger name */
242  int nargs; /* # of args specified in CREATE TRIGGER */
243  char **args; /* arguments: as described above */
244  char **args_temp;
245  int nrefs; /* number of references (== # of plans) */
246  char action; /* 'R'estrict | 'S'etnull | 'C'ascade */
247  int nkeys; /* # of key columns */
248  Datum *kvals; /* key values */
249  char *relname; /* referencing relation name */
250  Relation rel; /* triggered relation */
251  HeapTuple trigtuple = NULL; /* tuple to being changed */
252  HeapTuple newtuple = NULL; /* tuple to return */
253  TupleDesc tupdesc; /* tuple description */
254  EPlan *plan; /* prepared plan(s) */
255  Oid *argtypes = NULL; /* key types to prepare execution plan */
256  bool isnull; /* to know is some column NULL or not */
257  bool isequal = true; /* are keys in both tuples equal (in UPDATE) */
258  char ident[2 * NAMEDATALEN]; /* to identify myself */
259  int is_update = 0;
260  int ret;
261  int i,
262  r;
263 
264 #ifdef DEBUG_QUERY
265  elog(DEBUG4, "check_foreign_key: Enter Function");
266 #endif
267 
268  /*
269  * Some checks first...
270  */
271 
272  /* Called by trigger manager ? */
273  if (!CALLED_AS_TRIGGER(fcinfo))
274  /* internal error */
275  elog(ERROR, "check_foreign_key: not fired by trigger manager");
276 
277  /* Should be called for ROW trigger */
278  if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
279  /* internal error */
280  elog(ERROR, "check_foreign_key: must be fired for row");
281 
282  /* Not should be called for INSERT */
283  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
284  /* internal error */
285  elog(ERROR, "check_foreign_key: cannot process INSERT events");
286 
287  /* Have to check tg_trigtuple - tuple being deleted */
288  trigtuple = trigdata->tg_trigtuple;
289 
290  /*
291  * But if this is UPDATE then we have to return tg_newtuple. Also, if key
292  * in tg_newtuple is the same as in tg_trigtuple then nothing to do.
293  */
294  is_update = 0;
295  if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
296  {
297  newtuple = trigdata->tg_newtuple;
298  is_update = 1;
299  }
300  trigger = trigdata->tg_trigger;
301  nargs = trigger->tgnargs;
302  args = trigger->tgargs;
303 
304  if (nargs < 5) /* nrefs, action, key, Relation, key - at
305  * least */
306  /* internal error */
307  elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
308 
309  nrefs = pg_strtoint32(args[0]);
310  if (nrefs < 1)
311  /* internal error */
312  elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
313  action = tolower((unsigned char) *(args[1]));
314  if (action != 'r' && action != 'c' && action != 's')
315  /* internal error */
316  elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
317  nargs -= 2;
318  args += 2;
319  nkeys = (nargs - nrefs) / (nrefs + 1);
320  if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
321  /* internal error */
322  elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
323  nargs + 2, nrefs);
324 
325  rel = trigdata->tg_relation;
326  tupdesc = rel->rd_att;
327 
328  /* Connect to SPI manager */
329  SPI_connect();
330 
331  /*
332  * We use SPI plan preparation feature, so allocate space to place key
333  * values.
334  */
335  kvals = (Datum *) palloc(nkeys * sizeof(Datum));
336 
337  /*
338  * Construct ident string as TriggerName $ TriggeredRelationId and try to
339  * find prepared execution plan(s).
340  */
341  snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
343 
344  /* if there is no plan(s) then allocate argtypes for preparation */
345  if (plan->nplans <= 0)
346  argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
347 
348  /*
349  * else - check that we have exactly nrefs plan(s) ready
350  */
351  else if (plan->nplans != nrefs)
352  /* internal error */
353  elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime",
354  trigger->tgname);
355 
356  /* For each column in key ... */
357  for (i = 0; i < nkeys; i++)
358  {
359  /* get index of column in tuple */
360  int fnumber = SPI_fnumber(tupdesc, args[i]);
361 
362  /* Bad guys may give us un-existing column in CREATE TRIGGER */
363  if (fnumber <= 0)
364  ereport(ERROR,
365  (errcode(ERRCODE_UNDEFINED_COLUMN),
366  errmsg("there is no attribute \"%s\" in relation \"%s\"",
367  args[i], SPI_getrelname(rel))));
368 
369  /* Well, get binary (in internal format) value of column */
370  kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
371 
372  /*
373  * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
374  * DON'T FORGET return tuple! Executor inserts tuple you're returning!
375  * If you return NULL then nothing will be inserted!
376  */
377  if (isnull)
378  {
379  SPI_finish();
380  return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
381  }
382 
383  /*
384  * If UPDATE then get column value from new tuple being inserted and
385  * compare is this the same as old one. For the moment we use string
386  * presentation of values...
387  */
388  if (newtuple != NULL)
389  {
390  char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
391  char *newval;
392 
393  /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
394  if (oldval == NULL)
395  /* internal error */
396  elog(ERROR, "check_foreign_key: SPI_getvalue returned %s", SPI_result_code_string(SPI_result));
397  newval = SPI_getvalue(newtuple, tupdesc, fnumber);
398  if (newval == NULL || strcmp(oldval, newval) != 0)
399  isequal = false;
400  }
401 
402  if (plan->nplans <= 0) /* Get typeId of column */
403  argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
404  }
405  args_temp = args;
406  nargs -= nkeys;
407  args += nkeys;
408 
409  /*
410  * If we have to prepare plans ...
411  */
412  if (plan->nplans <= 0)
413  {
414  SPIPlanPtr pplan;
415  char sql[8192];
416  char **args2 = args;
417 
419  nrefs * sizeof(SPIPlanPtr));
420 
421  for (r = 0; r < nrefs; r++)
422  {
423  relname = args2[0];
424 
425  /*---------
426  * For 'R'estrict action we construct SELECT query:
427  *
428  * SELECT 1
429  * FROM _referencing_relation_
430  * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
431  *
432  * to check is tuple referenced or not.
433  *---------
434  */
435  if (action == 'r')
436 
437  snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
438 
439  /*---------
440  * For 'C'ascade action we construct DELETE query
441  *
442  * DELETE
443  * FROM _referencing_relation_
444  * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
445  *
446  * to delete all referencing tuples.
447  *---------
448  */
449 
450  /*
451  * Max : Cascade with UPDATE query i create update query that
452  * updates new key values in referenced tables
453  */
454 
455 
456  else if (action == 'c')
457  {
458  if (is_update == 1)
459  {
460  int fn;
461  char *nv;
462  int k;
463 
464  snprintf(sql, sizeof(sql), "update %s set ", relname);
465  for (k = 1; k <= nkeys; k++)
466  {
467  int is_char_type = 0;
468  char *type;
469 
470  fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
471  Assert(fn > 0); /* already checked above */
472  nv = SPI_getvalue(newtuple, tupdesc, fn);
473  type = SPI_gettype(tupdesc, fn);
474 
475  if (strcmp(type, "text") == 0 ||
476  strcmp(type, "varchar") == 0 ||
477  strcmp(type, "char") == 0 ||
478  strcmp(type, "bpchar") == 0 ||
479  strcmp(type, "date") == 0 ||
480  strcmp(type, "timestamp") == 0)
481  is_char_type = 1;
482 #ifdef DEBUG_QUERY
483  elog(DEBUG4, "check_foreign_key Debug value %s type %s %d",
484  nv, type, is_char_type);
485 #endif
486 
487  /*
488  * is_char_type =1 i set ' ' for define a new value
489  */
490  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
491  " %s = %s%s%s %s ",
492  args2[k], (is_char_type > 0) ? "'" : "",
493  nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
494  }
495  strcat(sql, " where ");
496  }
497  else
498  /* DELETE */
499  snprintf(sql, sizeof(sql), "delete from %s where ", relname);
500  }
501 
502  /*
503  * For 'S'etnull action we construct UPDATE query - UPDATE
504  * _referencing_relation_ SET Fkey1 null [, Fkey2 null [...]]
505  * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]] - to set key columns in
506  * all referencing tuples to NULL.
507  */
508  else if (action == 's')
509  {
510  snprintf(sql, sizeof(sql), "update %s set ", relname);
511  for (i = 1; i <= nkeys; i++)
512  {
513  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
514  "%s = null%s",
515  args2[i], (i < nkeys) ? ", " : "");
516  }
517  strcat(sql, " where ");
518  }
519 
520  /* Construct WHERE qual */
521  for (i = 1; i <= nkeys; i++)
522  {
523  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
524  args2[i], i, (i < nkeys) ? "and " : "");
525  }
526 
527  /* Prepare plan for query */
528  pplan = SPI_prepare(sql, nkeys, argtypes);
529  if (pplan == NULL)
530  /* internal error */
531  elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
532 
533  /*
534  * Remember that SPI_prepare places plan in current memory context
535  * - so, we have to save plan in Top memory context for later use.
536  */
537  if (SPI_keepplan(pplan))
538  /* internal error */
539  elog(ERROR, "check_foreign_key: SPI_keepplan failed");
540 
541  plan->splan[r] = pplan;
542 
543  args2 += nkeys + 1; /* to the next relation */
544  }
545  plan->nplans = nrefs;
546 #ifdef DEBUG_QUERY
547  elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql);
548 #endif
549  }
550 
551  /*
552  * If UPDATE and key is not changed ...
553  */
554  if (newtuple != NULL && isequal)
555  {
556  SPI_finish();
557  return PointerGetDatum(newtuple);
558  }
559 
560  /*
561  * Ok, execute prepared plan(s).
562  */
563  for (r = 0; r < nrefs; r++)
564  {
565  /*
566  * For 'R'estrict we may to execute plan for one tuple only, for other
567  * actions - for all tuples.
568  */
569  int tcount = (action == 'r') ? 1 : 0;
570 
571  relname = args[0];
572 
573  snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
575  ret = SPI_execp(plan->splan[r], kvals, NULL, tcount);
576  /* we have no NULLs - so we pass ^^^^ here */
577 
578  if (ret < 0)
579  ereport(ERROR,
580  (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
581  errmsg("SPI_execp returned %d", ret)));
582 
583  /* If action is 'R'estrict ... */
584  if (action == 'r')
585  {
586  /* If there is tuple returned by SELECT then ... */
587  if (SPI_processed > 0)
588  ereport(ERROR,
589  (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
590  errmsg("\"%s\": tuple is referenced in \"%s\"",
591  trigger->tgname, relname)));
592  }
593  else
594  {
595 #ifdef REFINT_VERBOSE
596  elog(NOTICE, "%s: " UINT64_FORMAT " tuple(s) of %s are %s",
597  trigger->tgname, SPI_processed, relname,
598  (action == 'c') ? "deleted" : "set to null");
599 #endif
600  }
601  args += nkeys + 1; /* to the next relation */
602  }
603 
604  SPI_finish();
605 
606  return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
607 }
608 
609 static EPlan *
610 find_plan(char *ident, EPlan **eplan, int *nplans)
611 {
612  EPlan *newp;
613  int i;
614  MemoryContext oldcontext;
615 
616  /*
617  * All allocations done for the plans need to happen in a session-safe
618  * context.
619  */
621 
622  if (*nplans > 0)
623  {
624  for (i = 0; i < *nplans; i++)
625  {
626  if (strcmp((*eplan)[i].ident, ident) == 0)
627  break;
628  }
629  if (i != *nplans)
630  {
631  MemoryContextSwitchTo(oldcontext);
632  return (*eplan + i);
633  }
634  *eplan = (EPlan *) repalloc(*eplan, (i + 1) * sizeof(EPlan));
635  newp = *eplan + i;
636  }
637  else
638  {
639  newp = *eplan = (EPlan *) palloc(sizeof(EPlan));
640  (*nplans) = i = 0;
641  }
642 
643  newp->ident = pstrdup(ident);
644  newp->nplans = 0;
645  newp->splan = NULL;
646  (*nplans)++;
647 
648  MemoryContextSwitchTo(oldcontext);
649  return newp;
650 }
#define Assert(condition)
Definition: c.h:837
#define UINT64_FORMAT
Definition: c.h:526
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
#define DEBUG4
Definition: elog.h:27
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define newval
#define ident
Definition: indent_codes.h:47
int i
Definition: isn.c:72
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
char * pstrdup(const char *in)
Definition: mcxt.c:1696
MemoryContext TopMemoryContext
Definition: mcxt.c:149
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1541
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
void * palloc(Size size)
Definition: mcxt.c:1317
int32 pg_strtoint32(const char *s)
Definition: numutils.c:383
NameData relname
Definition: pg_class.h:38
#define NAMEDATALEN
#define plan(x)
Definition: pg_regress.c:161
#define snprintf
Definition: port.h:238
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
unsigned int Oid
Definition: postgres_ext.h:31
MemoryContextSwitchTo(old_ctx)
static int nPPlans
Definition: refint.c:30
static EPlan * FPlans
Definition: refint.c:27
PG_MODULE_MAGIC
Definition: refint.c:18
Datum check_foreign_key(PG_FUNCTION_ARGS)
Definition: refint.c:238
Datum check_primary_key(PG_FUNCTION_ARGS)
Definition: refint.c:47
static EPlan * PPlans
Definition: refint.c:29
PG_FUNCTION_INFO_V1(check_primary_key)
static EPlan * find_plan(char *ident, EPlan **eplan, int *nplans)
Definition: refint.c:610
static int nFPlans
Definition: refint.c:28
char * SPI_getrelname(Relation rel)
Definition: spi.c:1326
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition: spi.c:1175
uint64 SPI_processed
Definition: spi.c:44
Oid SPI_gettypeid(TupleDesc tupdesc, int fnumber)
Definition: spi.c:1308
int SPI_connect(void)
Definition: spi.c:94
int SPI_result
Definition: spi.c:46
const char * SPI_result_code_string(int code)
Definition: spi.c:1972
int SPI_finish(void)
Definition: spi.c:182
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:860
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:976
int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
Definition: spi.c:704
char * SPI_gettype(TupleDesc tupdesc, int fnumber)
Definition: spi.c:1268
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Definition: spi.c:1220
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
Definition: spi.c:1252
Definition: refint.c:21
SPIPlanPtr * splan
Definition: refint.c:24
int nplans
Definition: refint.c:23
char * ident
Definition: refint.c:22
TupleDesc rd_att
Definition: rel.h:112
Relation tg_relation
Definition: trigger.h:35
TriggerEvent tg_event
Definition: trigger.h:34
HeapTuple tg_newtuple
Definition: trigger.h:37
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
int16 tgnargs
Definition: reltrigger.h:38
static void * fn(void *arg)
Definition: thread-alloc.c:119
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:113
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:26
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:122
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:110
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:116
const char * type