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