Re: Huge memory consumption on partitioned table with FKs - Mailing list pgsql-hackers

From Kyotaro Horiguchi
Subject Re: Huge memory consumption on partitioned table with FKs
Date
Msg-id 20201204.120522.2170603007747047845.horikyota.ntt@gmail.com
Whole thread Raw
In response to Re: Huge memory consumption on partitioned table with FKs  (Alvaro Herrera <alvherre@alvh.no-ip.org>)
Responses Re: Huge memory consumption on partitioned table with FKs
List pgsql-hackers
Thanks, but sorry for the confusion.

I intended just to show how it looks like if we share
RI_ConstraintInfo among partition relations.

At Thu, 3 Dec 2020 10:22:47 -0300, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote in 
> Hello
> 
> I haven't followed this thread's latest posts, but I'm unclear on the
> lifetime of the new struct that's being allocated in TopMemoryContext.
> At what point are those structs freed?

The choice of memory context is tentative and in order to shrink the
patch'es footprint. I think we don't use CurrentDynaHashCxt for the
additional struct so a context for this use is needed.

The struct is freed only when the parent struct (RI_ConstraintInfo) is
found to be able to share the child struct (RI_ConstraintParam) with
the parent constraint.  It seems like inefficient (or tending to make
"hole"s in the heap area) but I chose it just to shrink the footprint.

We could create the new RI_ConstraintInfo on stack then copy it to the
cache after we find that the RI_ConstraintInfo needs its own
RI_ConstriantParam.

> Also, the comment that was in RI_ConstraintInfo now appears in
> RI_ConstraintParam, and the new struct (RI_ConstraintInfo) is now
> undocumented.  What is the relationship between those two structs?  I
> see that they have pointers to each other, but I think the relationship
> should be documented more clearly.

I'm not sure the footprint of this patch worth doing but here is a bit
more polished version.

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
From 55946e09d33fc7fa43bed04ef548bf8a3f67155d Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyoga.ntt@gmail.com>
Date: Thu, 3 Dec 2020 16:15:57 +0900
Subject: [PATCH 1/2] separte riinfo

---
 src/backend/utils/adt/ri_triggers.c | 290 ++++++++++++++++------------
 1 file changed, 168 insertions(+), 122 deletions(-)

diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 02b1a3868f..0306bf7739 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -97,14 +97,29 @@
  * Information extracted from an FK pg_constraint entry.  This is cached in
  * ri_constraint_cache.
  */
+struct RI_ConstraintParam;
+
 typedef struct RI_ConstraintInfo
 {
-    Oid            constraint_id;    /* OID of pg_constraint entry (hash key) */
+    Oid            constraint_id;
     bool        valid;            /* successfully initialized? */
     uint32        oidHashValue;    /* hash value of pg_constraint OID */
     NameData    conname;        /* name of the FK constraint */
-    Oid            pk_relid;        /* referenced relation */
     Oid            fk_relid;        /* referencing relation */
+    struct RI_ConstraintParam *param;    /* sharable part  */
+    dlist_node    valid_link;        /* Link in list of valid entries */
+} RI_ConstraintInfo;
+
+/*
+ * RI_ConstraintParam
+ *
+ * The part sharable among relations in a partitioned table of the cached
+ * constraint information.
+ */
+typedef struct RI_ConstraintParam
+{
+    /* begin with identity members */
+    Oid            pk_relid;        /* referenced relation */
     char        confupdtype;    /* foreign key's ON UPDATE action */
     char        confdeltype;    /* foreign key's ON DELETE action */
     char        confmatchtype;    /* foreign key's match type */
@@ -114,8 +129,12 @@ typedef struct RI_ConstraintInfo
     Oid            pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
     Oid            pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
     Oid            ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
-    dlist_node    valid_link;        /* Link in list of valid entries */
-} RI_ConstraintInfo;
+
+    /* These should be at the end of struct, see ri_LoadConstraintInfo */
+    Oid            query_key;        /* key for planned statment */
+    RI_ConstraintInfo *ownerinfo; /* owner RI_ConstraintInfo of this param */
+} RI_ConstraintParam;
+
 
 /*
  * RI_QueryKey
@@ -163,6 +182,7 @@ typedef struct RI_CompareHashEntry
 /*
  * Local data
  */
+static MemoryContext ri_constraint_cache_cxt = NULL;
 static HTAB *ri_constraint_cache = NULL;
 static HTAB *ri_query_cache = NULL;
 static HTAB *ri_compare_cache = NULL;
@@ -264,7 +284,7 @@ RI_FKey_check(TriggerData *trigdata)
      * SELECT FOR KEY SHARE will get on it.
      */
     fk_rel = trigdata->tg_relation;
