mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Load FK defs into relcache for use by planner
Fastpath ignores this if no triggers defined. Author: Tomas Vondra, with fastpath and comments added by me Reviewers: David Rowley, Simon Riggs
This commit is contained in:
parent
f2b1b3079c
commit
015e88942a
@ -2137,6 +2137,16 @@ _outIndexOptInfo(StringInfo str, const IndexOptInfo *node)
|
|||||||
/* we don't bother with fields copied from the index AM's API struct */
|
/* 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
|
static void
|
||||||
_outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
|
_outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
|
||||||
{
|
{
|
||||||
@ -3607,6 +3617,9 @@ _outNode(StringInfo str, const void *obj)
|
|||||||
case T_IndexOptInfo:
|
case T_IndexOptInfo:
|
||||||
_outIndexOptInfo(str, obj);
|
_outIndexOptInfo(str, obj);
|
||||||
break;
|
break;
|
||||||
|
case T_ForeignKeyOptInfo:
|
||||||
|
_outForeignKeyOptInfo(str, obj);
|
||||||
|
break;
|
||||||
case T_EquivalenceClass:
|
case T_EquivalenceClass:
|
||||||
_outEquivalenceClass(str, obj);
|
_outEquivalenceClass(str, obj);
|
||||||
break;
|
break;
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/pg_am.h"
|
#include "catalog/pg_am.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "foreign/fdwapi.h"
|
#include "foreign/fdwapi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
@ -41,6 +42,7 @@
|
|||||||
#include "rewrite/rewriteManip.h"
|
#include "rewrite/rewriteManip.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
#include "utils/snapmgr.h"
|
#include "utils/snapmgr.h"
|
||||||
|
|
||||||
@ -94,6 +96,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
|
|||||||
Relation relation;
|
Relation relation;
|
||||||
bool hasindex;
|
bool hasindex;
|
||||||
List *indexinfos = NIL;
|
List *indexinfos = NIL;
|
||||||
|
List *fkinfos = NIL;
|
||||||
|
List *fkoidlist;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need not lock the relation since it was already locked, either by
|
* 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)
|
if (hasindex)
|
||||||
{
|
{
|
||||||
List *indexoidlist;
|
List *indexoidlist;
|
||||||
ListCell *l;
|
|
||||||
LOCKMODE lmode;
|
LOCKMODE lmode;
|
||||||
|
|
||||||
indexoidlist = RelationGetIndexList(relation);
|
indexoidlist = RelationGetIndexList(relation);
|
||||||
@ -388,6 +392,85 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
|
|||||||
|
|
||||||
rel->indexlist = indexinfos;
|
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 */
|
/* Grab foreign-table info using the relcache, while we have it */
|
||||||
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
|
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
|
||||||
{
|
{
|
||||||
|
75
src/backend/utils/cache/relcache.c
vendored
75
src/backend/utils/cache/relcache.c
vendored
@ -2031,6 +2031,7 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
|
|||||||
FreeTupleDesc(relation->rd_att);
|
FreeTupleDesc(relation->rd_att);
|
||||||
}
|
}
|
||||||
list_free(relation->rd_indexlist);
|
list_free(relation->rd_indexlist);
|
||||||
|
list_free(relation->rd_fkeylist);
|
||||||
bms_free(relation->rd_indexattr);
|
bms_free(relation->rd_indexattr);
|
||||||
bms_free(relation->rd_keyattr);
|
bms_free(relation->rd_keyattr);
|
||||||
bms_free(relation->rd_idattr);
|
bms_free(relation->rd_idattr);
|
||||||
@ -3956,6 +3957,79 @@ RelationGetIndexList(Relation relation)
|
|||||||
return result;
|
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_ordered_oid
|
||||||
* Insert a new Oid into a sorted list of Oids, preserving ordering
|
* 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_indexattr = NULL;
|
||||||
rel->rd_keyattr = NULL;
|
rel->rd_keyattr = NULL;
|
||||||
rel->rd_idattr = NULL;
|
rel->rd_idattr = NULL;
|
||||||
|
rel->rd_fkeylist = NIL;
|
||||||
rel->rd_createSubid = InvalidSubTransactionId;
|
rel->rd_createSubid = InvalidSubTransactionId;
|
||||||
rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
|
rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
|
||||||
rel->rd_amcache = NULL;
|
rel->rd_amcache = NULL;
|
||||||
|
@ -223,6 +223,7 @@ typedef enum NodeTag
|
|||||||
T_PlannerGlobal,
|
T_PlannerGlobal,
|
||||||
T_RelOptInfo,
|
T_RelOptInfo,
|
||||||
T_IndexOptInfo,
|
T_IndexOptInfo,
|
||||||
|
T_ForeignKeyOptInfo,
|
||||||
T_ParamPathInfo,
|
T_ParamPathInfo,
|
||||||
T_Path,
|
T_Path,
|
||||||
T_IndexPath,
|
T_IndexPath,
|
||||||
|
@ -516,6 +516,7 @@ typedef struct RelOptInfo
|
|||||||
List *lateral_vars; /* LATERAL Vars and PHVs referenced by rel */
|
List *lateral_vars; /* LATERAL Vars and PHVs referenced by rel */
|
||||||
Relids lateral_referencers; /* rels that reference me laterally */
|
Relids lateral_referencers; /* rels that reference me laterally */
|
||||||
List *indexlist; /* list of IndexOptInfo */
|
List *indexlist; /* list of IndexOptInfo */
|
||||||
|
List *fkeylist; /* list of ForeignKeyOptInfo */
|
||||||
BlockNumber pages; /* size estimates derived from pg_class */
|
BlockNumber pages; /* size estimates derived from pg_class */
|
||||||
double tuples;
|
double tuples;
|
||||||
double allvisfrac;
|
double allvisfrac;
|
||||||
@ -621,6 +622,27 @@ typedef struct IndexOptInfo
|
|||||||
void (*amcostestimate) (); /* AM's cost estimator */
|
void (*amcostestimate) (); /* AM's cost estimator */
|
||||||
} IndexOptInfo;
|
} 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
|
* EquivalenceClasses
|
||||||
|
@ -94,6 +94,9 @@ typedef struct RelationData
|
|||||||
Oid rd_oidindex; /* OID of unique index on OID, if any */
|
Oid rd_oidindex; /* OID of unique index on OID, if any */
|
||||||
Oid rd_replidindex; /* OID of replica identity index, 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: */
|
/* data managed by RelationGetIndexAttrBitmap: */
|
||||||
Bitmapset *rd_indexattr; /* identifies columns used in indexes */
|
Bitmapset *rd_indexattr; /* identifies columns used in indexes */
|
||||||
Bitmapset *rd_keyattr; /* cols that can be ref'd by foreign keys */
|
Bitmapset *rd_keyattr; /* cols that can be ref'd by foreign keys */
|
||||||
|
@ -38,6 +38,7 @@ extern void RelationClose(Relation relation);
|
|||||||
* Routines to compute/retrieve additional cached information
|
* Routines to compute/retrieve additional cached information
|
||||||
*/
|
*/
|
||||||
extern List *RelationGetIndexList(Relation relation);
|
extern List *RelationGetIndexList(Relation relation);
|
||||||
|
extern List *RelationGetFKeyList(Relation relation);
|
||||||
extern Oid RelationGetOidIndex(Relation relation);
|
extern Oid RelationGetOidIndex(Relation relation);
|
||||||
extern Oid RelationGetReplicaIndex(Relation relation);
|
extern Oid RelationGetReplicaIndex(Relation relation);
|
||||||
extern List *RelationGetIndexExpressions(Relation relation);
|
extern List *RelationGetIndexExpressions(Relation relation);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user