PostgreSQL Source Code  git master
ri_triggers.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_proc.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/spi.h"
#include "lib/ilist.h"
#include "miscadmin.h"
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rangetypes.h"
#include "utils/rel.h"
#include "utils/rls.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for ri_triggers.c:

Go to the source code of this file.

Data Structures

struct  RI_ConstraintInfo
 
struct  RI_QueryKey
 
struct  RI_QueryHashEntry
 
struct  RI_CompareKey
 
struct  RI_CompareHashEntry
 

Macros

#define RI_MAX_NUMKEYS   INDEX_MAX_KEYS
 
#define RI_INIT_CONSTRAINTHASHSIZE   64
 
#define RI_INIT_QUERYHASHSIZE   (RI_INIT_CONSTRAINTHASHSIZE * 4)
 
#define RI_KEYS_ALL_NULL   0
 
#define RI_KEYS_SOME_NULL   1
 
#define RI_KEYS_NONE_NULL   2
 
#define RI_PLAN_CHECK_LOOKUPPK   1
 
#define RI_PLAN_CHECK_LOOKUPPK_FROM_PK   2
 
#define RI_PLAN_LAST_ON_PK   RI_PLAN_CHECK_LOOKUPPK_FROM_PK
 
#define RI_PLAN_CASCADE_ONDELETE   3
 
#define RI_PLAN_CASCADE_ONUPDATE   4
 
#define RI_PLAN_RESTRICT   5
 
#define RI_PLAN_SETNULL_ONDELETE   6
 
#define RI_PLAN_SETNULL_ONUPDATE   7
 
#define RI_PLAN_SETDEFAULT_ONDELETE   8
 
#define RI_PLAN_SETDEFAULT_ONUPDATE   9
 
#define MAX_QUOTED_NAME_LEN   (NAMEDATALEN*2+3)
 
#define MAX_QUOTED_REL_NAME_LEN   (MAX_QUOTED_NAME_LEN*2)
 
#define RIAttName(rel, attnum)   NameStr(*attnumAttName(rel, attnum))
 
#define RIAttType(rel, attnum)   attnumTypeId(rel, attnum)
 
#define RIAttCollation(rel, attnum)   attnumCollationId(rel, attnum)
 
#define RI_TRIGTYPE_INSERT   1
 
#define RI_TRIGTYPE_UPDATE   2
 
#define RI_TRIGTYPE_DELETE   3
 

Typedefs

typedef struct RI_ConstraintInfo RI_ConstraintInfo
 
typedef struct RI_QueryKey RI_QueryKey
 
typedef struct RI_QueryHashEntry RI_QueryHashEntry
 
typedef struct RI_CompareKey RI_CompareKey
 
typedef struct RI_CompareHashEntry RI_CompareHashEntry
 

Functions

static bool ri_Check_Pk_Match (Relation pk_rel, Relation fk_rel, TupleTableSlot *oldslot, const RI_ConstraintInfo *riinfo)
 
static Datum ri_restrict (TriggerData *trigdata, bool is_no_action)
 
static Datum ri_set (TriggerData *trigdata, bool is_set_null, int tgkind)
 
static void quoteOneName (char *buffer, const char *name)
 
static void quoteRelationName (char *buffer, Relation rel)
 
static void ri_GenerateQual (StringInfo buf, const char *sep, const char *leftop, Oid leftoptype, Oid opoid, const char *rightop, Oid rightoptype)
 
static void ri_GenerateQualCollation (StringInfo buf, Oid collation)
 
static int ri_NullCheck (TupleDesc tupDesc, TupleTableSlot *slot, const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 
static void ri_BuildQueryKey (RI_QueryKey *key, const RI_ConstraintInfo *riinfo, int32 constr_queryno)
 
static bool ri_KeysEqual (Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 
static bool ri_CompareWithCast (Oid eq_opr, Oid typeid, Datum lhs, Datum rhs)
 
static void ri_InitHashTables (void)
 
static void InvalidateConstraintCacheCallBack (Datum arg, int cacheid, uint32 hashvalue)
 
static SPIPlanPtr ri_FetchPreparedPlan (RI_QueryKey *key)
 
static void ri_HashPreparedPlan (RI_QueryKey *key, SPIPlanPtr plan)
 
static RI_CompareHashEntryri_HashCompareOp (Oid eq_opr, Oid typeid)
 
static void ri_CheckTrigger (FunctionCallInfo fcinfo, const char *funcname, int tgkind)
 
static const RI_ConstraintInfori_FetchConstraintInfo (Trigger *trigger, Relation trig_rel, bool rel_is_pk)
 
static const RI_ConstraintInfori_LoadConstraintInfo (Oid constraintOid)
 
static Oid get_ri_constraint_root (Oid constrOid)
 
static SPIPlanPtr ri_PlanCheck (const char *querystr, int nargs, Oid *argtypes, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel)
 
static bool ri_PerformCheck (const RI_ConstraintInfo *riinfo, RI_QueryKey *qkey, SPIPlanPtr qplan, Relation fk_rel, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, bool detectNewRows, int expect_OK)
 
static void ri_ExtractValues (Relation rel, TupleTableSlot *slot, const RI_ConstraintInfo *riinfo, bool rel_is_pk, Datum *vals, char *nulls)
 
static void ri_ReportViolation (const RI_ConstraintInfo *riinfo, Relation pk_rel, Relation fk_rel, TupleTableSlot *violatorslot, TupleDesc tupdesc, int queryno, bool partgone) pg_attribute_noreturn()
 
static Datum RI_FKey_check (TriggerData *trigdata)
 
Datum RI_FKey_check_ins (PG_FUNCTION_ARGS)
 
Datum RI_FKey_check_upd (PG_FUNCTION_ARGS)
 
Datum RI_FKey_noaction_del (PG_FUNCTION_ARGS)
 
Datum RI_FKey_restrict_del (PG_FUNCTION_ARGS)
 
Datum RI_FKey_noaction_upd (PG_FUNCTION_ARGS)
 
Datum RI_FKey_restrict_upd (PG_FUNCTION_ARGS)
 
Datum RI_FKey_cascade_del (PG_FUNCTION_ARGS)
 
Datum RI_FKey_cascade_upd (PG_FUNCTION_ARGS)
 
Datum RI_FKey_setnull_del (PG_FUNCTION_ARGS)
 
Datum RI_FKey_setnull_upd (PG_FUNCTION_ARGS)
 
Datum RI_FKey_setdefault_del (PG_FUNCTION_ARGS)
 
Datum RI_FKey_setdefault_upd (PG_FUNCTION_ARGS)
 
bool RI_FKey_pk_upd_check_required (Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
bool RI_FKey_fk_upd_check_required (Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
bool RI_Initial_Check (Trigger *trigger, Relation fk_rel, Relation pk_rel)
 
void RI_PartitionRemove_Check (Trigger *trigger, Relation fk_rel, Relation pk_rel)
 
int RI_FKey_trigger_type (Oid tgfoid)
 

Variables

static HTABri_constraint_cache = NULL
 
static HTABri_query_cache = NULL
 
static HTABri_compare_cache = NULL
 
static dclist_head ri_constraint_cache_valid_list
 

Macro Definition Documentation

◆ MAX_QUOTED_NAME_LEN

#define MAX_QUOTED_NAME_LEN   (NAMEDATALEN*2+3)

Definition at line 84 of file ri_triggers.c.

◆ MAX_QUOTED_REL_NAME_LEN

#define MAX_QUOTED_REL_NAME_LEN   (MAX_QUOTED_NAME_LEN*2)

Definition at line 85 of file ri_triggers.c.

◆ RI_INIT_CONSTRAINTHASHSIZE

#define RI_INIT_CONSTRAINTHASHSIZE   64

Definition at line 62 of file ri_triggers.c.

◆ RI_INIT_QUERYHASHSIZE

#define RI_INIT_QUERYHASHSIZE   (RI_INIT_CONSTRAINTHASHSIZE * 4)

Definition at line 63 of file ri_triggers.c.

◆ RI_KEYS_ALL_NULL

#define RI_KEYS_ALL_NULL   0

Definition at line 65 of file ri_triggers.c.

◆ RI_KEYS_NONE_NULL

#define RI_KEYS_NONE_NULL   2

Definition at line 67 of file ri_triggers.c.

◆ RI_KEYS_SOME_NULL

#define RI_KEYS_SOME_NULL   1

Definition at line 66 of file ri_triggers.c.

◆ RI_MAX_NUMKEYS

#define RI_MAX_NUMKEYS   INDEX_MAX_KEYS

Definition at line 60 of file ri_triggers.c.

◆ RI_PLAN_CASCADE_ONDELETE

#define RI_PLAN_CASCADE_ONDELETE   3

Definition at line 75 of file ri_triggers.c.

◆ RI_PLAN_CASCADE_ONUPDATE

#define RI_PLAN_CASCADE_ONUPDATE   4

Definition at line 76 of file ri_triggers.c.

◆ RI_PLAN_CHECK_LOOKUPPK

#define RI_PLAN_CHECK_LOOKUPPK   1

Definition at line 71 of file ri_triggers.c.

◆ RI_PLAN_CHECK_LOOKUPPK_FROM_PK

#define RI_PLAN_CHECK_LOOKUPPK_FROM_PK   2

Definition at line 72 of file ri_triggers.c.

◆ RI_PLAN_LAST_ON_PK

#define RI_PLAN_LAST_ON_PK   RI_PLAN_CHECK_LOOKUPPK_FROM_PK

Definition at line 73 of file ri_triggers.c.

◆ RI_PLAN_RESTRICT

#define RI_PLAN_RESTRICT   5

Definition at line 78 of file ri_triggers.c.

◆ RI_PLAN_SETDEFAULT_ONDELETE

#define RI_PLAN_SETDEFAULT_ONDELETE   8

Definition at line 81 of file ri_triggers.c.

◆ RI_PLAN_SETDEFAULT_ONUPDATE

#define RI_PLAN_SETDEFAULT_ONUPDATE   9

Definition at line 82 of file ri_triggers.c.

◆ RI_PLAN_SETNULL_ONDELETE

#define RI_PLAN_SETNULL_ONDELETE   6

Definition at line 79 of file ri_triggers.c.

◆ RI_PLAN_SETNULL_ONUPDATE

#define RI_PLAN_SETNULL_ONUPDATE   7

Definition at line 80 of file ri_triggers.c.

◆ RI_TRIGTYPE_DELETE

#define RI_TRIGTYPE_DELETE   3

Definition at line 93 of file ri_triggers.c.

◆ RI_TRIGTYPE_INSERT

#define RI_TRIGTYPE_INSERT   1

Definition at line 91 of file ri_triggers.c.

◆ RI_TRIGTYPE_UPDATE

#define RI_TRIGTYPE_UPDATE   2

Definition at line 92 of file ri_triggers.c.

◆ RIAttCollation

#define RIAttCollation (   rel,
  attnum 
)    attnumCollationId(rel, attnum)

Definition at line 89 of file ri_triggers.c.

◆ RIAttName

#define RIAttName (   rel,
  attnum 
)    NameStr(*attnumAttName(rel, attnum))

Definition at line 87 of file ri_triggers.c.

◆ RIAttType

#define RIAttType (   rel,
  attnum 
)    attnumTypeId(rel, attnum)

Definition at line 88 of file ri_triggers.c.

Typedef Documentation

◆ RI_CompareHashEntry

◆ RI_CompareKey

typedef struct RI_CompareKey RI_CompareKey

◆ RI_ConstraintInfo

◆ RI_QueryHashEntry

◆ RI_QueryKey

typedef struct RI_QueryKey RI_QueryKey

Function Documentation

◆ get_ri_constraint_root()

static Oid get_ri_constraint_root ( Oid  constrOid)
static

Definition at line 2293 of file ri_triggers.c.

2294 {
2295  for (;;)
2296  {
2297  HeapTuple tuple;
2298  Oid constrParentOid;
2299 
2300  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
2301  if (!HeapTupleIsValid(tuple))
2302  elog(ERROR, "cache lookup failed for constraint %u", constrOid);
2303  constrParentOid = ((Form_pg_constraint) GETSTRUCT(tuple))->conparentid;
2304  ReleaseSysCache(tuple);
2305  if (!OidIsValid(constrParentOid))
2306  break; /* we reached the root constraint */
2307  constrOid = constrParentOid;
2308  }
2309  return constrOid;
2310 }
#define OidIsValid(objectId)
Definition: c.h:762
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
FormData_pg_constraint * Form_pg_constraint
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
unsigned int Oid
Definition: postgres_ext.h:31
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218

References elog, ERROR, GETSTRUCT, HeapTupleIsValid, ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), and SearchSysCache1().

Referenced by ri_LoadConstraintInfo().

◆ InvalidateConstraintCacheCallBack()

static void InvalidateConstraintCacheCallBack ( Datum  arg,
int  cacheid,
uint32  hashvalue 
)
static

Definition at line 2327 of file ri_triggers.c.

2328 {
2329  dlist_mutable_iter iter;
2330 
2331  Assert(ri_constraint_cache != NULL);
2332 
2333  /*
2334  * If the list of currently valid entries gets excessively large, we mark
2335  * them all invalid so we can empty the list. This arrangement avoids
2336  * O(N^2) behavior in situations where a session touches many foreign keys
2337  * and also does many ALTER TABLEs, such as a restore from pg_dump.
2338  */
2340  hashvalue = 0; /* pretend it's a cache reset */
2341 
2343  {
2345  valid_link, iter.cur);
2346 
2347  /*
2348  * We must invalidate not only entries directly matching the given
2349  * hash value, but also child entries, in case the invalidation
2350  * affects a root constraint.
2351  */
2352  if (hashvalue == 0 ||
2353  riinfo->oidHashValue == hashvalue ||
2354  riinfo->rootHashValue == hashvalue)
2355  {
2356  riinfo->valid = false;
2357  /* Remove invalidated entries from the list, too */
2359  }
2360  }
2361 }
#define dclist_container(type, membername, ptr)
Definition: ilist.h:947
static uint32 dclist_count(const dclist_head *head)
Definition: ilist.h:932
static void dclist_delete_from(dclist_head *head, dlist_node *node)
Definition: ilist.h:763
#define dclist_foreach_modify(iter, lhead)
Definition: ilist.h:973
Assert(fmt[strlen(fmt) - 1] !='\n')
static dclist_head ri_constraint_cache_valid_list
Definition: ri_triggers.c:184
static HTAB * ri_constraint_cache
Definition: ri_triggers.c:181
dlist_node * cur
Definition: ilist.h:200

References Assert(), dlist_mutable_iter::cur, dclist_container, dclist_count(), dclist_delete_from(), dclist_foreach_modify, RI_ConstraintInfo::oidHashValue, ri_constraint_cache, ri_constraint_cache_valid_list, RI_ConstraintInfo::rootHashValue, and RI_ConstraintInfo::valid.

Referenced by ri_InitHashTables().

◆ quoteOneName()

static void quoteOneName ( char *  buffer,
const char *  name 
)
static

Definition at line 1957 of file ri_triggers.c.

1958 {
1959  /* Rather than trying to be smart, just always quote it. */
1960  *buffer++ = '"';
1961  while (*name)
1962  {
1963  if (*name == '"')
1964  *buffer++ = '"';
1965  *buffer++ = *name++;
1966  }
1967  *buffer++ = '"';
1968  *buffer = '\0';
1969 }
const char * name

References name.

Referenced by quoteRelationName(), ri_Check_Pk_Match(), RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check(), ri_GenerateQualCollation(), RI_Initial_Check(), RI_PartitionRemove_Check(), ri_restrict(), and ri_set().

◆ quoteRelationName()

static void quoteRelationName ( char *  buffer,
Relation  rel 
)
static

Definition at line 1977 of file ri_triggers.c.

1978 {
1980  buffer += strlen(buffer);
1981  *buffer++ = '.';
1982  quoteOneName(buffer, RelationGetRelationName(rel));
1983 }
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3344
#define RelationGetRelationName(relation)
Definition: rel.h:541
#define RelationGetNamespace(relation)
Definition: rel.h:548
static void quoteOneName(char *buffer, const char *name)
Definition: ri_triggers.c:1957

References get_namespace_name(), quoteOneName(), RelationGetNamespace, and RelationGetRelationName.

Referenced by ri_Check_Pk_Match(), RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check(), RI_Initial_Check(), RI_PartitionRemove_Check(), ri_restrict(), and ri_set().

◆ ri_BuildQueryKey()

static void ri_BuildQueryKey ( RI_QueryKey key,
const RI_ConstraintInfo riinfo,
int32  constr_queryno 
)
static

Definition at line 2064 of file ri_triggers.c.

2066 {
2067  /*
2068  * Inherited constraints with a common ancestor can share ri_query_cache
2069  * entries for all query types except RI_PLAN_CHECK_LOOKUPPK_FROM_PK.
2070  * Except in that case, the query processes the other table involved in
2071  * the FK constraint (i.e., not the table on which the trigger has been
2072  * fired), and so it will be the same for all members of the inheritance
2073  * tree. So we may use the root constraint's OID in the hash key, rather
2074  * than the constraint's own OID. This avoids creating duplicate SPI
2075  * plans, saving lots of work and memory when there are many partitions
2076  * with similar FK constraints.
2077  *
2078  * (Note that we must still have a separate RI_ConstraintInfo for each
2079  * constraint, because partitions can have different column orders,
2080  * resulting in different pk_attnums[] or fk_attnums[] array contents.)
2081  *
2082  * We assume struct RI_QueryKey contains no padding bytes, else we'd need
2083  * to use memset to clear them.
2084  */
2085  if (constr_queryno != RI_PLAN_CHECK_LOOKUPPK_FROM_PK)
2086  key->constr_id = riinfo->constraint_root_id;
2087  else
2088  key->constr_id = riinfo->constraint_id;
2089  key->constr_queryno = constr_queryno;
2090 }
#define RI_PLAN_CHECK_LOOKUPPK_FROM_PK
Definition: ri_triggers.c:72

