Commit be234322 authored by Alvaro Herrera's avatar Alvaro Herrera

Protect against hypothetical memory leaks in RelationGetPartitionKey

Also, fix a comment that commit 8a0596cb made obsolete.

Reported-by: Robert Haas
Discussion: http://postgr.es/m/CA+TgmoYbpuUUUp2GhYNwWm0qkah39spiU7uOiNXLz20ASfKYoA@mail.gmail.com
parent b726eaa3
...@@ -807,17 +807,16 @@ RelationBuildRuleLock(Relation relation) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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; ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment