]> granicus.if.org Git - postgresql/commitdiff
Load FK defs into relcache for use by planner
authorSimon Riggs <simon@2ndQuadrant.com>
Thu, 7 Apr 2016 11:08:33 +0000 (12:08 +0100)
committerSimon Riggs <simon@2ndQuadrant.com>
Thu, 7 Apr 2016 11:08:33 +0000 (12:08 +0100)
Fastpath ignores this if no triggers defined.

Author: Tomas Vondra, with fastpath and comments added by me
Reviewers: David Rowley, Simon Riggs

src/backend/nodes/outfuncs.c
src/backend/optimizer/util/plancat.c
src/backend/utils/cache/relcache.c
src/include/nodes/nodes.h
src/include/nodes/relation.h
src/include/utils/rel.h
src/include/utils/relcache.h

index e39c374f48c621cc7088e4d2dd1130b7c4dbef3a..f783a49ebac2129b364f8059f5a61c046e809a5e 100644 (file)
@@ -2137,6 +2137,16 @@ _outIndexOptInfo(StringInfo str, const IndexOptInfo *node)
        /* we don't bother with fields copied from the index AM's API struct */
 }
 
+static void
+_outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node)
+{
+       WRITE_NODE_TYPE("FOREIGNKEYOPTINFO");
+
+       WRITE_OID_FIELD(conrelid);
+       WRITE_OID_FIELD(confrelid);
+       WRITE_INT_FIELD(nkeys);
+}
+
 static void
 _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 {
@@ -3607,6 +3617,9 @@ _outNode(StringInfo str, const void *obj)
                        case T_IndexOptInfo:
                                _outIndexOptInfo(str, obj);
                                break;
+                       case T_ForeignKeyOptInfo:
+                               _outForeignKeyOptInfo(str, obj);
+                               break;
                        case T_EquivalenceClass:
                                _outEquivalenceClass(str, obj);
                                break;
index 5bdeac0df64af14ec5c50586a9c536ad29fe862c..0f291275dcc08eb2358b3de91df8765f45906286 100644 (file)
@@ -28,6 +28,7 @@
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -41,6 +42,7 @@
 #include "rewrite/rewriteManip.h"
 #include "storage/bufmgr.h"
 #include "utils/lsyscache.h"
+#include "utils/syscache.h"
 #include "utils/rel.h"
 #include "utils/snapmgr.h"
 
@@ -94,6 +96,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
        Relation        relation;
        bool            hasindex;
        List       *indexinfos = NIL;
+       List       *fkinfos = NIL;
+       List       *fkoidlist;
+       ListCell   *l;
 
        /*
         * We need not lock the relation since it was already locked, either by
@@ -141,7 +146,6 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
        if (hasindex)
        {
                List       *indexoidlist;
-               ListCell   *l;
                LOCKMODE        lmode;
 
                indexoidlist = RelationGetIndexList(relation);
@@ -388,6 +392,85 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
        rel->indexlist = indexinfos;
 
+       /*
+        * Load foreign key data. Note this is the definitional data from the
+        * catalog only and does not lock the referenced tables here. The
+        * precise definition of the FK is important and may affect the usage
+        * elsewhere in the planner, e.g. if the constraint is deferred or
+        * if the constraint is not valid then relying upon this in the executor
+        * may not be accurate, though might be considered a useful estimate for
+        * planning purposes.
+        */
+       fkoidlist = RelationGetFKeyList(relation);
+
+       foreach(l, fkoidlist)
+       {
+               int                     i;
+               ArrayType  *arr;
+               Datum           adatum;
+               bool            isnull;
+               int                     numkeys;
+               Oid                     fkoid = lfirst_oid(l);
+
+               HeapTuple       htup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fkoid));
+               Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
+
+               ForeignKeyOptInfo *info;
+
+               Assert(constraint->contype == CONSTRAINT_FOREIGN);
+
+               info = makeNode(ForeignKeyOptInfo);
+
+               info->conrelid = constraint->conrelid;
+               info->confrelid = constraint->confrelid;
+
+               /* conkey */
+               adatum = SysCacheGetAttr(CONSTROID, htup,
+                                                                       Anum_pg_constraint_conkey, &isnull);
+               Assert(!isnull);
+
+               arr = DatumGetArrayTypeP(adatum);
+               numkeys = ARR_DIMS(arr)[0];
+               info->conkeys = (int*)palloc0(numkeys * sizeof(int));
+
+               for (i = 0; i < numkeys; i++)
+                       info->conkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
+
+               /* confkey */
+               adatum = SysCacheGetAttr(CONSTROID, htup,
+                                                                       Anum_pg_constraint_confkey, &isnull);
+               Assert(!isnull);
+
+               arr = DatumGetArrayTypeP(adatum);
+               numkeys = ARR_DIMS(arr)[0];
+               info->confkeys = (int*)palloc0(numkeys * sizeof(int));
+
+               for (i = 0; i < numkeys; i++)
+                       info->confkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
+
+               /* conpfeqop */
+               adatum = SysCacheGetAttr(CONSTROID, htup,
+                                                                       Anum_pg_constraint_conpfeqop, &isnull);
+               Assert(!isnull);
+
+               arr = DatumGetArrayTypeP(adatum);
+               numkeys = ARR_DIMS(arr)[0];
+               info->conpfeqop = (Oid*)palloc0(numkeys * sizeof(Oid));
+
+               for (i = 0; i < numkeys; i++)
+                       info->conpfeqop[i] = ((Oid *) ARR_DATA_PTR(arr))[i];
+
+               info->nkeys = numkeys;
+
+               ReleaseSysCache(htup);
+
+               fkinfos = lcons(info, fkinfos);
+       }
+
+       list_free(fkoidlist);
+
+       rel->fkeylist = fkinfos;
+
        /* Grab foreign-table info using the relcache, while we have it */
        if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
        {
index 130c06d81c8a921076642afa94170008923d9cb8..432feefa6094593ba934a7da68f24e959748b90f 100644 (file)
@@ -2031,6 +2031,7 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
                        FreeTupleDesc(relation->rd_att);
        }
        list_free(relation->rd_indexlist);