References RI_ConstraintInfo::constraint_id, RI_ConstraintInfo::constraint_root_id, sort-test::key, and RI_PLAN_CHECK_LOOKUPPK_FROM_PK.

Referenced by ri_Check_Pk_Match(), RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check(), ri_restrict(), and ri_set().

◆ ri_Check_Pk_Match()

static bool ri_Check_Pk_Match ( Relation  pk_rel,
Relation  fk_rel,
TupleTableSlot oldslot,
const RI_ConstraintInfo riinfo 
)
static

Definition at line 508 of file ri_triggers.c.

511 {
512  SPIPlanPtr qplan;
513  RI_QueryKey qkey;
514  bool result;
515 
516  /* Only called for non-null rows */
517  Assert(ri_NullCheck(RelationGetDescr(pk_rel), oldslot, riinfo, true) == RI_KEYS_NONE_NULL);
518 
519  if (SPI_connect() != SPI_OK_CONNECT)
520  elog(ERROR, "SPI_connect failed");
521 
522  /*
523  * Fetch or prepare a saved plan for checking PK table with values coming
524  * from a PK row
525  */
527 
528  if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
529  {
530  StringInfoData querybuf;
531  char pkrelname[MAX_QUOTED_REL_NAME_LEN];
533  char paramname[16];
534  const char *querysep;
535  const char *pk_only;
536  Oid queryoids[RI_MAX_NUMKEYS];
537 
538  /* ----------
539  * The query string built is
540  * SELECT 1 FROM [ONLY] <pktable> x WHERE pkatt1 = $1 [AND ...]
541  * FOR KEY SHARE OF x
542  * The type id's for the $ parameters are those of the
543  * PK attributes themselves.
544  * But for temporal FKs we need to make sure
545  * the FK's range is completely covered.
546  * So we use this query instead:
547  * SELECT 1
548  * FROM (
549  * SELECT pkperiodatt AS r
550  * FROM [ONLY] pktable x
551  * WHERE pkatt1 = $1 [AND ...]
552  * AND pkperiodatt && $n
553  * FOR KEY SHARE OF x
554  * ) x1
555  * HAVING $n <@ range_agg(x1.r)
556  * Note if FOR KEY SHARE ever allows GROUP BY and HAVING
557  * we can make this a bit simpler.
558  * ----------
559  */
560  initStringInfo(&querybuf);
561  pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
562  "" : "ONLY ";
563  quoteRelationName(pkrelname, pk_rel);
564  if (riinfo->hasperiod)
565  {
566  quoteOneName(attname, RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
567 
568  appendStringInfo(&querybuf,
569  "SELECT 1 FROM (SELECT %s AS r FROM %s%s x",
570  attname, pk_only, pkrelname);
571  }
572  else
573  {
574  appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
575  pk_only, pkrelname);
576  }
577  querysep = "WHERE";
578  for (int i = 0; i < riinfo->nkeys; i++)
579  {
580  Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
581 
583  RIAttName(pk_rel, riinfo->pk_attnums[i]));
584  sprintf(paramname, "$%d", i + 1);
585  ri_GenerateQual(&querybuf, querysep,
586  attname, pk_type,
587  riinfo->pp_eq_oprs[i],
588  paramname, pk_type);
589  querysep = "AND";
590  queryoids[i] = pk_type;
591  }
592  appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
593  if (riinfo->hasperiod)
594  {
595  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[riinfo->nkeys - 1]);
596 
597  appendStringInfo(&querybuf, ") x1 HAVING ");
598  sprintf(paramname, "$%d", riinfo->nkeys);
599  ri_GenerateQual(&querybuf, "",
600  paramname, fk_type,
602  "pg_catalog.range_agg", ANYMULTIRANGEOID);
603  appendStringInfo(&querybuf, "(x1.r)");
604  }
605 
606  /* Prepare and save the plan */
607  qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
608  &qkey, fk_rel, pk_rel);
609  }
610 
611  /*
612  * We have a plan now. Run it.
613  */
614  result = ri_PerformCheck(riinfo, &qkey, qplan,
615  fk_rel, pk_rel,
616  oldslot, NULL,
617  true, /* treat like update */
618  SPI_OK_SELECT);
619 
620  if (SPI_finish() != SPI_OK_FINISH)
621  elog(ERROR, "SPI_finish failed");
622 
623  return result;
624 }
int i
Definition: isn.c:73
NameData attname
Definition: pg_attribute.h:41
#define sprintf
Definition: port.h:240
#define RelationGetDescr(relation)
Definition: rel.h:533
static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo, RI_QueryKey *qkey, SPIPlanPtr qplan, Relation fk_rel, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, bool detectNewRows, int expect_OK)
Definition: ri_triggers.c:2411
#define RIAttType(rel, attnum)
Definition: ri_triggers.c:88
#define MAX_QUOTED_REL_NAME_LEN
Definition: ri_triggers.c:85
static void quoteRelationName(char *buffer, Relation rel)
Definition: ri_triggers.c:1977
static int ri_NullCheck(TupleDesc tupDesc, TupleTableSlot *slot, const RI_ConstraintInfo *riinfo, bool rel_is_pk)
Definition: ri_triggers.c:2735
static void ri_GenerateQual(StringInfo buf, const char *sep, const char *leftop, Oid leftoptype, Oid opoid, const char *rightop, Oid rightoptype)
Definition: ri_triggers.c:1994
#define RI_KEYS_NONE_NULL
Definition: ri_triggers.c:67
static void ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo, int32 constr_queryno)
Definition: ri_triggers.c:2064
static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:2368
#define MAX_QUOTED_NAME_LEN
Definition: ri_triggers.c:84
#define RIAttName(rel, attnum)
Definition: ri_triggers.c:87
#define RI_MAX_NUMKEYS
Definition: ri_triggers.c:60
static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key)
Definition: ri_triggers.c:2808
int SPI_connect(void)
Definition: spi.c:94
int SPI_finish(void)
Definition: spi.c:182
#define SPI_OK_CONNECT
Definition: spi.h:82
#define SPI_OK_FINISH
Definition: spi.h:83
#define SPI_OK_SELECT
Definition: spi.h:86
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:97
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
int16 pk_attnums[RI_MAX_NUMKEYS]
Definition: ri_triggers.c:125
Oid agged_period_contained_by_oper
Definition: ri_triggers.c:131
int16 fk_attnums[RI_MAX_NUMKEYS]
Definition: ri_triggers.c:126
Oid pp_eq_oprs[RI_MAX_NUMKEYS]
Definition: ri_triggers.c:128
Form_pg_class rd_rel
Definition: rel.h:111

References RI_ConstraintInfo::agged_period_contained_by_oper, appendStringInfo(), appendStringInfoString(), Assert(), attname, StringInfoData::data, elog, ERROR, RI_ConstraintInfo::fk_attnums, RI_ConstraintInfo::hasperiod, i, initStringInfo(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, RI_ConstraintInfo::nkeys, RI_ConstraintInfo::pk_attnums, RI_ConstraintInfo::pp_eq_oprs, quoteOneName(), quoteRelationName(), RelationData::rd_rel, RelationGetDescr, ri_BuildQueryKey(), ri_FetchPreparedPlan(), ri_GenerateQual(), RI_KEYS_NONE_NULL, RI_MAX_NUMKEYS, ri_NullCheck(), ri_PerformCheck(), RI_PLAN_CHECK_LOOKUPPK_FROM_PK, ri_PlanCheck(), RIAttName, RIAttType, SPI_connect(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_OK_SELECT, and sprintf.

Referenced by ri_restrict().

◆ ri_CheckTrigger()

static void ri_CheckTrigger ( FunctionCallInfo  fcinfo,
const char *  funcname,
int  tgkind 
)
static

Definition at line 2096 of file ri_triggers.c.

2097 {
2098  TriggerData *trigdata = (TriggerData *) fcinfo->context;
2099 
2100  if (!CALLED_AS_TRIGGER(fcinfo))
2101  ereport(ERROR,
2102  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2103  errmsg("function \"%s\" was not called by trigger manager", funcname)));
2104 
2105  /*
2106  * Check proper event
2107  */
2108  if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
2109  !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
2110  ereport(ERROR,
2111  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2112  errmsg("function \"%s\" must be fired AFTER ROW", funcname)));
2113 
2114  switch (tgkind)
2115  {
2116  case RI_TRIGTYPE_INSERT:
2117  if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
2118  ereport(ERROR,
2119  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2120  errmsg("function \"%s\" must be fired for INSERT", funcname)));
2121  break;
2122  case RI_TRIGTYPE_UPDATE:
2123  if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
2124  ereport(ERROR,
2125  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2126  errmsg("function \"%s\" must be fired for UPDATE", funcname)));
2127  break;
2128  case RI_TRIGTYPE_DELETE:
2129  if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
2130  ereport(ERROR,
2131  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2132  errmsg("function \"%s\" must be fired for DELETE", funcname)));
2133  break;
2134  }
2135 }
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ereport(elevel,...)
Definition: elog.h:149
#define funcname
Definition: indent_codes.h:69
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
#define RI_TRIGTYPE_INSERT
Definition: ri_triggers.c:91
#define RI_TRIGTYPE_DELETE
Definition: ri_triggers.c:93
#define RI_TRIGTYPE_UPDATE
Definition: ri_triggers.c:92
fmNodePtr context
Definition: fmgr.h:88
TriggerEvent tg_event
Definition: trigger.h:34
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:113
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:26
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:122
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:131
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:110
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:116

References CALLED_AS_TRIGGER, FunctionCallInfoBaseData::context, ereport, errcode(), errmsg(), ERROR, funcname, if(), RI_TRIGTYPE_DELETE, RI_TRIGTYPE_INSERT, RI_TRIGTYPE_UPDATE, TriggerData::tg_event, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, and TRIGGER_FIRED_FOR_ROW.

Referenced by RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check_ins(), RI_FKey_check_upd(), RI_FKey_noaction_del(), RI_FKey_noaction_upd(), RI_FKey_restrict_del(), RI_FKey_restrict_upd(), RI_FKey_setdefault_del(), RI_FKey_setdefault_upd(), RI_FKey_setnull_del(), and RI_FKey_setnull_upd().

◆ ri_CompareWithCast()

static bool ri_CompareWithCast ( Oid  eq_opr,
Oid  typeid,
Datum  lhs,
Datum  rhs 
)
static

Definition at line 2982 of file ri_triggers.c.

2984 {
2985  RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
2986 
2987  /* Do we need to cast the values? */
2988  if (OidIsValid(entry->cast_func_finfo.fn_oid))
2989  {
2990  lhs = FunctionCall3(&entry->cast_func_finfo,
2991  lhs,
2992  Int32GetDatum(-1), /* typmod */
2993  BoolGetDatum(false)); /* implicit coercion */
2994  rhs = FunctionCall3(&entry->cast_func_finfo,
2995  rhs,
2996  Int32GetDatum(-1), /* typmod */
2997  BoolGetDatum(false)); /* implicit coercion */
2998  }
2999 
3000  /*
3001  * Apply the comparison operator.
3002  *
3003  * Note: This function is part of a call stack that determines whether an
3004  * update to a row is significant enough that it needs checking or action
3005  * on the other side of a foreign-key constraint. Therefore, the
3006  * comparison here would need to be done with the collation of the *other*
3007  * table. For simplicity (e.g., we might not even have the other table
3008  * open), we'll just use the default collation here, which could lead to
3009  * some false negatives. All this would break if we ever allow
3010  * database-wide collations to be nondeterministic.
3011  *
3012  * With range/multirangetypes, the collation of the base type is stored as
3013  * part of the rangetype (pg_range.rngcollation), and always used, so
3014  * there is no danger of inconsistency even using a non-equals operator.
3015  * But if we support arbitrary types with PERIOD, we should perhaps just
3016  * always force a re-check.
3017  */
3019  DEFAULT_COLLATION_OID,
3020  lhs, rhs));
3021 }
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
#define FunctionCall3(flinfo, arg1, arg2, arg3)
Definition: fmgr.h:664
static bool DatumGetBool(Datum X)
Definition: postgres.h:90
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
static RI_CompareHashEntry * ri_HashCompareOp(Oid eq_opr, Oid typeid)
Definition: ri_triggers.c:3030
Oid fn_oid
Definition: fmgr.h:59
FmgrInfo cast_func_finfo
Definition: ri_triggers.c:174

References BoolGetDatum(), RI_CompareHashEntry::cast_func_finfo, DatumGetBool(), RI_CompareHashEntry::eq_opr_finfo, FmgrInfo::fn_oid, FunctionCall2Coll(), FunctionCall3, Int32GetDatum(), OidIsValid, and ri_HashCompareOp().

Referenced by ri_KeysEqual().

◆ ri_ExtractValues()

static void ri_ExtractValues ( Relation  rel,
TupleTableSlot slot,
const RI_ConstraintInfo riinfo,
bool  rel_is_pk,
Datum vals,
char *  nulls 
)
static

Definition at line 2548 of file ri_triggers.c.

2551 {
2552  const int16 *attnums;
2553  bool isnull;
2554 
2555  if (rel_is_pk)
2556  attnums = riinfo->pk_attnums;
2557  else
2558  attnums = riinfo->fk_attnums;
2559 
2560  for (int i = 0; i < riinfo->nkeys; i++)
2561  {
2562  vals[i] = slot_getattr(slot, attnums[i], &isnull);
2563  nulls[i] = isnull ? 'n' : ' ';
2564  }
2565 }
signed short int16
Definition: c.h:480
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:395

References RI_ConstraintInfo::fk_attnums, i, RI_ConstraintInfo::nkeys, RI_ConstraintInfo::pk_attnums, and slot_getattr().

Referenced by ri_PerformCheck().

◆ ri_FetchConstraintInfo()

static const RI_ConstraintInfo * ri_FetchConstraintInfo ( Trigger trigger,
Relation  trig_rel,
bool  rel_is_pk 
)
static

Definition at line 2142 of file ri_triggers.c.

2143 {
2144  Oid constraintOid = trigger->tgconstraint;
2145  const RI_ConstraintInfo *riinfo;
2146 
2147  /*
2148  * Check that the FK constraint's OID is available; it might not be if
2149  * we've been invoked via an ordinary trigger or an old-style "constraint
2150  * trigger".
2151  */
2152  if (!OidIsValid(constraintOid))
2153  ereport(ERROR,
2154  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2155  errmsg("no pg_constraint entry for trigger \"%s\" on table \"%s\"",
2156  trigger->tgname, RelationGetRelationName(trig_rel)),
2157  errhint("Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
2158 
2159  /* Find or create a hashtable entry for the constraint */
2160  riinfo = ri_LoadConstraintInfo(constraintOid);
2161 
2162  /* Do some easy cross-checks against the trigger call data */
2163  if (rel_is_pk)
2164  {
2165  if (riinfo->fk_relid != trigger->tgconstrrelid ||
2166  riinfo->pk_relid != RelationGetRelid(trig_rel))
2167  elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
2168  trigger->tgname, RelationGetRelationName(trig_rel));
2169  }
2170  else
2171  {
2172  if (riinfo->fk_relid != RelationGetRelid(trig_rel) ||
2173  riinfo->pk_relid != trigger->tgconstrrelid)
2174  elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
2175  trigger->tgname, RelationGetRelationName(trig_rel));
2176  }
2177 
2178  if (riinfo->confmatchtype != FKCONSTR_MATCH_FULL &&
2181  elog(ERROR, "unrecognized confmatchtype: %d",
2182  riinfo->confmatchtype);
2183 
2184  if (riinfo->confmatchtype == FKCONSTR_MATCH_PARTIAL)
2185  ereport(ERROR,
2186  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2187  errmsg("MATCH PARTIAL not yet implemented")));
2188 
2189  return riinfo;
2190 }
int errhint(const char *fmt,...)
Definition: elog.c:1319
#define FKCONSTR_MATCH_SIMPLE
Definition: parsenodes.h:2648
#define FKCONSTR_MATCH_PARTIAL
Definition: parsenodes.h:2647
#define FKCONSTR_MATCH_FULL
Definition: parsenodes.h:2646
#define RelationGetRelid(relation)
Definition: rel.h:507
static const RI_ConstraintInfo * ri_LoadConstraintInfo(Oid constraintOid)
Definition: ri_triggers.c:2196
Oid tgconstraint
Definition: reltrigger.h:35
Oid tgconstrrelid
Definition: reltrigger.h:33
char * tgname
Definition: reltrigger.h:27

References RI_ConstraintInfo::confmatchtype, elog, ereport, errcode(), errhint(), errmsg(), ERROR, RI_ConstraintInfo::fk_relid, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, OidIsValid, RI_ConstraintInfo::pk_relid, RelationGetRelationName, RelationGetRelid, ri_LoadConstraintInfo(), Trigger::tgconstraint, Trigger::tgconstrrelid, and Trigger::tgname.

Referenced by RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check(), RI_FKey_fk_upd_check_required(), RI_FKey_pk_upd_check_required(), RI_Initial_Check(), RI_PartitionRemove_Check(), ri_restrict(), and ri_set().

◆ ri_FetchPreparedPlan()

static SPIPlanPtr ri_FetchPreparedPlan ( RI_QueryKey key)
static

Definition at line 2808 of file ri_triggers.c.

