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