Skip to content

Commit 015e889

Browse files
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
1 parent f2b1b30 commit 015e889

File tree

7 files changed

+199
-1
lines changed

7 files changed

+199
-1
lines changed

src/backend/nodes/outfuncs.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,6 +2137,16 @@ _outIndexOptInfo(StringInfo str, const IndexOptInfo *node)
21372137
/* we don't bother with fields copied from the index AM's API struct */
21382138
}
21392139

2140+
static void
2141+
_outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node)
2142+
{
2143+
WRITE_NODE_TYPE("FOREIGNKEYOPTINFO");
2144+
2145+
WRITE_OID_FIELD(conrelid);
2146+
WRITE_OID_FIELD(confrelid);
2147+
WRITE_INT_FIELD(nkeys);
2148+
}
2149+
21402150
static void
21412151
_outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
21422152
{
@@ -3607,6 +3617,9 @@ _outNode(StringInfo str, const void *obj)
36073617
case T_IndexOptInfo:
36083618
_outIndexOptInfo(str, obj);
36093619
break;
3620+
case T_ForeignKeyOptInfo:
3621+
_outForeignKeyOptInfo(str, obj);
3622+
break;
36103623
case T_EquivalenceClass:
36113624
_outEquivalenceClass(str, obj);
36123625
break;

src/backend/optimizer/util/plancat.c

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "catalog/dependency.h"
2929
#include "catalog/heap.h"
3030
#include "catalog/pg_am.h"
31+
#include "catalog/pg_constraint.h"
3132
#include "foreign/fdwapi.h"
3233
#include "miscadmin.h"
3334
#include "nodes/makefuncs.h"
@@ -41,6 +42,7 @@
4142
#include "rewrite/rewriteManip.h"
4243
#include "storage/bufmgr.h"
4344
#include "utils/lsyscache.h"
45+
#include "utils/syscache.h"
4446
#include "utils/rel.h"
4547
#include "utils/snapmgr.h"
4648

@@ -94,6 +96,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
9496
Relation relation;
9597
bool hasindex;
9698
List *indexinfos = NIL;
99+
List *fkinfos = NIL;
100+
List *fkoidlist;
101+
ListCell *l;
97102

98103
/*
99104
* 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,
141146
if (hasindex)
142147
{
143148
List *indexoidlist;
144-
ListCell *l;
145149
LOCKMODE lmode;
146150

147151
indexoidlist = RelationGetIndexList(relation);
@@ -388,6 +392,85 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
388392

389393
rel->indexlist = indexinfos;
390394

395+
/*
396+
* Load foreign key data. Note this is the definitional data from the
397+
* catalog only and does not lock the referenced tables here. The
398+
* precise definition of the FK is important and may affect the usage
399+
* elsewhere in the planner, e.g. if the constraint is deferred or
400+
* if the constraint is not valid then relying upon this in the executor
401+
* may not be accurate, though might be considered a useful estimate for
402+
* planning purposes.
403+
*/
404+
fkoidlist = RelationGetFKeyList(relation);
405+
406+
foreach(l, fkoidlist)
407+
{
408+
int i;
409+
ArrayType *arr;
410+
Datum adatum;
411+
bool isnull;
412+
int numkeys;
413+
Oid fkoid = lfirst_oid(l);
414+
415+
HeapTuple htup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fkoid));
416+
Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
417+
418+
ForeignKeyOptInfo *info;
419+
420+
Assert(constraint->contype == CONSTRAINT_FOREIGN);
421+
422+
info = makeNode(ForeignKeyOptInfo);
423+
424+
info->conrelid = constraint->conrelid;
425+
info->confrelid = constraint->confrelid;
426+
427+
/* conkey */
428+
adatum = SysCacheGetAttr(CONSTROID, htup,
429+
Anum_pg_constraint_conkey, &isnull);
430+
Assert(!isnull);
431+
432+
arr = DatumGetArrayTypeP(adatum);
433+
numkeys = ARR_DIMS(arr)[0];
434+
info->conkeys = (int*)palloc0(numkeys * sizeof(int));
435+
436+
for (i = 0; i < numkeys; i++)
437+
info->conkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
438+
439+
/* confkey */
440+
adatum = SysCacheGetAttr(CONSTROID, htup,
441+
Anum_pg_constraint_confkey, &isnull);
442+
Assert(!isnull);
443+
444+
arr = DatumGetArrayTypeP(adatum);
445+
numkeys = ARR_DIMS(arr)[0];
446+
info->confkeys = (int*)palloc0(numkeys * sizeof(int));
447+
448+
for (i = 0; i < numkeys; i++)
449+
info->confkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
450+
451+
/* conpfeqop */
452+
adatum = SysCacheGetAttr(CONSTROID, htup,
453+
Anum_pg_constraint_conpfeqop, &isnull);
454+
Assert(!isnull);
455+
456+
arr = DatumGetArrayTypeP(adatum);
457+
numkeys = ARR_DIMS(arr)[0];
458+
info->conpfeqop = (Oid*)palloc0(numkeys * sizeof(Oid));
459+
460+
for (i = 0; i < numkeys; i++)
461+
info->conpfeqop[i] = ((Oid *) ARR_DATA_PTR(arr))[i];
462+
463+
info->nkeys = numkeys;
464+
465+
ReleaseSysCache(htup);
466+
467+
fkinfos = lcons(info, fkinfos);
468+
}
469+
470+
list_free(fkoidlist);
471+
472+
rel->fkeylist = fkinfos;
473+
391474
/* Grab foreign-table info using the relcache, while we have it */
392475
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
393476
{

src/backend/utils/cache/relcache.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,6 +2031,7 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
20312031
FreeTupleDesc(relation->rd_att);
20322032
}
20332033
list_free(relation->rd_indexlist);
2034+
list_free(relation->rd_fkeylist);
20342035
bms_free(relation->rd_indexattr);
20352036
bms_free(relation->rd_keyattr);
20362037
bms_free(relation->rd_idattr);
@@ -3956,6 +3957,79 @@ RelationGetIndexList(Relation relation)
39563957
return result;
39573958
}
39583959

