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

Generated by: LCOV version 2.0-1