-    pk_rel = table_open(riinfo->pk_relid, RowShareLock);
+    pk_rel = table_open(riinfo->param->pk_relid, RowShareLock);
 
     switch (ri_NullCheck(RelationGetDescr(fk_rel), newslot, riinfo, false))
     {
@@ -283,7 +303,7 @@ RI_FKey_check(TriggerData *trigdata)
              * This is the only case that differs between the three kinds of
              * MATCH.
              */
-            switch (riinfo->confmatchtype)
+            switch (riinfo->param->confmatchtype)
             {
                 case FKCONSTR_MATCH_FULL:
 
@@ -364,17 +384,17 @@ RI_FKey_check(TriggerData *trigdata)
         appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
                          pk_only, pkrelname);
         querysep = "WHERE";
-        for (int i = 0; i < riinfo->nkeys; i++)
+        for (int i = 0; i < riinfo->param->nkeys; i++)
         {
-            Oid            pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
+            Oid            pk_type = RIAttType(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid            fk_type = RIAttType(fk_rel, riinfo->param->fk_attnums[i]);
 
             quoteOneName(attname,
-                         RIAttName(pk_rel, riinfo->pk_attnums[i]));
+                         RIAttName(pk_rel, riinfo->param->pk_attnums[i]));
             sprintf(paramname, "$%d", i + 1);
             ri_GenerateQual(&querybuf, querysep,
                             attname, pk_type,
-                            riinfo->pf_eq_oprs[i],
+                            riinfo->param->pf_eq_oprs[i],
                             paramname, fk_type);
             querysep = "AND";
             queryoids[i] = fk_type;
@@ -382,7 +402,7 @@ RI_FKey_check(TriggerData *trigdata)
         appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
 
         /* Prepare and save the plan */
-        qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+        qplan = ri_PlanCheck(querybuf.data, riinfo->param->nkeys, queryoids,
                              &qkey, fk_rel, pk_rel);
     }
 
@@ -492,16 +512,16 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
         appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
                          pk_only, pkrelname);
         querysep = "WHERE";
-        for (int i = 0; i < riinfo->nkeys; i++)
+        for (int i = 0; i < riinfo->param->nkeys; i++)
         {
-            Oid            pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
+            Oid    pk_type = RIAttType(pk_rel, riinfo->param->pk_attnums[i]);
 
             quoteOneName(attname,
-                         RIAttName(pk_rel, riinfo->pk_attnums[i]));
+                         RIAttName(pk_rel, riinfo->param->pk_attnums[i]));
             sprintf(paramname, "$%d", i + 1);
             ri_GenerateQual(&querybuf, querysep,
                             attname, pk_type,
-                            riinfo->pp_eq_oprs[i],
+                            riinfo->param->pp_eq_oprs[i],
                             paramname, pk_type);
             querysep = "AND";
             queryoids[i] = pk_type;
@@ -509,7 +529,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
         appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
 
         /* Prepare and save the plan */
-        qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+        qplan = ri_PlanCheck(querybuf.data, riinfo->param->nkeys, queryoids,
                              &qkey, fk_rel, pk_rel);
     }
 
@@ -679,19 +699,19 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
         appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
                          fk_only, fkrelname);
         querysep = "WHERE";