3960+
/*
3961+
* RelationGetFKeyList -- get a list of foreign key oids
3962+
*
3963+
* Use an index scan on pg_constraint to load in FK definitions,
3964+
* intended for use within the planner, not for enforcing FKs.
3965+
*
3966+
* Data is ordered by Oid, though this is not critical at this point
3967+
* since we do not lock the referenced relations.
3968+
*/
3969+
List *
3970+
RelationGetFKeyList(Relation relation)
3971+
{
3972+
Relation conrel;
3973+
SysScanDesc conscan;
3974+
ScanKeyData skey;
3975+
HeapTuple htup;
3976+
List *result;
3977+
List *oldlist;
3978+
MemoryContext oldcxt;
3979+
3980+
/* Quick exit if we already computed the list. */
3981+
if (relation->rd_fkeylist)
3982+
return list_copy(relation->rd_fkeylist);
3983+
3984+
/* Fast path if no FKs... if it doesn't have a trigger, it can't have a FK */
3985+
if (!relation->rd_rel->relhastriggers)
3986+
return NIL;
3987+
/*
3988+
* We build the list we intend to return (in the caller's context) while
3989+
* doing the scan. After successfully completing the scan, we copy that
3990+
* list into the relcache entry. This avoids cache-context memory leakage
3991+
* if we get some sort of error partway through.
3992+
*/
3993+
result = NIL;
3994+
3995+
/* Prepare to scan pg_constraint for entries having conrelid = this rel. */
3996+
ScanKeyInit(&skey,
3997+
Anum_pg_constraint_conrelid,
3998+
BTEqualStrategyNumber, F_OIDEQ,
3999+
ObjectIdGetDatum(RelationGetRelid(relation)));
4000+
4001+
conrel = heap_open(ConstraintRelationId, AccessShareLock);
4002+
conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
4003+
NULL, 1, &skey);
4004+
4005+
while (HeapTupleIsValid(htup = systable_getnext(conscan)))
4006+
{
4007+
Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
4008+
4009+
/* return only foreign keys */
4010+
if (constraint->contype != CONSTRAINT_FOREIGN)
4011+
continue;
4012+
4013+
/* Add FK's OID to result list in the proper order */
4014+
result = insert_ordered_oid(result, HeapTupleGetOid(htup));
4015+
}
4016+
4017+
systable_endscan(conscan);
4018+
4019+
heap_close(conrel, AccessShareLock);
4020+
4021+
/* Now save a copy of the completed list in the relcache entry. */
4022+
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
4023+
oldlist = relation->rd_fkeylist;
4024+
relation->rd_fkeylist = list_copy(result);
4025+
MemoryContextSwitchTo(oldcxt);
4026+
4027+
/* Don't leak the old list, if there is one */
4028+
list_free(oldlist);
4029+
4030+
return result;
4031+
}
4032+
39594033
/*
39604034
* insert_ordered_oid
39614035
* Insert a new Oid into a sorted list of Oids, preserving ordering
@@ -4920,6 +4994,7 @@ load_relcache_init_file(bool shared)
49204994
rel->rd_indexattr = NULL;
49214995
rel->rd_keyattr = NULL;
49224996
rel->rd_idattr = NULL;
4997+
rel->rd_fkeylist = NIL;
49234998
rel->rd_createSubid = InvalidSubTransactionId;
49244999
rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
49255000
rel->rd_amcache = NULL;

src/include/nodes/nodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ typedef enum NodeTag
223223
T_PlannerGlobal,
224224
T_RelOptInfo,
225225
T_IndexOptInfo,
226+
T_ForeignKeyOptInfo,
226227
T_ParamPathInfo,
227228
T_Path,
228229
T_IndexPath,

src/include/nodes/relation.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ typedef struct RelOptInfo
516516
List *lateral_vars; /* LATERAL Vars and PHVs referenced by rel */
517517
Relids lateral_referencers; /* rels that reference me laterally */
518518
List *indexlist; /* list of IndexOptInfo */
519+
List *fkeylist; /* list of ForeignKeyOptInfo */
519520
BlockNumber pages; /* size estimates derived from pg_class */
520521
double tuples;
521522
double allvisfrac;
@@ -621,6 +622,27 @@ typedef struct IndexOptInfo
621622
void (*amcostestimate) (); /* AM's cost estimator */
622623
} IndexOptInfo;
623624

