PostgreSQL Source Code  git master
refint.c File Reference
#include "postgres.h"
#include <ctype.h>
#include "commands/trigger.h"
#include "executor/spi.h"
#include "utils/builtins.h"
#include "utils/rel.h"
Include dependency graph for refint.c:

Go to the source code of this file.

Data Structures

struct  EPlan
 

Functions

static EPlanfind_plan (char *ident, EPlan **eplan, int *nplans)
 
 PG_FUNCTION_INFO_V1 (check_primary_key)
 
Datum check_primary_key (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (check_foreign_key)
 
Datum check_foreign_key (PG_FUNCTION_ARGS)
 

Variables

 PG_MODULE_MAGIC
 
static EPlanFPlans = NULL
 
static int nFPlans = 0
 
static EPlanPPlans = NULL
 
static int nPPlans = 0
 

Function Documentation

◆ check_foreign_key()

Datum check_foreign_key ( PG_FUNCTION_ARGS  )

Definition at line 238 of file refint.c.

References generate_unaccent_rules::action, generate_unaccent_rules::args, Assert, CALLED_AS_TRIGGER, DEBUG4, elog, ereport, errcode(), errmsg(), ERROR, find_plan(), fn(), i, malloc, NAMEDATALEN, newval, nFPlans, NOTICE, palloc(), pg_atoi(), PointerGetDatum, RelationData::rd_att, snprintf(), SPI_connect(), SPI_execp(), SPI_finish(), SPI_fnumber(), SPI_getbinval(), SPI_getrelname(), SPI_gettype(), SPI_gettypeid(), SPI_getvalue(), SPI_keepplan(), SPI_prepare(), SPI_processed, SPI_result, SPI_result_code_string(), TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgnargs, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, generate_unaccent_rules::type, and UINT64_FORMAT.

Referenced by check_primary_key().

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_atoi(args[0], sizeof(int), 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  if ((ret = SPI_connect()) < 0)
330  /* internal error */
331  elog(ERROR, "check_foreign_key: SPI_connect returned %d", ret);
332 
333  /*
334  * We use SPI plan preparation feature, so allocate space to place key
335  * values.
336  */
337  kvals = (Datum *) palloc(nkeys * sizeof(Datum));
338 
339  /*
340  * Construct ident string as TriggerName $ TriggeredRelationId and try to
341  * find prepared execution plan(s).
342  */
343  snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
344  plan = find_plan(ident, &FPlans, &nFPlans);
345 
346  /* if there is no plan(s) then allocate argtypes for preparation */
347  if (plan->nplans <= 0)
348  argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
349 
350  /*
351  * else - check that we have exactly nrefs plan(s) ready
352  */
353  else if (plan->nplans != nrefs)
354  /* internal error */
355  elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime",
356  trigger->tgname);
357 
358  /* For each column in key ... */
359  for (i = 0; i < nkeys; i++)
360  {
361  /* get index of column in tuple */
362  int fnumber = SPI_fnumber(tupdesc, args[i]);
363 
364  /* Bad guys may give us un-existing column in CREATE TRIGGER */
365  if (fnumber <= 0)
366  ereport(ERROR,
367  (errcode(ERRCODE_UNDEFINED_COLUMN),
368  errmsg("there is no attribute \"%s\" in relation \"%s\"",
369  args[i], SPI_getrelname(rel))));
370 
371  /* Well, get binary (in internal format) value of column */
372  kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
373 
374  /*
375  * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
376  * DON'T FORGET return tuple! Executor inserts tuple you're returning!
377  * If you return NULL then nothing will be inserted!
378  */
379  if (isnull)
380  {
381  SPI_finish();
382  return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
383  }
384 
385  /*
386  * If UPDATE then get column value from new tuple being inserted and
387  * compare is this the same as old one. For the moment we use string
388  * presentation of values...
389  */
390  if (newtuple != NULL)
391  {
392  char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
393  char *newval;
394 
395  /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
396  if (oldval == NULL)
397  /* internal error */
398  elog(ERROR, "check_foreign_key: SPI_getvalue returned %s", SPI_result_code_string(SPI_result));
399  newval = SPI_getvalue(newtuple, tupdesc, fnumber);
400  if (newval == NULL || strcmp(oldval, newval) != 0)
401  isequal = false;
402  }
403 
404  if (plan->nplans <= 0) /* Get typeId of column */
405  argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
406  }
407  args_temp = args;
408  nargs -= nkeys;
409  args += nkeys;
410 
411  /*
412  * If we have to prepare plans ...
413  */
414  if (plan->nplans <= 0)
415  {
416  SPIPlanPtr pplan;
417  char sql[8192];
418  char **args2 = args;
419 
420  plan->splan = (SPIPlanPtr *) malloc(nrefs * sizeof(SPIPlanPtr));
421 
422  for (r = 0; r < nrefs; r++)
423  {
424  relname = args2[0];
425 
426  /*---------
427  * For 'R'estrict action we construct SELECT query:
428  *
429  * SELECT 1
430  * FROM _referencing_relation_
431  * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
432  *
433  * to check is tuple referenced or not.
434  *---------
435  */
436  if (action == 'r')
437 
438  snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
439 
440  /*---------
441  * For 'C'ascade action we construct DELETE query
442  *
443  * DELETE
444  * FROM _referencing_relation_
445  * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
446  *
447  * to delete all referencing tuples.
448  *---------
449  */
450 
451  /*
452  * Max : Cascade with UPDATE query i create update query that
453  * updates new key values in referenced tables
454  */
455 
456 
457  else if (action == 'c')
458  {
459  if (is_update == 1)
460  {
461  int fn;
462  char *nv;
463  int k;
464 
465  snprintf(sql, sizeof(sql), "update %s set ", relname);
466  for (k = 1; k <= nkeys; k++)
467  {
468  int is_char_type = 0;
469  char *type;
470 
471  fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
472  Assert(fn > 0); /* already checked above */
473  nv = SPI_getvalue(newtuple, tupdesc, fn);
474  type = SPI_gettype(tupdesc, fn);
475 
476  if ((strcmp(type, "text") && strcmp(type, "varchar") &&
477  strcmp(type, "char") && strcmp(type, "bpchar") &&
478  strcmp(type, "date") && strcmp(type, "timestamp")) == 0)
479  is_char_type = 1;
480 #ifdef DEBUG_QUERY
481  elog(DEBUG4, "check_foreign_key Debug value %s type %s %d",
482  nv, type, is_char_type);
483 #endif
484 
485  /*
486  * is_char_type =1 i set ' ' for define a new value
487  */
488  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
489  " %s = %s%s%s %s ",
490  args2[k], (is_char_type > 0) ? "'" : "",
491  nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
492  }
493  strcat(sql, " where ");
494 
495  }
496  else
497  /* DELETE */
498  snprintf(sql, sizeof(sql), "delete from %s where ", relname);
499 
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);
574  plan = find_plan(ident, &FPlans, &nFPlans);
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 }
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition: spi.c:855
Oid SPI_gettypeid(TupleDesc tupdesc, int fnumber)
Definition: spi.c:988
static int nFPlans
Definition: refint.c:27
int SPI_connect(void)
Definition: spi.c:84
#define PointerGetDatum(X)
Definition: postgres.h:539
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:576
int SPI_finish(void)
Definition: spi.c:161
int errcode(int sqlerrcode)
Definition: elog.c:575
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
unsigned int Oid
Definition: postgres_ext.h:31
#define DEBUG4
Definition: elog.h:22
uint64 SPI_processed
Definition: spi.c:39
HeapTuple tg_trigtuple
Definition: trigger.h:35
char * SPI_gettype(TupleDesc tupdesc, int fnumber)
Definition: spi.c:948
#define malloc(a)
Definition: header.h:50
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Definition: spi.c:900
#define NAMEDATALEN
int SPI_result
Definition: spi.c:42
#define ERROR
Definition: elog.h:43
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
Definition: spi.c:932
const char * SPI_result_code_string(int code)
Definition: spi.c:1609
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:654
static EPlan * find_plan(char *ident, EPlan **eplan, int *nplans)
Definition: refint.c:610
#define ereport(elevel, rest)
Definition: elog.h:122
uintptr_t Datum
Definition: postgres.h:365
Trigger * tg_trigger
Definition: trigger.h:37
TupleDesc rd_att
Definition: rel.h:85
HeapTuple tg_newtuple
Definition: trigger.h:36
char * SPI_getrelname(Relation rel)
Definition: spi.c:1006
static void * fn(void *arg)
#define NOTICE
Definition: elog.h:37
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:25
#define Assert(condition)
Definition: c.h:699
TriggerEvent tg_event
Definition: trigger.h:33
Definition: refint.c:19
#define newval
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:116
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
Definition: spi.c:460
int16 tgnargs
Definition: reltrigger.h:37
#define elog
Definition: elog.h:219
static EPlan * FPlans
Definition: refint.c:26
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:128
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:122
#define UINT64_FORMAT
Definition: c.h:368
int32 pg_atoi(const char *s, int size, int c)
Definition: numutils.c:37
Relation tg_relation
Definition: trigger.h:34

◆ check_primary_key()

Datum check_primary_key ( PG_FUNCTION_ARGS  )

Definition at line 46 of file refint.c.

References generate_unaccent_rules::args, CALLED_AS_TRIGGER, check_foreign_key(), DEBUG4, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, find_plan(), i, malloc, NAMEDATALEN, nPPlans, palloc(), PG_FUNCTION_INFO_V1(), PointerGetDatum, RelationData::rd_att, snprintf(), SPI_connect(), SPI_execp(), SPI_finish(), SPI_fnumber(), SPI_getbinval(), SPI_getrelname(), SPI_gettypeid(), SPI_keepplan(), SPI_prepare(), SPI_processed, SPI_result, SPI_result_code_string(), TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgnargs, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, and TRIGGER_FIRED_FOR_ROW.

47 {
48  TriggerData *trigdata = (TriggerData *) fcinfo->context;
49  Trigger *trigger; /* to get trigger name */
50  int nargs; /* # of args specified in CREATE TRIGGER */
51  char **args; /* arguments: column names and table name */
52  int nkeys; /* # of key columns (= nargs / 2) */
53  Datum *kvals; /* key values */
54  char *relname; /* referenced relation name */
55  Relation rel; /* triggered relation */
56  HeapTuple tuple = NULL; /* tuple to return */
57  TupleDesc tupdesc; /* tuple description */
58  EPlan *plan; /* prepared plan */
59  Oid *argtypes = NULL; /* key types to prepare execution plan */
60  bool isnull; /* to know is some column NULL or not */
61  char ident[2 * NAMEDATALEN]; /* to identify myself */
62  int ret;
63  int i;
64 
65 #ifdef DEBUG_QUERY
66  elog(DEBUG4, "check_primary_key: Enter Function");
67 #endif
68 
69  /*
70  * Some checks first...
71  */
72 
73  /* Called by trigger manager ? */
74  if (!CALLED_AS_TRIGGER(fcinfo))
75  /* internal error */
76  elog(ERROR, "check_primary_key: not fired by trigger manager");
77 
78  /* Should be called for ROW trigger */
79  if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
80  /* internal error */
81  elog(ERROR, "check_primary_key: must be fired for row");
82 
83  /* If INSERTion then must check Tuple to being inserted */
84  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
85  tuple = trigdata->tg_trigtuple;
86 
87  /* Not should be called for DELETE */
88  else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
89  /* internal error */
90  elog(ERROR, "check_primary_key: cannot process DELETE events");
91 
92  /* If UPDATE, then must check new Tuple, not old one */
93  else
94  tuple = trigdata->tg_newtuple;
95 
96  trigger = trigdata->tg_trigger;
97  nargs = trigger->tgnargs;
98  args = trigger->tgargs;
99 
100  if (nargs % 2 != 1) /* odd number of arguments! */
101  /* internal error */
102  elog(ERROR, "check_primary_key: odd number of arguments should be specified");
103 
104  nkeys = nargs / 2;
105  relname = args[nkeys];
106  rel = trigdata->tg_relation;
107  tupdesc = rel->rd_att;
108 
109  /* Connect to SPI manager */
110  if ((ret = SPI_connect()) < 0)
111  /* internal error */
112  elog(ERROR, "check_primary_key: SPI_connect returned %d", ret);
113 
114  /*
115  * We use SPI plan preparation feature, so allocate space to place key
116  * values.
117  */
118  kvals = (Datum *) palloc(nkeys * sizeof(Datum));
119 
120  /*
121  * Construct ident string as TriggerName $ TriggeredRelationId and try to
122  * find prepared execution plan.
123  */
124  snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
125  plan = find_plan(ident, &PPlans, &nPPlans);
126 
127  /* if there is no plan then allocate argtypes for preparation */
128  if (plan->nplans <= 0)
129  argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
130 
131  /* For each column in key ... */
132  for (i = 0; i < nkeys; i++)
133  {
134  /* get index of column in tuple */
135  int fnumber = SPI_fnumber(tupdesc, args[i]);
136 
137  /* Bad guys may give us un-existing column in CREATE TRIGGER */
138  if (fnumber <= 0)
139  ereport(ERROR,
140  (errcode(ERRCODE_UNDEFINED_COLUMN),
141  errmsg("there is no attribute \"%s\" in relation \"%s\"",
142  args[i], SPI_getrelname(rel))));
143 
144  /* Well, get binary (in internal format) value of column */
145  kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
146 
147  /*
148  * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
149  * DON'T FORGET return tuple! Executor inserts tuple you're returning!
150  * If you return NULL then nothing will be inserted!
151  */
152  if (isnull)
153  {
154  SPI_finish();
155  return PointerGetDatum(tuple);
156  }
157 
158  if (plan->nplans <= 0) /* Get typeId of column */
159  argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
160  }
161 
162  /*
163  * If we have to prepare plan ...
164  */
165  if (plan->nplans <= 0)
166  {
167  SPIPlanPtr pplan;
168  char sql[8192];
169 
170  /*
171  * Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 =
172  * $1 [AND Pkey2 = $2 [...]]
173  */
174  snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
175  for (i = 0; i < nkeys; i++)
176  {
177  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
178  args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
179  }
180 
181  /* Prepare plan for query */
182  pplan = SPI_prepare(sql, nkeys, argtypes);
183  if (pplan == NULL)
184  /* internal error */
185  elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
186 
187  /*
188  * Remember that SPI_prepare places plan in current memory context -
189  * so, we have to save plan in Top memory context for later use.
190  */
191  if (SPI_keepplan(pplan))
192  /* internal error */
193  elog(ERROR, "check_primary_key: SPI_keepplan failed");
194  plan->splan = (SPIPlanPtr *) malloc(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 }
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition: spi.c:855
Oid SPI_gettypeid(TupleDesc tupdesc, int fnumber)
Definition: spi.c:988
int SPI_connect(void)
Definition: spi.c:84
#define PointerGetDatum(X)
Definition: postgres.h:539
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:576
int SPI_finish(void)
Definition: spi.c:161
int errcode(int sqlerrcode)
Definition: elog.c:575
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
unsigned int Oid
Definition: postgres_ext.h:31
#define DEBUG4
Definition: elog.h:22
uint64 SPI_processed
Definition: spi.c:39
HeapTuple tg_trigtuple
Definition: trigger.h:35
#define malloc(a)
Definition: header.h:50
#define NAMEDATALEN
int SPI_result
Definition: spi.c:42
#define ERROR
Definition: elog.h:43
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
Definition: spi.c:932
const char * SPI_result_code_string(int code)
Definition: spi.c:1609
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:654
int errdetail(const char *fmt,...)
Definition: elog.c:873
static EPlan * find_plan(char *ident, EPlan **eplan, int *nplans)
Definition: refint.c:610
#define ereport(elevel, rest)
Definition: elog.h:122
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:119
uintptr_t Datum
Definition: postgres.h:365
Trigger * tg_trigger
Definition: trigger.h:37
TupleDesc rd_att
Definition: rel.h:85
HeapTuple tg_newtuple
Definition: trigger.h:36
char * SPI_getrelname(Relation rel)
Definition: spi.c:1006
static EPlan * PPlans
Definition: refint.c:28
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:25
TriggerEvent tg_event
Definition: trigger.h:33
Definition: refint.c:19
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:116
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
Definition: spi.c:460
int16 tgnargs
Definition: reltrigger.h:37
#define elog
Definition: elog.h:219
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:128
static int nPPlans
Definition: refint.c:29
Relation tg_relation
Definition: trigger.h:34

◆ find_plan()

static EPlan * find_plan ( char *  ident,
EPlan **  eplan,
int *  nplans 
)
static

Definition at line 610 of file refint.c.

References i, EPlan::ident, malloc, EPlan::nplans, realloc, and EPlan::splan.

Referenced by check_foreign_key(), and check_primary_key().

611 {
612  EPlan *newp;
613  int i;
614 
615  if (*nplans > 0)
616  {
617  for (i = 0; i < *nplans; i++)
618  {
619  if (strcmp((*eplan)[i].ident, ident) == 0)
620  break;
621  }
622  if (i != *nplans)
623  return (*eplan + i);
624  *eplan = (EPlan *) realloc(*eplan, (i + 1) * sizeof(EPlan));
625  newp = *eplan + i;
626  }
627  else
628  {
629  newp = *eplan = (EPlan *) malloc(sizeof(EPlan));
630  (*nplans) = i = 0;
631  }
632 
633  newp->ident = strdup(ident);
634  newp->nplans = 0;
635  newp->splan = NULL;
636  (*nplans)++;
637 
638  return newp;
639 }
#define malloc(a)
Definition: header.h:50
int nplans
Definition: refint.c:22
SPIPlanPtr * splan
Definition: refint.c:23
char * ident
Definition: refint.c:21
Definition: refint.c:19
#define realloc(a, b)
Definition: header.h:60
int i

◆ PG_FUNCTION_INFO_V1() [1/2]

PG_FUNCTION_INFO_V1 ( check_primary_key  )

Referenced by check_primary_key().

◆ PG_FUNCTION_INFO_V1() [2/2]

PG_FUNCTION_INFO_V1 ( check_foreign_key  )

Variable Documentation

◆ FPlans

EPlan* FPlans = NULL
static

Definition at line 26 of file refint.c.

◆ nFPlans

int nFPlans = 0
static

Definition at line 27 of file refint.c.

Referenced by check_foreign_key().

◆ nPPlans

int nPPlans = 0
static

Definition at line 29 of file refint.c.

Referenced by check_primary_key().

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 17 of file refint.c.

◆ PPlans

EPlan* PPlans = NULL
static

Definition at line 28 of file refint.c.