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