LCOV - differential code coverage report
Current view: top level - contrib/spi - refint.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: 380a8b2ea024c33a35e7abc8628e7c4f52f9f9f9 vs db5ed03217b9c238703df8b4b286115d6e940488 Lines: 83.5 % 212 177 35 2 175 2
Current Date: 2026-05-29 21:51:00 -0400 Functions: 100.0 % 6 6 2 4
Baseline: lcov-20260530-034037-baseline Branches: 53.5 % 198 106 92 106
Baseline Date: 2026-05-29 14:39:03 -0700 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 82.1 % 28 23 5 23
(30,360] days: 100.0 % 2 2 2
(360..) days: 83.5 % 182 152 30 152
Function coverage date bins:
(360..) days: 100.0 % 6 6 2 4
Branch coverage date bins:
(7,30] days: 75.0 % 12 9 3 9
(360..) days: 52.2 % 186 97 89 97

 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/memutils.h"
                                 16                 :                : #include "utils/rel.h"
                                 17                 :                : 
  430 tgl@sss.pgh.pa.us          18                 :CBC           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                 :                : 
 9322                            47                 :              2 : PG_FUNCTION_INFO_V1(check_primary_key);
                                 48                 :                : 
                                 49                 :                : Datum
 9497                            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 */
10488 vadim4o@yahoo.com          60                 :             19 :     HeapTuple   tuple = NULL;   /* tuple to return */
                                 61                 :                :     TupleDesc   tupdesc;        /* tuple description */
                                 62                 :                :     EPlan      *plan;           /* prepared plan */
 8980 bruce@momjian.us           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 ? */
 9497 tgl@sss.pgh.pa.us          78   [ +  -  -  + ]:             19 :     if (!CALLED_AS_TRIGGER(fcinfo))
                                 79                 :                :         /* internal error */
 9497 tgl@sss.pgh.pa.us          80         [ #  # ]:UBC           0 :         elog(ERROR, "check_primary_key: not fired by trigger manager");
                                 81                 :                : 
                                 82                 :                :     /* Should be called for ROW trigger */
 5713 tgl@sss.pgh.pa.us          83         [ -  + ]:CBC          19 :     if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
                                 84                 :                :         /* internal error */
 5713 tgl@sss.pgh.pa.us          85         [ #  # ]:UBC           0 :         elog(ERROR, "check_primary_key: must be fired for row");
                                 86                 :                : 
  418 tgl@sss.pgh.pa.us          87         [ -  + ]:CBC          19 :     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
                                 88                 :                :         /* internal error */
  418 tgl@sss.pgh.pa.us          89         [ #  # ]:UBC           0 :         elog(ERROR, "check_primary_key: must be fired by AFTER trigger");
                                 90                 :                : 
                                 91                 :                :     /* If INSERTion then must check Tuple to being inserted */
 9497 tgl@sss.pgh.pa.us          92         [ +  + ]:CBC          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 */
 7058 bruce@momjian.us           98         [ #  # ]:UBC           0 :         elog(ERROR, "check_primary_key: cannot process DELETE events");
                                 99                 :                : 
                                100                 :                :     /* If UPDATE, then must check new Tuple, not old one */
                                101                 :                :     else
 9497 tgl@sss.pgh.pa.us         102                 :CBC           3 :         tuple = trigdata->tg_newtuple;
                                103                 :                : 
                                104                 :             19 :     trigger = trigdata->tg_trigger;
10488 vadim4o@yahoo.com         105                 :             19 :     nargs = trigger->tgnargs;
                                106                 :             19 :     args = trigger->tgargs;
                                107                 :                : 
 9882 bruce@momjian.us          108         [ -  + ]:             19 :     if (nargs % 2 != 1)         /* odd number of arguments! */
                                109                 :                :         /* internal error */
 9882 bruce@momjian.us          110         [ #  # ]:UBC           0 :         elog(ERROR, "check_primary_key: odd number of arguments should be specified");
                                111                 :                : 
 9882 bruce@momjian.us          112                 :CBC          19 :     nkeys = nargs / 2;
10488 vadim4o@yahoo.com         113                 :             19 :     relname = args[nkeys];
 9497 tgl@sss.pgh.pa.us         114                 :             19 :     rel = trigdata->tg_relation;
10488 vadim4o@yahoo.com         115                 :             19 :     tupdesc = rel->rd_att;
                                116                 :                : 
                                117                 :                :     /* Connect to SPI manager */
  628 tgl@sss.pgh.pa.us         118                 :             19 :     SPI_connect();
                                119                 :                : 
                                120                 :                :     /*
                                121                 :                :      * We use SPI plan preparation feature, so allocate space to place key
                                122                 :                :      * values.
                                123                 :                :      */
10487 vadim4o@yahoo.com         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                 :                :      */
 8671 bruce@momjian.us          130                 :             19 :     snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
10488 vadim4o@yahoo.com         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 */
 3490 tgl@sss.pgh.pa.us         144         [ -  + ]:             31 :         if (fnumber <= 0)
 8346 tgl@sss.pgh.pa.us         145         [ #  # ]:UBC           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 */
10488 vadim4o@yahoo.com         151                 :CBC          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                 :                :         {
10488 vadim4o@yahoo.com         160                 :UBC           0 :             SPI_finish();
 9497 tgl@sss.pgh.pa.us         161                 :              0 :             return PointerGetDatum(tuple);
                                162                 :                :         }
                                163                 :                : 
10488 vadim4o@yahoo.com         164         [ +  + ]:CBC          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                 :                : 
   19 nathan@postgresql.or      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);
10488 vadim4o@yahoo.com         192         [ -  + ]:              3 :         if (pplan == NULL)
                                193                 :                :             /* internal error */
 3195 peter_e@gmx.net           194         [ #  # ]:UBC           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                 :                :          */
 5370 tgl@sss.pgh.pa.us         200         [ -  + ]:CBC           3 :         if (SPI_keepplan(pplan))
                                201                 :                :             /* internal error */
 5370 tgl@sss.pgh.pa.us         202         [ #  # ]:UBC           0 :             elog(ERROR, "check_primary_key: SPI_keepplan failed");
 2334 michael@paquier.xyz       203                 :CBC           3 :         plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
                                204                 :                :                                                         sizeof(SPIPlanPtr));
10488 vadim4o@yahoo.com         205                 :              3 :         *(plan->splan) = pplan;
                                206                 :              3 :         plan->nplans = 1;
                                207                 :                : 
   19 nathan@postgresql.or      208                 :              3 :         pfree(sql.data);
                                209                 :                :     }
                                210                 :                : 
                                211                 :                :     /*
                                212                 :                :      * Ok, execute prepared plan.
                                213                 :                :      */
10488 vadim4o@yahoo.com         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 */
10371 bruce@momjian.us          219         [ #  # ]:UBC           0 :         elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
                                220                 :                : 
                                221                 :                :     /*
                                222                 :                :      * If there are no tuples returned by SELECT then ...
                                223                 :                :      */
 9882 bruce@momjian.us          224         [ +  + ]:CBC          19 :     if (SPI_processed == 0)
 8346 tgl@sss.pgh.pa.us         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                 :                : 
10488 vadim4o@yahoo.com         230                 :             16 :     SPI_finish();
                                231                 :                : 
 9497 tgl@sss.pgh.pa.us         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                 :                : 
 9322                           247                 :              2 : PG_FUNCTION_INFO_V1(check_foreign_key);
                                248                 :                : 
                                249                 :                : Datum
 9497                           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 */
 3265                           263                 :              6 :     HeapTuple   trigtuple = NULL;   /* tuple to being changed */
 8980 bruce@momjian.us          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 */
 7532                           269                 :              6 :     bool        isequal = true; /* are keys in both tuples equal (in UPDATE) */
                                270                 :                :     char        ident[2 * NAMEDATALEN]; /* to identify myself */
 9867                           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 ? */
 9497 tgl@sss.pgh.pa.us         285   [ +  -  -  + ]:              6 :     if (!CALLED_AS_TRIGGER(fcinfo))
                                286                 :                :         /* internal error */
 9497 tgl@sss.pgh.pa.us         287         [ #  # ]:UBC           0 :         elog(ERROR, "check_foreign_key: not fired by trigger manager");
                                288                 :                : 
                                289                 :                :     /* Should be called for ROW trigger */
 5713 tgl@sss.pgh.pa.us         290         [ -  + ]:CBC           6 :     if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
                                291                 :                :         /* internal error */
 5713 tgl@sss.pgh.pa.us         292         [ #  # ]:UBC           0 :         elog(ERROR, "check_foreign_key: must be fired for row");
                                293                 :                : 
                                294                 :                :     /* Not should be called for INSERT */
 9497 tgl@sss.pgh.pa.us         295         [ -  + ]:CBC           6 :     if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
                                296                 :                :         /* internal error */
 7058 bruce@momjian.us          297         [ #  # ]:UBC           0 :         elog(ERROR, "check_foreign_key: cannot process INSERT events");
                                298                 :                : 
  418 tgl@sss.pgh.pa.us         299         [ -  + ]:CBC           6 :     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
                                300                 :                :         /* internal error */
  418 tgl@sss.pgh.pa.us         301         [ #  # ]:UBC           0 :         elog(ERROR, "check_foreign_key: must be fired by AFTER trigger");
                                302                 :                : 
                                303                 :                :     /* Have to check tg_trigtuple - tuple being deleted */
 9497 tgl@sss.pgh.pa.us         304                 :CBC           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                 :                :      */
 9867 bruce@momjian.us          310                 :              6 :     is_update = 0;
 9497 tgl@sss.pgh.pa.us         311         [ +  + ]:              6 :     if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
                                312                 :                :     {
                                313                 :              2 :         newtuple = trigdata->tg_newtuple;
 9867 bruce@momjian.us          314                 :              2 :         is_update = 1;
                                315                 :                :     }
 9497 tgl@sss.pgh.pa.us         316                 :              6 :     trigger = trigdata->tg_trigger;
10488 vadim4o@yahoo.com         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 */
10371 bruce@momjian.us          323         [ #  # ]:UBC           0 :         elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
                                324                 :                : 
 2869 andres@anarazel.de        325                 :CBC           6 :     nrefs = pg_strtoint32(args[0]);
10488 vadim4o@yahoo.com         326         [ -  + ]:              6 :     if (nrefs < 1)
                                327                 :                :         /* internal error */
10371 bruce@momjian.us          328         [ #  # ]:UBC           0 :         elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
  333 jdavis@postgresql.or      329                 :GNC           6 :     action = pg_ascii_tolower((unsigned char) *(args[1]));
10488 vadim4o@yahoo.com         330   [ +  +  -  +  :CBC           6 :     if (action != 'r' && action != 'c' && action != 's')
                                              -  - ]
                                331                 :                :         /* internal error */
10371 bruce@momjian.us          332         [ #  # ]:UBC           0 :         elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
10488 vadim4o@yahoo.com         333                 :CBC           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 */
10371 bruce@momjian.us          338         [ #  # ]:UBC           0 :         elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
                                339                 :                :              nargs + 2, nrefs);
                                340                 :                : 
 9497 tgl@sss.pgh.pa.us         341                 :CBC           6 :     rel = trigdata->tg_relation;
10488 vadim4o@yahoo.com         342                 :              6 :     tupdesc = rel->rd_att;
                                343                 :                : 
                                344                 :                :     /* Connect to SPI manager */
  628 tgl@sss.pgh.pa.us         345                 :              6 :     SPI_connect();
                                346                 :                : 
                                347                 :                :     /*
                                348                 :                :      * We use SPI plan preparation feature, so allocate space to place key
                                349                 :                :      * values.
                                350                 :                :      */
10487 vadim4o@yahoo.com         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                 :                :      */
  418 tgl@sss.pgh.pa.us         357         [ +  + ]:              6 :     snprintf(ident, sizeof(ident), "%s$%u$%c", trigger->tgname, rel->rd_id, is_update ? 'U' : 'D');
10488 vadim4o@yahoo.com         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 */
10371 bruce@momjian.us          369         [ #  # ]:UBC           0 :         elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime",
                                370                 :                :              trigger->tgname);
                                371                 :                : 
                                372                 :                :     /* For each column in key ... */
10488 vadim4o@yahoo.com         373         [ +  + ]:CBC          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 */
 3490 tgl@sss.pgh.pa.us         379         [ -  + ]:              9 :         if (fnumber <= 0)
 8346 tgl@sss.pgh.pa.us         380         [ #  # ]:UBC           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 */
10488 vadim4o@yahoo.com         386                 :CBC           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                 :                :         {
10488 vadim4o@yahoo.com         395                 :UBC           0 :             SPI_finish();
 9497 tgl@sss.pgh.pa.us         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                 :                :          */
10488 vadim4o@yahoo.com         404         [ +  + ]:CBC           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 */
 3195 peter_e@gmx.net           412         [ #  # ]:UBC           0 :                 elog(ERROR, "check_foreign_key: SPI_getvalue returned %s", SPI_result_code_string(SPI_result));
10488 vadim4o@yahoo.com         413                 :CBC           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                 :                :     }
 9867 bruce@momjian.us          421                 :              6 :     args_temp = args;
10488 vadim4o@yahoo.com         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;
 9867 bruce@momjian.us          431                 :              4 :         char      **args2 = args;
                                432                 :                : 
 2334 michael@paquier.xyz       433                 :              4 :         plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
                                434                 :                :                                                         nrefs * sizeof(SPIPlanPtr));
                                435                 :                : 
10488 vadim4o@yahoo.com         436         [ +  + ]:             10 :         for (r = 0; r < nrefs; r++)
                                437                 :                :         {
                                438                 :                :             StringInfoData sql;
                                439                 :                : 
   19 nathan@postgresql.or      440                 :              6 :             initStringInfo(&sql);
                                441                 :                : 
10488 vadim4o@yahoo.com         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')
   19 nathan@postgresql.or      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                 :                : 
 9867 bruce@momjian.us          474         [ +  - ]:              4 :             else if (action == 'c')
                                475                 :                :             {
                                476         [ +  + ]:              4 :                 if (is_update == 1)
                                477                 :                :                 {
                                478                 :                :                     int         fn;
                                479                 :                :                     char       *nv;
                                480                 :                :                     int         k;
                                481                 :                : 
   19 nathan@postgresql.or      482                 :              2 :                     appendStringInfo(&sql, "update %s set ", relname);
 9867 bruce@momjian.us          483         [ +  + ]:              6 :                     for (k = 1; k <= nkeys; k++)
                                484                 :                :                     {
                                485                 :              4 :                         fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
 3490 tgl@sss.pgh.pa.us         486         [ -  + ]:              4 :                         Assert(fn > 0); /* already checked above */
 9867 bruce@momjian.us          487                 :              4 :                         nv = SPI_getvalue(newtuple, tupdesc, fn);
                                488                 :                : 
   19 nathan@postgresql.or      489         [ +  - ]:              4 :                         appendStringInfo(&sql, " %s = %s ",
   16                           490                 :              4 :                                          args2[k],
                                491                 :              4 :                                          nv ? quote_literal_cstr(nv) : "NULL");
   19                           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                 :                :              */
10488 vadim4o@yahoo.com         508         [ #  # ]:UBC           0 :             else if (action == 's')
                                509                 :                :             {
   19 nathan@postgresql.or      510                 :              0 :                 appendStringInfo(&sql, "update %s set ", relname);
10488 vadim4o@yahoo.com         511         [ #  # ]:              0 :                 for (i = 1; i <= nkeys; i++)
                                512                 :                :                 {
   19 nathan@postgresql.or      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 */
10488 vadim4o@yahoo.com         521         [ +  + ]:CBC          16 :             for (i = 1; i <= nkeys; i++)
                                522                 :                :             {
   19 nathan@postgresql.or      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);
10488 vadim4o@yahoo.com         530         [ -  + ]:              6 :             if (pplan == NULL)
                                531                 :                :                 /* internal error */
 3195 peter_e@gmx.net           532         [ #  # ]:UBC           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                 :                :              */
 5370 tgl@sss.pgh.pa.us         538         [ -  + ]:CBC           6 :             if (SPI_keepplan(pplan))
                                539                 :                :                 /* internal error */
 5370 tgl@sss.pgh.pa.us         540         [ #  # ]:UBC           0 :                 elog(ERROR, "check_foreign_key: SPI_keepplan failed");
                                541                 :                : 
10488 vadim4o@yahoo.com         542                 :CBC           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                 :                : 
   19 nathan@postgresql.or      550                 :              6 :             pfree(sql.data);
                                551                 :                :         }
10488 vadim4o@yahoo.com         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();
 9497 tgl@sss.pgh.pa.us         561                 :              1 :         return PointerGetDatum(newtuple);
                                562                 :                :     }
                                563                 :                : 
                                564                 :                :     /*
                                565                 :                :      * Ok, execute prepared plan(s).
                                566                 :                :      */
10488 vadim4o@yahoo.com         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)
 8346 tgl@sss.pgh.pa.us         581         [ #  # ]:UBC           0 :             ereport(ERROR,
                                582                 :                :                     (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
                                583                 :                :                      errmsg("SPI_execp returned %d", ret)));
                                584                 :                : 
                                585                 :                :         /* If action is 'R'estrict ... */
10488 vadim4o@yahoo.com         586         [ +  + ]:CBC           7 :         if (action == 'r')
                                587                 :                :         {
                                588                 :                :             /* If there is tuple returned by SELECT then ... */
                                589         [ +  + ]:              2 :             if (SPI_processed > 0)
 8346 tgl@sss.pgh.pa.us         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                 :                : 
  418                           600         [ +  - ]:              5 :             if (action == 'c')
                                601         [ +  + ]:              5 :                 operation = is_update ? "updated" : "deleted";
                                602                 :                :             else
  418 tgl@sss.pgh.pa.us         603                 :UBC           0 :                 operation = "set to null";
                                604                 :                : 
 3731 tgl@sss.pgh.pa.us         605         [ +  - ]:CBC           5 :             elog(NOTICE, "%s: " UINT64_FORMAT " tuple(s) of %s are %s",
                                606                 :                :                  trigger->tgname, SPI_processed, relname, operation);
                                607                 :                : #endif
                                608                 :                :         }
10488 vadim4o@yahoo.com         609                 :              6 :         args += nkeys + 1;      /* to the next relation */
                                610                 :                :     }
                                611                 :                : 
                                612                 :              3 :     SPI_finish();
                                613                 :                : 
 9497 tgl@sss.pgh.pa.us         614         [ +  + ]:              3 :     return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
                                615                 :                : }
                                616                 :                : 
                                617                 :                : static EPlan *
 6197 bruce@momjian.us          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                 :                :      */
 2334 michael@paquier.xyz       628                 :             25 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
                                629                 :                : 
10488 vadim4o@yahoo.com         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                 :                :         {
 2334 michael@paquier.xyz       639                 :             18 :             MemoryContextSwitchTo(oldcontext);
10488 vadim4o@yahoo.com         640                 :             18 :             return (*eplan + i);
                                641                 :                :         }
 2334 michael@paquier.xyz       642                 :              5 :         *eplan = (EPlan *) repalloc(*eplan, (i + 1) * sizeof(EPlan));
10488 vadim4o@yahoo.com         643                 :              5 :         newp = *eplan + i;
                                644                 :                :     }
                                645                 :                :     else
                                646                 :                :     {
  176 michael@paquier.xyz       647                 :GNC           2 :         newp = *eplan = palloc_object(EPlan);
10488 vadim4o@yahoo.com         648                 :CBC           2 :         (*nplans) = i = 0;
                                649                 :                :     }
                                650                 :                : 
 2334 michael@paquier.xyz       651                 :              7 :     newp->ident = pstrdup(ident);
10488 vadim4o@yahoo.com         652                 :              7 :     newp->nplans = 0;
                                653                 :              7 :     newp->splan = NULL;
                                654                 :              7 :     (*nplans)++;
                                655                 :                : 
 2334 michael@paquier.xyz       656                 :              7 :     MemoryContextSwitchTo(oldcontext);
 3208 peter_e@gmx.net           657                 :              7 :     return newp;
                                658                 :                : }
        

Generated by: LCOV version 2.5.0-beta