mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Remove RI fast-path code for calling cast functions
The previous commit stopped using the fast path when FK values need a cast function, which leaves its code for calling them unused. So remove the following: the cached FmgrInfos for the cast and equality functions and the memory context they used, the cast step in build_index_scankeys(), the EXECUTE privilege check on the cast function, and the unused forward declaration of RI_CompareHashEntry. Also revert the changes made to ri_HashCompareOp() for the fast path's cross-type operators and domain-typed keys in commits2da86c1ef9and68ace967c1. Fast-path code no longer uses the function. Reviewed-by: Matheus Alcantara <matheusssilv97@gmail.com> Discussion: https://postgr.es/m/CAM527d9BgPjeOOYmbCBTd57R145qHCk-dzw9qNq+nOrDq1j__A@mail.gmail.com Backpatch-through: 19
This commit is contained in:
@@ -161,30 +161,14 @@ typedef struct RI_ConstraintInfo
|
||||
FastPathMeta *fpmeta;
|
||||
} RI_ConstraintInfo;
|
||||
|
||||
typedef struct RI_CompareHashEntry RI_CompareHashEntry;
|
||||
|
||||
/* Fast-path metadata for RI checks on foreign key referencing tables */
|
||||
typedef struct FastPathMeta
|
||||
{
|
||||
FmgrInfo eq_opr_finfo[RI_MAX_NUMKEYS];
|
||||
FmgrInfo cast_func_finfo[RI_MAX_NUMKEYS];
|
||||
RegProcedure regops[RI_MAX_NUMKEYS];
|
||||
Oid subtypes[RI_MAX_NUMKEYS];
|
||||
int strats[RI_MAX_NUMKEYS];
|
||||
AttrNumber index_attnos[RI_MAX_NUMKEYS]; /* index column positions */
|
||||
|
||||
/*
|
||||
* fn_mcxt for the cached FmgrInfos above. Cast and equality functions
|
||||
* (e.g. record_eq()) use fn_mcxt as scratch space, caching state there
|
||||
* and keeping a pointer to it in FmgrInfo.fn_extra. Give them a context
|
||||
* of their own, created with this struct and destroyed with it in
|
||||
* AtEOXact_RI().
|
||||
*
|
||||
* Note this context must not be reset while the FmgrInfos remain in use,
|
||||
* since that would free the state fn_extra still points at.
|
||||
*/
|
||||
MemoryContext scratch_cxt;
|
||||
|
||||
/* Link in ri_fpmeta_dead_list while awaiting deferred release */
|
||||
struct FastPathMeta *next_dead;
|
||||
} FastPathMeta;
|
||||
@@ -313,11 +297,11 @@ static bool recheck_matched_pk_tuple(Relation idxrel, ScanKeyData *skeys,
|
||||
static void ri_CheckFunctionPermissions(const RI_ConstraintInfo *riinfo,
|
||||
const FastPathMeta *fpmeta);
|
||||
static void build_index_scankeys(const RI_ConstraintInfo *riinfo,
|
||||
FastPathMeta *fpmeta,
|
||||
Relation idx_rel, Datum *pk_vals,
|
||||
char *pk_nulls, ScanKey skeys);
|
||||
const FastPathMeta *fpmeta,
|
||||
Relation idx_rel, const Datum *pk_vals,
|
||||
ScanKey skeys);
|
||||
static void ri_populate_fastpath_metadata(RI_ConstraintInfo *riinfo,
|
||||
Relation fk_rel, Relation idx_rel);
|
||||
Relation idx_rel);
|
||||
static void ri_ExtractValues(Relation rel, TupleTableSlot *slot,
|
||||
const RI_ConstraintInfo *riinfo, bool rel_is_pk,
|
||||
Datum *vals, char *nulls);
|
||||
@@ -2562,13 +2546,12 @@ InvalidateConstraintCacheCallBack(Datum arg, SysCacheIdentifier cacheid,
|
||||
/*
|
||||
* Detach any fast-path metadata so that the next check
|
||||
* repopulates it, but do not free it here. ri_FastPathCheck()
|
||||
* copies riinfo->fpmeta into a local (and takes FmgrInfo pointers
|
||||
* into it) and then runs index scans, tuple locking, and
|
||||
* user-supplied cast and equality functions, all of which can
|
||||
* accept invalidation messages and reach this callback. Freeing
|
||||
* now would leave those callers reading freed memory. Queue it
|
||||
* instead; AtEOXact_RI() releases it once no RI check can be
|
||||
* running.
|
||||
* copies riinfo->fpmeta into a local and then does catalog
|
||||
* lookups, index scans, tuple locking, and calls equality
|
||||
* functions, all of which can accept invalidation messages and
|
||||
* reach this callback. Freeing now would leave it reading freed
|
||||
* memory. Queue it instead; AtEOXact_RI() releases it once no RI
|
||||
* check can be running.
|
||||
*/
|
||||
if (riinfo->fpmeta)
|
||||
{
|
||||
@@ -2841,10 +2824,10 @@ ri_FastPathCheck(RI_ConstraintInfo *riinfo,
|
||||
* InvalidSnapshot, so SPI takes the snapshot after the
|
||||
* referenced-relation lock has been acquired.
|
||||
*
|
||||
* Make this snapshot active too, as SPI does. STABLE cast and equality
|
||||
* functions use the active snapshot, so leaving the outer query's
|
||||
* snapshot active could hide changes made by earlier triggers even though
|
||||
* the index scan can see them.
|
||||
* Make this snapshot active too, as SPI does. STABLE equality functions
|
||||
* use the active snapshot, so leaving the outer query's snapshot active
|
||||
* could hide changes made by earlier triggers even though the index scan
|
||||
* can see them.
|
||||
*/
|
||||
snapshot = RegisterSnapshot(GetTransactionSnapshot());
|
||||
PushActiveSnapshot(snapshot);
|
||||
@@ -2873,7 +2856,7 @@ ri_FastPathCheck(RI_ConstraintInfo *riinfo,
|
||||
{
|
||||
/* Reload to ensure it's valid. */
|
||||
riinfo = ri_LoadConstraintInfo(riinfo->constraint_id);
|
||||
ri_populate_fastpath_metadata(riinfo, fk_rel, idx_rel);
|
||||
ri_populate_fastpath_metadata(riinfo, idx_rel);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2887,7 +2870,7 @@ ri_FastPathCheck(RI_ConstraintInfo *riinfo,
|
||||
Assert(fpmeta);
|
||||
ri_CheckFunctionPermissions(riinfo, fpmeta);
|
||||
ri_ExtractValues(fk_rel, newslot, riinfo, false, pk_vals, pk_nulls);
|
||||
build_index_scankeys(riinfo, fpmeta, idx_rel, pk_vals, pk_nulls, skey);
|
||||
build_index_scankeys(riinfo, fpmeta, idx_rel, pk_vals, skey);
|
||||
found = ri_FastPathProbeOne(pk_rel, idx_rel, scandesc, slot,
|
||||
snapshot, riinfo, skey, riinfo->nkeys);
|
||||
SetUserIdAndSecContext(saved_userid, saved_sec_context);
|
||||
@@ -3282,21 +3265,15 @@ ri_CheckFunctionPermissions(const RI_ConstraintInfo *riinfo,
|
||||
{
|
||||
for (int i = 0; i < riinfo->nkeys; i++)
|
||||
{
|
||||
Oid funcs[2] = {fpmeta->regops[i], fpmeta->cast_func_finfo[i].fn_oid};
|
||||
Oid funcid = fpmeta->regops[i];
|
||||
AclResult aclresult;
|
||||
|
||||
for (int j = 0; j < lengthof(funcs); j++)
|
||||
{
|
||||
AclResult aclresult;
|
||||
|
||||
if (!OidIsValid(funcs[j]))
|
||||
continue;
|
||||
aclresult = object_aclcheck(ProcedureRelationId, funcs[j],
|
||||
GetUserId(), ACL_EXECUTE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, OBJECT_FUNCTION,
|
||||
get_func_name(funcs[j]));
|
||||
InvokeFunctionExecuteHook(funcs[j]);
|
||||
}
|
||||
aclresult = object_aclcheck(ProcedureRelationId, funcid,
|
||||
GetUserId(), ACL_EXECUTE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, OBJECT_FUNCTION,
|
||||
get_func_name(funcid));
|
||||
InvokeFunctionExecuteHook(funcid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3304,33 +3281,19 @@ ri_CheckFunctionPermissions(const RI_ConstraintInfo *riinfo,
|
||||
* build_index_scankeys
|
||||
* Build ScanKeys for a direct index probe of the PK's unique index.
|
||||
*
|
||||
* Uses cached compare entries, operator procedures, and strategy numbers
|
||||
* from ri_populate_fastpath_metadata() rather than looking them up on
|
||||
* each invocation. Casts FK values to the operator's expected input
|
||||
* type if needed.
|
||||
* Uses operator procedures and strategy numbers cached by
|
||||
* ri_populate_fastpath_metadata() rather than looking them up on each
|
||||
* invocation. The FK values are used as they are: the fast path is only
|
||||
* used when they need no coercion to the operator's input type.
|
||||
*/
|
||||
static void
|
||||
build_index_scankeys(const RI_ConstraintInfo *riinfo,
|
||||
FastPathMeta *fpmeta,
|
||||
Relation idx_rel, Datum *pk_vals,
|
||||
char *pk_nulls, ScanKey skeys)
|
||||
const FastPathMeta *fpmeta,
|
||||
Relation idx_rel, const Datum *pk_vals,
|
||||
ScanKey skeys)
|
||||
{
|
||||
Assert(fpmeta);
|
||||
|
||||
/*
|
||||
* May need to cast each of the individual values of the foreign key to
|
||||
* the corresponding PK column's type if the equality operator demands it.
|
||||
*/
|
||||
for (int i = 0; i < riinfo->nkeys; i++)
|
||||
{
|
||||
if (pk_nulls[i] != 'n' &&
|
||||
OidIsValid(fpmeta->cast_func_finfo[i].fn_oid))
|
||||
pk_vals[i] = FunctionCall3(&fpmeta->cast_func_finfo[i],
|
||||
pk_vals[i],
|
||||
Int32GetDatum(-1), /* typmod */
|
||||
BoolGetDatum(false)); /* implicit coercion */
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up ScanKeys for the index scan. This is essentially how
|
||||
* ExecIndexBuildScanKeys() sets them up. Use the cached index_attnos and
|
||||
@@ -3355,13 +3318,12 @@ build_index_scankeys(const RI_ConstraintInfo *riinfo,
|
||||
* ri_populate_fastpath_metadata
|
||||
* Cache per-key metadata needed by build_index_scankeys().
|
||||
*
|
||||
* Looks up the compare hash entry, operator procedure OID, and index
|
||||
* strategy/subtype for each key column. Called lazily on first use
|
||||
* and persists for the lifetime of the RI_ConstraintInfo entry.
|
||||
* Looks up the operator procedure OID and index strategy/subtype for each
|
||||
* key column. Called lazily on first use and persists for the lifetime of
|
||||
* the RI_ConstraintInfo entry.
|
||||
*/
|
||||
static void
|
||||
ri_populate_fastpath_metadata(RI_ConstraintInfo *riinfo,
|
||||
Relation fk_rel, Relation idx_rel)
|
||||
ri_populate_fastpath_metadata(RI_ConstraintInfo *riinfo, Relation idx_rel)
|
||||
{
|
||||
FastPathMeta *fpmeta;
|
||||
MemoryContext oldcxt = MemoryContextSwitchTo(TopMemoryContext);
|
||||
@@ -3372,16 +3334,10 @@ ri_populate_fastpath_metadata(RI_ConstraintInfo *riinfo,
|
||||
fpmeta = palloc_object(FastPathMeta);
|
||||
fpmeta->next_dead = NULL;
|
||||
|
||||
/* Scratch context for the cached FmgrInfos' fn_mcxt; see FastPathMeta. */
|
||||
fpmeta->scratch_cxt = AllocSetContextCreate(TopMemoryContext,
|
||||
"RI fast-path finfo scratch",
|
||||
ALLOCSET_SMALL_SIZES);
|
||||
for (int i = 0; i < riinfo->nkeys; i++)
|
||||
{
|
||||
Oid eq_opr = riinfo->pf_eq_oprs[i];
|
||||
Oid typeid = RIAttType(fk_rel, riinfo->fk_attnums[i]);
|
||||
Oid lefttype;
|
||||
RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
|
||||
int idx_col;
|
||||
|
||||
/*
|
||||
@@ -3400,10 +3356,6 @@ ri_populate_fastpath_metadata(RI_ConstraintInfo *riinfo,
|
||||
/* 1-based attribute number */
|
||||
fpmeta->index_attnos[i] = idx_col + 1;
|
||||
|
||||
fmgr_info_copy(&fpmeta->cast_func_finfo[i], &entry->cast_func_finfo,
|
||||
fpmeta->scratch_cxt);
|
||||
fmgr_info_copy(&fpmeta->eq_opr_finfo[i], &entry->eq_opr_finfo,
|
||||
fpmeta->scratch_cxt);
|
||||
fpmeta->regops[i] = get_opcode(eq_opr);
|
||||
|
||||
get_op_opfamily_properties(eq_opr,
|
||||
@@ -3919,11 +3871,8 @@ ri_CompareWithCast(Oid eq_opr, Oid typeid, Oid collid,
|
||||
/*
|
||||
* ri_HashCompareOp -
|
||||
*
|
||||
* Look up or create a cache entry for the given equality operator and
|
||||
* the caller's value type (typeid). The entry holds the operator's
|
||||
* FmgrInfo and, if typeid doesn't match what the operator expects as
|
||||
* its right-hand input, a cast function to coerce the value before
|
||||
* comparison.
|
||||
* See if we know how to compare two values, and create a new hash entry
|
||||
* if not.
|
||||
*/
|
||||
static RI_CompareHashEntry *
|
||||
ri_HashCompareOp(Oid eq_opr, Oid typeid)
|
||||
@@ -3979,15 +3928,8 @@ ri_HashCompareOp(Oid eq_opr, Oid typeid)
|
||||
* moment since that will never be generated for implicit coercions.
|
||||
*/
|
||||
op_input_types(eq_opr, &lefttype, &righttype);
|
||||
|
||||
/*
|
||||
* pf_eq_oprs (used by the fast path) can be cross-type when the FK
|
||||
* and PK columns differ in type, e.g. int48eq for int4 PK / int8 FK.
|
||||
* If the FK column's type, or the base type of a domain over it,
|
||||
* already matches what the operator expects as its right-hand input,
|
||||
* no cast is needed.
|
||||
*/
|
||||
if (getBaseType(typeid) == righttype)
|
||||
Assert(lefttype == righttype);
|
||||
if (typeid == lefttype)
|
||||
castfunc = InvalidOid; /* simplest case */
|
||||
else
|
||||
{
|
||||
@@ -4077,7 +4019,6 @@ AtEOXact_RI(bool isCommit)
|
||||
FastPathMeta *dead = ri_fpmeta_dead_list;
|
||||
|
||||
ri_fpmeta_dead_list = dead->next_dead;
|
||||
MemoryContextDelete(dead->scratch_cxt);
|
||||
pfree(dead);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user