Make libpqsrv_cancel's return const char *, not char *
[pgsql.git] / contrib / spi / refint.c
blob18062eb1cffbd6ac3347f0053bb8db0ccc313d53
1 /*
2 * contrib/spi/refint.c
5 * refint.c -- set of functions to define referential integrity
6 * constraints using general triggers.
7 */
8 #include "postgres.h"
10 #include <ctype.h>
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"
18 PG_MODULE_MAGIC;
20 typedef struct
22 char *ident;
23 int nplans;
24 SPIPlanPtr *splan;
25 } EPlan;
27 static EPlan *FPlans = NULL;
28 static int nFPlans = 0;
29 static EPlan *PPlans = NULL;
30 static int nPPlans = 0;
32 static EPlan *find_plan(char *ident, EPlan **eplan, int *nplans);
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').
44 PG_FUNCTION_INFO_V1(check_primary_key);
46 Datum
47 check_primary_key(PG_FUNCTION_ARGS)
49 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 HeapTuple tuple = NULL; /* tuple to return */
58 TupleDesc tupdesc; /* tuple description */
59 EPlan *plan; /* prepared plan */
60 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;
66 #ifdef DEBUG_QUERY
67 elog(DEBUG4, "check_primary_key: Enter Function");
68 #endif
71 * Some checks first...
74 /* Called by trigger manager ? */
75 if (!CALLED_AS_TRIGGER(fcinfo))
76 /* internal error */
77 elog(ERROR, "check_primary_key: not fired by trigger manager");
79 /* Should be called for ROW trigger */
80 if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
81 /* internal error */
82 elog(ERROR, "check_primary_key: must be fired for row");
84 /* If INSERTion then must check Tuple to being inserted */
85 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
86 tuple = trigdata->tg_trigtuple;
88 /* Not should be called for DELETE */
89 else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
90 /* internal error */
91 elog(ERROR, "check_primary_key: cannot process DELETE events");
93 /* If UPDATE, then must check new Tuple, not old one */
94 else
95 tuple = trigdata->tg_newtuple;
97 trigger = trigdata->tg_trigger;
98 nargs = trigger->tgnargs;
99 args = trigger->tgargs;
101 if (nargs % 2 != 1) /* odd number of arguments! */
102 /* internal error */
103 elog(ERROR, "check_primary_key: odd number of arguments should be specified");
105 nkeys = nargs / 2;
106 relname = args[nkeys];
107 rel = trigdata->tg_relation;
108 tupdesc = rel->rd_att;
110 /* Connect to SPI manager */
111 if ((ret = SPI_connect()) < 0)
112 /* internal error */
113 elog(ERROR, "check_primary_key: SPI_connect returned %d", ret);
116 * We use SPI plan preparation feature, so allocate space to place key
117 * values.
119 kvals = (Datum *) palloc(nkeys * sizeof(Datum));
122 * Construct ident string as TriggerName $ TriggeredRelationId and try to
123 * find prepared execution plan.
125 snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
126 plan = find_plan(ident, &PPlans, &nPPlans);
128 /* if there is no plan then allocate argtypes for preparation */
129 if (plan->nplans <= 0)
130 argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
132 /* For each column in key ... */
133 for (i = 0; i < nkeys; i++)
135 /* get index of column in tuple */
136 int fnumber = SPI_fnumber(tupdesc, args[i]);
138 /* Bad guys may give us un-existing column in CREATE TRIGGER */
139 if (fnumber <= 0)
140 ereport(ERROR,
141 (errcode(ERRCODE_UNDEFINED_COLUMN),
142 errmsg("there is no attribute \"%s\" in relation \"%s\"",
143 args[i], SPI_getrelname(rel))));
145 /* Well, get binary (in internal format) value of column */
146 kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
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!
153 if (isnull)
155 SPI_finish();
156 return PointerGetDatum(tuple);
159 if (plan->nplans <= 0) /* Get typeId of column */
160 argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
164 * If we have to prepare plan ...
166 if (plan->nplans <= 0)
168 SPIPlanPtr pplan;
169 char sql[8192];
172 * Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 =
173 * $1 [AND Pkey2 = $2 [...]]
175 snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
176 for (i = 0; i < nkeys; i++)
178 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
179 args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
182 /* Prepare plan for query */
183 pplan = SPI_prepare(sql, nkeys, argtypes);
184 if (pplan == NULL)
185 /* internal error */
186 elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
189 * Remember that SPI_prepare places plan in current memory context -
190 * so, we have to save plan in TopMemoryContext for later use.
192 if (SPI_keepplan(pplan))
193 /* internal error */
194 elog(ERROR, "check_primary_key: SPI_keepplan failed");
195 plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
196 sizeof(SPIPlanPtr));
197 *(plan->splan) = pplan;
198 plan->nplans = 1;
202 * Ok, execute prepared plan.
204 ret = SPI_execp(*(plan->splan), kvals, NULL, 1);
205 /* we have no NULLs - so we pass ^^^^ here */
207 if (ret < 0)
208 /* internal error */
209 elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
212 * If there are no tuples returned by SELECT then ...
214 if (SPI_processed == 0)
215 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)));
220 SPI_finish();
222 return PointerGetDatum(tuple);
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').
237 PG_FUNCTION_INFO_V1(check_foreign_key);
239 Datum
240 check_foreign_key(PG_FUNCTION_ARGS)
242 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 HeapTuple trigtuple = NULL; /* tuple to being changed */
254 HeapTuple newtuple = NULL; /* tuple to return */
255 TupleDesc tupdesc; /* tuple description */
256 EPlan *plan; /* prepared plan(s) */
257 Oid *argtypes = NULL; /* key types to prepare execution plan */
258 bool isnull; /* to know is some column NULL or not */
259 bool isequal = true; /* are keys in both tuples equal (in UPDATE) */
260 char ident[2 * NAMEDATALEN]; /* to identify myself */
261 int is_update = 0;
262 int ret;
263 int i,
266 #ifdef DEBUG_QUERY
267 elog(DEBUG4, "check_foreign_key: Enter Function");
268 #endif
271 * Some checks first...
274 /* Called by trigger manager ? */
275 if (!CALLED_AS_TRIGGER(fcinfo))
276 /* internal error */
277 elog(ERROR, "check_foreign_key: not fired by trigger manager");
279 /* Should be called for ROW trigger */
280 if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
281 /* internal error */
282 elog(ERROR, "check_foreign_key: must be fired for row");
284 /* Not should be called for INSERT */
285 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
286 /* internal error */
287 elog(ERROR, "check_foreign_key: cannot process INSERT events");
289 /* Have to check tg_trigtuple - tuple being deleted */
290 trigtuple = trigdata->tg_trigtuple;
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.
296 is_update = 0;
297 if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
299 newtuple = trigdata->tg_newtuple;
300 is_update = 1;
302 trigger = trigdata->tg_trigger;
303 nargs = trigger->tgnargs;
304 args = trigger->tgargs;
306 if (nargs < 5) /* nrefs, action, key, Relation, key - at
307 * least */
308 /* internal error */
309 elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
311 nrefs = pg_strtoint32(args[0]);
312 if (nrefs < 1)
313 /* internal error */
314 elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
315 action = tolower((unsigned char) *(args[1]));
316 if (action != 'r' && action != 'c' && action != 's')
317 /* internal error */
318 elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
319 nargs -= 2;
320 args += 2;
321 nkeys = (nargs - nrefs) / (nrefs + 1);
322 if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
323 /* internal error */
324 elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
325 nargs + 2, nrefs);
327 rel = trigdata->tg_relation;
328 tupdesc = rel->rd_att;
330 /* Connect to SPI manager */
331 if ((ret = SPI_connect()) < 0)
332 /* internal error */
333 elog(ERROR, "check_foreign_key: SPI_connect returned %d", ret);
336 * We use SPI plan preparation feature, so allocate space to place key
337 * values.
339 kvals = (Datum *) palloc(nkeys * sizeof(Datum));
342 * Construct ident string as TriggerName $ TriggeredRelationId and try to
343 * find prepared execution plan(s).
345 snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
346 plan = find_plan(ident, &FPlans, &nFPlans);
348 /* if there is no plan(s) then allocate argtypes for preparation */
349 if (plan->nplans <= 0)
350 argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
353 * else - check that we have exactly nrefs plan(s) ready
355 else if (plan->nplans != nrefs)
356 /* internal error */
357 elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime",
358 trigger->tgname);
360 /* For each column in key ... */
361 for (i = 0; i < nkeys; i++)
363 /* get index of column in tuple */
364 int fnumber = SPI_fnumber(tupdesc, args[i]);
366 /* Bad guys may give us un-existing column in CREATE TRIGGER */
367 if (fnumber <= 0)
368 ereport(ERROR,
369 (errcode(ERRCODE_UNDEFINED_COLUMN),
370 errmsg("there is no attribute \"%s\" in relation \"%s\"",
371 args[i], SPI_getrelname(rel))));
373 /* Well, get binary (in internal format) value of column */
374 kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
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!
381 if (isnull)
383 SPI_finish();
384 return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
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...
392 if (newtuple != NULL)
394 char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
395 char *newval;
397 /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
398 if (oldval == NULL)
399 /* internal error */
400 elog(ERROR, "check_foreign_key: SPI_getvalue returned %s", SPI_result_code_string(SPI_result));
401 newval = SPI_getvalue(newtuple, tupdesc, fnumber);
402 if (newval == NULL || strcmp(oldval, newval) != 0)
403 isequal = false;
406 if (plan->nplans <= 0) /* Get typeId of column */
407 argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
409 args_temp = args;
410 nargs -= nkeys;
411 args += nkeys;
414 * If we have to prepare plans ...
416 if (plan->nplans <= 0)
418 SPIPlanPtr pplan;
419 char sql[8192];
420 char **args2 = args;
422 plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
423 nrefs * sizeof(SPIPlanPtr));
425 for (r = 0; r < nrefs; r++)
427 relname = args2[0];
429 /*---------
430 * For 'R'estrict action we construct SELECT query:
432 * SELECT 1
433 * FROM _referencing_relation_
434 * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
436 * to check is tuple referenced or not.
437 *---------
439 if (action == 'r')
441 snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
443 /*---------
444 * For 'C'ascade action we construct DELETE query
446 * DELETE
447 * FROM _referencing_relation_
448 * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
450 * to delete all referencing tuples.
451 *---------
455 * Max : Cascade with UPDATE query i create update query that
456 * updates new key values in referenced tables
460 else if (action == 'c')
462 if (is_update == 1)
464 int fn;
465 char *nv;
466 int k;
468 snprintf(sql, sizeof(sql), "update %s set ", relname);
469 for (k = 1; k <= nkeys; k++)
471 int is_char_type = 0;
472 char *type;
474 fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
475 Assert(fn > 0); /* already checked above */
476 nv = SPI_getvalue(newtuple, tupdesc, fn);
477 type = SPI_gettype(tupdesc, fn);
479 if (strcmp(type, "text") == 0 ||
480 strcmp(type, "varchar") == 0 ||
481 strcmp(type, "char") == 0 ||
482 strcmp(type, "bpchar") == 0 ||
483 strcmp(type, "date") == 0 ||
484 strcmp(type, "timestamp") == 0)
485 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
492 * is_char_type =1 i set ' ' for define a new value
494 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
495 " %s = %s%s%s %s ",
496 args2[k], (is_char_type > 0) ? "'" : "",
497 nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
499 strcat(sql, " where ");
501 else
502 /* DELETE */
503 snprintf(sql, sizeof(sql), "delete from %s where ", relname);
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.
512 else if (action == 's')
514 snprintf(sql, sizeof(sql), "update %s set ", relname);
515 for (i = 1; i <= nkeys; i++)
517 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
518 "%s = null%s",
519 args2[i], (i < nkeys) ? ", " : "");
521 strcat(sql, " where ");
524 /* Construct WHERE qual */
525 for (i = 1; i <= nkeys; i++)
527 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
528 args2[i], i, (i < nkeys) ? "and " : "");
531 /* Prepare plan for query */
532 pplan = SPI_prepare(sql, nkeys, argtypes);
533 if (pplan == NULL)
534 /* internal error */
535 elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
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.
541 if (SPI_keepplan(pplan))
542 /* internal error */
543 elog(ERROR, "check_foreign_key: SPI_keepplan failed");
545 plan->splan[r] = pplan;
547 args2 += nkeys + 1; /* to the next relation */
549 plan->nplans = nrefs;
550 #ifdef DEBUG_QUERY
551 elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql);
552 #endif
556 * If UPDATE and key is not changed ...
558 if (newtuple != NULL && isequal)
560 SPI_finish();
561 return PointerGetDatum(newtuple);
565 * Ok, execute prepared plan(s).
567 for (r = 0; r < nrefs; r++)
570 * For 'R'estrict we may to execute plan for one tuple only, for other
571 * actions - for all tuples.
573 int tcount = (action == 'r') ? 1 : 0;
575 relname = args[0];
577 snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
578 plan = find_plan(ident, &FPlans, &nFPlans);
579 ret = SPI_execp(plan->splan[r], kvals, NULL, tcount);
580 /* we have no NULLs - so we pass ^^^^ here */
582 if (ret < 0)
583 ereport(ERROR,
584 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
585 errmsg("SPI_execp returned %d", ret)));
587 /* If action is 'R'estrict ... */
588 if (action == 'r')
590 /* If there is tuple returned by SELECT then ... */
591 if (SPI_processed > 0)
592 ereport(ERROR,
593 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
594 errmsg("\"%s\": tuple is referenced in \"%s\"",
595 trigger->tgname, relname)));
597 else
599 #ifdef REFINT_VERBOSE
600 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
605 args += nkeys + 1; /* to the next relation */
608 SPI_finish();
610 return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
613 static EPlan *
614 find_plan(char *ident, EPlan **eplan, int *nplans)
616 EPlan *newp;
617 int i;
618 MemoryContext oldcontext;
621 * All allocations done for the plans need to happen in a session-safe
622 * context.
624 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
626 if (*nplans > 0)
628 for (i = 0; i < *nplans; i++)
630 if (strcmp((*eplan)[i].ident, ident) == 0)
631 break;
633 if (i != *nplans)
635 MemoryContextSwitchTo(oldcontext);
636 return (*eplan + i);
638 *eplan = (EPlan *) repalloc(*eplan, (i + 1) * sizeof(EPlan));
639 newp = *eplan + i;
641 else
643 newp = *eplan = (EPlan *) palloc(sizeof(EPlan));
644 (*nplans) = i = 0;
647 newp->ident = pstrdup(ident);
648 newp->nplans = 0;
649 newp->splan = NULL;
650 (*nplans)++;
652 MemoryContextSwitchTo(oldcontext);
653 return newp;