-        for (int i = 0; i < riinfo->nkeys; i++)
+        for (int i = 0; i < riinfo->param->nkeys; i++)
         {
-            Oid            pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-            Oid            pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
+            Oid pk_type = RIAttType(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid fk_type = RIAttType(fk_rel, riinfo->param->fk_attnums[i]);
+            Oid pk_coll = RIAttCollation(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid fk_coll = RIAttCollation(fk_rel, riinfo->param->fk_attnums[i]);
 
             quoteOneName(attname,
-                         RIAttName(fk_rel, riinfo->fk_attnums[i]));
+                         RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
             sprintf(paramname, "$%d", i + 1);
             ri_GenerateQual(&querybuf, querysep,
                             paramname, pk_type,
-                            riinfo->pf_eq_oprs[i],
+                            riinfo->param->pf_eq_oprs[i],
                             attname, fk_type);
             if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
                 ri_GenerateQualCollation(&querybuf, pk_coll);
@@ -701,7 +721,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
         appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
 
         /* Prepare and save the plan */
-        qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+        qplan = ri_PlanCheck(querybuf.data, riinfo->param->nkeys, queryoids,
                              &qkey, fk_rel, pk_rel);
     }
 
@@ -785,19 +805,19 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
         appendStringInfo(&querybuf, "DELETE FROM %s%s",
                          fk_only, fkrelname);
         querysep = "WHERE";
-        for (int i = 0; i < riinfo->nkeys; i++)
+        for (int i = 0; i < riinfo->param->nkeys; i++)
         {
-            Oid            pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-            Oid            pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
+            Oid pk_type = RIAttType(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid fk_type = RIAttType(fk_rel, riinfo->param->fk_attnums[i]);
+            Oid pk_coll = RIAttCollation(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid fk_coll = RIAttCollation(fk_rel, riinfo->param->fk_attnums[i]);
 
             quoteOneName(attname,
-                         RIAttName(fk_rel, riinfo->fk_attnums[i]));
+                         RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
             sprintf(paramname, "$%d", i + 1);
             ri_GenerateQual(&querybuf, querysep,
                             paramname, pk_type,
-                            riinfo->pf_eq_oprs[i],
+                            riinfo->param->pf_eq_oprs[i],
                             attname, fk_type);
             if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
                 ri_GenerateQualCollation(&querybuf, pk_coll);
@@ -806,7 +826,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
         }
 
         /* Prepare and save the plan */
-        qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+        qplan = ri_PlanCheck(querybuf.data, riinfo->param->nkeys, queryoids,
                              &qkey, fk_rel, pk_rel);
     }
 
@@ -901,22 +921,24 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
                          fk_only, fkrelname);
         querysep = "";
         qualsep = "WHERE";
-        for (int i = 0, j = riinfo->nkeys; i < riinfo->nkeys; i++, j++)
+        for (int i = 0, j = riinfo->param->nkeys;
+             i < riinfo->param->nkeys;
+             i++, j++)
         {
-            Oid            pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-            Oid            pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
+            Oid pk_type = RIAttType(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid fk_type = RIAttType(fk_rel, riinfo->param->fk_attnums[i]);
+            Oid pk_coll = RIAttCollation(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid fk_coll = RIAttCollation(fk_rel, riinfo->param->fk_attnums[i]);
 
             quoteOneName(attname,
-                         RIAttName(fk_rel, riinfo->fk_attnums[i]));
+                         RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
             appendStringInfo(&querybuf,
                              "%s %s = $%d",
                              querysep, attname, i + 1);
             sprintf(paramname, "$%d", j + 1);
             ri_GenerateQual(&qualbuf, qualsep,
                             paramname, pk_type,
-                            riinfo->pf_eq_oprs[i],
+                            riinfo->param->pf_eq_oprs[i],
                             attname, fk_type);
             if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
                 ri_GenerateQualCollation(&querybuf, pk_coll);
@@ -928,7 +950,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
         appendBinaryStringInfo(&querybuf, qualbuf.data, qualbuf.len);
 
         /* Prepare and save the plan */
-        qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids,
+        qplan = ri_PlanCheck(querybuf.data, riinfo->param->nkeys * 2, queryoids,
                              &qkey, fk_rel, pk_rel);
     }
 
@@ -1080,15 +1102,15 @@ ri_set(TriggerData *trigdata, bool is_set_null)
                          fk_only, fkrelname);
         querysep = "";
         qualsep = "WHERE";
-        for (int i = 0; i < riinfo->nkeys; i++)
+        for (int i = 0; i < riinfo->param->nkeys; i++)
         {
-            Oid            pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-            Oid            pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
-            Oid            fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
+            Oid pk_type = RIAttType(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid fk_type = RIAttType(fk_rel, riinfo->param->fk_attnums[i]);
+            Oid pk_coll = RIAttCollation(pk_rel, riinfo->param->pk_attnums[i]);
+            Oid fk_coll = RIAttCollation(fk_rel, riinfo->param->fk_attnums[i]);
 
             quoteOneName(attname,
-                         RIAttName(fk_rel, riinfo->fk_attnums[i]));
+                         RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
             appendStringInfo(&querybuf,
                              "%s %s = %s",
                              querysep, attname,
@@ -1096,7 +1118,7 @@ ri_set(TriggerData *trigdata, bool is_set_null)
             sprintf(paramname, "$%d", i + 1);
             ri_GenerateQual(&qualbuf, qualsep,
                             paramname, pk_type,
-                            riinfo->pf_eq_oprs[i],
+                            riinfo->param->pf_eq_oprs[i],
                             attname, fk_type);
             if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
                 ri_GenerateQualCollation(&querybuf, pk_coll);
@@ -1107,7 +1129,7 @@ ri_set(TriggerData *trigdata, bool is_set_null)
         appendBinaryStringInfo(&querybuf, qualbuf.data, qualbuf.len);
 
         /* Prepare and save the plan */
-        qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+        qplan = ri_PlanCheck(querybuf.data, riinfo->param->nkeys, queryoids,
                              &qkey, fk_rel, pk_rel);
     }
 
@@ -1217,7 +1239,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
      */
     else if (ri_nullcheck == RI_KEYS_SOME_NULL)
     {
-        switch (riinfo->confmatchtype)
+        switch (riinfo->param->confmatchtype)
         {
             case FKCONSTR_MATCH_SIMPLE:
 
@@ -1333,14 +1355,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
     fkrte->rellockmode = AccessShareLock;
     fkrte->requiredPerms = ACL_SELECT;
 
-    for (int i = 0; i < riinfo->nkeys; i++)
+    for (int i = 0; i < riinfo->param->nkeys; i++)
     {
         int            attno;
 
-        attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
+        attno = riinfo->param->pk_attnums[i] -
+            FirstLowInvalidHeapAttributeNumber;
         pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno);
 
-        attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
+        attno = riinfo->param->fk_attnums[i] -
+            FirstLowInvalidHeapAttributeNumber;
         fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno);
     }
 
@@ -1377,10 +1401,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
     initStringInfo(&querybuf);
     appendStringInfoString(&querybuf, "SELECT ");
     sep = "";
-    for (int i = 0; i < riinfo->nkeys; i++)
+    for (int i = 0; i < riinfo->param->nkeys; i++)
     {
         quoteOneName(fkattname,
-                     RIAttName(fk_rel, riinfo->fk_attnums[i]));
+                     RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
         appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
         sep = ", ";
     }
@@ -1398,20 +1422,20 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
     strcpy(pkattname, "pk.");
     strcpy(fkattname, "fk.");
     sep = "(";
-    for (int i = 0; i < riinfo->nkeys; i++)
+    for (int i = 0; i < riinfo->param->nkeys; i++)
     {
-        Oid            pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
-        Oid            fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-        Oid            pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
-        Oid            fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
+        Oid pk_type = RIAttType(pk_rel, riinfo->param->pk_attnums[i]);
+        Oid fk_type = RIAttType(fk_rel, riinfo->param->fk_attnums[i]);
+        Oid pk_coll = RIAttCollation(pk_rel, riinfo->param->pk_attnums[i]);
+        Oid fk_coll = RIAttCollation(fk_rel, riinfo->param->fk_attnums[i]);
 
         quoteOneName(pkattname + 3,
-                     RIAttName(pk_rel, riinfo->pk_attnums[i]));
+                     RIAttName(pk_rel, riinfo->param->pk_attnums[i]));
         quoteOneName(fkattname + 3,
-                     RIAttName(fk_rel, riinfo->fk_attnums[i]));
+                     RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
         ri_GenerateQual(&querybuf, sep,
                         pkattname, pk_type,
-                        riinfo->pf_eq_oprs[i],
+                        riinfo->param->pf_eq_oprs[i],
                         fkattname, fk_type);
         if (pk_coll != fk_coll)
             ri_GenerateQualCollation(&querybuf, pk_coll);
@@ -1422,17 +1446,17 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
      * It's sufficient to test any one pk attribute for null to detect a join
      * failure.
      */
-    quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
+    quoteOneName(pkattname, RIAttName(pk_rel, riinfo->param->pk_attnums[0]));
     appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
 
     sep = "";
-    for (int i = 0; i < riinfo->nkeys; i++)
+    for (int i = 0; i < riinfo->param->nkeys; i++)
     {
-        quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+        quoteOneName(fkattname, RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
         appendStringInfo(&querybuf,
                          "%sfk.%s IS NOT NULL",
                          sep, fkattname);
-        switch (riinfo->confmatchtype)
+        switch (riinfo->param->confmatchtype)
         {
             case FKCONSTR_MATCH_SIMPLE:
                 sep = " AND ";
@@ -1505,6 +1529,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
         HeapTuple    tuple = SPI_tuptable->vals[0];
         TupleDesc    tupdesc = SPI_tuptable->tupdesc;
         RI_ConstraintInfo fake_riinfo;
+        RI_ConstraintParam fake_riparam;
+
+        fake_riinfo.param = &fake_riparam;
 
         slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
 
@@ -1522,15 +1549,15 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
          * or fk_rel's tupdesc.
          */
         memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo));
-        for (int i = 0; i < fake_riinfo.nkeys; i++)
-            fake_riinfo.fk_attnums[i] = i + 1;
+        for (int i = 0; i < fake_riinfo.param->nkeys; i++)
+            fake_riinfo.param->fk_attnums[i] = i + 1;
 
         /*
          * If it's MATCH FULL, and there are any nulls in the FK keys,
          * complain about that rather than the lack of a match.  MATCH FULL
          * disallows partially-null FK rows.
          */
-        if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
+        if (fake_riinfo.param->confmatchtype == FKCONSTR_MATCH_FULL &&
             ri_NullCheck(tupdesc, slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
             ereport(ERROR,
                     (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
@@ -1615,10 +1642,10 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
     initStringInfo(&querybuf);
     appendStringInfoString(&querybuf, "SELECT ");
     sep = "";
-    for (i = 0; i < riinfo->nkeys; i++)
+    for (i = 0; i < riinfo->param->nkeys; i++)
     {
         quoteOneName(fkattname,
-                     RIAttName(fk_rel, riinfo->fk_attnums[i]));
+                     RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
         appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
         sep = ", ";
     }
@@ -1633,20 +1660,20 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
     strcpy(pkattname, "pk.");
     strcpy(fkattname, "fk.");
     sep = "(";
-    for (i = 0; i < riinfo->nkeys; i++)
+    for (i = 0; i < riinfo->param->nkeys; i++)
     {
-        Oid            pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
-        Oid            fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-        Oid            pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
-        Oid            fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
+        Oid pk_type = RIAttType(pk_rel, riinfo->param->pk_attnums[i]);
+        Oid fk_type = RIAttType(fk_rel, riinfo->param->fk_attnums[i]);
+        Oid pk_coll = RIAttCollation(pk_rel, riinfo->param->pk_attnums[i]);
+        Oid fk_coll = RIAttCollation(fk_rel, riinfo->param->fk_attnums[i]);
 
         quoteOneName(pkattname + 3,
-                     RIAttName(pk_rel, riinfo->pk_attnums[i]));
+                     RIAttName(pk_rel, riinfo->param->pk_attnums[i]));
         quoteOneName(fkattname + 3,
-                     RIAttName(fk_rel, riinfo->fk_attnums[i]));
+                     RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
         ri_GenerateQual(&querybuf, sep,
                         pkattname, pk_type,
-                        riinfo->pf_eq_oprs[i],
+                        riinfo->param->pf_eq_oprs[i],
                         fkattname, fk_type);
         if (pk_coll != fk_coll)
             ri_GenerateQualCollation(&querybuf, pk_coll);
@@ -1666,13 +1693,13 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
         appendStringInfoString(&querybuf, ") WHERE (");
 
     sep = "";
-    for (i = 0; i < riinfo->nkeys; i++)
+    for (i = 0; i < riinfo->param->nkeys; i++)
     {
-        quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+        quoteOneName(fkattname, RIAttName(fk_rel, riinfo->param->fk_attnums[i]));
         appendStringInfo(&querybuf,
                          "%sfk.%s IS NOT NULL",
                          sep, fkattname);
-        switch (riinfo->confmatchtype)
+        switch (riinfo->param->confmatchtype)
         {
             case FKCONSTR_MATCH_SIMPLE:
                 sep = " AND ";
@@ -1762,8 +1789,8 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
          * or fk_rel's tupdesc.
          */
         memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo));
-        for (i = 0; i < fake_riinfo.nkeys; i++)
-            fake_riinfo.pk_attnums[i] = i + 1;
+        for (i = 0; i < fake_riinfo.param->nkeys; i++)
+            fake_riinfo.param->pk_attnums[i] = i + 1;
 
         ri_ReportViolation(&fake_riinfo, pk_rel, fk_rel,
                            slot, tupdesc, 0, true);
@@ -1905,7 +1932,7 @@ ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
      * We assume struct RI_QueryKey contains no padding bytes, else we'd need
      * to use memset to clear them.
      */
-    key->constr_id = riinfo->constraint_id;
+    key->constr_id = riinfo->param->query_key;
     key->constr_queryno = constr_queryno;
 }
 
@@ -1983,25 +2010,25 @@ ri_FetchConstraintInfo(Trigger *trigger, Relation trig_rel, bool rel_is_pk)
     if (rel_is_pk)
     {
         if (riinfo->fk_relid != trigger->tgconstrrelid ||
-            riinfo->pk_relid != RelationGetRelid(trig_rel))
+            riinfo->param->pk_relid != RelationGetRelid(trig_rel))
             elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
                  trigger->tgname, RelationGetRelationName(trig_rel));
     }
     else
     {
         if (riinfo->fk_relid != RelationGetRelid(trig_rel) ||
-            riinfo->pk_relid != trigger->tgconstrrelid)
+            riinfo->param->pk_relid != trigger->tgconstrrelid)
             elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
                  trigger->tgname, RelationGetRelationName(trig_rel));
     }
 
-    if (riinfo->confmatchtype != FKCONSTR_MATCH_FULL &&
-        riinfo->confmatchtype != FKCONSTR_MATCH_PARTIAL &&
-        riinfo->confmatchtype != FKCONSTR_MATCH_SIMPLE)
+    if (riinfo->param->confmatchtype != FKCONSTR_MATCH_FULL &&
+        riinfo->param->confmatchtype != FKCONSTR_MATCH_PARTIAL &&
+        riinfo->param->confmatchtype != FKCONSTR_MATCH_SIMPLE)
         elog(ERROR, "unrecognized confmatchtype: %d",
-             riinfo->confmatchtype);
+             riinfo->param->confmatchtype);
 
-    if (riinfo->confmatchtype == FKCONSTR_MATCH_PARTIAL)
+    if (riinfo->param->confmatchtype == FKCONSTR_MATCH_PARTIAL)
         ereport(ERROR,
                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                  errmsg("MATCH PARTIAL not yet implemented")));
@@ -2032,8 +2059,15 @@ ri_LoadConstraintInfo(Oid constraintOid)
     riinfo = (RI_ConstraintInfo *) hash_search(ri_constraint_cache,
                                                (void *) &constraintOid,
                                                HASH_ENTER, &found);
+
     if (!found)
+    {
         riinfo->valid = false;
+        riinfo->param = (RI_ConstraintParam *)
+            MemoryContextAlloc(ri_constraint_cache_cxt,
+                               sizeof(RI_ConstraintParam));
+        riinfo->param->ownerinfo = riinfo;
+    }
     else if (riinfo->valid)
         return riinfo;
 
@@ -2051,22 +2085,23 @@ ri_LoadConstraintInfo(Oid constraintOid)
 
     /* And extract data */
     Assert(riinfo->constraint_id == constraintOid);
+    riinfo->param->query_key = riinfo->constraint_id;
     riinfo->oidHashValue = GetSysCacheHashValue1(CONSTROID,
                                                  ObjectIdGetDatum(constraintOid));
     memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData));
-    riinfo->pk_relid = conForm->confrelid;
+    riinfo->param->pk_relid = conForm->confrelid;
     riinfo->fk_relid = conForm->conrelid;
-    riinfo->confupdtype = conForm->confupdtype;
-    riinfo->confdeltype = conForm->confdeltype;
-    riinfo->confmatchtype = conForm->confmatchtype;
+    riinfo->param->confupdtype = conForm->confupdtype;
+    riinfo->param->confdeltype = conForm->confdeltype;
+    riinfo->param->confmatchtype = conForm->confmatchtype;
 
     DeconstructFkConstraintRow(tup,
-                               &riinfo->nkeys,
-                               riinfo->fk_attnums,
-                               riinfo->pk_attnums,
-                               riinfo->pf_eq_oprs,
-                               riinfo->pp_eq_oprs,
-                               riinfo->ff_eq_oprs);
+                               &riinfo->param->nkeys,
+                               riinfo->param->fk_attnums,
+                               riinfo->param->pk_attnums,
+                               riinfo->param->pf_eq_oprs,
+                               riinfo->param->pp_eq_oprs,
+                               riinfo->param->ff_eq_oprs);
 
     ReleaseSysCache(tup);
 