625+
/*
626+
* ForeignKeyOptInfo
627+
* Per-foreign-key information for planning/optimization
628+
*
629+
* Only includes columns from pg_constraint related to foreign keys.
630+
*
631+
* conkeys[], confkeys[] and conpfeqop[] each have nkeys entries.
632+
*/
633+
typedef struct ForeignKeyOptInfo
634+
{
635+
NodeTag type;
636+
637+
Oid conrelid; /* relation constrained by the foreign key */
638+
Oid confrelid; /* relation referenced by the foreign key */
639+
640+
int nkeys; /* number of columns in the foreign key */
641+
int *conkeys; /* attnums of columns in the constrained table */
642+
int *confkeys; /* attnums of columns in the referenced table */
643+
Oid *conpfeqop; /* OIDs of equality operators used by the FK */
644+
645+
} ForeignKeyOptInfo;
624646

625647
/*
626648
* EquivalenceClasses

src/include/utils/rel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ typedef struct RelationData
9494
Oid rd_oidindex; /* OID of unique index on OID, if any */
9595
Oid rd_replidindex; /* OID of replica identity index, if any */
9696

97+
/* data managed by RelationGetFKList: */
98+
List *rd_fkeylist; /* OIDs of foreign keys */
99+
97100
/* data managed by RelationGetIndexAttrBitmap: */
98101
Bitmapset *rd_indexattr; /* identifies columns used in indexes */
99102
Bitmapset *rd_keyattr; /* cols that can be ref'd by foreign keys */

src/include/utils/relcache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ extern void RelationClose(Relation relation);
3838
* Routines to compute/retrieve additional cached information
3939
*/
4040
extern List *RelationGetIndexList(Relation relation);
41+
extern List *RelationGetFKeyList(Relation relation);
4142
extern Oid RelationGetOidIndex(Relation relation);
4243
extern Oid RelationGetReplicaIndex(Relation relation);
4344
extern List *RelationGetIndexExpressions(Relation relation);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy