LCOV - differential code coverage report
Current view: top level - contrib/spi - refint.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 83.9 % 211 177 34 1 176 1
Current Date: 2025-09-06 07:49:51 +0900 Functions: 100.0 % 6 6 1 5
Baseline: lcov-20250906-005545-baseline Branches: 54.7 % 212 116 96 116
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 70.0 % 10 7 3 1 6
(360..) days: 84.6 % 201 170 31 170
Function coverage date bins:
(30,360] days: 100.0 % 1 1 1
(360..) days: 100.0 % 5 5 1 4
Branch coverage date bins:
(30,360] days: 50.0 % 14 7 7 7
(360..) days: 55.1 % 198 109 89 109

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

Generated by: LCOV version 2.4-beta