@@ -2227,7 +2262,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
                          vals, nulls);
         if (oldslot)
             ri_ExtractValues(source_rel, oldslot, riinfo, source_is_pk,
-                             vals + riinfo->nkeys, nulls + riinfo->nkeys);
+                             vals + riinfo->param->nkeys, nulls + riinfo->param->nkeys);
     }
     else
     {
@@ -2320,11 +2355,11 @@ ri_ExtractValues(Relation rel, TupleTableSlot *slot,
     bool        isnull;
 
     if (rel_is_pk)
-        attnums = riinfo->pk_attnums;
+        attnums = riinfo->param->pk_attnums;
     else
-        attnums = riinfo->fk_attnums;
+        attnums = riinfo->param->fk_attnums;
 
-    for (int i = 0; i < riinfo->nkeys; i++)
+    for (int i = 0; i < riinfo->param->nkeys; i++)
     {
         vals[i] = slot_getattr(slot, attnums[i], &isnull);
         nulls[i] = isnull ? 'n' : ' ';
@@ -2361,14 +2396,14 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
     onfk = (queryno == RI_PLAN_CHECK_LOOKUPPK);
     if (onfk)
     {
-        attnums = riinfo->fk_attnums;
+        attnums = riinfo->param->fk_attnums;
         rel_oid = fk_rel->rd_id;
         if (tupdesc == NULL)
             tupdesc = fk_rel->rd_att;
     }
     else
     {
-        attnums = riinfo->pk_attnums;
+        attnums = riinfo->param->pk_attnums;
         rel_oid = pk_rel->rd_id;
         if (tupdesc == NULL)
             tupdesc = pk_rel->rd_att;
@@ -2395,7 +2430,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
         if (aclresult != ACLCHECK_OK)
         {
             /* Try for column-level permissions */
-            for (int idx = 0; idx < riinfo->nkeys; idx++)
+            for (int idx = 0; idx < riinfo->param->nkeys; idx++)
             {
                 aclresult = pg_attribute_aclcheck(rel_oid, attnums[idx],
                                                   GetUserId(),
@@ -2418,7 +2453,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
         /* Get printable versions of the keys involved */
         initStringInfo(&key_names);
         initStringInfo(&key_values);
-        for (int idx = 0; idx < riinfo->nkeys; idx++)
+        for (int idx = 0; idx < riinfo->param->nkeys; idx++)
         {
             int            fnum = attnums[idx];
             Form_pg_attribute att = TupleDescAttr(tupdesc, fnum - 1);
@@ -2508,11 +2543,11 @@ ri_NullCheck(TupleDesc tupDesc,
     bool        nonenull = true;
 
     if (rel_is_pk)
-        attnums = riinfo->pk_attnums;
+        attnums = riinfo->param->pk_attnums;
     else
-        attnums = riinfo->fk_attnums;
+        attnums = riinfo->param->fk_attnums;
 
-    for (int i = 0; i < riinfo->nkeys; i++)
+    for (int i = 0; i < riinfo->param->nkeys; i++)
     {
         if (slot_attisnull(slot, attnums[i]))
             nonenull = false;
@@ -2540,12 +2575,19 @@ ri_InitHashTables(void)
 {
     HASHCTL        ctl;
 
+    Assert(ri_constraint_cache_cxt == NULL);
+    ri_constraint_cache_cxt = AllocSetContextCreate(CacheMemoryContext,
+                                                    "RI constraint cache",
+                                                    ALLOCSET_DEFAULT_SIZES);
+        
     memset(&ctl, 0, sizeof(ctl));
     ctl.keysize = sizeof(Oid);
     ctl.entrysize = sizeof(RI_ConstraintInfo);
+    ctl.hcxt = ri_constraint_cache_cxt;
     ri_constraint_cache = hash_create("RI constraint cache",
                                       RI_INIT_CONSTRAINTHASHSIZE,
-                                      &ctl, HASH_ELEM | HASH_BLOBS);
+                                      &ctl,
+                                      HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
     /* Arrange to flush cache on pg_constraint changes */
     CacheRegisterSyscacheCallback(CONSTROID,
@@ -2555,16 +2597,18 @@ ri_InitHashTables(void)
     memset(&ctl, 0, sizeof(ctl));
     ctl.keysize = sizeof(RI_QueryKey);
     ctl.entrysize = sizeof(RI_QueryHashEntry);
+    ctl.hcxt = ri_constraint_cache_cxt;
     ri_query_cache = hash_create("RI query cache",
                                  RI_INIT_QUERYHASHSIZE,
-                                 &ctl, HASH_ELEM | HASH_BLOBS);
+                                 &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
     memset(&ctl, 0, sizeof(ctl));
     ctl.keysize = sizeof(RI_CompareKey);
     ctl.entrysize = sizeof(RI_CompareHashEntry);
+    ctl.hcxt = ri_constraint_cache_cxt;
     ri_compare_cache = hash_create("RI compare cache",
                                    RI_INIT_QUERYHASHSIZE,
-                                   &ctl, HASH_ELEM | HASH_BLOBS);
+                                   &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 }
 
 
@@ -2667,12 +2711,12 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
     const int16 *attnums;
 
     if (rel_is_pk)
-        attnums = riinfo->pk_attnums;
+        attnums = riinfo->param->pk_attnums;
     else
-        attnums = riinfo->fk_attnums;
+        attnums = riinfo->param->fk_attnums;
 
     /* XXX: could be worthwhile to fetch all necessary attrs at once */
-    for (int i = 0; i < riinfo->nkeys; i++)
+    for (int i = 0; i < riinfo->param->nkeys; i++)
     {
         Datum        oldvalue;
         Datum        newvalue;
@@ -2702,7 +2746,8 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
              * difference for ON UPDATE CASCADE, but for consistency we treat
              * all changes to the PK the same.
              */
-            Form_pg_attribute att = TupleDescAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1);
+            Form_pg_attribute att =
+                TupleDescAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1);
 
             if (!datum_image_eq(oldvalue, newvalue, att->attbyval, att->attlen))
                 return false;
@@ -2714,7 +2759,8 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
              * operator.  Changes that compare equal will still satisfy the
              * constraint after the update.
              */
-            if (!ri_AttributesEqual(riinfo->ff_eq_oprs[i], RIAttType(rel, attnums[i]),
+            if (!ri_AttributesEqual(riinfo->param->ff_eq_oprs[i],
+                                    RIAttType(rel, attnums[i]),
                                     oldvalue, newvalue))
                 return false;
         }
-- 
2.18.4

From 67cbe62a069b3cb6b50eab7215094f71660c4cf5 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyoga.ntt@gmail.com>
Date: Fri, 4 Dec 2020 12:01:26 +0900
Subject: [PATCH 2/2] share param part of riinfo

---
 src/backend/utils/adt/ri_triggers.c | 66 +++++++++++++++++++++++++++--
 1 file changed, 62 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 0306bf7739..187884f622 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2043,9 +2043,11 @@ static const RI_ConstraintInfo *
 ri_LoadConstraintInfo(Oid constraintOid)
 {
     RI_ConstraintInfo *riinfo;
+    RI_ConstraintParam tmpparam;
     bool        found;
     HeapTuple    tup;
     Form_pg_constraint conForm;
+    Oid            conparentid;
 
     /*
      * On the first call initialize the hashtable
@@ -2063,10 +2065,10 @@ ri_LoadConstraintInfo(Oid constraintOid)
     if (!found)
     {
         riinfo->valid = false;
-        riinfo->param = (RI_ConstraintParam *)
-            MemoryContextAlloc(ri_constraint_cache_cxt,
-                               sizeof(RI_ConstraintParam));
-        riinfo->param->ownerinfo = riinfo;
+
+        /* tentatively link the tmp parameter area to the riinfo */
+        riinfo->param = &tmpparam;
+        riinfo->param->ownerinfo = NULL;
     }
     else if (riinfo->valid)
         return riinfo;
@@ -2103,8 +2105,64 @@ ri_LoadConstraintInfo(Oid constraintOid)
                                riinfo->param->pp_eq_oprs,
                                riinfo->param->ff_eq_oprs);
 
+    conparentid = conForm->conparentid;
     ReleaseSysCache(tup);
 
+    Assert(riinfo->param);
+
+    /* check if this relation can share the parameter part with the parent. */
+    if (OidIsValid(conparentid))
+    {
+        const RI_ConstraintInfo *priinfo;
+        RI_ConstraintParam        *pparam;
+        RI_ConstraintParam        *param = riinfo->param;
+
+        /* load parent's riinfo (recurses to the topmost parent) */
+        priinfo = ri_LoadConstraintInfo(conparentid);
+        pparam = priinfo->param;
+
+        /* check if the parameter is identical with the parent */
+        if (memcmp(param, pparam,
+                   offsetof(RI_ConstraintParam, pk_attnums)) == 0)
+        {
+            int i;
+
+            /*
+             * The unused elements of the arrays are not guaranteed to be
+             * zero-filled. Check only the used elements.
+             */
+            for (i = 0 ; i < param->nkeys ; i++)
+            {
+                if (param->pk_attnums[i] != pparam->pk_attnums[i] ||
+                    param->fk_attnums[i] != pparam->fk_attnums[i] ||
+                    param->pf_eq_oprs[i] != pparam->pf_eq_oprs[i] ||
+                    param->pp_eq_oprs[i] != pparam->pp_eq_oprs[i] ||
+                    param->ff_eq_oprs[i] != pparam->ff_eq_oprs[i])
+                    break;
+            }
+
+            if (i == param->nkeys)
+            {
+                /* all parameters match. share the parameters with the parent */
+                if (riinfo->param->ownerinfo == riinfo)
+                    pfree(riinfo->param);
+                riinfo->param = pparam;
+            }
+        }
+    }
+
+    /* make a permanent copy if the parameter area is tentative */
+    if (riinfo->param == &tmpparam)
+    {
+        riinfo->param = (RI_ConstraintParam *)
+            MemoryContextAlloc(ri_constraint_cache_cxt,
+                               sizeof(RI_ConstraintParam));
+        /* copy parameter values */
+        memcpy(riinfo->param, &tmpparam,
+               offsetof(RI_ConstraintParam, ownerinfo));
+        riinfo->param->ownerinfo = riinfo;
+    }
+
     /*
      * For efficient processing of invalidation messages below, we keep a
      * doubly-linked list, and a count, of all currently valid entries.
-- 
2.18.4


pgsql-hackers by date:

Previous
From: Bharath Rupireddy
Date:
Subject: Re: [bug fix] ALTER TABLE SET LOGGED/UNLOGGED on a partitioned table does nothing silently
Next
From: "Hou, Zhijie"
Date:
Subject: RE: A new function to wait for the backend exit after termination