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;
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 14 : PG_FUNCTION_INFO_V1(check_primary_key);
45 :
46 : Datum
47 96 : check_primary_key(PG_FUNCTION_ARGS)
48 : {
49 96 : 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 96 : HeapTuple tuple = NULL; /* tuple to return */
58 : TupleDesc tupdesc; /* tuple description */
59 : EPlan *plan; /* prepared plan */
60 96 : 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 96 : if (!CALLED_AS_TRIGGER(fcinfo))
76 : /* internal error */
77 0 : elog(ERROR, "check_primary_key: not fired by trigger manager");
78 :
79 : /* Should be called for ROW trigger */
80 96 : if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
81 : /* internal error */
82 0 : elog(ERROR, "check_primary_key: must be fired for row");
83 :
84 : /* If INSERTion then must check Tuple to being inserted */
85 96 : if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
86 96 : tuple = trigdata->tg_trigtuple;
87 :
88 : /* Not should be called for DELETE */
89 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 96 : trigger = trigdata->tg_trigger;
98 96 : nargs = trigger->tgnargs;
99 96 : args = trigger->tgargs;
100 :
101 96 : if (nargs % 2 != 1) /* odd number of arguments! */
102 : /* internal error */
103 0 : elog(ERROR, "check_primary_key: odd number of arguments should be specified");
104 :
105 96 : nkeys = nargs / 2;
106 96 : relname = args[nkeys];
107 96 : rel = trigdata->tg_relation;
108 96 : tupdesc = rel->rd_att;
109 :
110 : /* Connect to SPI manager */
111 96 : SPI_connect();
112 :
113 : /*
114 : * We use SPI plan preparation feature, so allocate space to place key
115 : * values.
116 : */
117 96 : 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 96 : snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
124 96 : plan = find_plan(ident, &PPlans, &nPPlans);
125 :
126 : /* if there is no plan then allocate argtypes for preparation */
127 96 : if (plan->nplans <= 0)
128 18 : argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
129 :
130 : /* For each column in key ... */
131 252 : for (i = 0; i < nkeys; i++)
132 : {
133 : /* get index of column in tuple */
134 156 : int fnumber = SPI_fnumber(tupdesc, args[i]);
135 :
136 : /* Bad guys may give us un-existing column in CREATE TRIGGER */
137 156 : if (fnumber <= 0)
138 0 : 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 156 : 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 156 : if (isnull)
152 : {
153 0 : SPI_finish();
154 0 : return PointerGetDatum(tuple);
155 : }
156 :
157 156 : if (plan->nplans <= 0) /* Get typeId of column */
158 30 : argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
159 : }
160 :
161 : /*
162 : * If we have to prepare plan ...
163 : */
164 96 : 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 18 : snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
174 48 : for (i = 0; i < nkeys; i++)
175 : {
176 30 : snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
177 60 : args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
178 : }
179 :
180 : /* Prepare plan for query */
181 18 : pplan = SPI_prepare(sql, nkeys, argtypes);
182 18 : if (pplan == NULL)
183 : /* internal error */
184 0 : 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 18 : if (SPI_keepplan(pplan))
191 : /* internal error */
192 0 : elog(ERROR, "check_primary_key: SPI_keepplan failed");
193 18 : plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
194 : sizeof(SPIPlanPtr));
195 18 : *(plan->splan) = pplan;
196 18 : plan->nplans = 1;
197 : }
198 :
199 : /*
200 : * Ok, execute prepared plan.
201 : */
202 96 : ret = SPI_execp(*(plan->splan), kvals, NULL, 1);
203 : /* we have no NULLs - so we pass ^^^^ here */
204 :
205 96 : if (ret < 0)
206 : /* internal error */
207 0 : elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
208 :
209 : /*
210 : * If there are no tuples returned by SELECT then ...
211 : */
212 96 : if (SPI_processed == 0)
213 18 : 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 78 : SPI_finish();
219 :
220 78 : 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 :
235 14 : PG_FUNCTION_INFO_V1(check_foreign_key);
236 :
237 : Datum
238 48 : check_foreign_key(PG_FUNCTION_ARGS)
239 : {
240 48 : 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 48 : HeapTuple trigtuple = NULL; /* tuple to being changed */
252 48 : HeapTuple newtuple = NULL; /* tuple to return */
253 : TupleDesc tupdesc; /* tuple description */
254 : EPlan *plan; /* prepared plan(s) */
255 48 : Oid *argtypes = NULL; /* key types to prepare execution plan */
256 : bool isnull; /* to know is some column NULL or not */
257 48 : bool isequal = true; /* are keys in both tuples equal (in UPDATE) */
258 : char ident[2 * NAMEDATALEN]; /* to identify myself */
259 48 : 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 48 : if (!CALLED_AS_TRIGGER(fcinfo))
274 : /* internal error */
275 0 : elog(ERROR, "check_foreign_key: not fired by trigger manager");
276 :
277 : /* Should be called for ROW trigger */
278 48 : if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
279 : /* internal error */
280 0 : elog(ERROR, "check_foreign_key: must be fired for row");
281 :
282 : /* Not should be called for INSERT */
283 48 : if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
284 : /* internal error */
285 0 : elog(ERROR, "check_foreign_key: cannot process INSERT events");
286 :
287 : /* Have to check tg_trigtuple - tuple being deleted */
288 48 : 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 48 : is_update = 0;
295 48 : if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
296 : {
297 12 : newtuple = trigdata->tg_newtuple;
298 12 : is_update = 1;
299 : }
300 48 : trigger = trigdata->tg_trigger;
301 48 : nargs = trigger->tgnargs;
302 48 : args = trigger->tgargs;
303 :
304 48 : if (nargs < 5) /* nrefs, action, key, Relation, key - at
305 : * least */
306 : /* internal error */
307 0 : elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
308 :
309 48 : nrefs = pg_strtoint32(args[0]);
310 48 : if (nrefs < 1)
311 : /* internal error */
312 0 : elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
313 48 : action = tolower((unsigned char) *(args[1]));
314 48 : if (action != 'r' && action != 'c' && action != 's')
315 : /* internal error */
316 0 : elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
317 48 : nargs -= 2;
318 48 : args += 2;
319 48 : nkeys = (nargs - nrefs) / (nrefs + 1);
320 48 : if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
321 : /* internal error */
322 0 : elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
323 : nargs + 2, nrefs);
324 :
325 48 : rel = trigdata->tg_relation;
326 48 : tupdesc = rel->rd_att;
327 :
328 : /* Connect to SPI manager */
329 48 : SPI_connect();
330 :
331 : /*
332 : * We use SPI plan preparation feature, so allocate space to place key
333 : * values.
334 : */
335 48 : 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 48 : snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
342 48 : plan = find_plan(ident, &FPlans, &nFPlans);
343 :
344 : /* if there is no plan(s) then allocate argtypes for preparation */
345 48 : if (plan->nplans <= 0)
346 12 : argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
347 :
348 : /*
349 : * else - check that we have exactly nrefs plan(s) ready
350 : */
351 36 : else if (plan->nplans != nrefs)
352 : /* internal error */
353 0 : elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime",
354 : trigger->tgname);
355 :
356 : /* For each column in key ... */
357 120 : for (i = 0; i < nkeys; i++)
358 : {
359 : /* get index of column in tuple */
360 72 : int fnumber = SPI_fnumber(tupdesc, args[i]);
361 :
362 : /* Bad guys may give us un-existing column in CREATE TRIGGER */
363 72 : if (fnumber <= 0)
364 0 : 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 72 : 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 72 : if (isnull)
378 : {
379 0 : SPI_finish();
380 0 : 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 72 : if (newtuple != NULL)
389 : {
390 24 : char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
391 : char *newval;
392 :
393 : /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
394 24 : if (oldval == NULL)
395 : /* internal error */
396 0 : elog(ERROR, "check_foreign_key: SPI_getvalue returned %s", SPI_result_code_string(SPI_result));
397 24 : newval = SPI_getvalue(newtuple, tupdesc, fnumber);
398 24 : if (newval == NULL || strcmp(oldval, newval) != 0)
399 24 : isequal = false;
400 : }
401 :
402 72 : if (plan->nplans <= 0) /* Get typeId of column */
403 18 : argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
404 : }
405 48 : args_temp = args;
406 48 : nargs -= nkeys;
407 48 : args += nkeys;
408 :
409 : /*
410 : * If we have to prepare plans ...
411 : */
412 48 : if (plan->nplans <= 0)
413 : {
414 : SPIPlanPtr pplan;
415 : char sql[8192];
416 12 : char **args2 = args;
417 :
418 12 : plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
419 : nrefs * sizeof(SPIPlanPtr));
420 :
421 30 : for (r = 0; r < nrefs; r++)
422 : {
423 18 : 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 18 : if (action == 'r')
436 :
437 6 : 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 12 : else if (action == 'c')
457 : {
458 12 : if (is_update == 1)
459 : {
460 : int fn;
461 : char *nv;
462 : int k;
463 :
464 0 : snprintf(sql, sizeof(sql), "update %s set ", relname);
465 0 : for (k = 1; k <= nkeys; k++)
466 : {
467 0 : int is_char_type = 0;
468 : char *type;
469 :
470 0 : fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
471 : Assert(fn > 0); /* already checked above */
472 0 : nv = SPI_getvalue(newtuple, tupdesc, fn);
473 0 : type = SPI_gettype(tupdesc, fn);
474 :
475 0 : if (strcmp(type, "text") == 0 ||
476 0 : strcmp(type, "varchar") == 0 ||
477 0 : strcmp(type, "char") == 0 ||
478 0 : strcmp(type, "bpchar") == 0 ||
479 0 : strcmp(type, "date") == 0 ||
480 0 : strcmp(type, "timestamp") == 0)
481 0 : 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 0 : snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
491 : " %s = %s%s%s %s ",
492 0 : args2[k], (is_char_type > 0) ? "'" : "",
493 : nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
494 : }
495 0 : strcat(sql, " where ");
496 : }
497 : else
498 : /* DELETE */
499 12 : 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 0 : else if (action == 's')
509 : {
510 0 : snprintf(sql, sizeof(sql), "update %s set ", relname);
511 0 : for (i = 1; i <= nkeys; i++)
512 : {
513 0 : snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
514 : "%s = null%s",
515 0 : args2[i], (i < nkeys) ? ", " : "");
516 : }
517 0 : strcat(sql, " where ");
518 : }
519 :
520 : /* Construct WHERE qual */
521 48 : for (i = 1; i <= nkeys; i++)
522 : {
523 30 : snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
524 30 : args2[i], i, (i < nkeys) ? "and " : "");
525 : }
526 :
527 : /* Prepare plan for query */
528 18 : pplan = SPI_prepare(sql, nkeys, argtypes);
529 18 : if (pplan == NULL)
530 : /* internal error */
531 0 : 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 18 : if (SPI_keepplan(pplan))
538 : /* internal error */
539 0 : elog(ERROR, "check_foreign_key: SPI_keepplan failed");
540 :
541 18 : plan->splan[r] = pplan;
542 :
543 18 : args2 += nkeys + 1; /* to the next relation */
544 : }
545 12 : 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 48 : if (newtuple != NULL && isequal)
555 : {
556 0 : SPI_finish();
557 0 : return PointerGetDatum(newtuple);
558 : }
559 :
560 : /*
561 : * Ok, execute prepared plan(s).
562 : */
563 96 : 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 72 : int tcount = (action == 'r') ? 1 : 0;
570 :
571 72 : relname = args[0];
572 :
573 72 : snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
574 72 : plan = find_plan(ident, &FPlans, &nFPlans);
575 72 : ret = SPI_execp(plan->splan[r], kvals, NULL, tcount);
576 : /* we have no NULLs - so we pass ^^^^ here */
577 :
578 60 : if (ret < 0)
579 0 : ereport(ERROR,
580 : (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
581 : errmsg("SPI_execp returned %d", ret)));
582 :
583 : /* If action is 'R'estrict ... */
584 60 : if (action == 'r')
585 : {
586 : /* If there is tuple returned by SELECT then ... */
587 24 : if (SPI_processed > 0)
588 12 : 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 36 : 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 48 : args += nkeys + 1; /* to the next relation */
602 : }
603 :
604 24 : SPI_finish();
605 :
606 24 : return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
607 : }
608 :
609 : static EPlan *
610 216 : 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 : */
620 216 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
621 :
622 216 : if (*nplans > 0)
623 : {
624 348 : for (i = 0; i < *nplans; i++)
625 : {
626 330 : if (strcmp((*eplan)[i].ident, ident) == 0)
627 186 : break;
628 : }
629 204 : if (i != *nplans)
630 : {
631 186 : MemoryContextSwitchTo(oldcontext);
632 186 : return (*eplan + i);
633 : }
634 18 : *eplan = (EPlan *) repalloc(*eplan, (i + 1) * sizeof(EPlan));
635 18 : newp = *eplan + i;
636 : }
637 : else
638 : {
639 12 : newp = *eplan = (EPlan *) palloc(sizeof(EPlan));
640 12 : (*nplans) = i = 0;
641 : }
642 :
643 30 : newp->ident = pstrdup(ident);
644 30 : newp->nplans = 0;
645 30 : newp->splan = NULL;
646 30 : (*nplans)++;
647 :
648 30 : MemoryContextSwitchTo(oldcontext);
649 30 : return newp;
650 : }
|