LCOV - code coverage report
Current view: top level - contrib/spi - refint.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 277 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 6 0
Legend: Lines:     hit not hit

            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            0 : 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            0 : PG_FUNCTION_INFO_V1(check_primary_key);
      48              : 
      49              : Datum
      50            0 : check_primary_key(PG_FUNCTION_ARGS)
      51              : {
      52            0 :         TriggerData *trigdata = (TriggerData *) fcinfo->context;
      53            0 :         Trigger    *trigger;            /* to get trigger name */
      54            0 :         int                     nargs;                  /* # of args specified in CREATE TRIGGER */
      55            0 :         char      **args;                       /* arguments: column names and table name */
      56            0 :         int                     nkeys;                  /* # of key columns (= nargs / 2) */
      57            0 :         Datum      *kvals;                      /* key values */
      58            0 :         char       *relname;            /* referenced relation name */
      59            0 :         Relation        rel;                    /* triggered relation */
      60            0 :         HeapTuple       tuple = NULL;   /* tuple to return */
      61            0 :         TupleDesc       tupdesc;                /* tuple description */
      62            0 :         EPlan      *plan;                       /* prepared plan */
      63            0 :         Oid                *argtypes = NULL;    /* key types to prepare execution plan */
      64            0 :         bool            isnull;                 /* to know is some column NULL or not */
      65            0 :         char            ident[2 * NAMEDATALEN]; /* to identify myself */
      66            0 :         int                     ret;
      67            0 :         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            0 :         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            0 :         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            0 :         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            0 :         if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
      93            0 :                 tuple = trigdata->tg_trigtuple;
      94              : 
      95              :         /* Not should be called for DELETE */
      96            0 :         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            0 :                 tuple = trigdata->tg_newtuple;
     103              : 
     104            0 :         trigger = trigdata->tg_trigger;
     105            0 :         nargs = trigger->tgnargs;
     106            0 :         args = trigger->tgargs;
     107              : 
     108            0 :         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            0 :         nkeys = nargs / 2;
     113            0 :         relname = args[nkeys];
     114            0 :         rel = trigdata->tg_relation;
     115            0 :         tupdesc = rel->rd_att;
     116              : 
     117              :         /* Connect to SPI manager */
     118            0 :         SPI_connect();
     119              : 
     120              :         /*
     121              :          * We use SPI plan preparation feature, so allocate space to place key
     122              :          * values.
     123              :          */
     124            0 :         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            0 :         snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
     131            0 :         plan = find_plan(ident, &PPlans, &nPPlans);
     132              : 
     133              :         /* if there is no plan then allocate argtypes for preparation */
     134            0 :         if (plan->nplans <= 0)
     135            0 :                 argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
     136              : 
     137              :         /* For each column in key ... */
     138            0 :         for (i = 0; i < nkeys; i++)
     139              :         {
     140              :                 /* get index of column in tuple */
     141            0 :                 int                     fnumber = SPI_fnumber(tupdesc, args[i]);
     142              : 
     143              :                 /* Bad guys may give us un-existing column in CREATE TRIGGER */
     144            0 :                 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            0 :                 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            0 :                 if (isnull)
     159              :                 {
     160            0 :                         SPI_finish();
     161            0 :                         return PointerGetDatum(tuple);
     162              :                 }
     163              : 
     164            0 :                 if (plan->nplans <= 0)    /* Get typeId of column */
     165            0 :                         argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
     166            0 :         }
     167              : 
     168              :         /*
     169              :          * If we have to prepare plan ...
     170              :          */
     171            0 :         if (plan->nplans <= 0)
     172              :         {
     173            0 :                 SPIPlanPtr      pplan;
     174            0 :                 char            sql[8192];
     175              : 
     176              :                 /*
     177              :                  * Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 =
     178              :                  * $1 [AND Pkey2 = $2 [...]]
     179              :                  */
     180            0 :                 snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
     181            0 :                 for (i = 0; i < nkeys; i++)
     182              :                 {
     183            0 :                         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
     184            0 :                                          args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
     185            0 :                 }
     186              : 
     187              :                 /* Prepare plan for query */
     188            0 :                 pplan = SPI_prepare(sql, nkeys, argtypes);
     189            0 :                 if (pplan == NULL)
     190              :                         /* internal error */
     191            0 :                         elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
     192              : 
     193              :                 /*
     194              :                  * Remember that SPI_prepare places plan in current memory context -
     195              :                  * so, we have to save plan in TopMemoryContext for later use.
     196              :                  */
     197            0 :                 if (SPI_keepplan(pplan))
     198              :                         /* internal error */
     199            0 :                         elog(ERROR, "check_primary_key: SPI_keepplan failed");
     200            0 :                 plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
     201              :                                                                                                                 sizeof(SPIPlanPtr));
     202            0 :                 *(plan->splan) = pplan;
     203            0 :                 plan->nplans = 1;
     204            0 :         }
     205              : 
     206              :         /*
     207              :          * Ok, execute prepared plan.
     208              :          */
     209            0 :         ret = SPI_execp(*(plan->splan), kvals, NULL, 1);
     210              :         /* we have no NULLs - so we pass   ^^^^   here */
     211              : 
     212            0 :         if (ret < 0)
     213              :                 /* internal error */
     214            0 :                 elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
     215              : 
     216              :         /*
     217              :          * If there are no tuples returned by SELECT then ...
     218              :          */
     219            0 :         if (SPI_processed == 0)
     220            0 :                 ereport(ERROR,
     221              :                                 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
     222              :                                  errmsg("tuple references non-existent key"),
     223              :                                  errdetail("Trigger \"%s\" found tuple referencing non-existent key in \"%s\".", trigger->tgname, relname)));
     224              : 
     225            0 :         SPI_finish();
     226              : 
     227            0 :         return PointerGetDatum(tuple);
     228            0 : }
     229              : 
     230              : /*
     231              :  * check_foreign_key () -- check that key in tuple being deleted/updated
     232              :  *                       is not referenced by tuples in "foreign" table(s).
     233              :  * Though it's called without args You have to specify (while creating trigger):
     234              :  * number of references, action to do if key referenced
     235              :  * ('restrict' | 'setnull' | 'cascade'), key field names in triggered
     236              :  * ("primary") table and referencing table(s)/keys:
     237              :  * EXECUTE PROCEDURE
     238              :  * check_foreign_key (2, 'restrict', 'Pkey1', 'Pkey2',
     239              :  * 'Ftable1', 'Fkey11', 'Fkey12', 'Ftable2', 'Fkey21', 'Fkey22').
     240              :  */
     241              : 
     242            0 : PG_FUNCTION_INFO_V1(check_foreign_key);
     243              : 
     244              : Datum
     245            0 : check_foreign_key(PG_FUNCTION_ARGS)
     246              : {
     247            0 :         TriggerData *trigdata = (TriggerData *) fcinfo->context;
     248            0 :         Trigger    *trigger;            /* to get trigger name */
     249            0 :         int                     nargs;                  /* # of args specified in CREATE TRIGGER */
     250            0 :         char      **args;                       /* arguments: as described above */
     251            0 :         char      **args_temp;
     252            0 :         int                     nrefs;                  /* number of references (== # of plans) */
     253            0 :         char            action;                 /* 'R'estrict | 'S'etnull | 'C'ascade */
     254            0 :         int                     nkeys;                  /* # of key columns */
     255            0 :         Datum      *kvals;                      /* key values */
     256            0 :         char       *relname;            /* referencing relation name */
     257            0 :         Relation        rel;                    /* triggered relation */
     258            0 :         HeapTuple       trigtuple = NULL;       /* tuple to being changed */
     259            0 :         HeapTuple       newtuple = NULL;        /* tuple to return */
     260            0 :         TupleDesc       tupdesc;                /* tuple description */
     261            0 :         EPlan      *plan;                       /* prepared plan(s) */
     262            0 :         Oid                *argtypes = NULL;    /* key types to prepare execution plan */
     263            0 :         bool            isnull;                 /* to know is some column NULL or not */
     264            0 :         bool            isequal = true; /* are keys in both tuples equal (in UPDATE) */
     265            0 :         char            ident[2 * NAMEDATALEN]; /* to identify myself */
     266            0 :         int                     is_update = 0;
     267            0 :         int                     ret;
     268            0 :         int                     i,
     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 ? */
     280            0 :         if (!CALLED_AS_TRIGGER(fcinfo))
     281              :                 /* internal error */
     282            0 :                 elog(ERROR, "check_foreign_key: not fired by trigger manager");
     283              : 
     284              :         /* Should be called for ROW trigger */
     285            0 :         if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
     286              :                 /* internal error */
     287            0 :                 elog(ERROR, "check_foreign_key: must be fired for row");
     288              : 
     289              :         /* Not should be called for INSERT */
     290            0 :         if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
     291              :                 /* internal error */
     292            0 :                 elog(ERROR, "check_foreign_key: cannot process INSERT events");
     293              : 
     294            0 :         if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
     295              :                 /* internal error */
     296            0 :                 elog(ERROR, "check_foreign_key: must be fired by AFTER trigger");
     297              : 
     298              :         /* Have to check tg_trigtuple - tuple being deleted */
     299            0 :         trigtuple = trigdata->tg_trigtuple;
     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            0 :         is_update = 0;
     306            0 :         if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
     307              :         {
     308            0 :                 newtuple = trigdata->tg_newtuple;
     309            0 :                 is_update = 1;
     310            0 :         }
     311            0 :         trigger = trigdata->tg_trigger;
     312            0 :         nargs = trigger->tgnargs;
     313            0 :         args = trigger->tgargs;
     314              : 
     315            0 :         if (nargs < 5)                               /* nrefs, action, key, Relation, key - at
     316              :                                                                  * least */
     317              :                 /* internal error */
     318            0 :                 elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
     319              : 
     320            0 :         nrefs = pg_strtoint32(args[0]);
     321            0 :         if (nrefs < 1)
     322              :                 /* internal error */
     323            0 :                 elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
     324            0 :         action = pg_ascii_tolower((unsigned char) *(args[1]));
     325            0 :         if (action != 'r' && action != 'c' && action != 's')
     326              :                 /* internal error */
     327            0 :                 elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
     328            0 :         nargs -= 2;
     329            0 :         args += 2;
     330            0 :         nkeys = (nargs - nrefs) / (nrefs + 1);
     331            0 :         if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
     332              :                 /* internal error */
     333            0 :                 elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
     334              :                          nargs + 2, nrefs);
     335              : 
     336            0 :         rel = trigdata->tg_relation;
     337            0 :         tupdesc = rel->rd_att;
     338              : 
     339              :         /* Connect to SPI manager */
     340            0 :         SPI_connect();
     341              : 
     342              :         /*
     343              :          * We use SPI plan preparation feature, so allocate space to place key
     344              :          * values.
     345              :          */
     346            0 :         kvals = (Datum *) palloc(nkeys * sizeof(Datum));
     347              : 
     348              :         /*
     349              :          * Construct ident string as TriggerName $ TriggeredRelationId $
     350              :          * OperationType and try to find prepared execution plan(s).
     351              :          */
     352            0 :         snprintf(ident, sizeof(ident), "%s$%u$%c", trigger->tgname, rel->rd_id, is_update ? 'U' : 'D');
     353            0 :         plan = find_plan(ident, &FPlans, &nFPlans);
     354              : 
     355              :         /* if there is no plan(s) then allocate argtypes for preparation */
     356            0 :         if (plan->nplans <= 0)
     357            0 :                 argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
     358              : 
     359              :         /*
     360              :          * else - check that we have exactly nrefs plan(s) ready
     361              :          */
     362            0 :         else if (plan->nplans != nrefs)
     363              :                 /* internal error */
     364            0 :                 elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime",
     365              :                          trigger->tgname);
     366              : 
     367              :         /* For each column in key ... */
     368            0 :         for (i = 0; i < nkeys; i++)
     369              :         {
     370              :                 /* get index of column in tuple */
     371            0 :                 int                     fnumber = SPI_fnumber(tupdesc, args[i]);
     372              : 
     373              :                 /* Bad guys may give us un-existing column in CREATE TRIGGER */
     374            0 :                 if (fnumber <= 0)
     375            0 :                         ereport(ERROR,
     376              :                                         (errcode(ERRCODE_UNDEFINED_COLUMN),
     377              :                                          errmsg("there is no attribute \"%s\" in relation \"%s\"",
     378              :                                                         args[i], SPI_getrelname(rel))));
     379              : 
     380              :                 /* Well, get binary (in internal format) value of column */
     381            0 :                 kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
     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            0 :                 if (isnull)
     389              :                 {
     390            0 :                         SPI_finish();
     391            0 :                         return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
     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            0 :                 if (newtuple != NULL)
     400              :                 {
     401            0 :                         char       *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
     402            0 :                         char       *newval;
     403              : 
     404              :                         /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
     405            0 :                         if (oldval == NULL)
     406              :                                 /* internal error */
     407            0 :                                 elog(ERROR, "check_foreign_key: SPI_getvalue returned %s", SPI_result_code_string(SPI_result));
     408            0 :                         newval = SPI_getvalue(newtuple, tupdesc, fnumber);
     409            0 :                         if (newval == NULL || strcmp(oldval, newval) != 0)
     410            0 :                                 isequal = false;
     411            0 :                 }
     412              : 
     413            0 :                 if (plan->nplans <= 0)    /* Get typeId of column */
     414            0 :                         argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
     415            0 :         }
     416            0 :         args_temp = args;
     417            0 :         nargs -= nkeys;
     418            0 :         args += nkeys;
     419              : 
     420              :         /*
     421              :          * If we have to prepare plans ...
     422              :          */
     423            0 :         if (plan->nplans <= 0)
     424              :         {
     425            0 :                 SPIPlanPtr      pplan;
     426            0 :                 char            sql[8192];
     427            0 :                 char      **args2 = args;
     428              : 
     429            0 :                 plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
     430            0 :                                                                                                                 nrefs * sizeof(SPIPlanPtr));
     431              : 
     432            0 :                 for (r = 0; r < nrefs; r++)
     433              :                 {
     434            0 :                         relname = args2[0];
     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              :                          */
     446            0 :                         if (action == 'r')
     447              : 
     448            0 :                                 snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
     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              : 
     467            0 :                         else if (action == 'c')
     468              :                         {
     469            0 :                                 if (is_update == 1)
     470              :                                 {
     471            0 :                                         int                     fn;
     472            0 :                                         char       *nv;
     473            0 :                                         int                     k;
     474              : 
     475            0 :                                         snprintf(sql, sizeof(sql), "update %s set ", relname);
     476            0 :                                         for (k = 1; k <= nkeys; k++)
     477              :                                         {
     478            0 :                                                 int                     is_char_type = 0;
     479            0 :                                                 char       *type;
     480              : 
     481            0 :                                                 fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
     482            0 :                                                 Assert(fn > 0); /* already checked above */
     483            0 :                                                 nv = SPI_getvalue(newtuple, tupdesc, fn);
     484            0 :                                                 type = SPI_gettype(tupdesc, fn);
     485              : 
     486            0 :                                                 if (strcmp(type, "text") == 0 ||
     487            0 :                                                         strcmp(type, "varchar") == 0 ||
     488            0 :                                                         strcmp(type, "char") == 0 ||
     489            0 :                                                         strcmp(type, "bpchar") == 0 ||
     490            0 :                                                         strcmp(type, "date") == 0 ||
     491            0 :                                                         strcmp(type, "timestamp") == 0)
     492            0 :                                                         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            0 :                                                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
     502              :                                                                  " %s = %s%s%s %s ",
     503            0 :                                                                  args2[k], (is_char_type > 0) ? "'" : "",
     504            0 :                                                                  nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
     505            0 :                                         }
     506            0 :                                         strcat(sql, " where ");
     507            0 :                                 }
     508              :                                 else
     509              :                                         /* DELETE */
     510            0 :                                         snprintf(sql, sizeof(sql), "delete from %s where ", relname);
     511            0 :                         }
     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              :                          */
     519            0 :                         else if (action == 's')
     520              :                         {
     521            0 :                                 snprintf(sql, sizeof(sql), "update %s set ", relname);
     522            0 :                                 for (i = 1; i <= nkeys; i++)
     523              :                                 {
     524            0 :                                         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
     525              :                                                          "%s = null%s",
     526            0 :                                                          args2[i], (i < nkeys) ? ", " : "");
     527            0 :                                 }
     528            0 :                                 strcat(sql, " where ");
     529            0 :                         }
     530              : 
     531              :                         /* Construct WHERE qual */
     532            0 :                         for (i = 1; i <= nkeys; i++)
     533              :                         {
     534            0 :                                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
     535            0 :                                                  args2[i], i, (i < nkeys) ? "and " : "");
     536            0 :                         }
     537              : 
     538              :                         /* Prepare plan for query */
     539            0 :                         pplan = SPI_prepare(sql, nkeys, argtypes);
     540            0 :                         if (pplan == NULL)
     541              :                                 /* internal error */
     542            0 :                                 elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
     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              :                          */
     548            0 :                         if (SPI_keepplan(pplan))
     549              :                                 /* internal error */
     550            0 :                                 elog(ERROR, "check_foreign_key: SPI_keepplan failed");
     551              : 
     552            0 :                         plan->splan[r] = pplan;
     553              : 
     554            0 :                         args2 += nkeys + 1; /* to the next relation */
     555            0 :                 }
     556            0 :                 plan->nplans = nrefs;
     557              : #ifdef  DEBUG_QUERY
     558              :                 elog(DEBUG4, "check_foreign_key Debug Query is :  %s ", sql);
     559              : #endif
     560            0 :         }
     561              : 
     562              :         /*
     563              :          * If UPDATE and key is not changed ...
     564              :          */
     565            0 :         if (newtuple != NULL && isequal)
     566              :         {
     567            0 :                 SPI_finish();
     568            0 :                 return PointerGetDatum(newtuple);
     569              :         }
     570              : 
     571              :         /*
     572              :          * Ok, execute prepared plan(s).
     573              :          */
     574            0 :         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            0 :                 int                     tcount = (action == 'r') ? 1 : 0;
     581              : 
     582            0 :                 relname = args[0];
     583              : 
     584            0 :                 ret = SPI_execp(plan->splan[r], kvals, NULL, tcount);
     585              :                 /* we have no NULLs - so we pass   ^^^^  here */
     586              : 
     587            0 :                 if (ret < 0)
     588            0 :                         ereport(ERROR,
     589              :                                         (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
     590              :                                          errmsg("SPI_execp returned %d", ret)));
     591              : 
     592              :                 /* If action is 'R'estrict ... */
     593            0 :                 if (action == 'r')
     594              :                 {
     595              :                         /* If there is tuple returned by SELECT then ... */
     596            0 :                         if (SPI_processed > 0)
     597            0 :                                 ereport(ERROR,
     598              :                                                 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
     599              :                                                  errmsg("\"%s\": tuple is referenced in \"%s\"",
     600              :                                                                 trigger->tgname, relname)));
     601            0 :                 }
     602              :                 else
     603              :                 {
     604              : #ifdef REFINT_VERBOSE
     605            0 :                         const char *operation;
     606              : 
     607            0 :                         if (action == 'c')
     608            0 :                                 operation = is_update ? "updated" : "deleted";
     609              :                         else
     610            0 :                                 operation = "set to null";
     611              : 
     612            0 :                         elog(NOTICE, "%s: " UINT64_FORMAT " tuple(s) of %s are %s",
     613              :                                  trigger->tgname, SPI_processed, relname, operation);
     614              : #endif
     615            0 :                 }
     616            0 :                 args += nkeys + 1;              /* to the next relation */
     617            0 :         }
     618              : 
     619            0 :         SPI_finish();
     620              : 
     621            0 :         return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
     622            0 : }
     623              : 
     624              : static EPlan *
     625            0 : find_plan(char *ident, EPlan **eplan, int *nplans)
     626              : {
     627            0 :         EPlan      *newp;
     628            0 :         int                     i;
     629            0 :         MemoryContext oldcontext;
     630              : 
     631              :         /*
     632              :          * All allocations done for the plans need to happen in a session-safe
     633              :          * context.
     634              :          */
     635            0 :         oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     636              : 
     637            0 :         if (*nplans > 0)
     638              :         {
     639            0 :                 for (i = 0; i < *nplans; i++)
     640              :                 {
     641            0 :                         if (strcmp((*eplan)[i].ident, ident) == 0)
     642            0 :                                 break;
     643            0 :                 }
     644            0 :                 if (i != *nplans)
     645              :                 {
     646            0 :                         MemoryContextSwitchTo(oldcontext);
     647            0 :                         return (*eplan + i);
     648              :                 }
     649            0 :                 *eplan = (EPlan *) repalloc(*eplan, (i + 1) * sizeof(EPlan));
     650            0 :                 newp = *eplan + i;
     651            0 :         }
     652              :         else
     653              :         {
     654            0 :                 newp = *eplan = palloc_object(EPlan);
     655            0 :                 (*nplans) = i = 0;
     656              :         }
     657              : 
     658            0 :         newp->ident = pstrdup(ident);
     659            0 :         newp->nplans = 0;
     660            0 :         newp->splan = NULL;
     661            0 :         (*nplans)++;
     662              : 
     663            0 :         MemoryContextSwitchTo(oldcontext);
     664            0 :         return newp;
     665            0 : }
        

Generated by: LCOV version 2.3.2-1