2809 {
2810  RI_QueryHashEntry *entry;
2811  SPIPlanPtr plan;
2812 
2813  /*
2814  * On the first call initialize the hashtable
2815  */
2816  if (!ri_query_cache)
2818 
2819  /*
2820  * Lookup for the key
2821  */
2823  key,
2824  HASH_FIND, NULL);
2825  if (entry == NULL)
2826  return NULL;
2827 
2828  /*
2829  * Check whether the plan is still valid. If it isn't, we don't want to
2830  * simply rely on plancache.c to regenerate it; rather we should start
2831  * from scratch and rebuild the query text too. This is to cover cases
2832  * such as table/column renames. We depend on the plancache machinery to
2833  * detect possible invalidations, though.
2834  *
2835  * CAUTION: this check is only trustworthy if the caller has already
2836  * locked both FK and PK rels.
2837  */
2838  plan = entry->plan;
2839  if (plan && SPI_plan_is_valid(plan))
2840  return plan;
2841 
2842  /*
2843  * Otherwise we might as well flush the cached plan now, to free a little
2844  * memory space before we make a new one.
2845  */
2846  entry->plan = NULL;
2847  if (plan)
2848  SPI_freeplan(plan);
2849 
2850  return NULL;
2851 }
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
@ HASH_FIND
Definition: hsearch.h:113
#define plan(x)
Definition: pg_regress.c:162
static HTAB * ri_query_cache
Definition: ri_triggers.c:182
static void ri_InitHashTables(void)
Definition: ri_triggers.c:2772
bool SPI_plan_is_valid(SPIPlanPtr plan)
Definition: spi.c:1945
int SPI_freeplan(SPIPlanPtr plan)
Definition: spi.c:1022
SPIPlanPtr plan
Definition: ri_triggers.c:152

References HASH_FIND, hash_search(), sort-test::key, RI_QueryHashEntry::plan, plan, ri_InitHashTables(), ri_query_cache, SPI_freeplan(), and SPI_plan_is_valid().

Referenced by ri_Check_Pk_Match(), RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check(), ri_restrict(), and ri_set().

◆ RI_FKey_cascade_del()

Datum RI_FKey_cascade_del ( PG_FUNCTION_ARGS  )

Definition at line 827 of file ri_triggers.c.

828 {
829  TriggerData *trigdata = (TriggerData *) fcinfo->context;
830  const RI_ConstraintInfo *riinfo;
831  Relation fk_rel;
832  Relation pk_rel;
833  TupleTableSlot *oldslot;
834  RI_QueryKey qkey;
835  SPIPlanPtr qplan;
836 
837  /* Check that this is a valid trigger call on the right time and event. */
838  ri_CheckTrigger(fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
839 
840  riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
841  trigdata->tg_relation, true);
842 
843  /*
844  * Get the relation descriptors of the FK and PK tables and the old tuple.
845  *
846  * fk_rel is opened in RowExclusiveLock mode since that's what our
847  * eventual DELETE will get on it.
848  */
849  fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
850  pk_rel = trigdata->tg_relation;
851  oldslot = trigdata->tg_trigslot;
852 
853  if (SPI_connect() != SPI_OK_CONNECT)
854  elog(ERROR, "SPI_connect failed");
855 
856  /* Fetch or prepare a saved plan for the cascaded delete */
858 
859  if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
860  {
861  StringInfoData querybuf;
862  char fkrelname[MAX_QUOTED_REL_NAME_LEN];
864  char paramname[16];
865  const char *querysep;
866  Oid queryoids[RI_MAX_NUMKEYS];
867  const char *fk_only;
868 
869  /* ----------
870  * The query string built is
871  * DELETE FROM [ONLY] <fktable> WHERE $1 = fkatt1 [AND ...]
872  * The type id's for the $ parameters are those of the
873  * corresponding PK attributes.
874  * ----------
875  */
876  initStringInfo(&querybuf);
877  fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
878  "" : "ONLY ";
879  quoteRelationName(fkrelname, fk_rel);
880  appendStringInfo(&querybuf, "DELETE FROM %s%s",
881  fk_only, fkrelname);
882  querysep = "WHERE";
883  for (int i = 0; i < riinfo->nkeys; i++)
884  {
885  Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
886  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
887  Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
888  Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
889 
891  RIAttName(fk_rel, riinfo->fk_attnums[i]));
892  sprintf(paramname, "$%d", i + 1);
893  ri_GenerateQual(&querybuf, querysep,
894  paramname, pk_type,
895  riinfo->pf_eq_oprs[i],
896  attname, fk_type);
897  if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
898  ri_GenerateQualCollation(&querybuf, pk_coll);
899  querysep = "AND";
900  queryoids[i] = pk_type;
901  }
902 
903  /* Prepare and save the plan */
904  qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
905  &qkey, fk_rel, pk_rel);
906  }
907 
908  /*
909  * We have a plan now. Build up the arguments from the key values in the
910  * deleted PK tuple and delete the referencing rows
911  */
912  ri_PerformCheck(riinfo, &qkey, qplan,
913  fk_rel, pk_rel,
914  oldslot, NULL,
915  true, /* must detect new rows */
916  SPI_OK_DELETE);
917 
918  if (SPI_finish() != SPI_OK_FINISH)
919  elog(ERROR, "SPI_finish failed");
920 
921  table_close(fk_rel, RowExclusiveLock);
922 
923  return PointerGetDatum(NULL);
924 }
#define RowExclusiveLock
Definition: lockdefs.h:38
bool get_collation_isdeterministic(Oid colloid)
Definition: lsyscache.c:1054
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
static void ri_GenerateQualCollation(StringInfo buf, Oid collation)
Definition: ri_triggers.c:2023
#define RIAttCollation(rel, attnum)
Definition: ri_triggers.c:89
static const RI_ConstraintInfo * ri_FetchConstraintInfo(Trigger *trigger, Relation trig_rel, bool rel_is_pk)
Definition: ri_triggers.c:2142
#define RI_PLAN_CASCADE_ONDELETE
Definition: ri_triggers.c:75
static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
Definition: ri_triggers.c:2096
#define SPI_OK_DELETE
Definition: spi.h:89
Relation tg_relation
Definition: trigger.h:35
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
Trigger * tg_trigger
Definition: trigger.h:38
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

References appendStringInfo(), attname, StringInfoData::data, elog, ERROR, get_collation_isdeterministic(), i, initStringInfo(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, PointerGetDatum(), quoteOneName(), quoteRelationName(), ri_BuildQueryKey(), ri_CheckTrigger(), ri_FetchConstraintInfo(), ri_FetchPreparedPlan(), ri_GenerateQual(), ri_GenerateQualCollation(), RI_MAX_NUMKEYS, ri_PerformCheck(), RI_PLAN_CASCADE_ONDELETE, ri_PlanCheck(), RI_TRIGTYPE_DELETE, RIAttCollation, RIAttName, RIAttType, RowExclusiveLock, SPI_connect(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_DELETE, SPI_OK_FINISH, sprintf, table_close(), table_open(), TriggerData::tg_relation, TriggerData::tg_trigger, and TriggerData::tg_trigslot.

◆ RI_FKey_cascade_upd()

Datum RI_FKey_cascade_upd ( PG_FUNCTION_ARGS  )

Definition at line 933 of file ri_triggers.c.

934 {
935  TriggerData *trigdata = (TriggerData *) fcinfo->context;
936  const RI_ConstraintInfo *riinfo;
937  Relation fk_rel;
938  Relation pk_rel;
939  TupleTableSlot *newslot;
940  TupleTableSlot *oldslot;
941  RI_QueryKey qkey;
942  SPIPlanPtr qplan;
943 
944  /* Check that this is a valid trigger call on the right time and event. */
945  ri_CheckTrigger(fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
946 
947  riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
948  trigdata->tg_relation, true);
949 
950  /*
951  * Get the relation descriptors of the FK and PK tables and the new and
952  * old tuple.
953  *
954  * fk_rel is opened in RowExclusiveLock mode since that's what our
955  * eventual UPDATE will get on it.
956  */
957  fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
958  pk_rel = trigdata->tg_relation;
959  newslot = trigdata->tg_newslot;
960  oldslot = trigdata->tg_trigslot;
961 
962  if (SPI_connect() != SPI_OK_CONNECT)
963  elog(ERROR, "SPI_connect failed");
964 
965  /* Fetch or prepare a saved plan for the cascaded update */
967 
968  if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
969  {
970  StringInfoData querybuf;
971  StringInfoData qualbuf;
972  char fkrelname[MAX_QUOTED_REL_NAME_LEN];
974  char paramname[16];
975  const char *querysep;
976  const char *qualsep;
977  Oid queryoids[RI_MAX_NUMKEYS * 2];
978  const char *fk_only;
979 
980  /* ----------
981  * The query string built is
982  * UPDATE [ONLY] <fktable> SET fkatt1 = $1 [, ...]
983  * WHERE $n = fkatt1 [AND ...]
984  * The type id's for the $ parameters are those of the
985  * corresponding PK attributes. Note that we are assuming
986  * there is an assignment cast from the PK to the FK type;
987  * else the parser will fail.
988  * ----------
989  */
990  initStringInfo(&querybuf);
991  initStringInfo(&qualbuf);
992  fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
993  "" : "ONLY ";
994  quoteRelationName(fkrelname, fk_rel);
995  appendStringInfo(&querybuf, "UPDATE %s%s SET",
996  fk_only, fkrelname);
997  querysep = "";
998  qualsep = "WHERE";
999  for (int i = 0, j = riinfo->nkeys; i < riinfo->nkeys; i++, j++)
1000  {
1001  Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
1002  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
1003  Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1004  Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
1005 
1007  RIAttName(fk_rel, riinfo->fk_attnums[i]));
1008  appendStringInfo(&querybuf,
1009  "%s %s = $%d",
1010  querysep, attname, i + 1);
1011  sprintf(paramname, "$%d", j + 1);
1012  ri_GenerateQual(&qualbuf, qualsep,
1013  paramname, pk_type,
1014  riinfo->pf_eq_oprs[i],
1015  attname, fk_type);
1016  if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
1017  ri_GenerateQualCollation(&querybuf, pk_coll);
1018  querysep = ",";
1019  qualsep = "AND";
1020  queryoids[i] = pk_type;
1021  queryoids[j] = pk_type;
1022  }
1023  appendBinaryStringInfo(&querybuf, qualbuf.data, qualbuf.len);
1024 
1025  /* Prepare and save the plan */
1026  qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids,
1027  &qkey, fk_rel, pk_rel);
1028  }
1029 
1030  /*
1031  * We have a plan now. Run it to update the existing references.
1032  */
1033  ri_PerformCheck(riinfo, &qkey, qplan,
1034  fk_rel, pk_rel,
1035  oldslot, newslot,
1036  true, /* must detect new rows */
1037  SPI_OK_UPDATE);
1038 
1039  if (SPI_finish() != SPI_OK_FINISH)
1040  elog(ERROR, "SPI_finish failed");
1041 
1042  table_close(fk_rel, RowExclusiveLock);
1043 
1044  return PointerGetDatum(NULL);
1045 }
int j
Definition: isn.c:74
#define RI_PLAN_CASCADE_ONUPDATE
Definition: ri_triggers.c:76
#define SPI_OK_UPDATE
Definition: spi.h:90
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition: stringinfo.c:233
TupleTableSlot * tg_newslot
Definition: trigger.h:40

References appendBinaryStringInfo(), appendStringInfo(), attname, StringInfoData::data, elog, ERROR, get_collation_isdeterministic(), i, initStringInfo(), j, StringInfoData::len, MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, PointerGetDatum(), quoteOneName(), quoteRelationName(), ri_BuildQueryKey(), ri_CheckTrigger(), ri_FetchConstraintInfo(), ri_FetchPreparedPlan(), ri_GenerateQual(), ri_GenerateQualCollation(), RI_MAX_NUMKEYS, ri_PerformCheck(), RI_PLAN_CASCADE_ONUPDATE, ri_PlanCheck(), RI_TRIGTYPE_UPDATE, RIAttCollation, RIAttName, RIAttType, RowExclusiveLock, SPI_connect(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_OK_UPDATE, sprintf, table_close(), table_open(), TriggerData::tg_newslot, TriggerData::tg_relation, TriggerData::tg_trigger, and TriggerData::tg_trigslot.

◆ RI_FKey_check()

static Datum RI_FKey_check ( TriggerData trigdata)
static

Definition at line 247 of file ri_triggers.c.

248 {
249  const RI_ConstraintInfo *riinfo;
250  Relation fk_rel;
251  Relation pk_rel;
252  TupleTableSlot *newslot;
253  RI_QueryKey qkey;
254  SPIPlanPtr qplan;
255 
256  riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
257  trigdata->tg_relation, false);
258 
259  if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
260  newslot = trigdata->tg_newslot;
261  else
262  newslot = trigdata->tg_trigslot;
263 
264  /*
265  * We should not even consider checking the row if it is no longer valid,
266  * since it was either deleted (so the deferred check should be skipped)
267  * or updated (in which case only the latest version of the row should be
268  * checked). Test its liveness according to SnapshotSelf. We need pin
269  * and lock on the buffer to call HeapTupleSatisfiesVisibility. Caller
270  * should be holding pin, but not lock.
271  */
272  if (!table_tuple_satisfies_snapshot(trigdata->tg_relation, newslot, SnapshotSelf))
273  return PointerGetDatum(NULL);
274 
275  /*
276  * Get the relation descriptors of the FK and PK tables.
277  *
278  * pk_rel is opened in RowShareLock mode since that's what our eventual
279  * SELECT FOR KEY SHARE will get on it.
280  */
281  fk_rel = trigdata->tg_relation;
282  pk_rel = table_open(riinfo->pk_relid, RowShareLock);
283 
284  switch (ri_NullCheck(RelationGetDescr(fk_rel), newslot, riinfo, false))
285  {
286  case RI_KEYS_ALL_NULL:
287 
288  /*
289  * No further check needed - an all-NULL key passes every type of
290  * foreign key constraint.
291  */
292  table_close(pk_rel, RowShareLock);
293  return PointerGetDatum(NULL);
294 
295  case RI_KEYS_SOME_NULL:
296 
297  /*
298  * This is the only case that differs between the three kinds of
299  * MATCH.
300  */
301  switch (riinfo->confmatchtype)
302  {
303  case FKCONSTR_MATCH_FULL:
304 
305  /*
306  * Not allowed - MATCH FULL says either all or none of the
307  * attributes can be NULLs
308  */
309  ereport(ERROR,
310  (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
311  errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
312  RelationGetRelationName(fk_rel),
313  NameStr(riinfo->conname)),
314  errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
315  errtableconstraint(fk_rel,
316  NameStr(riinfo->conname))));
317  table_close(pk_rel, RowShareLock);
318  return PointerGetDatum(NULL);
319 
321 
322  /*
323  * MATCH SIMPLE - if ANY column is null, the key passes
324  * the constraint.
325  */
326  table_close(pk_rel, RowShareLock);
327  return PointerGetDatum(NULL);
328 
329 #ifdef NOT_USED
331 
332  /*
333  * MATCH PARTIAL - all non-null columns must match. (not
334  * implemented, can be done by modifying the query below
335  * to only include non-null columns, or by writing a
336  * special version here)
337  */
338  break;
339 #endif
340  }
341 
342  case RI_KEYS_NONE_NULL:
343 
344  /*
345  * Have a full qualified key - continue below for all three kinds
346  * of MATCH.
347  */
348  break;
349  }
350 
351  if (SPI_connect() != SPI_OK_CONNECT)
352  elog(ERROR, "SPI_connect failed");
353 
354  /* Fetch or prepare a saved plan for the real check */
356 
357  if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
358  {
359  StringInfoData querybuf;
360  char pkrelname[MAX_QUOTED_REL_NAME_LEN];
362  char paramname[16];
363  const char *querysep;
364  Oid queryoids[RI_MAX_NUMKEYS];
365  const char *pk_only;
366 
367  /* ----------
368  * The query string built is
369  * SELECT 1 FROM [ONLY] <pktable> x WHERE pkatt1 = $1 [AND ...]
370  * FOR KEY SHARE OF x
371  * The type id's for the $ parameters are those of the
372  * corresponding FK attributes.
373  *
374  * But for temporal FKs we need to make sure
375  * the FK's range is completely covered.
376  * So we use this query instead:
377  * SELECT 1
378  * FROM (
379  * SELECT pkperiodatt AS r
380  * FROM [ONLY] pktable x
381  * WHERE pkatt1 = $1 [AND ...]
382  * AND pkperiodatt && $n
383  * FOR KEY SHARE OF x
384  * ) x1
385  * HAVING $n <@ range_agg(x1.r)
386  * Note if FOR KEY SHARE ever allows GROUP BY and HAVING
387  * we can make this a bit simpler.
388  * ----------
389  */
390  initStringInfo(&querybuf);
391  pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
392  "" : "ONLY ";
393  quoteRelationName(pkrelname, pk_rel);
394  if (riinfo->hasperiod)
395  {
397  RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
398 
399  appendStringInfo(&querybuf,
400  "SELECT 1 FROM (SELECT %s AS r FROM %s%s x",
401  attname, pk_only, pkrelname);
402  }
403  else
404  {
405  appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
406  pk_only, pkrelname);
407  }
408  querysep = "WHERE";
409  for (int i = 0; i < riinfo->nkeys; i++)
410  {
411  Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
412  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
413 
415  RIAttName(pk_rel, riinfo->pk_attnums[i]));
416  sprintf(paramname, "$%d", i + 1);
417  ri_GenerateQual(&querybuf, querysep,
418  attname, pk_type,
419  riinfo->pf_eq_oprs[i],
420  paramname, fk_type);
421  querysep = "AND";
422  queryoids[i] = fk_type;
423  }
424  appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
425  if (riinfo->hasperiod)
426  {
427  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[riinfo->nkeys - 1]);
428 
429  appendStringInfo(&querybuf, ") x1 HAVING ");
430  sprintf(paramname, "$%d", riinfo->nkeys);
431  ri_GenerateQual(&querybuf, "",
432  paramname, fk_type,
434  "pg_catalog.range_agg", ANYMULTIRANGEOID);
435  appendStringInfo(&querybuf, "(x1.r)");
436  }
437 
438  /* Prepare and save the plan */
439  qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
440  &qkey, fk_rel, pk_rel);
441  }
442 
443  /*
444  * Now check that foreign key exists in PK table
445  *
446  * XXX detectNewRows must be true when a partitioned table is on the
447  * referenced side. The reason is that our snapshot must be fresh in
448  * order for the hack in find_inheritance_children() to work.
449  */
450  ri_PerformCheck(riinfo, &qkey, qplan,
451  fk_rel, pk_rel,
452  NULL, newslot,
453  pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE,
454  SPI_OK_SELECT);
455 
456  if (SPI_finish() != SPI_OK_FINISH)
457  elog(ERROR, "SPI_finish failed");
458 
459  table_close(pk_rel, RowShareLock);
460 
461  return PointerGetDatum(NULL);
462 }
#define NameStr(name)
Definition: c.h:733
int errdetail(const char *fmt,...)
Definition: elog.c:1205
#define RowShareLock
Definition: lockdefs.h:37
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:5990
#define RI_KEYS_SOME_NULL
Definition: ri_triggers.c:66
#define RI_KEYS_ALL_NULL
Definition: ri_triggers.c:65
#define RI_PLAN_CHECK_LOOKUPPK
Definition: ri_triggers.c:71
#define SnapshotSelf
Definition: snapmgr.h:32
Oid pf_eq_oprs[RI_MAX_NUMKEYS]
Definition: ri_triggers.c:127
static bool table_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Definition: tableam.h:1344