+       list_free(relation->rd_fkeylist);
        bms_free(relation->rd_indexattr);
        bms_free(relation->rd_keyattr);
        bms_free(relation->rd_idattr);
@@ -3956,6 +3957,79 @@ RelationGetIndexList(Relation relation)
        return result;
 }
 
+/*
+ * RelationGetFKeyList -- get a list of foreign key oids
+ *
+ * Use an index scan on pg_constraint to load in FK definitions,
+ * intended for use within the planner, not for enforcing FKs.
+ *
+ * Data is ordered by Oid, though this is not critical at this point
+ * since we do not lock the referenced relations.
+ */
+List *
+RelationGetFKeyList(Relation relation)
+{
+       Relation        conrel;
+       SysScanDesc conscan;
+       ScanKeyData skey;
+       HeapTuple       htup;
+       List       *result;
+       List       *oldlist;
+       MemoryContext oldcxt;
+
+       /* Quick exit if we already computed the list. */
+       if (relation->rd_fkeylist)
+               return list_copy(relation->rd_fkeylist);
+
+       /* Fast path if no FKs... if it doesn't have a trigger, it can't have a FK */
+       if (!relation->rd_rel->relhastriggers)
+               return NIL;
+       /*
+        * We build the list we intend to return (in the caller's context) while
+        * doing the scan.  After successfully completing the scan, we copy that
+        * list into the relcache entry.  This avoids cache-context memory leakage
+        * if we get some sort of error partway through.
+        */
+       result = NIL;
+
+       /* Prepare to scan pg_constraint for entries having conrelid = this rel. */
+       ScanKeyInit(&skey,
+                               Anum_pg_constraint_conrelid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(RelationGetRelid(relation)));
+
+       conrel = heap_open(ConstraintRelationId, AccessShareLock);
+       conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
+                                                                NULL, 1, &skey);
+
+       while (HeapTupleIsValid(htup = systable_getnext(conscan)))
+       {
+               Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
+
+               /* return only foreign keys */
+               if (constraint->contype != CONSTRAINT_FOREIGN)
+                       continue;
+
+               /* Add FK's OID to result list in the proper order */
+               result = insert_ordered_oid(result, HeapTupleGetOid(htup));
+       }
+
+       systable_endscan(conscan);
+
+       heap_close(conrel, AccessShareLock);
+
+       /* Now save a copy of the completed list in the relcache entry. */
+       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+       oldlist = relation->rd_fkeylist;
+       relation->rd_fkeylist = list_copy(result);
+       MemoryContextSwitchTo(oldcxt);
+
+       /* Don't leak the old list, if there is one */
+       list_free(oldlist);
+
+       return result;
+}
+
 /*
  * insert_ordered_oid
  *             Insert a new Oid into a sorted list of Oids, preserving ordering
@@ -4920,6 +4994,7 @@ load_relcache_init_file(bool shared)
                rel->rd_indexattr = NULL;
                rel->rd_keyattr = NULL;
                rel->rd_idattr = NULL;
+               rel->rd_fkeylist = NIL;
                rel->rd_createSubid = InvalidSubTransactionId;
                rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
                rel->rd_amcache = NULL;
index 3f22bdb5a8a188464d83b48d1ef248b070fd394b..84efa8e886d266b7352c2803c105a6e23298867d 100644 (file)
@@ -223,6 +223,7 @@ typedef enum NodeTag
        T_PlannerGlobal,
        T_RelOptInfo,
        T_IndexOptInfo,
+       T_ForeignKeyOptInfo,
        T_ParamPathInfo,
        T_Path,
        T_IndexPath,
index 5264d3cc76ddd3de71f68c9d3c82b314143cd66c..d430f6e9fd4774787cfbb224c40c6e3a7475ac68 100644 (file)
@@ -516,6 +516,7 @@ typedef struct RelOptInfo
        List       *lateral_vars;       /* LATERAL Vars and PHVs referenced by rel */
        Relids          lateral_referencers;    /* rels that reference me laterally */
        List       *indexlist;          /* list of IndexOptInfo */
