246{
248 Trigger *trigger;
/* to get trigger name */
249 int nargs; /* # of args specified in CREATE TRIGGER */
250 char **
args;
/* arguments: as described above */
251 char **args_temp;
252 int nrefs; /* number of references (== # of plans) */
253 char action;
/* 'R'estrict | 'S'etnull | 'C'ascade */
254 int nkeys; /* # of key columns */
255 Datum *kvals;
/* key values */
256 char *
relname;
/* referencing relation name */
257 Relation rel;
/* triggered relation */
258 HeapTuple trigtuple = NULL;
/* tuple to being changed */
259 HeapTuple newtuple = NULL;
/* tuple to return */
260 TupleDesc tupdesc;
/* tuple description */
262 Oid *argtypes = NULL;
/* key types to prepare execution plan */
263 bool isnull; /* to know is some column NULL or not */
264 bool isequal = true; /* are keys in both tuples equal (in UPDATE) */
266 int is_update = 0;
267 int ret;
269 r;
270
271#ifdef DEBUG_QUERY
272 elog(
DEBUG4,
"check_foreign_key: Enter Function");
273#endif
274
275 /*
276 * Some checks first...
277 */
278
279 /* Called by trigger manager ? */
281 /* internal error */
282 elog(
ERROR,
"check_foreign_key: not fired by trigger manager");
283
284 /* Should be called for ROW trigger */
286 /* internal error */
287 elog(
ERROR,
"check_foreign_key: must be fired for row");
288
289 /* Not should be called for INSERT */
291 /* internal error */
292 elog(
ERROR,
"check_foreign_key: cannot process INSERT events");
293
295 /* internal error */
296 elog(
ERROR,
"check_foreign_key: must be fired by AFTER trigger");
297
298 /* Have to check tg_trigtuple - tuple being deleted */
300
301 /*
302 * But if this is UPDATE then we have to return tg_newtuple. Also, if key
303 * in tg_newtuple is the same as in tg_trigtuple then nothing to do.
304 */
305 is_update = 0;
307 {
309 is_update = 1;
310 }
313 args = trigger->tgargs;
314
315 if (nargs < 5) /* nrefs, action, key, Relation, key - at
316 * least */
317 /* internal error */
318 elog(
ERROR,
"check_foreign_key: too short %d (< 5) list of arguments", nargs);
319
321 if (nrefs < 1)
322 /* internal error */
323 elog(
ERROR,
"check_foreign_key: %d (< 1) number of references specified", nrefs);
326 /* internal error */
327 elog(
ERROR,
"check_foreign_key: invalid action %s",
args[1]);
328 nargs -= 2;
330 nkeys = (nargs - nrefs) / (nrefs + 1);
331 if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
332 /* internal error */
333 elog(
ERROR,
"check_foreign_key: invalid number of arguments %d for %d references",
334 nargs + 2, nrefs);
335
338
339 /* Connect to SPI manager */
341
342 /*
343 * We use SPI plan preparation feature, so allocate space to place key
344 * values.
345 */
347
348 /*
349 * Construct ident string as TriggerName $ TriggeredRelationId $
350 * OperationType and try to find prepared execution plan(s).
351 */
352 snprintf(
ident,
sizeof(
ident),
"%s$%u$%c", trigger->tgname, rel->rd_id, is_update ?
'U' :
'D');
354
355 /* if there is no plan(s) then allocate argtypes for preparation */
356 if (
plan->nplans <= 0)
358
359 /*
360 * else - check that we have exactly nrefs plan(s) ready
361 */
362 else if (
plan->nplans != nrefs)
363 /* internal error */
364 elog(
ERROR,
"%s: check_foreign_key: # of plans changed in meantime",
365 trigger->tgname);
366
367 /* For each column in key ... */
368 for (
i = 0;
i < nkeys;
i++)
369 {
370 /* get index of column in tuple */
372
373 /* Bad guys may give us un-existing column in CREATE TRIGGER */
374 if (fnumber <= 0)
376 (
errcode(ERRCODE_UNDEFINED_COLUMN),
377 errmsg(
"there is no attribute \"%s\" in relation \"%s\"",
379
380 /* Well, get binary (in internal format) value of column */
382
383 /*
384 * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
385 * DON'T FORGET return tuple! Executor inserts tuple you're returning!
386 * If you return NULL then nothing will be inserted!
387 */
388 if (isnull)
389 {
392 }
393
394 /*
395 * If UPDATE then get column value from new tuple being inserted and
396 * compare is this the same as old one. For the moment we use string
397 * presentation of values...
398 */
399 if (newtuple != NULL)
400 {
401 char *oldval =
SPI_getvalue(trigtuple, tupdesc, fnumber);
403
404 /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
405 if (oldval == NULL)
406 /* internal error */
410 isequal = false;
411 }
412
413 if (
plan->nplans <= 0)
/* Get typeId of column */
415 }
417 nargs -= nkeys;
419
420 /*
421 * If we have to prepare plans ...
422 */
423 if (
plan->nplans <= 0)
424 {
426 char sql[8192];
428
431
432 for (r = 0; r < nrefs; r++)
433 {
435
436 /*---------
437 * For 'R'estrict action we construct SELECT query:
438 *
439 * SELECT 1
440 * FROM _referencing_relation_
441 * WHERE Fkey1 = 1ドル [AND Fkey2 = 2ドル [...]]
442 *
443 * to check is tuple referenced or not.
444 *---------
445 */
447
449
450 /*---------
451 * For 'C'ascade action we construct DELETE query
452 *
453 * DELETE
454 * FROM _referencing_relation_
455 * WHERE Fkey1 = 1ドル [AND Fkey2 = 2ドル [...]]
456 *
457 * to delete all referencing tuples.
458 *---------
459 */
460
461 /*
462 * Max : Cascade with UPDATE query i create update query that
463 * updates new key values in referenced tables
464 */
465
466
468 {
469 if (is_update == 1)
470 {
472 char *nv;
473 int k;
474
476 for (k = 1; k <= nkeys; k++)
477 {
478 int is_char_type = 0;
480
482 Assert(
fn > 0);
/* already checked above */
485
486 if (strcmp(
type,
"text") == 0 ||
487 strcmp(
type,
"varchar") == 0 ||
488 strcmp(
type,
"char") == 0 ||
489 strcmp(
type,
"bpchar") == 0 ||
490 strcmp(
type,
"date") == 0 ||
491 strcmp(
type,
"timestamp") == 0)
492 is_char_type = 1;
493#ifdef DEBUG_QUERY
494 elog(
DEBUG4,
"check_foreign_key Debug value %s type %s %d",
495 nv,
type, is_char_type);
496#endif
497
498 /*
499 * is_char_type =1 i set ' ' for define a new value
500 */
501 snprintf(sql + strlen(sql),
sizeof(sql) - strlen(sql),
502 " %s = %s%s%s %s ",
503 args2[k], (is_char_type > 0) ? "'" : "",
504 nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
505 }
506 strcat(sql, " where ");
507 }
508 else
509 /* DELETE */
511 }
512
513 /*
514 * For 'S'etnull action we construct UPDATE query - UPDATE
515 * _referencing_relation_ SET Fkey1 null [, Fkey2 null [...]]
516 * WHERE Fkey1 = 1ドル [AND Fkey2 = 2ドル [...]] - to set key columns in
517 * all referencing tuples to NULL.
518 */
520 {
522 for (
i = 1;
i <= nkeys;
i++)
523 {
524 snprintf(sql + strlen(sql),
sizeof(sql) - strlen(sql),
525 "%s = null%s",
526 args2[
i], (
i < nkeys) ?
", " :
"");
527 }
528 strcat(sql, " where ");
529 }
530
531 /* Construct WHERE qual */
532 for (
i = 1;
i <= nkeys;
i++)
533 {
534 snprintf(sql + strlen(sql),
sizeof(sql) - strlen(sql),
"%s = $%d %s",
535 args2[
i],
i, (
i < nkeys) ?
"and " :
"");
536 }
537
538 /* Prepare plan for query */
540 if (pplan == NULL)
541 /* internal error */
543
544 /*
545 * Remember that SPI_prepare places plan in current memory context
546 * - so, we have to save plan in Top memory context for later use.
547 */
549 /* internal error */
550 elog(
ERROR,
"check_foreign_key: SPI_keepplan failed");
551
552 plan->splan[r] = pplan;
553
554 args2 += nkeys + 1; /* to the next relation */
555 }
556 plan->nplans = nrefs;
557#ifdef DEBUG_QUERY
558 elog(
DEBUG4,
"check_foreign_key Debug Query is : %s ", sql);
559#endif
560 }
561
562 /*
563 * If UPDATE and key is not changed ...
564 */
565 if (newtuple != NULL && isequal)
566 {
569 }
570
571 /*
572 * Ok, execute prepared plan(s).
573 */
574 for (r = 0; r < nrefs; r++)
575 {
576 /*
577 * For 'R'estrict we may to execute plan for one tuple only, for other
578 * actions - for all tuples.
579 */
580 int tcount = (
action ==
'r') ? 1 : 0;
581
583
585 /* we have no NULLs - so we pass ^^^^ here */
586
587 if (ret < 0)
589 (
errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
590 errmsg(
"SPI_execp returned %d", ret)));
591
592 /* If action is 'R'estrict ... */
594 {
595 /* If there is tuple returned by SELECT then ... */
598 (
errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
599 errmsg(
"\"%s\": tuple is referenced in \"%s\"",
601 }
602 else
603 {
604#ifdef REFINT_VERBOSE
605 const char *operation;
606
608 operation = is_update ? "updated" : "deleted";
609 else
610 operation = "set to null";
611
614#endif
615 }
616 args += nkeys + 1;
/* to the next relation */
617 }
618
620
622}
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
Assert(PointerIsAligned(start, uint64))
void * MemoryContextAlloc(MemoryContext context, Size size)
MemoryContext TopMemoryContext
int32 pg_strtoint32(const char *s)
unsigned char pg_ascii_tolower(unsigned char ch)
static Datum PointerGetDatum(const void *X)
static EPlan * find_plan(char *ident, EPlan **eplan, int *nplans)
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
char * SPI_gettype(TupleDesc tupdesc, int fnumber)
Oid SPI_gettypeid(TupleDesc tupdesc, int fnumber)
const char * SPI_result_code_string(int code)
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
int SPI_keepplan(SPIPlanPtr plan)
int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
char * SPI_getrelname(Relation rel)
static void * fn(void *arg)
#define CALLED_AS_TRIGGER(fcinfo)
#define TRIGGER_FIRED_FOR_ROW(event)
#define TRIGGER_FIRED_AFTER(event)
#define TRIGGER_FIRED_BY_INSERT(event)
#define TRIGGER_FIRED_BY_UPDATE(event)