References RI_ConstraintInfo::agged_period_contained_by_oper, appendStringInfo(), appendStringInfoString(), attname, RI_ConstraintInfo::confmatchtype, RI_ConstraintInfo::conname, StringInfoData::data, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, errtableconstraint(), RI_ConstraintInfo::fk_attnums, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, RI_ConstraintInfo::hasperiod, i, initStringInfo(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, NameStr, RI_ConstraintInfo::nkeys, RI_ConstraintInfo::pf_eq_oprs, RI_ConstraintInfo::pk_attnums, RI_ConstraintInfo::pk_relid, PointerGetDatum(), quoteOneName(), quoteRelationName(), RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, ri_BuildQueryKey(), ri_FetchConstraintInfo(), ri_FetchPreparedPlan(), ri_GenerateQual(), RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL, RI_KEYS_SOME_NULL, RI_MAX_NUMKEYS, ri_NullCheck(), ri_PerformCheck(), RI_PLAN_CHECK_LOOKUPPK, ri_PlanCheck(), RIAttName, RIAttType, RowShareLock, SnapshotSelf, SPI_connect(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_OK_SELECT, sprintf, table_close(), table_open(), table_tuple_satisfies_snapshot(), TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, and TRIGGER_FIRED_BY_UPDATE.

Referenced by RI_FKey_check_ins(), and RI_FKey_check_upd().

◆ RI_FKey_check_ins()

Datum RI_FKey_check_ins ( PG_FUNCTION_ARGS  )

Definition at line 471 of file ri_triggers.c.

472 {
473  /* Check that this is a valid trigger call on the right time and event. */
474  ri_CheckTrigger(fcinfo, "RI_FKey_check_ins", RI_TRIGTYPE_INSERT);
475 
476  /* Share code with UPDATE case. */
477  return RI_FKey_check((TriggerData *) fcinfo->context);
478 }
static Datum RI_FKey_check(TriggerData *trigdata)
Definition: ri_triggers.c:247

References ri_CheckTrigger(), RI_FKey_check(), and RI_TRIGTYPE_INSERT.

Referenced by validateForeignKeyConstraint().

◆ RI_FKey_check_upd()

Datum RI_FKey_check_upd ( PG_FUNCTION_ARGS  )

Definition at line 487 of file ri_triggers.c.

488 {
489  /* Check that this is a valid trigger call on the right time and event. */
490  ri_CheckTrigger(fcinfo, "RI_FKey_check_upd", RI_TRIGTYPE_UPDATE);
491 
492  /* Share code with INSERT case. */
493  return RI_FKey_check((TriggerData *) fcinfo->context);
494 }

References ri_CheckTrigger(), RI_FKey_check(), and RI_TRIGTYPE_UPDATE.

◆ RI_FKey_fk_upd_check_required()

bool RI_FKey_fk_upd_check_required ( Trigger trigger,
Relation  fk_rel,
TupleTableSlot oldslot,
TupleTableSlot newslot 
)

Definition at line 1342 of file ri_triggers.c.

1344 {
1345  const RI_ConstraintInfo *riinfo;
1346  int ri_nullcheck;
1347 
1348  /*
1349  * AfterTriggerSaveEvent() handles things such that this function is never
1350  * called for partitioned tables.
1351  */
1352  Assert(fk_rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE);
1353 
1354  riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
1355 
1356  ri_nullcheck = ri_NullCheck(RelationGetDescr(fk_rel), newslot, riinfo, false);
1357 
1358  /*
1359  * If all new key values are NULL, the row satisfies the constraint, so no
1360  * check is needed.
1361  */
1362  if (ri_nullcheck == RI_KEYS_ALL_NULL)
1363  return false;
1364 
1365  /*
1366  * If some new key values are NULL, the behavior depends on the match
1367  * type.
1368  */
1369  else if (ri_nullcheck == RI_KEYS_SOME_NULL)
1370  {
1371  switch (riinfo->confmatchtype)
1372  {
1373  case FKCONSTR_MATCH_SIMPLE:
1374 
1375  /*
1376  * If any new key value is NULL, the row must satisfy the
1377  * constraint, so no check is needed.
1378  */
1379  return false;
1380 
1382 
1383  /*
1384  * Don't know, must run full check.
1385  */
1386  break;
1387 
1388  case FKCONSTR_MATCH_FULL:
1389 
1390  /*
1391  * If some new key values are NULL, the row fails the
1392  * constraint. We must not throw error here, because the row
1393  * might get invalidated before the constraint is to be
1394  * checked, but we should queue the event to apply the check
1395  * later.
1396  */
1397  return true;
1398  }
1399  }
1400 
1401  /*
1402  * Continues here for no new key values are NULL, or we couldn't decide
1403  * yet.
1404  */
1405 
1406  /*
1407  * If the original row was inserted by our own transaction, we must fire
1408  * the trigger whether or not the keys are equal. This is because our
1409  * UPDATE will invalidate the INSERT so that the INSERT RI trigger will
1410  * not do anything; so we had better do the UPDATE check. (We could skip
1411  * this if we knew the INSERT trigger already fired, but there is no easy
1412  * way to know that.)
1413  */
1414  if (slot_is_current_xact_tuple(oldslot))
1415  return true;
1416 
1417  /* If all old and new key values are equal, no check is needed */
1418  if (ri_KeysEqual(fk_rel, oldslot, newslot, riinfo, false))
1419  return false;
1420 
1421  /* Else we need to fire the trigger. */
1422  return true;
1423 }
static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, const RI_ConstraintInfo *riinfo, bool rel_is_pk)
Definition: ri_triggers.c:2897
static bool slot_is_current_xact_tuple(TupleTableSlot *slot)
Definition: tuptable.h:445

References Assert(), RI_ConstraintInfo::confmatchtype, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, RelationData::rd_rel, RelationGetDescr, ri_FetchConstraintInfo(), RI_KEYS_ALL_NULL, RI_KEYS_SOME_NULL, ri_KeysEqual(), ri_NullCheck(), and slot_is_current_xact_tuple().

Referenced by AfterTriggerSaveEvent().

◆ RI_FKey_noaction_del()

Datum RI_FKey_noaction_del ( PG_FUNCTION_ARGS  )

Definition at line 635 of file ri_triggers.c.

636 {
637  /* Check that this is a valid trigger call on the right time and event. */
638  ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
639 
640  /* Share code with RESTRICT/UPDATE cases. */
641  return ri_restrict((TriggerData *) fcinfo->context, true);
642 }
static Datum ri_restrict(TriggerData *trigdata, bool is_no_action)
Definition: ri_triggers.c:708

References ri_CheckTrigger(), ri_restrict(), and RI_TRIGTYPE_DELETE.

◆ RI_FKey_noaction_upd()

Datum RI_FKey_noaction_upd ( PG_FUNCTION_ARGS  )

Definition at line 672 of file ri_triggers.c.

673 {
674  /* Check that this is a valid trigger call on the right time and event. */
675  ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
676 
677  /* Share code with RESTRICT/DELETE cases. */
678  return ri_restrict((TriggerData *) fcinfo->context, true);
679 }

References ri_CheckTrigger(), ri_restrict(), and RI_TRIGTYPE_UPDATE.

◆ RI_FKey_pk_upd_check_required()

bool RI_FKey_pk_upd_check_required ( Trigger trigger,
Relation  pk_rel,
TupleTableSlot oldslot,
TupleTableSlot newslot 
)

Definition at line 1310 of file ri_triggers.c.

1312 {
1313  const RI_ConstraintInfo *riinfo;
1314 
1315  riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true);
1316 
1317  /*
1318  * If any old key value is NULL, the row could not have been referenced by
1319  * an FK row, so no check is needed.
1320  */
1321  if (ri_NullCheck(RelationGetDescr(pk_rel), oldslot, riinfo, true) != RI_KEYS_NONE_NULL)
1322  return false;
1323 
1324  /* If all old and new key values are equal, no check is needed */
1325  if (newslot && ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
1326  return false;
1327 
1328  /* Else we need to fire the trigger. */
1329  return true;
1330 }

References RelationGetDescr, ri_FetchConstraintInfo(), RI_KEYS_NONE_NULL, ri_KeysEqual(), and ri_NullCheck().

Referenced by AfterTriggerSaveEvent().

◆ RI_FKey_restrict_del()

Datum RI_FKey_restrict_del ( PG_FUNCTION_ARGS  )

Definition at line 655 of file ri_triggers.c.

656 {
657  /* Check that this is a valid trigger call on the right time and event. */
658  ri_CheckTrigger(fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
659 
660  /* Share code with NO ACTION/UPDATE cases. */
661  return ri_restrict((TriggerData *) fcinfo->context, false);
662 }

References ri_CheckTrigger(), ri_restrict(), and RI_TRIGTYPE_DELETE.

◆ RI_FKey_restrict_upd()

Datum RI_FKey_restrict_upd ( PG_FUNCTION_ARGS  )

Definition at line 692 of file ri_triggers.c.

693 {
694  /* Check that this is a valid trigger call on the right time and event. */
695  ri_CheckTrigger(fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
696 
697  /* Share code with NO ACTION/DELETE cases. */
698  return ri_restrict((TriggerData *) fcinfo->context, false);
699 }

References ri_CheckTrigger(), ri_restrict(), and RI_TRIGTYPE_UPDATE.

◆ RI_FKey_setdefault_del()

Datum RI_FKey_setdefault_del ( PG_FUNCTION_ARGS  )

Definition at line 1084 of file ri_triggers.c.

1085 {
1086  /* Check that this is a valid trigger call on the right time and event. */
1087  ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
1088 
1089  /* Share code with UPDATE case */
1090  return ri_set((TriggerData *) fcinfo->context, false, RI_TRIGTYPE_DELETE);
1091 }
static Datum ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
Definition: ri_triggers.c:1115

References ri_CheckTrigger(), ri_set(), and RI_TRIGTYPE_DELETE.

◆ RI_FKey_setdefault_upd()

Datum RI_FKey_setdefault_upd ( PG_FUNCTION_ARGS  )

Definition at line 1099 of file ri_triggers.c.

1100 {
1101  /* Check that this is a valid trigger call on the right time and event. */
1102  ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
1103 
1104  /* Share code with DELETE case */
1105  return ri_set((TriggerData *) fcinfo->context, false, RI_TRIGTYPE_UPDATE);
1106 }

References ri_CheckTrigger(), ri_set(), and RI_TRIGTYPE_UPDATE.

◆ RI_FKey_setnull_del()

Datum RI_FKey_setnull_del ( PG_FUNCTION_ARGS  )

Definition at line 1054 of file ri_triggers.c.

1055 {
1056  /* Check that this is a valid trigger call on the right time and event. */
1057  ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
1058 
1059  /* Share code with UPDATE case */
1060  return ri_set((TriggerData *) fcinfo->context, true, RI_TRIGTYPE_DELETE);
1061 }

References ri_CheckTrigger(), ri_set(), and RI_TRIGTYPE_DELETE.

◆ RI_FKey_setnull_upd()

Datum RI_FKey_setnull_upd ( PG_FUNCTION_ARGS  )

Definition at line 1069 of file ri_triggers.c.

1070 {
1071  /* Check that this is a valid trigger call on the right time and event. */
1072  ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
1073 
1074  /* Share code with DELETE case */
1075  return ri_set((TriggerData *) fcinfo->context, true, RI_TRIGTYPE_UPDATE);
1076 }

References ri_CheckTrigger(), ri_set(), and RI_TRIGTYPE_UPDATE.

◆ RI_FKey_trigger_type()

int RI_FKey_trigger_type ( Oid  tgfoid)

Definition at line 3123 of file ri_triggers.c.

3124 {
3125  switch (tgfoid)
3126  {
3127  case F_RI_FKEY_CASCADE_DEL:
3128  case F_RI_FKEY_CASCADE_UPD:
3129  case F_RI_FKEY_RESTRICT_DEL:
3130  case F_RI_FKEY_RESTRICT_UPD:
3131  case F_RI_FKEY_SETNULL_DEL:
3132  case F_RI_FKEY_SETNULL_UPD:
3133  case F_RI_FKEY_SETDEFAULT_DEL:
3134  case F_RI_FKEY_SETDEFAULT_UPD:
3135  case F_RI_FKEY_NOACTION_DEL:
3136  case F_RI_FKEY_NOACTION_UPD:
3137  return RI_TRIGGER_PK;
3138 
3139  case F_RI_FKEY_CHECK_INS:
3140  case F_RI_FKEY_CHECK_UPD:
3141  return RI_TRIGGER_FK;
3142  }
3143 
3144  return RI_TRIGGER_NONE;
3145 }
#define RI_TRIGGER_FK
Definition: trigger.h:283
#define RI_TRIGGER_NONE
Definition: trigger.h:284
#define RI_TRIGGER_PK
Definition: trigger.h:282

References RI_TRIGGER_FK, RI_TRIGGER_NONE, and RI_TRIGGER_PK.

Referenced by AfterTriggerSaveEvent(), ExecCrossPartitionUpdateForeignKey(), GetForeignKeyActionTriggers(), and GetForeignKeyCheckTriggers().

◆ ri_GenerateQual()

static void ri_GenerateQual ( StringInfo  buf,
const char *  sep,
const char *  leftop,
Oid  leftoptype,
Oid  opoid,
const char *  rightop,
Oid  rightoptype 
)
static

Definition at line 1994 of file ri_triggers.c.

1999 {
2000  appendStringInfo(buf, " %s ", sep);
2001  generate_operator_clause(buf, leftop, leftoptype, opoid,
2002  rightop, rightoptype);
2003 }
static char * buf
Definition: pg_test_fsync.c:73
void generate_operator_clause(StringInfo buf, const char *leftop, Oid leftoptype, Oid opoid, const char *rightop, Oid rightoptype)
Definition: ruleutils.c:12761

References appendStringInfo(), buf, and generate_operator_clause().

Referenced by ri_Check_Pk_Match(), RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check(), RI_Initial_Check(), RI_PartitionRemove_Check(), ri_restrict(), and ri_set().

◆ ri_GenerateQualCollation()

static void ri_GenerateQualCollation ( StringInfo  buf,
Oid  collation 
)
static

Definition at line 2023 of file ri_triggers.c.

2024 {
2025  HeapTuple tp;
2026  Form_pg_collation colltup;
2027  char *collname;
2028  char onename[MAX_QUOTED_NAME_LEN];
2029 
2030  /* Nothing to do if it's a noncollatable data type */
2031  if (!OidIsValid(collation))
2032  return;
2033 
2034  tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
2035  if (!HeapTupleIsValid(tp))
2036  elog(ERROR, "cache lookup failed for collation %u", collation);
2037  colltup = (Form_pg_collation) GETSTRUCT(tp);
2038  collname = NameStr(colltup->collname);
2039 
2040  /*
2041  * We qualify the name always, for simplicity and to ensure the query is
2042  * not search-path-dependent.
2043  */
2044  quoteOneName(onename, get_namespace_name(colltup->collnamespace));
2045  appendStringInfo(buf, " COLLATE %s", onename);
2046  quoteOneName(onename, collname);
2047  appendStringInfo(buf, ".%s", onename);
2048 
2049  ReleaseSysCache(tp);
2050 }
FormData_pg_collation * Form_pg_collation
Definition: pg_collation.h:58

References appendStringInfo(), buf, elog, ERROR, get_namespace_name(), GETSTRUCT, HeapTupleIsValid, MAX_QUOTED_NAME_LEN, NameStr, ObjectIdGetDatum(), OidIsValid, quoteOneName(), ReleaseSysCache(), and SearchSysCache1().

Referenced by RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_Initial_Check(), RI_PartitionRemove_Check(), ri_restrict(), and ri_set().

◆ ri_HashCompareOp()

static RI_CompareHashEntry * ri_HashCompareOp ( Oid  eq_opr,
Oid  typeid 
)
static

Definition at line 3030 of file ri_triggers.c.

3031 {
3033  RI_CompareHashEntry *entry;
3034  bool found;
3035 
3036  /*
3037  * On the first call initialize the hashtable
3038  */
3039  if (!ri_compare_cache)
3041 
3042  /*
3043  * Find or create a hash entry. Note we're assuming RI_CompareKey
3044  * contains no struct padding.
3045  */
3046  key.eq_opr = eq_opr;
3047  key.typeid = typeid;
3049  &key,
3050  HASH_ENTER, &found);
3051  if (!found)
3052  entry->valid = false;
3053 
3054  /*
3055  * If not already initialized, do so. Since we'll keep this hash entry
3056  * for the life of the backend, put any subsidiary info for the function
3057  * cache structs into TopMemoryContext.
3058  */
3059  if (!entry->valid)
3060  {
3061  Oid lefttype,
3062  righttype,
3063  castfunc;
3064  CoercionPathType pathtype;
3065 
3066  /* We always need to know how to call the equality operator */
3067  fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo,
3069 
3070  /*
3071  * If we chose to use a cast from FK to PK type, we may have to apply
3072  * the cast function to get to the operator's input type.
3073  *
3074  * XXX eventually it would be good to support array-coercion cases
3075  * here and in ri_CompareWithCast(). At the moment there is no point
3076  * because cases involving nonidentical array types will be rejected
3077  * at constraint creation time.
3078  *
3079  * XXX perhaps also consider supporting CoerceViaIO? No need at the
3080  * moment since that will never be generated for implicit coercions.
3081  */
3082  op_input_types(eq_opr, &lefttype, &righttype);
3083  Assert(lefttype == righttype);
3084  if (typeid == lefttype)
3085  castfunc = InvalidOid; /* simplest case */
3086  else
3087  {
3088  pathtype = find_coercion_pathway(lefttype, typeid,
3090  &castfunc);
3091  if (pathtype != COERCION_PATH_FUNC &&
3092  pathtype != COERCION_PATH_RELABELTYPE)
3093  {
3094  /*
3095  * The declared input type of the eq_opr might be a
3096  * polymorphic type such as ANYARRAY or ANYENUM, or other
3097  * special cases such as RECORD; find_coercion_pathway
3098  * currently doesn't subsume these special cases.
3099  */
3100  if (!IsBinaryCoercible(typeid, lefttype))
3101  elog(ERROR, "no conversion function from %s to %s",
3102  format_type_be(typeid),
3103  format_type_be(lefttype));
3104  }
3105  }
3106  if (OidIsValid(castfunc))
3107  fmgr_info_cxt(castfunc, &entry->cast_func_finfo,
3109  else
3111  entry->valid = true;
3112  }
3113 
3114  return entry;
3115 }
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
@ HASH_ENTER
Definition: hsearch.h:114
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1263
void op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
Definition: lsyscache.c:1336
MemoryContext TopMemoryContext
Definition: mcxt.c:137
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
bool IsBinaryCoercible(Oid srctype, Oid targettype)
CoercionPathType
Definition: parse_coerce.h:25
@ COERCION_PATH_FUNC
Definition: parse_coerce.h:27
@ COERCION_PATH_RELABELTYPE
Definition: parse_coerce.h:28
#define InvalidOid
Definition: postgres_ext.h:36
@ COERCION_IMPLICIT
Definition: primnodes.h:670
static HTAB * ri_compare_cache
Definition: ri_triggers.c:183

References Assert(), RI_CompareHashEntry::cast_func_finfo, COERCION_IMPLICIT, COERCION_PATH_FUNC, COERCION_PATH_RELABELTYPE, elog, RI_CompareHashEntry::eq_opr_finfo, ERROR, find_coercion_pathway(), fmgr_info_cxt(), FmgrInfo::fn_oid, format_type_be(), get_opcode(), HASH_ENTER, hash_search(), InvalidOid, IsBinaryCoercible(), sort-test::key, OidIsValid, op_input_types(), ri_compare_cache, ri_InitHashTables(), TopMemoryContext, and RI_CompareHashEntry::valid.

Referenced by ri_CompareWithCast().

◆ ri_HashPreparedPlan()

static void ri_HashPreparedPlan ( RI_QueryKey key,
SPIPlanPtr  plan 
)
static

Definition at line 2860 of file ri_triggers.c.

2861 {
2862  RI_QueryHashEntry *entry;
2863  bool found;
2864 
2865  /*
2866  * On the first call initialize the hashtable
2867  */
2868  if (!ri_query_cache)
2870 
2871  /*
2872  * Add the new plan. We might be overwriting an entry previously found
2873  * invalid by ri_FetchPreparedPlan.
2874  */
2876  key,
2877  HASH_ENTER, &found);
2878  Assert(!found || entry->plan == NULL);
2879  entry->plan = plan;
2880 }

References Assert(), HASH_ENTER, hash_search(), sort-test::key, RI_QueryHashEntry::plan, plan, ri_InitHashTables(), and ri_query_cache.

Referenced by ri_PlanCheck().

◆ ri_InitHashTables()

static void ri_InitHashTables ( void  )
static

Definition at line 2772 of file ri_triggers.c.

2773 {
2774  HASHCTL ctl;
2775 
2776  ctl.keysize = sizeof(Oid);
2777  ctl.entrysize = sizeof(RI_ConstraintInfo);
2778  ri_constraint_cache = hash_create("RI constraint cache",
2780  &ctl, HASH_ELEM | HASH_BLOBS);
2781 
2782  /* Arrange to flush cache on pg_constraint changes */
2785  (Datum) 0);
2786 
2787  ctl.keysize = sizeof(RI_QueryKey);
2788  ctl.entrysize = sizeof(RI_QueryHashEntry);
2789  ri_query_cache = hash_create("RI query cache",
2791  &ctl, HASH_ELEM | HASH_BLOBS);
2792 
2793  ctl.keysize = sizeof(RI_CompareKey);
2794  ctl.entrysize = sizeof(RI_CompareHashEntry);
2795  ri_compare_cache = hash_create("RI compare cache",
2797  &ctl, HASH_ELEM | HASH_BLOBS);
2798 }
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
void CacheRegisterSyscacheCallback(int cacheid, SyscacheCallbackFunction func, Datum arg)
Definition: inval.c:1516
uintptr_t Datum
Definition: postgres.h:64
tree ctl
Definition: radixtree.h:1807
struct RI_ConstraintInfo RI_ConstraintInfo
static void InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
Definition: ri_triggers.c:2327
struct RI_QueryKey RI_QueryKey
struct RI_CompareHashEntry RI_CompareHashEntry
struct RI_QueryHashEntry RI_QueryHashEntry
#define RI_INIT_QUERYHASHSIZE
Definition: ri_triggers.c:63
#define RI_INIT_CONSTRAINTHASHSIZE
Definition: ri_triggers.c:62
struct RI_CompareKey RI_CompareKey

References CacheRegisterSyscacheCallback(), ctl, HASH_BLOBS, hash_create(), HASH_ELEM, InvalidateConstraintCacheCallBack(), ri_compare_cache, ri_constraint_cache, RI_INIT_CONSTRAINTHASHSIZE, RI_INIT_QUERYHASHSIZE, and ri_query_cache.

Referenced by ri_FetchPreparedPlan(), ri_HashCompareOp(), ri_HashPreparedPlan(), and ri_LoadConstraintInfo().

◆ RI_Initial_Check()

bool RI_Initial_Check ( Trigger trigger,
Relation  fk_rel,
Relation  pk_rel 
)

Definition at line 1443 of file ri_triggers.c.

1444 {
1445  const RI_ConstraintInfo *riinfo;
1446  StringInfoData querybuf;
1447  char pkrelname[MAX_QUOTED_REL_NAME_LEN];
1448  char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1449  char pkattname[MAX_QUOTED_NAME_LEN + 3];
1450  char fkattname[MAX_QUOTED_NAME_LEN + 3];
1451  RangeTblEntry *rte;
1452  RTEPermissionInfo *pk_perminfo;
1453  RTEPermissionInfo *fk_perminfo;
1454  List *rtes = NIL;
1455  List *perminfos = NIL;
1456  const char *sep;
1457  const char *fk_only;
1458  const char *pk_only;
1459  int save_nestlevel;
1460  char workmembuf[32];
1461  int spi_result;
1462  SPIPlanPtr qplan;
1463 
1464  riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
1465 
1466  /*
1467  * Check to make sure current user has enough permissions to do the test
1468  * query. (If not, caller can fall back to the trigger method, which
1469  * works because it changes user IDs on the fly.)
1470  *
1471  * XXX are there any other show-stopper conditions to check?
1472  */
1473  pk_perminfo = makeNode(RTEPermissionInfo);
1474  pk_perminfo->relid = RelationGetRelid(pk_rel);
1475  pk_perminfo->requiredPerms = ACL_SELECT;
1476  perminfos = lappend(perminfos, pk_perminfo);
1477  rte = makeNode(RangeTblEntry);
1478  rte->rtekind = RTE_RELATION;
1479  rte->relid = RelationGetRelid(pk_rel);
1480  rte->relkind = pk_rel->rd_rel->relkind;
1481  rte->rellockmode = AccessShareLock;
1482  rte->perminfoindex = list_length(perminfos);
1483  rtes = lappend(rtes, rte);
1484 
1485  fk_perminfo = makeNode(RTEPermissionInfo);
1486  fk_perminfo->relid = RelationGetRelid(fk_rel);
1487  fk_perminfo->requiredPerms = ACL_SELECT;
1488  perminfos = lappend(perminfos, fk_perminfo);
1489  rte = makeNode(RangeTblEntry);
1490  rte->rtekind = RTE_RELATION;
1491  rte->relid = RelationGetRelid(fk_rel);
1492  rte->relkind = fk_rel->rd_rel->relkind;
1493  rte->rellockmode = AccessShareLock;
1494  rte->perminfoindex = list_length(perminfos);
1495  rtes = lappend(rtes, rte);
1496 
1497  for (int i = 0; i < riinfo->nkeys; i++)
1498  {
1499  int attno;
1500 
1501  attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
1502  pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno);
1503 
1504  attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
1505  fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno);
1506  }
1507 
1508  if (!ExecCheckPermissions(rtes, perminfos, false))
1509  return false;
1510 
1511  /*
1512  * Also punt if RLS is enabled on either table unless this role has the
1513  * bypassrls right or is the table owner of the table(s) involved which
1514  * have RLS enabled.
1515  */
1517  ((pk_rel->rd_rel->relrowsecurity &&
1518  !object_ownercheck(RelationRelationId, RelationGetRelid(pk_rel),
1519  GetUserId())) ||
1520  (fk_rel->rd_rel->relrowsecurity &&
1521  !object_ownercheck(RelationRelationId, RelationGetRelid(fk_rel),
1522  GetUserId()))))
1523  return false;
1524 
1525  /*----------
1526  * The query string built is:
1527  * SELECT fk.keycols FROM [ONLY] relname fk
1528  * LEFT OUTER JOIN [ONLY] pkrelname pk
1529  * ON (pk.pkkeycol1=fk.keycol1 [AND ...])
1530  * WHERE pk.pkkeycol1 IS NULL AND
1531  * For MATCH SIMPLE:
1532  * (fk.keycol1 IS NOT NULL [AND ...])
1533  * For MATCH FULL:
1534  * (fk.keycol1 IS NOT NULL [OR ...])
1535  *
1536  * We attach COLLATE clauses to the operators when comparing columns
1537  * that have different collations.
1538  *----------
1539  */
1540  initStringInfo(&querybuf);
1541  appendStringInfoString(&querybuf, "SELECT ");
1542  sep = "";
1543  for (int i = 0; i < riinfo->nkeys; i++)
1544  {
1545  quoteOneName(fkattname,
1546  RIAttName(fk_rel, riinfo->fk_attnums[i]));
1547  appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
1548  sep = ", ";
1549  }
1550 
1551  quoteRelationName(pkrelname, pk_rel);
1552  quoteRelationName(fkrelname, fk_rel);
1553  fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1554  "" : "ONLY ";
1555  pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1556  "" : "ONLY ";
1557  appendStringInfo(&querybuf,
1558  " FROM %s%s fk LEFT OUTER JOIN %s%s pk ON",
1559  fk_only, fkrelname, pk_only, pkrelname);
1560 
1561  strcpy(pkattname, "pk.");
1562  strcpy(fkattname, "fk.");
1563  sep = "(";
1564  for (int i = 0; i < riinfo->nkeys; i++)
1565  {
1566  Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
1567  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
1568  Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1569  Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
1570 
1571  quoteOneName(pkattname + 3,
1572  RIAttName(pk_rel, riinfo->pk_attnums[i]));
1573  quoteOneName(fkattname + 3,
1574  RIAttName(fk_rel, riinfo->fk_attnums[i]));
1575  ri_GenerateQual(&querybuf, sep,
1576  pkattname, pk_type,
1577  riinfo->pf_eq_oprs[i],
1578  fkattname, fk_type);
1579  if (pk_coll != fk_coll)
1580  ri_GenerateQualCollation(&querybuf, pk_coll);
1581  sep = "AND";
1582  }
1583 
1584  /*
1585  * It's sufficient to test any one pk attribute for null to detect a join
1586  * failure.
1587  */
1588  quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
1589  appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
1590 
1591  sep = "";
1592  for (int i = 0; i < riinfo->nkeys; i++)
1593  {
1594  quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
1595  appendStringInfo(&querybuf,
1596  "%sfk.%s IS NOT NULL",
1597  sep, fkattname);
1598  switch (riinfo->confmatchtype)
1599  {
1600  case FKCONSTR_MATCH_SIMPLE:
1601  sep = " AND ";
1602  break;
1603  case FKCONSTR_MATCH_FULL:
1604  sep = " OR ";
1605  break;
1606  }
1607  }
1608  appendStringInfoChar(&querybuf, ')');
1609 
1610  /*
1611  * Temporarily increase work_mem so that the check query can be executed
1612  * more efficiently. It seems okay to do this because the query is simple
1613  * enough to not use a multiple of work_mem, and one typically would not
1614  * have many large foreign-key validations happening concurrently. So
1615  * this seems to meet the criteria for being considered a "maintenance"
1616  * operation, and accordingly we use maintenance_work_mem. However, we
1617  * must also set hash_mem_multiplier to 1, since it is surely not okay to
1618  * let that get applied to the maintenance_work_mem value.
1619  *
1620  * We use the equivalent of a function SET option to allow the setting to
1621  * persist for exactly the duration of the check query. guc.c also takes
1622  * care of undoing the setting on error.
1623  */
1624  save_nestlevel = NewGUCNestLevel();
1625 
1626  snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
1627  (void) set_config_option("work_mem", workmembuf,
1629  GUC_ACTION_SAVE, true, 0, false);
1630  (void) set_config_option("hash_mem_multiplier", "1",
1632  GUC_ACTION_SAVE, true, 0, false);
1633 
1634  if (SPI_connect() != SPI_OK_CONNECT)
1635  elog(ERROR, "SPI_connect failed");
1636 
1637  /*
1638  * Generate the plan. We don't need to cache it, and there are no
1639  * arguments to the plan.
1640  */
1641  qplan = SPI_prepare(querybuf.data, 0, NULL);
1642 
1643  if (qplan == NULL)
1644  elog(ERROR, "SPI_prepare returned %s for %s",
1646 
1647  /*
1648  * Run the plan. For safety we force a current snapshot to be used. (In
1649  * transaction-snapshot mode, this arguably violates transaction isolation
1650  * rules, but we really haven't got much choice.) We don't need to
1651  * register the snapshot, because SPI_execute_snapshot will see to it. We
1652  * need at most one tuple returned, so pass limit = 1.
1653  */
1654  spi_result = SPI_execute_snapshot(qplan,
1655  NULL, NULL,
1658  true, false, 1);
1659 
1660  /* Check result */
1661  if (spi_result != SPI_OK_SELECT)
1662  elog(ERROR, "SPI_execute_snapshot returned %s", SPI_result_code_string(spi_result));
1663 
1664  /* Did we find a tuple violating the constraint? */
1665  if (SPI_processed > 0)
1666  {
1667  TupleTableSlot *slot;
1668  HeapTuple tuple = SPI_tuptable->vals[0];
1669  TupleDesc tupdesc = SPI_tuptable->tupdesc;
1670  RI_ConstraintInfo fake_riinfo;
1671 
1672  slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
1673 
1674  heap_deform_tuple(tuple, tupdesc,
1675  slot->tts_values, slot->tts_isnull);
1676  ExecStoreVirtualTuple(slot);
1677 
1678  /*
1679  * The columns to look at in the result tuple are 1..N, not whatever
1680  * they are in the fk_rel. Hack up riinfo so that the subroutines
1681  * called here will behave properly.
1682  *
1683  * In addition to this, we have to pass the correct tupdesc to
1684  * ri_ReportViolation, overriding its normal habit of using the pk_rel
1685  * or fk_rel's tupdesc.
1686  */
1687  memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo));
1688  for (int i = 0; i < fake_riinfo.nkeys; i++)
1689  fake_riinfo.fk_attnums[i] = i + 1;
1690 
1691  /*
1692  * If it's MATCH FULL, and there are any nulls in the FK keys,
1693  * complain about that rather than the lack of a match. MATCH FULL
1694  * disallows partially-null FK rows.
1695  */
1696  if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
1697  ri_NullCheck(tupdesc, slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
1698  ereport(ERROR,
1699  (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
1700  errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
1701  RelationGetRelationName(fk_rel),
1702  NameStr(fake_riinfo.conname)),
1703  errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
1704  errtableconstraint(fk_rel,
1705  NameStr(fake_riinfo.conname))));
1706 
1707  /*
1708  * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
1709  * query, which isn't true, but will cause it to use
1710  * fake_riinfo.fk_attnums as we need.
1711  */
1712  ri_ReportViolation(&fake_riinfo,
1713  pk_rel, fk_rel,
1714  slot, tupdesc,
1715  RI_PLAN_CHECK_LOOKUPPK, false);
1716 
1718  }
1719 
1720  if (SPI_finish() != SPI_OK_FINISH)
1721  elog(ERROR, "SPI_finish failed");
1722 
1723  /*
1724  * Restore work_mem and hash_mem_multiplier.
1725  */
1726  AtEOXact_GUC(true, save_nestlevel);
1727 
1728  return true;
1729 }
bool has_bypassrls_privilege(Oid roleid)
Definition: aclchk.c:4230
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4130
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
bool ExecCheckPermissions(List *rangeTable, List *rteperminfos, bool ereport_on_violation)
Definition: execMain.c:579
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1639
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1341
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1325
int maintenance_work_mem
Definition: globals.c:130
int NewGUCNestLevel(void)
Definition: guc.c:2237
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2264
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3333
@ GUC_ACTION_SAVE
Definition: guc.h:201
@ PGC_S_SESSION
Definition: guc.h:122
@ PGC_USERSET
Definition: guc.h:75
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1345
List * lappend(List *list, void *datum)
Definition: list.c:339
#define AccessShareLock
Definition: lockdefs.h:36
Oid GetUserId(void)
Definition: miscinit.c:514
#define makeNode(_type_)
Definition: nodes.h:155
@ RTE_RELATION
Definition: parsenodes.h:1011
#define ACL_SELECT
Definition: parsenodes.h:77
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define snprintf
Definition: port.h:238
static void ri_ReportViolation(const RI_ConstraintInfo *riinfo, Relation pk_rel, Relation fk_rel, TupleTableSlot *violatorslot, TupleDesc tupdesc, int queryno, bool partgone) pg_attribute_noreturn()
Definition: ri_triggers.c:2577
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:291
#define InvalidSnapshot
Definition: snapshot.h:123
uint64 SPI_processed
Definition: spi.c:44
SPITupleTable * SPI_tuptable
Definition: spi.c:45
int SPI_execute_snapshot(SPIPlanPtr plan, Datum *Values, const char *Nulls, Snapshot snapshot, Snapshot crosscheck_snapshot, bool read_only, bool fire_triggers, long tcount)
Definition: spi.c:770
int SPI_result
Definition: spi.c:46
const char * SPI_result_code_string(int code)
Definition: spi.c:1969
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:857
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:194
Definition: pg_list.h:54
Bitmapset * selectedCols
Definition: parsenodes.h:1280
AclMode requiredPerms
Definition: parsenodes.h:1278
RTEKind rtekind
Definition: parsenodes.h:1040
TupleDesc tupdesc
Definition: spi.h:25
HeapTuple * vals
Definition: spi.h:26
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27