+       List       *fkeylist;                   /* list of ForeignKeyOptInfo */
        BlockNumber pages;                      /* size estimates derived from pg_class */
        double          tuples;
        double          allvisfrac;
@@ -621,6 +622,27 @@ typedef struct IndexOptInfo
        void            (*amcostestimate) ();   /* AM's cost estimator */
 } IndexOptInfo;
 
+/*
+ * ForeignKeyOptInfo
+ *             Per-foreign-key information for planning/optimization
+ *
+ * Only includes columns from pg_constraint related to foreign keys.
+ *
+ * conkeys[], confkeys[] and conpfeqop[] each have nkeys entries.
+ */
+typedef struct ForeignKeyOptInfo
+{
+       NodeTag         type;
+
+       Oid                     conrelid;       /* relation constrained by the foreign key */
+       Oid                     confrelid;      /* relation referenced by the foreign key */
+
+       int                     nkeys;          /* number of columns in the foreign key */
+       int                *conkeys;    /* attnums of columns in the constrained table */
+       int                *confkeys;   /* attnums of columns in the referenced table */
+       Oid                *conpfeqop;  /* OIDs of equality operators used by the FK */
+
+} ForeignKeyOptInfo;
 
 /*
  * EquivalenceClasses
index f2bebf2c3ddb0cdcca1911aa3a1d083e6609fde9..51eb27a381bb16797b75c23871511753c4d9bb58 100644 (file)
@@ -94,6 +94,9 @@ typedef struct RelationData
        Oid                     rd_oidindex;    /* OID of unique index on OID, if any */
        Oid                     rd_replidindex; /* OID of replica identity index, if any */
 
+       /* data managed by RelationGetFKList: */
+       List       *rd_fkeylist;                /* OIDs of foreign keys */
+
        /* data managed by RelationGetIndexAttrBitmap: */
        Bitmapset  *rd_indexattr;       /* identifies columns used in indexes */
        Bitmapset  *rd_keyattr;         /* cols that can be ref'd by foreign keys */
index 1b4830462d52ba0c3fbfbdd341ee0abd741d631c..7f07c269145b3a76cead7f5efd6755a05d2916c5 100644 (file)
@@ -38,6 +38,7 @@ extern void RelationClose(Relation relation);
  * Routines to compute/retrieve additional cached information
  */
 extern List *RelationGetIndexList(Relation relation);
+extern List *RelationGetFKeyList(Relation relation);
 extern Oid     RelationGetOidIndex(Relation relation);
 extern Oid     RelationGetReplicaIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);