Age Owner Branch data 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/rel.h"
16 : :
461 tgl@sss.pgh.pa.us 17 :CBC 1 : PG_MODULE_MAGIC_EXT(
18 : : .name = "refint",
19 : : .version = PG_VERSION
20 : : );
21 : :
22 : : /*
23 : : * check_primary_key () -- check that key in tuple being inserted/updated
24 : : * references existing tuple in "primary" table.
25 : : * Though it's called without args You have to specify referenced
26 : : * table/keys while creating trigger: key field names in triggered table,
27 : : * referenced table name, referenced key field names:
28 : : * EXECUTE PROCEDURE
29 : : * check_primary_key ('Fkey1', 'Fkey2', 'Ptable', 'Pkey1', 'Pkey2').
30 : : */
31 : :
9353 32 : 2 : PG_FUNCTION_INFO_V1(check_primary_key);
33 : :
34 : : Datum
9528 35 : 19 : check_primary_key(PG_FUNCTION_ARGS)
36 : : {
37 : 19 : TriggerData *trigdata = (TriggerData *) fcinfo->context;
38 : : Trigger *trigger; /* to get trigger name */
39 : : int nargs; /* # of args specified in CREATE TRIGGER */
40 : : char **args; /* arguments: column names and table name */
41 : : int nkeys; /* # of key columns (= nargs / 2) */
42 : : Datum *kvals; /* key values */
43 : : char *relname; /* referenced relation name */
44 : : Relation rel; /* triggered relation */
10519 vadim4o@yahoo.com 45 : 19 : HeapTuple tuple = NULL; /* tuple to return */
46 : : TupleDesc tupdesc; /* tuple description */
47 : : SPIPlanPtr pplan; /* prepared plan */
9011 bruce@momjian.us 48 : 19 : Oid *argtypes = NULL; /* key types to prepare execution plan */
49 : : bool isnull; /* to know is some column NULL or not */
50 : : int ret;
51 : : int i;
52 : : StringInfoData sql;
53 : :
54 : : #ifdef DEBUG_QUERY
55 : : elog(DEBUG4, "check_primary_key: Enter Function");
56 : : #endif
57 : :
58 : : /*
59 : : * Some checks first...
60 : : */
61 : :
62 : : /* Called by trigger manager ? */
9528 tgl@sss.pgh.pa.us 63 [ + - - + ]: 19 : if (!CALLED_AS_TRIGGER(fcinfo))
64 : : /* internal error */
9528 tgl@sss.pgh.pa.us 65 [ # # ]:UBC 0 : elog(ERROR, "check_primary_key: not fired by trigger manager");
66 : :
67 : : /* Should be called for ROW trigger */
5744 tgl@sss.pgh.pa.us 68 [ - + ]:CBC 19 : if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
69 : : /* internal error */
5744 tgl@sss.pgh.pa.us 70 [ # # ]:UBC 0 : elog(ERROR, "check_primary_key: must be fired for row");
71 : :
449 tgl@sss.pgh.pa.us 72 [ - + ]:CBC 19 : if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
73 : : /* internal error */
449 tgl@sss.pgh.pa.us 74 [ # # ]:UBC 0 : elog(ERROR, "check_primary_key: must be fired by AFTER trigger");
75 : :
76 : : /* If INSERTion then must check Tuple to being inserted */
9528 tgl@sss.pgh.pa.us 77 [ + + ]:CBC 19 : if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
78 : 16 : tuple = trigdata->tg_trigtuple;
79 : :
80 : : /* Not should be called for DELETE */
81 [ - + ]: 3 : else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
82 : : /* internal error */
7089 bruce@momjian.us 83 [ # # ]:UBC 0 : elog(ERROR, "check_primary_key: cannot process DELETE events");
84 : :
85 : : /* If UPDATE, then must check new Tuple, not old one */
86 : : else
9528 tgl@sss.pgh.pa.us 87 :CBC 3 : tuple = trigdata->tg_newtuple;
88 : :
89 : 19 : trigger = trigdata->tg_trigger;
10519 vadim4o@yahoo.com 90 : 19 : nargs = trigger->tgnargs;
91 : 19 : args = trigger->tgargs;
92 : :
9913 bruce@momjian.us 93 [ - + ]: 19 : if (nargs % 2 != 1) /* odd number of arguments! */
94 : : /* internal error */
9913 bruce@momjian.us 95 [ # # ]:UBC 0 : elog(ERROR, "check_primary_key: odd number of arguments should be specified");
96 : :
9913 bruce@momjian.us 97 :CBC 19 : nkeys = nargs / 2;
10519 vadim4o@yahoo.com 98 : 19 : relname = args[nkeys];
9528 tgl@sss.pgh.pa.us 99 : 19 : rel = trigdata->tg_relation;
10519 vadim4o@yahoo.com 100 : 19 : tupdesc = rel->rd_att;
101 : :
102 : : /* Connect to SPI manager */
659 tgl@sss.pgh.pa.us 103 : 19 : SPI_connect();
104 : :
105 : : /*
106 : : * We use SPI plan preparation feature, so allocate space to place key
107 : : * values.
108 : : */
10518 vadim4o@yahoo.com 109 : 19 : kvals = (Datum *) palloc(nkeys * sizeof(Datum));
110 : :
111 : : /* allocate argtypes for preparation */
25 nathan@postgresql.or 112 : 19 : argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
113 : :
114 : : /* For each column in key ... */
10519 vadim4o@yahoo.com 115 [ + + ]: 50 : for (i = 0; i < nkeys; i++)
116 : : {
117 : : /* get index of column in tuple */
118 : 31 : int fnumber = SPI_fnumber(tupdesc, args[i]);
119 : :
120 : : /* Bad guys may give us un-existing column in CREATE TRIGGER */
3521 tgl@sss.pgh.pa.us 121 [ - + ]: 31 : if (fnumber <= 0)
8377 tgl@sss.pgh.pa.us 122 [ # # ]:UBC 0 : ereport(ERROR,
123 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
124 : : errmsg("there is no attribute \"%s\" in relation \"%s\"",
125 : : args[i], SPI_getrelname(rel))));
126 : :
127 : : /* Well, get binary (in internal format) value of column */
10519 vadim4o@yahoo.com 128 :CBC 31 : kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
129 : :
130 : : /*
131 : : * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
132 : : * DON'T FORGET return tuple! Executor inserts tuple you're returning!
133 : : * If you return NULL then nothing will be inserted!
134 : : */
135 [ - + ]: 31 : if (isnull)
136 : : {
10519 vadim4o@yahoo.com 137 :UBC 0 : SPI_finish();
9528 tgl@sss.pgh.pa.us 138 : 0 : return PointerGetDatum(tuple);
139 : : }
140 : :
141 : : /* Get typeId of column */
25 nathan@postgresql.or 142 :CBC 31 : argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
143 : : }
144 : :
145 : 19 : initStringInfo(&sql);
146 : :
147 : : /*
148 : : * Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 = $1
149 : : * [AND Pkey2 = $2 [...]]
150 : : */
151 : 19 : appendStringInfo(&sql, "select 1 from %s where ", relname);
152 [ + + ]: 50 : for (i = 1; i <= nkeys; i++)
153 : : {
154 : 31 : appendStringInfo(&sql, "%s = $%d ", args[i + nkeys], i);
155 [ + + ]: 31 : if (i < nkeys)
156 : 12 : appendStringInfoString(&sql, "and ");
157 : : }
158 : :
159 : : /* Prepare plan for query */
160 : 19 : pplan = SPI_prepare(sql.data, nkeys, argtypes);
161 [ - + ]: 19 : if (pplan == NULL)
162 : : /* internal error */
25 nathan@postgresql.or 163 [ # # ]:UBC 0 : elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
164 : :
25 nathan@postgresql.or 165 :CBC 19 : pfree(sql.data);
166 : :
167 : : /*
168 : : * Ok, execute prepared plan.
169 : : */
170 : 19 : ret = SPI_execp(pplan, kvals, NULL, 1);
171 : : /* we have no NULLs - so we pass ^^^^ here */
172 : :
10519 vadim4o@yahoo.com 173 [ - + ]: 19 : if (ret < 0)
174 : : /* internal error */
10402 bruce@momjian.us 175 [ # # ]:UBC 0 : elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
176 : :
177 : : /*
178 : : * If there are no tuples returned by SELECT then ...
179 : : */
9913 bruce@momjian.us 180 [ + + ]:CBC 19 : if (SPI_processed == 0)
8377 tgl@sss.pgh.pa.us 181 [ + - ]: 3 : ereport(ERROR,
182 : : (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
183 : : errmsg("tuple references non-existent key"),
184 : : errdetail("Trigger \"%s\" found tuple referencing non-existent key in \"%s\".", trigger->tgname, relname)));
185 : :
10519 vadim4o@yahoo.com 186 : 16 : SPI_finish();
187 : :
9528 tgl@sss.pgh.pa.us 188 : 16 : return PointerGetDatum(tuple);
189 : : }
190 : :
191 : : /*
192 : : * check_foreign_key () -- check that key in tuple being deleted/updated
193 : : * is not referenced by tuples in "foreign" table(s).
194 : : * Though it's called without args You have to specify (while creating trigger):
195 : : * number of references, action to do if key referenced
196 : : * ('restrict' | 'setnull' | 'cascade'), key field names in triggered
197 : : * ("primary") table and referencing table(s)/keys:
198 : : * EXECUTE PROCEDURE
199 : : * check_foreign_key (2, 'restrict', 'Pkey1', 'Pkey2',
200 : : * 'Ftable1', 'Fkey11', 'Fkey12', 'Ftable2', 'Fkey21', 'Fkey22').
201 : : */
202 : :
9353 203 : 2 : PG_FUNCTION_INFO_V1(check_foreign_key);
204 : :
205 : : Datum
9528 206 : 6 : check_foreign_key(PG_FUNCTION_ARGS)
207 : : {
208 : 6 : TriggerData *trigdata = (TriggerData *) fcinfo->context;
209 : : Trigger *trigger; /* to get trigger name */
210 : : int nargs; /* # of args specified in CREATE TRIGGER */
211 : : char **args; /* arguments: as described above */
212 : : char **args_temp;
213 : : int nrefs; /* number of references (== # of plans) */
214 : : char action; /* 'R'estrict | 'S'etnull | 'C'ascade */
215 : : int nkeys; /* # of key columns */
216 : : Datum *kvals; /* key values */
217 : : char *relname; /* referencing relation name */
218 : : Relation rel; /* triggered relation */
3296 219 : 6 : HeapTuple trigtuple = NULL; /* tuple to being changed */
9011 bruce@momjian.us 220 : 6 : HeapTuple newtuple = NULL; /* tuple to return */
221 : : TupleDesc tupdesc; /* tuple description */
222 : : SPIPlanPtr *splan; /* prepared plan(s) */
223 : 6 : Oid *argtypes = NULL; /* key types to prepare execution plan */
224 : : bool isnull; /* to know is some column NULL or not */
7563 225 : 6 : bool isequal = true; /* are keys in both tuples equal (in UPDATE) */
9898 226 : 6 : int is_update = 0;
227 : : int ret;
228 : : int i,
229 : : r;
230 : : char **args2;
231 : :
232 : : #ifdef DEBUG_QUERY
233 : : elog(DEBUG4, "check_foreign_key: Enter Function");
234 : : #endif
235 : :
236 : : /*
237 : : * Some checks first...
238 : : */
239 : :
240 : : /* Called by trigger manager ? */
9528 tgl@sss.pgh.pa.us 241 [ + - - + ]: 6 : if (!CALLED_AS_TRIGGER(fcinfo))
242 : : /* internal error */
9528 tgl@sss.pgh.pa.us 243 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: not fired by trigger manager");
244 : :
245 : : /* Should be called for ROW trigger */
5744 tgl@sss.pgh.pa.us 246 [ - + ]:CBC 6 : if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
247 : : /* internal error */
5744 tgl@sss.pgh.pa.us 248 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: must be fired for row");
249 : :
250 : : /* Not should be called for INSERT */
9528 tgl@sss.pgh.pa.us 251 [ - + ]:CBC 6 : if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
252 : : /* internal error */
7089 bruce@momjian.us 253 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: cannot process INSERT events");
254 : :
449 tgl@sss.pgh.pa.us 255 [ - + ]:CBC 6 : if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
256 : : /* internal error */
449 tgl@sss.pgh.pa.us 257 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: must be fired by AFTER trigger");
258 : :
259 : : /* Have to check tg_trigtuple - tuple being deleted */
9528 tgl@sss.pgh.pa.us 260 :CBC 6 : trigtuple = trigdata->tg_trigtuple;
261 : :
262 : : /*
263 : : * But if this is UPDATE then we have to return tg_newtuple. Also, if key
264 : : * in tg_newtuple is the same as in tg_trigtuple then nothing to do.
265 : : */
9898 bruce@momjian.us 266 : 6 : is_update = 0;
9528 tgl@sss.pgh.pa.us 267 [ + + ]: 6 : if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
268 : : {
269 : 2 : newtuple = trigdata->tg_newtuple;
9898 bruce@momjian.us 270 : 2 : is_update = 1;
271 : : }
9528 tgl@sss.pgh.pa.us 272 : 6 : trigger = trigdata->tg_trigger;
10519 vadim4o@yahoo.com 273 : 6 : nargs = trigger->tgnargs;
274 : 6 : args = trigger->tgargs;
275 : :
276 [ - + ]: 6 : if (nargs < 5) /* nrefs, action, key, Relation, key - at
277 : : * least */
278 : : /* internal error */
10402 bruce@momjian.us 279 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
280 : :
2900 andres@anarazel.de 281 :CBC 6 : nrefs = pg_strtoint32(args[0]);
10519 vadim4o@yahoo.com 282 [ - + ]: 6 : if (nrefs < 1)
283 : : /* internal error */
10402 bruce@momjian.us 284 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
364 jdavis@postgresql.or 285 :GNC 6 : action = pg_ascii_tolower((unsigned char) *(args[1]));
10519 vadim4o@yahoo.com 286 [ + + - + :CBC 6 : if (action != 'r' && action != 'c' && action != 's')
- - ]
287 : : /* internal error */
10402 bruce@momjian.us 288 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
10519 vadim4o@yahoo.com 289 :CBC 6 : nargs -= 2;
290 : 6 : args += 2;
291 : 6 : nkeys = (nargs - nrefs) / (nrefs + 1);
292 [ + - - + ]: 6 : if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
293 : : /* internal error */
10402 bruce@momjian.us 294 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
295 : : nargs + 2, nrefs);
296 : :
9528 tgl@sss.pgh.pa.us 297 :CBC 6 : rel = trigdata->tg_relation;
10519 vadim4o@yahoo.com 298 : 6 : tupdesc = rel->rd_att;
299 : :
300 : : /* Connect to SPI manager */
659 tgl@sss.pgh.pa.us 301 : 6 : SPI_connect();
302 : :
303 : : /*
304 : : * We use SPI plan preparation feature, so allocate space to place key
305 : : * values.
306 : : */
10518 vadim4o@yahoo.com 307 : 6 : kvals = (Datum *) palloc(nkeys * sizeof(Datum));
308 : :
309 : : /* allocate argtypes for preparation */
25 nathan@postgresql.or 310 : 6 : argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
311 : :
312 : : /* For each column in key ... */
10519 vadim4o@yahoo.com 313 [ + + ]: 15 : for (i = 0; i < nkeys; i++)
314 : : {
315 : : /* get index of column in tuple */
316 : 9 : int fnumber = SPI_fnumber(tupdesc, args[i]);
317 : :
318 : : /* Bad guys may give us un-existing column in CREATE TRIGGER */
3521 tgl@sss.pgh.pa.us 319 [ - + ]: 9 : if (fnumber <= 0)
8377 tgl@sss.pgh.pa.us 320 [ # # ]:UBC 0 : ereport(ERROR,
321 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
322 : : errmsg("there is no attribute \"%s\" in relation \"%s\"",
323 : : args[i], SPI_getrelname(rel))));
324 : :
325 : : /* Well, get binary (in internal format) value of column */
10519 vadim4o@yahoo.com 326 :CBC 9 : kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
327 : :
328 : : /*
329 : : * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
330 : : * DON'T FORGET return tuple! Executor inserts tuple you're returning!
331 : : * If you return NULL then nothing will be inserted!
332 : : */
333 [ - + ]: 9 : if (isnull)
334 : : {
10519 vadim4o@yahoo.com 335 :UBC 0 : SPI_finish();
9528 tgl@sss.pgh.pa.us 336 [ # # ]: 0 : return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
337 : : }
338 : :
339 : : /*
340 : : * If UPDATE then get column value from new tuple being inserted and
341 : : * compare is this the same as old one. For the moment we use string
342 : : * presentation of values...
343 : : */
10519 vadim4o@yahoo.com 344 [ + + ]:CBC 9 : if (newtuple != NULL)
345 : : {
346 : 3 : char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
347 : : char *newval;
348 : :
349 : : /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
350 [ - + ]: 3 : if (oldval == NULL)
351 : : /* internal error */
3226 peter_e@gmx.net 352 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: SPI_getvalue returned %s", SPI_result_code_string(SPI_result));
10519 vadim4o@yahoo.com 353 :CBC 3 : newval = SPI_getvalue(newtuple, tupdesc, fnumber);
354 [ + - + + ]: 3 : if (newval == NULL || strcmp(oldval, newval) != 0)
355 : 2 : isequal = false;
356 : : }
357 : :
358 : : /* Get typeId of column */
25 nathan@postgresql.or 359 : 9 : argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
360 : : }
9898 bruce@momjian.us 361 : 6 : args_temp = args;
10519 vadim4o@yahoo.com 362 : 6 : nargs -= nkeys;
363 : 6 : args += nkeys;
25 nathan@postgresql.or 364 : 6 : args2 = args;
365 : :
366 : 6 : splan = (SPIPlanPtr *) palloc(nrefs * sizeof(SPIPlanPtr));
367 : :
368 [ + + ]: 15 : for (r = 0; r < nrefs; r++)
369 : : {
370 : : StringInfoData sql;
371 : : SPIPlanPtr pplan;
372 : :
373 : 9 : initStringInfo(&sql);
374 : :
375 : 9 : relname = args2[0];
376 : :
377 : : /*---------
378 : : * For 'R'estrict action we construct SELECT query:
379 : : *
380 : : * SELECT 1
381 : : * FROM _referencing_relation_
382 : : * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
383 : : *
384 : : * to check is tuple referenced or not.
385 : : *---------
386 : : */
387 [ + + ]: 9 : if (action == 'r')
388 : 3 : appendStringInfo(&sql, "select 1 from %s where ", relname);
389 : :
390 : : /*---------
391 : : * For 'C'ascade action we construct DELETE query
392 : : *
393 : : * DELETE
394 : : * FROM _referencing_relation_
395 : : * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
396 : : *
397 : : * to delete all referencing tuples.
398 : : *---------
399 : : */
400 : :
401 : : /*
402 : : * Max : Cascade with UPDATE query i create update query that updates
403 : : * new key values in referenced tables
404 : : */
405 : :
406 : :
407 [ + - ]: 6 : else if (action == 'c')
408 : : {
409 [ + + ]: 6 : if (is_update == 1)
410 : : {
411 : : int fn;
412 : : char *nv;
413 : : int k;
414 : :
50 415 : 2 : appendStringInfo(&sql, "update %s set ", relname);
25 416 [ + + ]: 6 : for (k = 1; k <= nkeys; k++)
417 : : {
418 : 4 : fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
419 [ - + ]: 4 : Assert(fn > 0); /* already checked above */
420 : 4 : nv = SPI_getvalue(newtuple, tupdesc, fn);
421 : :
422 [ + - ]: 4 : appendStringInfo(&sql, " %s = %s ",
423 : 4 : args2[k],
424 : 4 : nv ? quote_literal_cstr(nv) : "NULL");
425 [ + + ]: 4 : if (k < nkeys)
50 426 : 2 : appendStringInfoString(&sql, ", ");
427 : : }
428 : 2 : appendStringInfoString(&sql, " where ");
429 : : }
430 : : else
431 : : /* DELETE */
25 432 : 4 : appendStringInfo(&sql, "delete from %s where ", relname);
433 : : }
434 : :
435 : : /*
436 : : * For 'S'etnull action we construct UPDATE query - UPDATE
437 : : * _referencing_relation_ SET Fkey1 null [, Fkey2 null [...]] WHERE
438 : : * Fkey1 = $1 [AND Fkey2 = $2 [...]] - to set key columns in all
439 : : * referencing tuples to NULL.
440 : : */
25 nathan@postgresql.or 441 [ # # ]:UBC 0 : else if (action == 's')
442 : : {
443 : 0 : appendStringInfo(&sql, "update %s set ", relname);
10519 vadim4o@yahoo.com 444 [ # # ]: 0 : for (i = 1; i <= nkeys; i++)
445 : : {
25 nathan@postgresql.or 446 : 0 : appendStringInfo(&sql, "%s = null", args2[i]);
50 447 [ # # ]: 0 : if (i < nkeys)
25 448 : 0 : appendStringInfoString(&sql, ", ");
449 : : }
450 : 0 : appendStringInfoString(&sql, " where ");
451 : : }
452 : :
453 : : /* Construct WHERE qual */
25 nathan@postgresql.or 454 [ + + ]:CBC 24 : for (i = 1; i <= nkeys; i++)
455 : : {
456 : 15 : appendStringInfo(&sql, "%s = $%d ", args2[i], i);
457 [ + + ]: 15 : if (i < nkeys)
458 : 6 : appendStringInfoString(&sql, "and ");
459 : : }
460 : :
461 : : /* Prepare plan for query */
462 : 9 : pplan = SPI_prepare(sql.data, nkeys, argtypes);
463 [ - + ]: 9 : if (pplan == NULL)
464 : : /* internal error */
25 nathan@postgresql.or 465 [ # # ]:UBC 0 : elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
466 : :
25 nathan@postgresql.or 467 :CBC 9 : splan[r] = pplan;
468 : :
469 : 9 : args2 += nkeys + 1; /* to the next relation */
470 : :
471 : : #ifdef DEBUG_QUERY
472 : : elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql.data);
473 : : #endif
474 : :
475 : 9 : pfree(sql.data);
476 : : }
477 : :
478 : : /*
479 : : * If UPDATE and key is not changed ...
480 : : */
10519 vadim4o@yahoo.com 481 [ + + + + ]: 6 : if (newtuple != NULL && isequal)
482 : : {
483 : 1 : SPI_finish();
9528 tgl@sss.pgh.pa.us 484 : 1 : return PointerGetDatum(newtuple);
485 : : }
486 : :
487 : : /*
488 : : * Ok, execute prepared plan(s).
489 : : */
10519 vadim4o@yahoo.com 490 [ + + ]: 11 : for (r = 0; r < nrefs; r++)
491 : : {
492 : : /*
493 : : * For 'R'estrict we may to execute plan for one tuple only, for other
494 : : * actions - for all tuples.
495 : : */
496 : 8 : int tcount = (action == 'r') ? 1 : 0;
497 : :
498 : 8 : relname = args[0];
499 : :
25 nathan@postgresql.or 500 : 8 : ret = SPI_execp(splan[r], kvals, NULL, tcount);
501 : : /* we have no NULLs - so we pass ^^^^ here */
502 : :
10519 vadim4o@yahoo.com 503 [ - + ]: 7 : if (ret < 0)
8377 tgl@sss.pgh.pa.us 504 [ # # ]:UBC 0 : ereport(ERROR,
505 : : (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
506 : : errmsg("SPI_execp returned %d", ret)));
507 : :
508 : : /* If action is 'R'estrict ... */
10519 vadim4o@yahoo.com 509 [ + + ]:CBC 7 : if (action == 'r')
510 : : {
511 : : /* If there is tuple returned by SELECT then ... */
512 [ + + ]: 2 : if (SPI_processed > 0)
8377 tgl@sss.pgh.pa.us 513 [ + - ]: 1 : ereport(ERROR,
514 : : (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
515 : : errmsg("\"%s\": tuple is referenced in \"%s\"",
516 : : trigger->tgname, relname)));
517 : : }
518 : : else
519 : : {
520 : : #ifdef REFINT_VERBOSE
521 : : const char *operation;
522 : :
449 523 [ + - ]: 5 : if (action == 'c')
524 [ + + ]: 5 : operation = is_update ? "updated" : "deleted";
525 : : else
449 tgl@sss.pgh.pa.us 526 :UBC 0 : operation = "set to null";
527 : :
3762 tgl@sss.pgh.pa.us 528 [ + - ]:CBC 5 : elog(NOTICE, "%s: " UINT64_FORMAT " tuple(s) of %s are %s",
529 : : trigger->tgname, SPI_processed, relname, operation);
530 : : #endif
531 : : }
10519 vadim4o@yahoo.com 532 : 6 : args += nkeys + 1; /* to the next relation */
533 : : }
534 : :
535 : 3 : SPI_finish();
536 : :
9528 tgl@sss.pgh.pa.us 537 [ + + ]: 3 : return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
538 : : }
|