References AccessShareLock, ACL_SELECT, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), AtEOXact_GUC(), bms_add_member(), RI_ConstraintInfo::confmatchtype, RI_ConstraintInfo::conname, StringInfoData::data, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, errtableconstraint(), ExecCheckPermissions(), ExecDropSingleTupleTableSlot(), ExecStoreVirtualTuple(), FirstLowInvalidHeapAttributeNumber, RI_ConstraintInfo::fk_attnums, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_SIMPLE, GetLatestSnapshot(), GetUserId(), GUC_ACTION_SAVE, has_bypassrls_privilege(), heap_deform_tuple(), i, if(), initStringInfo(), InvalidSnapshot, lappend(), list_length(), maintenance_work_mem, makeNode, MakeSingleTupleTableSlot(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, NameStr, NewGUCNestLevel(), NIL, RI_ConstraintInfo::nkeys, object_ownercheck(), RI_ConstraintInfo::pf_eq_oprs, PGC_S_SESSION, PGC_USERSET, RI_ConstraintInfo::pk_attnums, quoteOneName(), quoteRelationName(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RangeTblEntry::relid, RTEPermissionInfo::relid, RTEPermissionInfo::requiredPerms, ri_FetchConstraintInfo(), ri_GenerateQual(), ri_GenerateQualCollation(), RI_KEYS_NONE_NULL, ri_NullCheck(), RI_PLAN_CHECK_LOOKUPPK, ri_ReportViolation(), RIAttCollation, RIAttName, RIAttType, RTE_RELATION, RangeTblEntry::rtekind, RTEPermissionInfo::selectedCols, set_config_option(), snprintf, SPI_connect(), SPI_execute_snapshot(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_OK_SELECT, SPI_prepare(), SPI_processed, SPI_result, SPI_result_code_string(), SPI_tuptable, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, TTSOpsVirtual, SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by validateForeignKeyConstraint().

◆ ri_KeysEqual()

static bool ri_KeysEqual ( Relation  rel,
TupleTableSlot oldslot,
TupleTableSlot newslot,
const RI_ConstraintInfo riinfo,
bool  rel_is_pk 
)
static

Definition at line 2897 of file ri_triggers.c.

2899 {
2900  const int16 *attnums;
2901 
2902  if (rel_is_pk)
2903  attnums = riinfo->pk_attnums;
2904  else
2905  attnums = riinfo->fk_attnums;
2906 
2907  /* XXX: could be worthwhile to fetch all necessary attrs at once */
2908  for (int i = 0; i < riinfo->nkeys; i++)
2909  {
2910  Datum oldvalue;
2911  Datum newvalue;
2912  bool isnull;
2913 
2914  /*
2915  * Get one attribute's oldvalue. If it is NULL - they're not equal.
2916  */
2917  oldvalue = slot_getattr(oldslot, attnums[i], &isnull);
2918  if (isnull)
2919  return false;
2920 
2921  /*
2922  * Get one attribute's newvalue. If it is NULL - they're not equal.
2923  */
2924  newvalue = slot_getattr(newslot, attnums[i], &isnull);
2925  if (isnull)
2926  return false;
2927 
2928  if (rel_is_pk)
2929  {
2930  /*
2931  * If we are looking at the PK table, then do a bytewise
2932  * comparison. We must propagate PK changes if the value is
2933  * changed to one that "looks" different but would compare as
2934  * equal using the equality operator. This only makes a
2935  * difference for ON UPDATE CASCADE, but for consistency we treat
2936  * all changes to the PK the same.
2937  */
2938  Form_pg_attribute att = TupleDescAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1);
2939 
2940  if (!datum_image_eq(oldvalue, newvalue, att->attbyval, att->attlen))
2941  return false;
2942  }
2943  else
2944  {
2945  Oid eq_opr;
2946 
2947  /*
2948  * When comparing the PERIOD columns we can skip the check
2949  * whenever the referencing column stayed equal or shrank, so test
2950  * with the contained-by operator instead.
2951  */
2952  if (riinfo->hasperiod && i == riinfo->nkeys - 1)
2953  eq_opr = riinfo->period_contained_by_oper;
2954  else
2955  eq_opr = riinfo->ff_eq_oprs[i];
2956 
2957  /*
2958  * For the FK table, compare with the appropriate equality
2959  * operator. Changes that compare equal will still satisfy the
2960  * constraint after the update.
2961  */
2962  if (!ri_CompareWithCast(eq_opr, RIAttType(rel, attnums[i]),
2963  newvalue, oldvalue))
2964  return false;
2965  }
2966  }
2967 
2968  return true;
2969 }
bool datum_image_eq(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:266
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
static bool ri_CompareWithCast(Oid eq_opr, Oid typeid, Datum lhs, Datum rhs)
Definition: ri_triggers.c:2982
Oid period_contained_by_oper
Definition: ri_triggers.c:130
Oid ff_eq_oprs[RI_MAX_NUMKEYS]
Definition: ri_triggers.c:129
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

References datum_image_eq(), RI_ConstraintInfo::ff_eq_oprs, RI_ConstraintInfo::fk_attnums, RI_ConstraintInfo::hasperiod, i, RI_ConstraintInfo::nkeys, RI_ConstraintInfo::period_contained_by_oper, RI_ConstraintInfo::pk_attnums, ri_CompareWithCast(), RIAttType, slot_getattr(), TupleTableSlot::tts_tupleDescriptor, and TupleDescAttr.

Referenced by RI_FKey_fk_upd_check_required(), and RI_FKey_pk_upd_check_required().

◆ ri_LoadConstraintInfo()

static const RI_ConstraintInfo * ri_LoadConstraintInfo ( Oid  constraintOid)
static

Definition at line 2196 of file ri_triggers.c.

2197 {
2198  RI_ConstraintInfo *riinfo;
2199  bool found;
2200  HeapTuple tup;
2201  Form_pg_constraint conForm;
2202 
2203  /*
2204  * On the first call initialize the hashtable
2205  */
2206  if (!ri_constraint_cache)
2208 
2209  /*
2210  * Find or create a hash entry. If we find a valid one, just return it.
2211  */
2213  &constraintOid,
2214  HASH_ENTER, &found);
2215  if (!found)
2216  riinfo->valid = false;
2217  else if (riinfo->valid)
2218  return riinfo;
2219 
2220  /*
2221  * Fetch the pg_constraint row so we can fill in the entry.
2222  */
2223  tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
2224  if (!HeapTupleIsValid(tup)) /* should not happen */
2225  elog(ERROR, "cache lookup failed for constraint %u", constraintOid);
2226  conForm = (Form_pg_constraint) GETSTRUCT(tup);
2227 
2228  if (conForm->contype != CONSTRAINT_FOREIGN) /* should not happen */
2229  elog(ERROR, "constraint %u is not a foreign key constraint",
2230  constraintOid);
2231 
2232  /* And extract data */
2233  Assert(riinfo->constraint_id == constraintOid);
2234  if (OidIsValid(conForm->conparentid))
2235  riinfo->constraint_root_id =
2236  get_ri_constraint_root(conForm->conparentid);
2237  else
2238  riinfo->constraint_root_id = constraintOid;
2239  riinfo->oidHashValue = GetSysCacheHashValue1(CONSTROID,
2240  ObjectIdGetDatum(constraintOid));
2241  riinfo->rootHashValue = GetSysCacheHashValue1(CONSTROID,
2243  memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData));
2244  riinfo->pk_relid = conForm->confrelid;
2245  riinfo->fk_relid = conForm->conrelid;
2246  riinfo->confupdtype = conForm->confupdtype;
2247  riinfo->confdeltype = conForm->confdeltype;
2248  riinfo->confmatchtype = conForm->confmatchtype;
2249  riinfo->hasperiod = conForm->conperiod;
2250 
2252  &riinfo->nkeys,
2253  riinfo->fk_attnums,
2254  riinfo->pk_attnums,
2255  riinfo->pf_eq_oprs,
2256  riinfo->pp_eq_oprs,
2257  riinfo->ff_eq_oprs,
2258  &riinfo->ndelsetcols,
2259  riinfo->confdelsetcols);
2260 
2261  /*
2262  * For temporal FKs, get the operators and functions we need. We ask the
2263  * opclass of the PK element for these. This all gets cached (as does the
2264  * generated plan), so there's no performance issue.
2265  */
2266  if (riinfo->hasperiod)
2267  {
2268  Oid opclass = get_index_column_opclass(conForm->conindid, riinfo->nkeys);
2269 
2270  FindFKPeriodOpers(opclass,
2271  &riinfo->period_contained_by_oper,
2273  }
2274 
2275  ReleaseSysCache(tup);
2276 
2277  /*
2278  * For efficient processing of invalidation messages below, we keep a
2279  * doubly-linked count list of all currently valid entries.
2280  */
2282 
2283  riinfo->valid = true;
2284 
2285  return riinfo;
2286 }
static void dclist_push_tail(dclist_head *head, dlist_node *node)
Definition: ilist.h:709
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition: lsyscache.c:3490
void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid)
static Oid get_ri_constraint_root(Oid constrOid)
Definition: ri_triggers.c:2293
dlist_node valid_link
Definition: ri_triggers.c:132
int16 confdelsetcols[RI_MAX_NUMKEYS]
Definition: ri_triggers.c:120
Definition: c.h:728
#define GetSysCacheHashValue1(cacheId, key1)
Definition: syscache.h:113

