Protect against hypothetical memory leaks in RelationGetPartitionKey
Also, fix a comment that commit 8a0596cb656e made obsolete. Reported-by: Robert Haas Discussion: http://postgr.es/m/CA+TgmoYbpuUUUp2GhYNwWm0qkah39spiU7uOiNXLz20ASfKYoA@mail.gmail.com
This commit is contained in:
parent
b726eaa37a
commit
be2343221f
53
src/backend/utils/cache/relcache.c
vendored
53
src/backend/utils/cache/relcache.c
vendored
@ -807,17 +807,16 @@ RelationBuildRuleLock(Relation relation)
|
|||||||
* RelationBuildPartitionKey
|
* RelationBuildPartitionKey
|
||||||
* Build and attach to relcache partition key data of relation
|
* Build and attach to relcache partition key data of relation
|
||||||
*
|
*
|
||||||
* Partitioning key data is stored in CacheMemoryContext to ensure it survives
|
* Partitioning key data is a complex structure; to avoid complicated logic to
|
||||||
* as long as the relcache. To avoid leaking memory in that context in case
|
* free individual elements whenever the relcache entry is flushed, we give it
|
||||||
* of an error partway through this function, we build the structure in the
|
* its own memory context, child of CacheMemoryContext, which can easily be
|
||||||
* working context (which must be short-lived) and copy the completed
|
* deleted on its own. To avoid leaking memory in that context in case of an
|
||||||
* structure into the cache memory.
|
* error partway through this function, the context is initially created as a
|
||||||
*
|
* child of CurTransactionContext and only re-parented to CacheMemoryContext
|
||||||
* Also, since the structure being created here is sufficiently complex, we
|
* at the end, when no further errors are possible. Also, we don't make this
|
||||||
* make a private child context of CacheMemoryContext for each relation that
|
* context the current context except in very brief code sections, out of fear
|
||||||
* has associated partition key information. That means no complicated logic
|
* that some of our callees allocate memory on their own which would be leaked
|
||||||
* to free individual elements whenever the relcache entry is flushed - just
|
* permanently.
|
||||||
* delete the context.
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
RelationBuildPartitionKey(Relation relation)
|
RelationBuildPartitionKey(Relation relation)
|
||||||
@ -850,9 +849,9 @@ RelationBuildPartitionKey(Relation relation)
|
|||||||
RelationGetRelationName(relation),
|
RelationGetRelationName(relation),
|
||||||
MEMCONTEXT_COPY_NAME,
|
MEMCONTEXT_COPY_NAME,
|
||||||
ALLOCSET_SMALL_SIZES);
|
ALLOCSET_SMALL_SIZES);
|
||||||
oldcxt = MemoryContextSwitchTo(partkeycxt);
|
|
||||||
|
|
||||||
key = (PartitionKey) palloc0(sizeof(PartitionKeyData));
|
key = (PartitionKey) MemoryContextAllocZero(partkeycxt,
|
||||||
|
sizeof(PartitionKeyData));
|
||||||
|
|
||||||
/* Fixed-length attributes */
|
/* Fixed-length attributes */
|
||||||
form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
|
form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
|
||||||
@ -894,17 +893,20 @@ RelationBuildPartitionKey(Relation relation)
|
|||||||
/*
|
/*
|
||||||
* Run the expressions through const-simplification since the planner
|
* Run the expressions through const-simplification since the planner
|
||||||
* will be comparing them to similarly-processed qual clause operands,
|
* will be comparing them to similarly-processed qual clause operands,
|
||||||
* and may fail to detect valid matches without this step. We don't
|
* and may fail to detect valid matches without this step; fix
|
||||||
* need to bother with canonicalize_qual() though, because partition
|
* opfuncids while at it. We don't need to bother with
|
||||||
* expressions are not full-fledged qualification clauses.
|
* canonicalize_qual() though, because partition expressions are not
|
||||||
|
* full-fledged qualification clauses.
|
||||||
*/
|
*/
|
||||||
expr = eval_const_expressions(NULL, (Node *) expr);
|
expr = eval_const_expressions(NULL, expr);
|
||||||
|
fix_opfuncids(expr);
|
||||||
|
|
||||||
/* May as well fix opfuncids too */
|
oldcxt = MemoryContextSwitchTo(partkeycxt);
|
||||||
fix_opfuncids((Node *) expr);
|
key->partexprs = (List *) copyObject(expr);
|
||||||
key->partexprs = (List *) expr;
|
MemoryContextSwitchTo(oldcxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldcxt = MemoryContextSwitchTo(partkeycxt);
|
||||||
key->partattrs = (AttrNumber *) palloc0(key->partnatts * sizeof(AttrNumber));
|
key->partattrs = (AttrNumber *) palloc0(key->partnatts * sizeof(AttrNumber));
|
||||||
key->partopfamily = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
key->partopfamily = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
||||||
key->partopcintype = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
key->partopcintype = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
||||||
@ -919,8 +921,9 @@ RelationBuildPartitionKey(Relation relation)
|
|||||||
key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
|
key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
|
||||||
key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
|
key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
|
||||||
key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
|
||||||
/* For the hash partitioning, an extended hash function will be used. */
|
/* determine support function number to search for */
|
||||||
procnum = (key->strategy == PARTITION_STRATEGY_HASH) ?
|
procnum = (key->strategy == PARTITION_STRATEGY_HASH) ?
|
||||||
HASHEXTENDED_PROC : BTORDER_PROC;
|
HASHEXTENDED_PROC : BTORDER_PROC;
|
||||||
|
|
||||||
@ -952,7 +955,7 @@ RelationBuildPartitionKey(Relation relation)
|
|||||||
if (!OidIsValid(funcid))
|
if (!OidIsValid(funcid))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
errmsg("operator class \"%s\" of access method %s is missing support function %d for data type \"%s\"",
|
errmsg("operator class \"%s\" of access method %s is missing support function %d for type %s",
|
||||||
NameStr(opclassform->opcname),
|
NameStr(opclassform->opcname),
|
||||||
(key->strategy == PARTITION_STRATEGY_HASH) ?
|
(key->strategy == PARTITION_STRATEGY_HASH) ?
|
||||||
"hash" : "btree",
|
"hash" : "btree",
|
||||||
@ -989,11 +992,13 @@ RelationBuildPartitionKey(Relation relation)
|
|||||||
|
|
||||||
ReleaseSysCache(tuple);
|
ReleaseSysCache(tuple);
|
||||||
|
|
||||||
/* Success --- make the relcache point to the newly constructed key */
|
/*
|
||||||
|
* Success --- reparent our context and make the relcache point to the
|
||||||
|
* newly constructed key
|
||||||
|
*/
|
||||||
MemoryContextSetParent(partkeycxt, CacheMemoryContext);
|
MemoryContextSetParent(partkeycxt, CacheMemoryContext);
|
||||||
relation->rd_partkeycxt = partkeycxt;
|
relation->rd_partkeycxt = partkeycxt;
|
||||||
relation->rd_partkey = key;
|
relation->rd_partkey = key;
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -338,7 +338,7 @@ typedef HashMetaPageData *HashMetaPage;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* When a new operator class is declared, we require that the user supply
|
* When a new operator class is declared, we require that the user supply
|
||||||
* us with an amproc procudure for hashing a key of the new type, returning
|
* us with an amproc procedure for hashing a key of the new type, returning
|
||||||
* a 32-bit hash value. We call this the "standard" hash procedure. We
|
* a 32-bit hash value. We call this the "standard" hash procedure. We
|
||||||
* also allow an optional "extended" hash procedure which accepts a salt and
|
* also allow an optional "extended" hash procedure which accepts a salt and
|
||||||
* returns a 64-bit hash value. This is highly recommended but, for reasons
|
* returns a 64-bit hash value. This is highly recommended but, for reasons
|
||||||
|
Loading…
x
Reference in New Issue
Block a user