LCOV - differential code coverage report
Current view: top level - contrib/spi - refint.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: d36b728949bf4e37ada1cd23e0f2aaa94f609a70 vs 52e118fe2f7e3381bdaa479816a7f72eda2ae517 Lines: 81.5 % 173 141 32 1 140 1
Current Date: 2026-06-29 16:15:13 +0200 Functions: 100.0 % 5 5 1 4
Baseline: lcov-20260630-baseline Branches: 49.4 % 164 81 83 81
Baseline Date: 2026-06-29 13:01:57 +0200 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 85.7 % 49 42 7 42
(30,360] days: 75.0 % 4 3 1 3
(360..) days: 80.0 % 120 96 24 1 95
Function coverage date bins:
(360..) days: 100.0 % 5 5 1 4
Branch coverage date bins:
(7,30] days: 67.6 % 34 23 11 23
(30,360] days: 0.0 % 2 0 2
(360..) days: 45.3 % 128 58 70 58

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

Generated by: LCOV version 2.0-1