References RI_ConstraintInfo::agged_period_contained_by_oper, Assert(), RI_ConstraintInfo::confdelsetcols, RI_ConstraintInfo::confdeltype, RI_ConstraintInfo::confmatchtype, RI_ConstraintInfo::confupdtype, RI_ConstraintInfo::conname, RI_ConstraintInfo::constraint_id, RI_ConstraintInfo::constraint_root_id, dclist_push_tail(), DeconstructFkConstraintRow(), elog, ERROR, RI_ConstraintInfo::ff_eq_oprs, FindFKPeriodOpers(), RI_ConstraintInfo::fk_attnums, RI_ConstraintInfo::fk_relid, get_index_column_opclass(), get_ri_constraint_root(), GETSTRUCT, GetSysCacheHashValue1, HASH_ENTER, hash_search(), RI_ConstraintInfo::hasperiod, HeapTupleIsValid, RI_ConstraintInfo::ndelsetcols, RI_ConstraintInfo::nkeys, ObjectIdGetDatum(), RI_ConstraintInfo::oidHashValue, OidIsValid, RI_ConstraintInfo::period_contained_by_oper, RI_ConstraintInfo::pf_eq_oprs, RI_ConstraintInfo::pk_attnums, RI_ConstraintInfo::pk_relid, RI_ConstraintInfo::pp_eq_oprs, ReleaseSysCache(), ri_constraint_cache, ri_constraint_cache_valid_list, ri_InitHashTables(), RI_ConstraintInfo::rootHashValue, SearchSysCache1(), RI_ConstraintInfo::valid, and RI_ConstraintInfo::valid_link.

Referenced by ri_FetchConstraintInfo().

◆ ri_NullCheck()

static int ri_NullCheck ( TupleDesc  tupDesc,
TupleTableSlot slot,
const RI_ConstraintInfo riinfo,
bool  rel_is_pk 
)
static

Definition at line 2735 of file ri_triggers.c.

2738 {
2739  const int16 *attnums;
2740  bool allnull = true;
2741  bool nonenull = true;
2742 
2743  if (rel_is_pk)
2744  attnums = riinfo->pk_attnums;
2745  else
2746  attnums = riinfo->fk_attnums;
2747 
2748  for (int i = 0; i < riinfo->nkeys; i++)
2749  {
2750  if (slot_attisnull(slot, attnums[i]))
2751  nonenull = false;
2752  else
2753  allnull = false;
2754  }
2755 
2756  if (allnull)
2757  return RI_KEYS_ALL_NULL;
2758 
2759  if (nonenull)
2760  return RI_KEYS_NONE_NULL;
2761 
2762  return RI_KEYS_SOME_NULL;
2763 }
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition: tuptable.h:381

References RI_ConstraintInfo::fk_attnums, i, RI_ConstraintInfo::nkeys, RI_ConstraintInfo::pk_attnums, RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL, RI_KEYS_SOME_NULL, and slot_attisnull().

Referenced by ri_Check_Pk_Match(), RI_FKey_check(), RI_FKey_fk_upd_check_required(), RI_FKey_pk_upd_check_required(), and RI_Initial_Check().

◆ RI_PartitionRemove_Check()

void RI_PartitionRemove_Check ( Trigger trigger,
Relation  fk_rel,
Relation  pk_rel 
)

Definition at line 1738 of file ri_triggers.c.

1739 {
1740  const RI_ConstraintInfo *riinfo;
1741  StringInfoData querybuf;
1742  char *constraintDef;
1743  char pkrelname[MAX_QUOTED_REL_NAME_LEN];
1744  char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1745  char pkattname[MAX_QUOTED_NAME_LEN + 3];
1746  char fkattname[MAX_QUOTED_NAME_LEN + 3];
1747  const char *sep;
1748  const char *fk_only;
1749  int save_nestlevel;
1750  char workmembuf[32];
1751  int spi_result;
1752  SPIPlanPtr qplan;
1753  int i;
1754 
1755  riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
1756 
1757  /*
1758  * We don't check permissions before displaying the error message, on the
1759  * assumption that the user detaching the partition must have enough
1760  * privileges to examine the table contents anyhow.
1761  */
1762 
1763  /*----------
1764  * The query string built is:
1765  * SELECT fk.keycols FROM [ONLY] relname fk
1766  * JOIN pkrelname pk
1767  * ON (pk.pkkeycol1=fk.keycol1 [AND ...])
1768  * WHERE (<partition constraint>) AND
1769  * For MATCH SIMPLE:
1770  * (fk.keycol1 IS NOT NULL [AND ...])
1771  * For MATCH FULL:
1772  * (fk.keycol1 IS NOT NULL [OR ...])
1773  *
1774  * We attach COLLATE clauses to the operators when comparing columns
1775  * that have different collations.
1776  *----------
1777  */
1778  initStringInfo(&querybuf);
1779  appendStringInfoString(&querybuf, "SELECT ");
1780  sep = "";
1781  for (i = 0; i < riinfo->nkeys; i++)
1782  {
1783  quoteOneName(fkattname,
1784  RIAttName(fk_rel, riinfo->fk_attnums[i]));
1785  appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
1786  sep = ", ";
1787  }
1788 
1789  quoteRelationName(pkrelname, pk_rel);
1790  quoteRelationName(fkrelname, fk_rel);
1791  fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1792  "" : "ONLY ";
1793  appendStringInfo(&querybuf,
1794  " FROM %s%s fk JOIN %s pk ON",
1795  fk_only, fkrelname, pkrelname);
1796  strcpy(pkattname, "pk.");
1797  strcpy(fkattname, "fk.");
1798  sep = "(";
1799  for (i = 0; i < riinfo->nkeys; i++)
1800  {
1801  Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
1802  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
1803  Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1804  Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
1805 
1806  quoteOneName(pkattname + 3,
1807  RIAttName(pk_rel, riinfo->pk_attnums[i]));
1808  quoteOneName(fkattname + 3,
1809  RIAttName(fk_rel, riinfo->fk_attnums[i]));
1810  ri_GenerateQual(&querybuf, sep,
1811  pkattname, pk_type,
1812  riinfo->pf_eq_oprs[i],
1813  fkattname, fk_type);
1814  if (pk_coll != fk_coll)
1815  ri_GenerateQualCollation(&querybuf, pk_coll);
1816  sep = "AND";
1817  }
1818 
1819  /*
1820  * Start the WHERE clause with the partition constraint (except if this is
1821  * the default partition and there's no other partition, because the
1822  * partition constraint is the empty string in that case.)
1823  */
1824  constraintDef = pg_get_partconstrdef_string(RelationGetRelid(pk_rel), "pk");
1825  if (constraintDef && constraintDef[0] != '\0')
1826  appendStringInfo(&querybuf, ") WHERE %s AND (",
1827  constraintDef);
1828  else
1829  appendStringInfoString(&querybuf, ") WHERE (");
1830 
1831  sep = "";
1832  for (i = 0; i < riinfo->nkeys; i++)
1833  {
1834  quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
1835  appendStringInfo(&querybuf,
1836  "%sfk.%s IS NOT NULL",
1837  sep, fkattname);
1838  switch (riinfo->confmatchtype)
1839  {
1840  case FKCONSTR_MATCH_SIMPLE:
1841  sep = " AND ";
1842  break;
1843  case FKCONSTR_MATCH_FULL:
1844  sep = " OR ";
1845  break;
1846  }
1847  }
1848  appendStringInfoChar(&querybuf, ')');
1849 
1850  /*
1851  * Temporarily increase work_mem so that the check query can be executed
1852  * more efficiently. It seems okay to do this because the query is simple
1853  * enough to not use a multiple of work_mem, and one typically would not
1854  * have many large foreign-key validations happening concurrently. So
1855  * this seems to meet the criteria for being considered a "maintenance"
1856  * operation, and accordingly we use maintenance_work_mem. However, we
1857  * must also set hash_mem_multiplier to 1, since it is surely not okay to
1858  * let that get applied to the maintenance_work_mem value.
1859  *
1860  * We use the equivalent of a function SET option to allow the setting to
1861  * persist for exactly the duration of the check query. guc.c also takes
1862  * care of undoing the setting on error.
1863  */
1864  save_nestlevel = NewGUCNestLevel();
1865 
1866  snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
1867  (void) set_config_option("work_mem", workmembuf,
1869  GUC_ACTION_SAVE, true, 0, false);
1870  (void) set_config_option("hash_mem_multiplier", "1",
1872  GUC_ACTION_SAVE, true, 0, false);
1873 
1874  if (SPI_connect() != SPI_OK_CONNECT)
1875  elog(ERROR, "SPI_connect failed");
1876 
1877  /*
1878  * Generate the plan. We don't need to cache it, and there are no
1879  * arguments to the plan.
1880  */
1881  qplan = SPI_prepare(querybuf.data, 0, NULL);
1882 
1883  if (qplan == NULL)
1884  elog(ERROR, "SPI_prepare returned %s for %s",
1886 
1887  /*
1888  * Run the plan. For safety we force a current snapshot to be used. (In
1889  * transaction-snapshot mode, this arguably violates transaction isolation
1890  * rules, but we really haven't got much choice.) We don't need to
1891  * register the snapshot, because SPI_execute_snapshot will see to it. We
1892  * need at most one tuple returned, so pass limit = 1.
1893  */
1894  spi_result = SPI_execute_snapshot(qplan,
1895  NULL, NULL,
1898  true, false, 1);
1899 
1900  /* Check result */
1901  if (spi_result != SPI_OK_SELECT)
1902  elog(ERROR, "SPI_execute_snapshot returned %s", SPI_result_code_string(spi_result));
1903 
1904  /* Did we find a tuple that would violate the constraint? */
1905  if (SPI_processed > 0)
1906  {
1907  TupleTableSlot *slot;
1908  HeapTuple tuple = SPI_tuptable->vals[0];
1909  TupleDesc tupdesc = SPI_tuptable->tupdesc;
1910  RI_ConstraintInfo fake_riinfo;
1911 
1912  slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
1913 
1914  heap_deform_tuple(tuple, tupdesc,
1915  slot->tts_values, slot->tts_isnull);
1916  ExecStoreVirtualTuple(slot);
1917 
1918  /*
1919  * The columns to look at in the result tuple are 1..N, not whatever
1920  * they are in the fk_rel. Hack up riinfo so that ri_ReportViolation
1921  * will behave properly.
1922  *
1923  * In addition to this, we have to pass the correct tupdesc to
1924  * ri_ReportViolation, overriding its normal habit of using the pk_rel
1925  * or fk_rel's tupdesc.
1926  */
1927  memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo));
1928  for (i = 0; i < fake_riinfo.nkeys; i++)
1929  fake_riinfo.pk_attnums[i] = i + 1;
1930 
1931  ri_ReportViolation(&fake_riinfo, pk_rel, fk_rel,
1932  slot, tupdesc, 0, true);
1933  }
1934 
1935  if (SPI_finish() != SPI_OK_FINISH)
1936  elog(ERROR, "SPI_finish failed");
1937 
1938  /*
1939  * Restore work_mem and hash_mem_multiplier.
1940  */
1941  AtEOXact_GUC(true, save_nestlevel);
1942 }
char * pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
Definition: ruleutils.c:2105

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), AtEOXact_GUC(), RI_ConstraintInfo::confmatchtype, StringInfoData::data, elog, ERROR, ExecStoreVirtualTuple(), RI_ConstraintInfo::fk_attnums, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_SIMPLE, GetLatestSnapshot(), GUC_ACTION_SAVE, heap_deform_tuple(), i, initStringInfo(), InvalidSnapshot, maintenance_work_mem, MakeSingleTupleTableSlot(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, NewGUCNestLevel(), RI_ConstraintInfo::nkeys, RI_ConstraintInfo::pf_eq_oprs, pg_get_partconstrdef_string(), PGC_S_SESSION, PGC_USERSET, RI_ConstraintInfo::pk_attnums, quoteOneName(), quoteRelationName(), RelationData::rd_rel, RelationGetRelid, ri_FetchConstraintInfo(), ri_GenerateQual(), ri_GenerateQualCollation(), ri_ReportViolation(), RIAttCollation, RIAttName, RIAttType, set_config_option(), snprintf, SPI_connect(), SPI_execute_snapshot(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_OK_SELECT, SPI_prepare(), SPI_processed, SPI_result, SPI_result_code_string(), SPI_tuptable, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, TTSOpsVirtual, SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by ATDetachCheckNoForeignKeyRefs().

◆ ri_PerformCheck()

static bool ri_PerformCheck ( const RI_ConstraintInfo riinfo,
RI_QueryKey qkey,
SPIPlanPtr  qplan,
Relation  fk_rel,
Relation  pk_rel,
TupleTableSlot oldslot,
TupleTableSlot newslot,
bool  detectNewRows,
int  expect_OK 
)
static

Definition at line 2411 of file ri_triggers.c.

2416 {
2417  Relation query_rel,
2418  source_rel;
2419  bool source_is_pk;
2420  Snapshot test_snapshot;
2421  Snapshot crosscheck_snapshot;
2422  int limit;
2423  int spi_result;
2424  Oid save_userid;
2425  int save_sec_context;
2426  Datum vals[RI_MAX_NUMKEYS * 2];
2427  char nulls[RI_MAX_NUMKEYS * 2];
2428 
2429  /*
2430  * Use the query type code to determine whether the query is run against
2431  * the PK or FK table; we'll do the check as that table's owner
2432  */
2433  if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
2434  query_rel = pk_rel;
2435  else
2436  query_rel = fk_rel;
2437 
2438  /*
2439  * The values for the query are taken from the table on which the trigger
2440  * is called - it is normally the other one with respect to query_rel. An
2441  * exception is ri_Check_Pk_Match(), which uses the PK table for both (and
2442  * sets queryno to RI_PLAN_CHECK_LOOKUPPK_FROM_PK). We might eventually
2443  * need some less klugy way to determine this.
2444  */
2446  {
2447  source_rel = fk_rel;
2448  source_is_pk = false;
2449  }
2450  else
2451  {
2452  source_rel = pk_rel;
2453  source_is_pk = true;
2454  }
2455 
2456  /* Extract the parameters to be passed into the query */
2457  if (newslot)
2458  {
2459  ri_ExtractValues(source_rel, newslot, riinfo, source_is_pk,
2460  vals, nulls);
2461  if (oldslot)
2462  ri_ExtractValues(source_rel, oldslot, riinfo, source_is_pk,
2463  vals + riinfo->nkeys, nulls + riinfo->nkeys);
2464  }
2465  else
2466  {
2467  ri_ExtractValues(source_rel, oldslot, riinfo, source_is_pk,
2468  vals, nulls);
2469  }
2470 
2471  /*
2472  * In READ COMMITTED mode, we just need to use an up-to-date regular
2473  * snapshot, and we will see all rows that could be interesting. But in
2474  * transaction-snapshot mode, we can't change the transaction snapshot. If
2475  * the caller passes detectNewRows == false then it's okay to do the query
2476  * with the transaction snapshot; otherwise we use a current snapshot, and
2477  * tell the executor to error out if it finds any rows under the current
2478  * snapshot that wouldn't be visible per the transaction snapshot. Note
2479  * that SPI_execute_snapshot will register the snapshots, so we don't need
2480  * to bother here.
2481  */
2482  if (IsolationUsesXactSnapshot() && detectNewRows)
2483  {
2484  CommandCounterIncrement(); /* be sure all my own work is visible */
2485  test_snapshot = GetLatestSnapshot();
2486  crosscheck_snapshot = GetTransactionSnapshot();
2487  }
2488  else
2489  {
2490  /* the default SPI behavior is okay */
2491  test_snapshot = InvalidSnapshot;
2492  crosscheck_snapshot = InvalidSnapshot;
2493  }
2494 
2495  /*
2496  * If this is a select query (e.g., for a 'no action' or 'restrict'
2497  * trigger), we only need to see if there is a single row in the table,
2498  * matching the key. Otherwise, limit = 0 - because we want the query to
2499  * affect ALL the matching rows.
2500  */
2501  limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
2502 
2503  /* Switch to proper UID to perform check as */
2504  GetUserIdAndSecContext(&save_userid, &save_sec_context);
2505  SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
2506  save_sec_context | SECURITY_LOCAL_USERID_CHANGE |
2508 
2509  /* Finally we can run the query. */
2510  spi_result = SPI_execute_snapshot(qplan,
2511  vals, nulls,
2512  test_snapshot, crosscheck_snapshot,
2513  false, false, limit);
2514 
2515  /* Restore UID and security context */
2516  SetUserIdAndSecContext(save_userid, save_sec_context);
2517 
2518  /* Check result */
2519  if (spi_result < 0)
2520  elog(ERROR, "SPI_execute_snapshot returned %s", SPI_result_code_string(spi_result));
2521 
2522  if (expect_OK >= 0 && spi_result != expect_OK)
2523  ereport(ERROR,
2524  (errcode(ERRCODE_INTERNAL_ERROR),
2525  errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
2526  RelationGetRelationName(pk_rel),
2527  NameStr(riinfo->conname),
2528  RelationGetRelationName(fk_rel)),
2529  errhint("This is most likely due to a rule having rewritten the query.")));
2530 
2531  /* XXX wouldn't it be clearer to do this part at the caller? */
2533  expect_OK == SPI_OK_SELECT &&
2535  ri_ReportViolation(riinfo,
2536  pk_rel, fk_rel,
2537  newslot ? newslot : oldslot,
2538  NULL,
2539  qkey->constr_queryno, false);
2540 
2541  return SPI_processed != 0;
2542 }
#define SECURITY_NOFORCE_RLS
Definition: miscadmin.h:316
#define SECURITY_LOCAL_USERID_CHANGE
Definition: miscadmin.h:314
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:635
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:642
#define RelationGetForm(relation)
Definition: rel.h:501
#define RI_PLAN_LAST_ON_PK
Definition: ri_triggers.c:73
static void ri_ExtractValues(Relation rel, TupleTableSlot *slot, const RI_ConstraintInfo *riinfo, bool rel_is_pk, Datum *vals, char *nulls)
Definition: ri_triggers.c:2548
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:216
int32 constr_queryno
Definition: ri_triggers.c:143
void CommandCounterIncrement(void)
Definition: xact.c:1097
#define IsolationUsesXactSnapshot()
Definition: xact.h:51

References CommandCounterIncrement(), RI_ConstraintInfo::conname, RI_QueryKey::constr_queryno, elog, ereport, errcode(), errhint(), errmsg(), ERROR, GetLatestSnapshot(), GetTransactionSnapshot(), GetUserIdAndSecContext(), InvalidSnapshot, IsolationUsesXactSnapshot, NameStr, RI_ConstraintInfo::nkeys, RelationGetForm, RelationGetRelationName, ri_ExtractValues(), RI_MAX_NUMKEYS, RI_PLAN_CHECK_LOOKUPPK, RI_PLAN_CHECK_LOOKUPPK_FROM_PK, RI_PLAN_LAST_ON_PK, ri_ReportViolation(), SECURITY_LOCAL_USERID_CHANGE, SECURITY_NOFORCE_RLS, SetUserIdAndSecContext(), SPI_execute_snapshot(), SPI_OK_SELECT, SPI_processed, and SPI_result_code_string().

Referenced by ri_Check_Pk_Match(), RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check(), ri_restrict(), and ri_set().

◆ ri_PlanCheck()

static SPIPlanPtr ri_PlanCheck ( const char *  querystr,
int  nargs,
Oid argtypes,
RI_QueryKey qkey,
Relation  fk_rel,
Relation  pk_rel 
)
static

Definition at line 2368 of file ri_triggers.c.

2370 {
2371  SPIPlanPtr qplan;
2372  Relation query_rel;
2373  Oid save_userid;
2374  int save_sec_context;
2375 
2376  /*
2377  * Use the query type code to determine whether the query is run against
2378  * the PK or FK table; we'll do the check as that table's owner
2379  */
2380  if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
2381  query_rel = pk_rel;
2382  else
2383  query_rel = fk_rel;
2384 
2385  /* Switch to proper UID to perform check as */
2386  GetUserIdAndSecContext(&save_userid, &save_sec_context);
2387  SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
2388  save_sec_context | SECURITY_LOCAL_USERID_CHANGE |
2390 
2391  /* Create the plan */
2392  qplan = SPI_prepare(querystr, nargs, argtypes);
2393 
2394  if (qplan == NULL)
2395  elog(ERROR, "SPI_prepare returned %s for %s", SPI_result_code_string(SPI_result), querystr);
2396 
2397  /* Restore UID and security context */
2398  SetUserIdAndSecContext(save_userid, save_sec_context);
2399 
2400  /* Save the plan */
2401  SPI_keepplan(qplan);
2402  ri_HashPreparedPlan(qkey, qplan);
2403 
2404  return qplan;
2405 }
static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
Definition: ri_triggers.c:2860
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:973

References RI_QueryKey::constr_queryno, elog, ERROR, GetUserIdAndSecContext(), RelationGetForm, ri_HashPreparedPlan(), RI_PLAN_LAST_ON_PK, SECURITY_LOCAL_USERID_CHANGE, SECURITY_NOFORCE_RLS, SetUserIdAndSecContext(), SPI_keepplan(), SPI_prepare(), SPI_result, and SPI_result_code_string().

Referenced by ri_Check_Pk_Match(), RI_FKey_cascade_del(), RI_FKey_cascade_upd(), RI_FKey_check(), ri_restrict(), and ri_set().

◆ ri_ReportViolation()

static void ri_ReportViolation ( const RI_ConstraintInfo riinfo,
Relation  pk_rel,
Relation  fk_rel,
TupleTableSlot violatorslot,
TupleDesc  tupdesc,
int  queryno,
bool  partgone 
)
static

Definition at line 2577 of file ri_triggers.c.

2581 {
2582  StringInfoData key_names;
2583  StringInfoData key_values;
2584  bool onfk;
2585  const int16 *attnums;
2586  Oid rel_oid;
2587  AclResult aclresult;
2588  bool has_perm = true;
2589 
2590  /*
2591  * Determine which relation to complain about. If tupdesc wasn't passed
2592  * by caller, assume the violator tuple came from there.
2593  */
2594  onfk = (queryno == RI_PLAN_CHECK_LOOKUPPK);
2595  if (onfk)
2596  {
2597  attnums = riinfo->fk_attnums;
2598  rel_oid = fk_rel->rd_id;
2599  if (tupdesc == NULL)
2600  tupdesc = fk_rel->rd_att;
2601  }
2602  else
2603  {
2604  attnums = riinfo->pk_attnums;
2605  rel_oid = pk_rel->rd_id;
2606  if (tupdesc == NULL)
2607  tupdesc = pk_rel->rd_att;
2608  }
2609 
2610  /*
2611  * Check permissions- if the user does not have access to view the data in
2612  * any of the key columns then we don't include the errdetail() below.
2613  *
2614  * Check if RLS is enabled on the relation first. If so, we don't return
2615  * any specifics to avoid leaking data.
2616  *
2617  * Check table-level permissions next and, failing that, column-level
2618  * privileges.
2619  *
2620  * When a partition at the referenced side is being detached/dropped, we
2621  * needn't check, since the user must be the table owner anyway.
2622  */
2623  if (partgone)
2624  has_perm = true;
2625  else if (check_enable_rls(rel_oid, InvalidOid, true) != RLS_ENABLED)
2626  {
2627  aclresult = pg_class_aclcheck(rel_oid, GetUserId(), ACL_SELECT);
2628  if (aclresult != ACLCHECK_OK)
2629  {
2630  /* Try for column-level permissions */
2631  for (int idx = 0; idx < riinfo->nkeys; idx++)
2632  {
2633  aclresult = pg_attribute_aclcheck(rel_oid, attnums[idx],
2634  GetUserId(),
2635  ACL_SELECT);
2636 
2637  /* No access to the key */
2638  if (aclresult != ACLCHECK_OK)
2639  {
2640  has_perm = false;
2641  break;
2642  }
2643  }
2644  }
2645  }
2646  else
2647  has_perm = false;
2648 
2649  if (has_perm)
2650  {
2651  /* Get printable versions of the keys involved */
2652  initStringInfo(&key_names);
2653  initStringInfo(&key_values);
2654  for (int idx = 0; idx < riinfo->nkeys; idx++)
2655  {
2656  int fnum = attnums[idx];
2657  Form_pg_attribute att = TupleDescAttr(tupdesc, fnum - 1);
2658  char *name,
2659  *val;
2660  Datum datum;
2661  bool isnull;
2662 
2663  name = NameStr(att->attname);
2664 
2665  datum = slot_getattr(violatorslot, fnum, &isnull);
2666  if (!isnull)
2667  {
2668  Oid foutoid;
2669  bool typisvarlena;
2670 
2671  getTypeOutputInfo(att->atttypid, &foutoid, &typisvarlena);
2672  val = OidOutputFunctionCall(foutoid, datum);
2673  }
2674  else
2675  val = "null";
2676 
2677  if (idx > 0)
2678  {
2679  appendStringInfoString(&key_names, ", ");
2680  appendStringInfoString(&key_values, ", ");
2681  }
2682  appendStringInfoString(&key_names, name);
2683  appendStringInfoString(&key_values, val);
2684  }
2685  }
2686 
2687  if (partgone)
2688  ereport(ERROR,
2689  (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
2690  errmsg("removing partition \"%s\" violates foreign key constraint \"%s\"",
2691  RelationGetRelationName(pk_rel),
2692  NameStr(riinfo->conname)),
2693  errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
2694  key_names.data, key_values.data,
2695  RelationGetRelationName(fk_rel)),
2696  errtableconstraint(fk_rel, NameStr(riinfo->conname))));
2697  else if (onfk)
2698  ereport(ERROR,
2699  (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
2700  errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
2701  RelationGetRelationName(fk_rel),
2702  NameStr(riinfo->conname)),
2703  has_perm ?
2704  errdetail("Key (%s)=(%s) is not present in table \"%s\".",
2705  key_names.data, key_values.data,
2706  RelationGetRelationName(pk_rel)) :
2707  errdetail("Key is not present in table \"%s\".",
2708  RelationGetRelationName(pk_rel)),
2709  errtableconstraint(fk_rel, NameStr(riinfo->conname))));
2710  else
2711  ereport(ERROR,
2712  (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
2713  errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
2714  RelationGetRelationName(pk_rel),
2715  NameStr(riinfo->conname),
2716  RelationGetRelationName(fk_rel)),
2717  has_perm ?
2718  errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
2719  key_names.data, key_values.data,
2720  RelationGetRelationName(fk_rel)) :
2721  errdetail("Key is still referenced from table \"%s\".",
2722  RelationGetRelationName(fk_rel)),
2723  errtableconstraint(fk_rel, NameStr(riinfo->conname))));
2724 }
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3908
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4079
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1763
long val
Definition: informix.c:670
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2885
int check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
Definition: rls.c:52
@ RLS_ENABLED
Definition: rls.h:45
TupleDesc rd_att
Definition: rel.h:112
Oid rd_id
Definition: rel.h:113

References ACL_SELECT, ACLCHECK_OK, appendStringInfoString(), check_enable_rls(), RI_ConstraintInfo::conname, StringInfoData::data, ereport, errcode(), errdetail(), errmsg(), ERROR, errtableconstraint(), RI_ConstraintInfo::fk_attnums, getTypeOutputInfo(), GetUserId(), idx(), initStringInfo(), InvalidOid, name, NameStr, RI_ConstraintInfo::nkeys, OidOutputFunctionCall(), pg_attribute_aclcheck(), pg_class_aclcheck(), RI_ConstraintInfo::pk_attnums, RelationData::rd_att, RelationData::rd_id, RelationGetRelationName, RI_PLAN_CHECK_LOOKUPPK, RLS_ENABLED, slot_getattr(), TupleDescAttr, and val.

Referenced by RI_Initial_Check(), RI_PartitionRemove_Check(), and ri_PerformCheck().

◆ ri_restrict()

static Datum ri_restrict ( TriggerData trigdata,
bool  is_no_action 
)
static

Definition at line 708 of file ri_triggers.c.

709 {
710  const RI_ConstraintInfo *riinfo;
711  Relation fk_rel;
712  Relation pk_rel;
713  TupleTableSlot *oldslot;
714  RI_QueryKey qkey;
715  SPIPlanPtr qplan;
716 
717  riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
718  trigdata->tg_relation, true);
719 
720  /*
721  * Get the relation descriptors of the FK and PK tables and the old tuple.
722  *
723  * fk_rel is opened in RowShareLock mode since that's what our eventual
724  * SELECT FOR KEY SHARE will get on it.
725  */
726  fk_rel = table_open(riinfo->fk_relid, RowShareLock);
727  pk_rel = trigdata->tg_relation;
728  oldslot = trigdata->tg_trigslot;
729 
730  /*
731  * If another PK row now exists providing the old key values, we should
732  * not do anything. However, this check should only be made in the NO
733  * ACTION case; in RESTRICT cases we don't wish to allow another row to be
734  * substituted.
735  */
736  if (is_no_action &&
737  ri_Check_Pk_Match(pk_rel, fk_rel, oldslot, riinfo))
738  {
739  table_close(fk_rel, RowShareLock);
740  return PointerGetDatum(NULL);
741  }
742 
743  if (SPI_connect() != SPI_OK_CONNECT)
744  elog(ERROR, "SPI_connect failed");
745 
746  /*
747  * Fetch or prepare a saved plan for the restrict lookup (it's the same
748  * query for delete and update cases)
749  */
750  ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT);
751 
752  if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
753  {
754  StringInfoData querybuf;
755  char fkrelname[MAX_QUOTED_REL_NAME_LEN];
757  char paramname[16];
758  const char *querysep;
759  Oid queryoids[RI_MAX_NUMKEYS];
760  const char *fk_only;
761 
762  /* ----------
763  * The query string built is
764  * SELECT 1 FROM [ONLY] <fktable> x WHERE $1 = fkatt1 [AND ...]
765  * FOR KEY SHARE OF x
766  * The type id's for the $ parameters are those of the
767  * corresponding PK attributes.
768  * ----------
769  */
770  initStringInfo(&querybuf);
771  fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
772  "" : "ONLY ";
773  quoteRelationName(fkrelname, fk_rel);
774  appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
775  fk_only, fkrelname);
776  querysep = "WHERE";
777  for (int i = 0; i < riinfo->nkeys; i++)
778  {
779  Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
780  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
781  Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
782  Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
783 
785  RIAttName(fk_rel, riinfo->fk_attnums[i]));
786  sprintf(paramname, "$%d", i + 1);
787  ri_GenerateQual(&querybuf, querysep,
788  paramname, pk_type,
789  riinfo->pf_eq_oprs[i],
790  attname, fk_type);
791  if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
792  ri_GenerateQualCollation(&querybuf, pk_coll);
793  querysep = "AND";
794  queryoids[i] = pk_type;
795  }
796  appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
797 
798  /* Prepare and save the plan */
799  qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
800  &qkey, fk_rel, pk_rel);
801  }
802 
803  /*
804  * We have a plan now. Run it to check for existing references.
805  */
806  ri_PerformCheck(riinfo, &qkey, qplan,
807  fk_rel, pk_rel,
808  oldslot, NULL,
809  true, /* must detect new rows */
810  SPI_OK_SELECT);
811 
812  if (SPI_finish() != SPI_OK_FINISH)
813  elog(ERROR, "SPI_finish failed");
814 
815  table_close(fk_rel, RowShareLock);
816 
817  return PointerGetDatum(NULL);
818 }
#define RI_PLAN_RESTRICT
Definition: ri_triggers.c:78
static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, TupleTableSlot *oldslot, const RI_ConstraintInfo *riinfo)
Definition: ri_triggers.c:508

References appendStringInfo(), appendStringInfoString(), attname, StringInfoData::data, elog, ERROR, RI_ConstraintInfo::fk_attnums, RI_ConstraintInfo::fk_relid, get_collation_isdeterministic(), i, initStringInfo(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, RI_ConstraintInfo::nkeys, RI_ConstraintInfo::pf_eq_oprs, RI_ConstraintInfo::pk_attnums, PointerGetDatum(), quoteOneName(), quoteRelationName(), RelationData::rd_rel, ri_BuildQueryKey(), ri_Check_Pk_Match(), ri_FetchConstraintInfo(), ri_FetchPreparedPlan(), ri_GenerateQual(), ri_GenerateQualCollation(), RI_MAX_NUMKEYS, ri_PerformCheck(), RI_PLAN_RESTRICT, ri_PlanCheck(), RIAttCollation, RIAttName, RIAttType, RowShareLock, SPI_connect(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_OK_SELECT, sprintf, table_close(), table_open(), TriggerData::tg_relation, TriggerData::tg_trigger, and TriggerData::tg_trigslot.

Referenced by RI_FKey_noaction_del(), RI_FKey_noaction_upd(), RI_FKey_restrict_del(), RI_FKey_restrict_upd(), and ri_set().

◆ ri_set()

static Datum ri_set ( TriggerData trigdata,
bool  is_set_null,
int  tgkind 
)
static

Definition at line 1115 of file ri_triggers.c.

1116 {
1117  const RI_ConstraintInfo *riinfo;
1118  Relation fk_rel;
1119  Relation pk_rel;
1120  TupleTableSlot *oldslot;
1121  RI_QueryKey qkey;
1122  SPIPlanPtr qplan;
1123  int32 queryno;
1124 
1125  riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
1126  trigdata->tg_relation, true);
1127 
1128  /*
1129  * Get the relation descriptors of the FK and PK tables and the old tuple.
1130  *
1131  * fk_rel is opened in RowExclusiveLock mode since that's what our
1132  * eventual UPDATE will get on it.
1133  */
1134  fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
1135  pk_rel = trigdata->tg_relation;
1136  oldslot = trigdata->tg_trigslot;
1137 
1138  if (SPI_connect() != SPI_OK_CONNECT)
1139  elog(ERROR, "SPI_connect failed");
1140 
1141  /*
1142  * Fetch or prepare a saved plan for the trigger.
1143  */
1144  switch (tgkind)
1145  {
1146  case RI_TRIGTYPE_UPDATE:
1147  queryno = is_set_null
1150  break;
1151  case RI_TRIGTYPE_DELETE:
1152  queryno = is_set_null
1155  break;
1156  default:
1157  elog(ERROR, "invalid tgkind passed to ri_set");
1158  }
1159 
1160  ri_BuildQueryKey(&qkey, riinfo, queryno);
1161 
1162  if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1163  {
1164  StringInfoData querybuf;
1165  char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1167  char paramname[16];
1168  const char *querysep;
1169  const char *qualsep;
1170  Oid queryoids[RI_MAX_NUMKEYS];
1171  const char *fk_only;
1172  int num_cols_to_set;
1173  const int16 *set_cols;
1174 
1175  switch (tgkind)
1176  {
1177  case RI_TRIGTYPE_UPDATE:
1178  num_cols_to_set = riinfo->nkeys;
1179  set_cols = riinfo->fk_attnums;
1180  break;
1181  case RI_TRIGTYPE_DELETE:
1182 
1183  /*
1184  * If confdelsetcols are present, then we only update the
1185  * columns specified in that array, otherwise we update all
1186  * the referencing columns.
1187  */
1188  if (riinfo->ndelsetcols != 0)
1189  {
1190  num_cols_to_set = riinfo->ndelsetcols;
1191  set_cols = riinfo->confdelsetcols;
1192  }
1193  else
1194  {
1195  num_cols_to_set = riinfo->nkeys;
1196  set_cols = riinfo->fk_attnums;
1197  }
1198  break;
1199  default:
1200  elog(ERROR, "invalid tgkind passed to ri_set");
1201  }
1202 
1203  /* ----------
1204  * The query string built is
1205  * UPDATE [ONLY] <fktable> SET fkatt1 = {NULL|DEFAULT} [, ...]
1206  * WHERE $1 = fkatt1 [AND ...]
1207  * The type id's for the $ parameters are those of the
1208  * corresponding PK attributes.
1209  * ----------
1210  */
1211  initStringInfo(&querybuf);
1212  fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1213  "" : "ONLY ";
1214  quoteRelationName(fkrelname, fk_rel);
1215  appendStringInfo(&querybuf, "UPDATE %s%s SET",
1216  fk_only, fkrelname);
1217 
1218  /*
1219  * Add assignment clauses
1220  */
1221  querysep = "";
1222  for (int i = 0; i < num_cols_to_set; i++)
1223  {
1224  quoteOneName(attname, RIAttName(fk_rel, set_cols[i]));
1225  appendStringInfo(&querybuf,
1226  "%s %s = %s",
1227  querysep, attname,
1228  is_set_null ? "NULL" : "DEFAULT");
1229  querysep = ",";
1230  }
1231 
1232  /*
1233  * Add WHERE clause
1234  */
1235  qualsep = "WHERE";
1236  for (int i = 0; i < riinfo->nkeys; i++)
1237  {
1238  Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
1239  Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
1240  Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1241  Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
1242 
1244  RIAttName(fk_rel, riinfo->fk_attnums[i]));
1245 
1246  sprintf(paramname, "$%d", i + 1);
1247  ri_GenerateQual(&querybuf, qualsep,
1248  paramname, pk_type,
1249  riinfo->pf_eq_oprs[i],
1250  attname, fk_type);
1251  if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
1252  ri_GenerateQualCollation(&querybuf, pk_coll);
1253  qualsep = "AND";
1254  queryoids[i] = pk_type;
1255  }
1256 
1257  /* Prepare and save the plan */
1258  qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
1259  &qkey, fk_rel, pk_rel);
1260  }
1261 
1262  /*
1263  * We have a plan now. Run it to update the existing references.
1264  */
1265  ri_PerformCheck(riinfo, &qkey, qplan,
1266  fk_rel, pk_rel,
1267  oldslot, NULL,
1268  true, /* must detect new rows */
1269  SPI_OK_UPDATE);
1270 
1271  if (SPI_finish() != SPI_OK_FINISH)
1272  elog(ERROR, "SPI_finish failed");
1273 
1274  table_close(fk_rel, RowExclusiveLock);
1275 
1276  if (is_set_null)
1277  return PointerGetDatum(NULL);
1278  else
1279  {
1280  /*
1281  * If we just deleted or updated the PK row whose key was equal to the
1282  * FK columns' default values, and a referencing row exists in the FK
1283  * table, we would have updated that row to the same values it already
1284  * had --- and RI_FKey_fk_upd_check_required would hence believe no
1285  * check is necessary. So we need to do another lookup now and in
1286  * case a reference still exists, abort the operation. That is
1287  * already implemented in the NO ACTION trigger, so just run it. (This
1288  * recheck is only needed in the SET DEFAULT case, since CASCADE would
1289  * remove such rows in case of a DELETE operation or would change the
1290  * FK key values in case of an UPDATE, while SET NULL is certain to
1291  * result in rows that satisfy the FK constraint.)
1292  */
1293  return ri_restrict(trigdata, true);
1294  }
1295 }
signed int int32
Definition: c.h:481
#define RI_PLAN_SETNULL_ONUPDATE
Definition: ri_triggers.c:80
#define RI_PLAN_SETDEFAULT_ONDELETE
Definition: ri_triggers.c:81
#define RI_PLAN_SETDEFAULT_ONUPDATE
Definition: ri_triggers.c:82
#define RI_PLAN_SETNULL_ONDELETE
Definition: ri_triggers.c:79

References appendStringInfo(), attname, RI_ConstraintInfo::confdelsetcols, StringInfoData::data, elog, ERROR, RI_ConstraintInfo::fk_attnums, RI_ConstraintInfo::fk_relid, get_collation_isdeterministic(), i, initStringInfo(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, RI_ConstraintInfo::ndelsetcols, RI_ConstraintInfo::nkeys, RI_ConstraintInfo::pf_eq_oprs, RI_ConstraintInfo::pk_attnums, PointerGetDatum(), quoteOneName(), quoteRelationName(), RelationData::rd_rel, ri_BuildQueryKey(), ri_FetchConstraintInfo(), ri_FetchPreparedPlan(), ri_GenerateQual(), ri_GenerateQualCollation(), RI_MAX_NUMKEYS, ri_PerformCheck(), RI_PLAN_SETDEFAULT_ONDELETE, RI_PLAN_SETDEFAULT_ONUPDATE, RI_PLAN_SETNULL_ONDELETE, RI_PLAN_SETNULL_ONUPDATE, ri_PlanCheck(), ri_restrict(), RI_TRIGTYPE_DELETE, RI_TRIGTYPE_UPDATE, RIAttCollation, RIAttName, RIAttType, RowExclusiveLock, SPI_connect(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_OK_UPDATE, sprintf, table_close(), table_open(), TriggerData::tg_relation, TriggerData::tg_trigger, and TriggerData::tg_trigslot.

Referenced by RI_FKey_setdefault_del(), RI_FKey_setdefault_upd(), RI_FKey_setnull_del(), and RI_FKey_setnull_upd().

Variable Documentation

◆ ri_compare_cache

HTAB* ri_compare_cache = NULL
static

Definition at line 183 of file ri_triggers.c.

Referenced by ri_HashCompareOp(), and ri_InitHashTables().

◆ ri_constraint_cache

HTAB* ri_constraint_cache = NULL
static

◆ ri_constraint_cache_valid_list

dclist_head ri_constraint_cache_valid_list
static

Definition at line 184 of file ri_triggers.c.

Referenced by InvalidateConstraintCacheCallBack(), and ri_LoadConstraintInfo().

◆ ri_query_cache

HTAB* ri_query_cache = NULL
static

Definition at line 182 of file ri_triggers.c.

Referenced by ri_FetchPreparedPlan(), ri_HashPreparedPlan(), and ri_InitHashTables().