mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +03:00
Use the right memory context for partkey's FmgrInfo
We were using CurrentMemoryContext to put the partsupfunc fmgr_info into, which isn't right, because we want the PartitionKey as a whole to be in the isolated Relation->rd_partkeycxt context. This can cause a crash with user-defined support functions in the operator classes used by partitioning keys. (Maybe this can cause problems with core-supplied opclasses too, not sure.) This is demonstrably broken in Postgres 10, too, but the initial proposed fix runs afoul of a problem discussed back when8a0596cb65
("Get rid of copy_partition_key") reorganized that code: namely that it is possible to jump out of RelationBuildPartitionKey because of some error and leave a dangling memory context child of CacheMemoryContext. Also, while reviewing this I noticed that the removed-in-pg11 copy_partition_key was doing something wrong, unfixed in pg10, namely doing memcpy() on the FmgrInfo, which is bogus (should be doing fmgr_info_copy). Therefore, in branch pg10, the sane fix seems to be to backpatch both the aforementioned8a0596cb65
and its followupbe2343221f
("Protect against hypothetical memory leaks in RelationGetPartitionKey"), so do that, then apply the fmgr_info memcxt bugfix on top. Add a test case exercising btree-based custom operator classes, which causes a crash prior to this fix. This is not a security problem, because in order to create an operator class you need superuser privileges anyway. Authors: Álvaro Herrera and Amit Langote Reported and diagnosed by: Amit Langote Discussion: https://postgr.es/m/3041e853-b1dd-a0c6-ff21-7cc5633bffd0@lab.ntt.co.jp
This commit is contained in:
109
src/backend/utils/cache/relcache.c
vendored
109
src/backend/utils/cache/relcache.c
vendored
@ -266,7 +266,6 @@ static Relation AllocateRelationDesc(Form_pg_class relp);
|
||||
static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
|
||||
static void RelationBuildTupleDesc(Relation relation);
|
||||
static void RelationBuildPartitionKey(Relation relation);
|
||||
static PartitionKey copy_partition_key(PartitionKey fromkey);
|
||||
static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
|
||||
static void RelationInitPhysicalAddr(Relation relation);
|
||||
static void load_critical_index(Oid indexoid, Oid heapoid);
|
||||
@ -811,17 +810,16 @@ RelationBuildRuleLock(Relation relation)
|
||||
* RelationBuildPartitionKey
|
||||
* Build and attach to relcache partition key data of relation
|
||||
*
|
||||
* Partitioning key data is stored in CacheMemoryContext to ensure it survives
|
||||
* as long as the relcache. To avoid leaking memory in that context in case
|
||||
* of an error partway through this function, we build the structure in the
|
||||
* working context (which must be short-lived) and copy the completed
|
||||
* structure into the cache memory.
|
||||
*
|
||||
* Also, since the structure being created here is sufficiently complex, we
|
||||
* make a private child context of CacheMemoryContext for each relation that
|
||||
* has associated partition key information. That means no complicated logic
|
||||
* to free individual elements whenever the relcache entry is flushed - just
|
||||
* delete the context.
|
||||
* Partitioning key data is a complex structure; to avoid complicated logic to
|
||||
* free individual elements whenever the relcache entry is flushed, we give it
|
||||
* its own memory context, child of CacheMemoryContext, which can easily be
|
||||
* deleted on its own. To avoid leaking memory in that context in case of an
|
||||
* error partway through this function, the context is initially created as a
|
||||
* child of CurTransactionContext and only re-parented to CacheMemoryContext
|
||||
* at the end, when no further errors are possible. Also, we don't make this
|
||||
* context the current context except in very brief code sections, out of fear
|
||||
* that some of our callees allocate memory on their own which would be leaked
|
||||
* permanently.
|
||||
*/
|
||||
static void
|
||||
RelationBuildPartitionKey(Relation relation)
|
||||
@ -849,7 +847,12 @@ RelationBuildPartitionKey(Relation relation)
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
return;
|
||||
|
||||
key = (PartitionKey) palloc0(sizeof(PartitionKeyData));
|
||||
partkeycxt = AllocSetContextCreate(CurTransactionContext,
|
||||
RelationGetRelationName(relation),
|
||||
ALLOCSET_SMALL_SIZES);
|
||||
|
||||
key = (PartitionKey) MemoryContextAllocZero(partkeycxt,
|
||||
sizeof(PartitionKeyData));
|
||||
|
||||
/* Fixed-length attributes */
|
||||
form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
|
||||
@ -896,13 +899,15 @@ RelationBuildPartitionKey(Relation relation)
|
||||
* expressions should be in canonical form already (ie, no need for
|
||||
* OR-merging or constant elimination).
|
||||
*/
|
||||
expr = eval_const_expressions(NULL, (Node *) expr);
|
||||
expr = eval_const_expressions(NULL, expr);
|
||||
fix_opfuncids(expr);
|
||||
|
||||
/* May as well fix opfuncids too */
|
||||
fix_opfuncids((Node *) expr);
|
||||
key->partexprs = (List *) expr;
|
||||
oldcxt = MemoryContextSwitchTo(partkeycxt);
|
||||
key->partexprs = (List *) copyObject(expr);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
}
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(partkeycxt);
|
||||
key->partattrs = (AttrNumber *) palloc0(key->partnatts * sizeof(AttrNumber));
|
||||
key->partopfamily = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
||||
key->partopcintype = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
||||
@ -917,6 +922,7 @@ RelationBuildPartitionKey(Relation relation)
|
||||
key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
|
||||
key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
|
||||
key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
/* Copy partattrs and fill other per-attribute info */
|
||||
memcpy(key->partattrs, attrs, key->partnatts * sizeof(int16));
|
||||
@ -951,7 +957,7 @@ RelationBuildPartitionKey(Relation relation)
|
||||
BTORDER_PROC, opclassform->opcintype, opclassform->opcintype,
|
||||
opclassform->opcfamily);
|
||||
|
||||
fmgr_info(funcid, &key->partsupfunc[i]);
|
||||
fmgr_info_cxt(funcid, &key->partsupfunc[i], partkeycxt);
|
||||
|
||||
/* Collation */
|
||||
key->partcollation[i] = collation->values[i];
|
||||
@ -984,68 +990,13 @@ RelationBuildPartitionKey(Relation relation)
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/* Success --- now copy to the cache memory */
|
||||
partkeycxt = AllocSetContextCreate(CacheMemoryContext,
|
||||
RelationGetRelationName(relation),
|
||||
ALLOCSET_SMALL_SIZES);
|
||||
/*
|
||||
* Success --- reparent our context and make the relcache point to the
|
||||
* newly constructed key
|
||||
*/
|
||||
MemoryContextSetParent(partkeycxt, CacheMemoryContext);
|
||||
relation->rd_partkeycxt = partkeycxt;
|
||||
oldcxt = MemoryContextSwitchTo(relation->rd_partkeycxt);
|
||||
relation->rd_partkey = copy_partition_key(key);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
}
|
||||
|
||||
/*
|
||||
* copy_partition_key
|
||||
*
|
||||
* The copy is allocated in the current memory context.
|
||||
*/
|
||||
static PartitionKey
|
||||
copy_partition_key(PartitionKey fromkey)
|
||||
{
|
||||
PartitionKey newkey;
|
||||
int n;
|
||||
|
||||
newkey = (PartitionKey) palloc(sizeof(PartitionKeyData));
|
||||
|
||||
newkey->strategy = fromkey->strategy;
|
||||
newkey->partnatts = n = fromkey->partnatts;
|
||||
|
||||
newkey->partattrs = (AttrNumber *) palloc(n * sizeof(AttrNumber));
|
||||
memcpy(newkey->partattrs, fromkey->partattrs, n * sizeof(AttrNumber));
|
||||
|
||||
newkey->partexprs = copyObject(fromkey->partexprs);
|
||||
|
||||
newkey->partopfamily = (Oid *) palloc(n * sizeof(Oid));
|
||||
memcpy(newkey->partopfamily, fromkey->partopfamily, n * sizeof(Oid));
|
||||
|
||||
newkey->partopcintype = (Oid *) palloc(n * sizeof(Oid));
|
||||
memcpy(newkey->partopcintype, fromkey->partopcintype, n * sizeof(Oid));
|
||||
|
||||
newkey->partsupfunc = (FmgrInfo *) palloc(n * sizeof(FmgrInfo));
|
||||
memcpy(newkey->partsupfunc, fromkey->partsupfunc, n * sizeof(FmgrInfo));
|
||||
|
||||
newkey->partcollation = (Oid *) palloc(n * sizeof(Oid));
|
||||
memcpy(newkey->partcollation, fromkey->partcollation, n * sizeof(Oid));
|
||||
|
||||
newkey->parttypid = (Oid *) palloc(n * sizeof(Oid));
|
||||
memcpy(newkey->parttypid, fromkey->parttypid, n * sizeof(Oid));
|
||||
|
||||
newkey->parttypmod = (int32 *) palloc(n * sizeof(int32));
|
||||
memcpy(newkey->parttypmod, fromkey->parttypmod, n * sizeof(int32));
|
||||
|
||||
newkey->parttyplen = (int16 *) palloc(n * sizeof(int16));
|
||||
memcpy(newkey->parttyplen, fromkey->parttyplen, n * sizeof(int16));
|
||||
|
||||
newkey->parttypbyval = (bool *) palloc(n * sizeof(bool));
|
||||
memcpy(newkey->parttypbyval, fromkey->parttypbyval, n * sizeof(bool));
|
||||
|
||||
newkey->parttypalign = (char *) palloc(n * sizeof(bool));
|
||||
memcpy(newkey->parttypalign, fromkey->parttypalign, n * sizeof(char));
|
||||
|
||||
newkey->parttypcoll = (Oid *) palloc(n * sizeof(Oid));
|
||||
memcpy(newkey->parttypcoll, fromkey->parttypcoll, n * sizeof(Oid));
|
||||
|
||||
return newkey;
|
||||
relation->rd_partkey = key;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -290,7 +290,7 @@ typedef HashMetaPageData *HashMetaPage;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* us with an amproc procedure for hashing a key of the new type.
|
||||
* Since we only have one such proc in amproc, it's number 1.
|
||||
*/
|
||||
#define HASHPROC 1
|
||||
|
@ -764,8 +764,22 @@ Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MA
|
||||
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
|
||||
|
||||
DROP TABLE range_parted4;
|
||||
-- user-defined operator class in partition key
|
||||
CREATE FUNCTION my_int4_sort(int4,int4) RETURNS int LANGUAGE sql
|
||||
AS $$ SELECT case WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN 1 ELSE -1 END; $$;
|
||||
CREATE OPERATOR CLASS test_int4_ops FOR TYPE int4 USING btree AS
|
||||
OPERATOR 1 < (int4,int4), OPERATOR 2 <= (int4,int4),
|
||||
OPERATOR 3 = (int4,int4), OPERATOR 4 >= (int4,int4),
|
||||
OPERATOR 5 > (int4,int4), FUNCTION 1 my_int4_sort(int4,int4);
|
||||
CREATE TABLE partkey_t (a int4) PARTITION BY RANGE (a test_int4_ops);
|
||||
CREATE TABLE partkey_t_1 PARTITION OF partkey_t FOR VALUES FROM (0) TO (1000);
|
||||
INSERT INTO partkey_t VALUES (100);
|
||||
INSERT INTO partkey_t VALUES (200);
|
||||
-- cleanup
|
||||
DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3;
|
||||
DROP TABLE partkey_t;
|
||||
DROP OPERATOR CLASS test_int4_ops USING btree;
|
||||
DROP FUNCTION my_int4_sort(int4,int4);
|
||||
-- comments on partitioned tables columns
|
||||
CREATE TABLE parted_col_comment (a int, b text) PARTITION BY LIST (a);
|
||||
COMMENT ON TABLE parted_col_comment IS 'Am partitioned table';
|
||||
|
@ -637,8 +637,23 @@ CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, M
|
||||
\d+ range_parted4_3
|
||||
DROP TABLE range_parted4;
|
||||
|
||||
-- user-defined operator class in partition key
|
||||
CREATE FUNCTION my_int4_sort(int4,int4) RETURNS int LANGUAGE sql
|
||||
AS $$ SELECT case WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN 1 ELSE -1 END; $$;
|
||||
CREATE OPERATOR CLASS test_int4_ops FOR TYPE int4 USING btree AS
|
||||
OPERATOR 1 < (int4,int4), OPERATOR 2 <= (int4,int4),
|
||||
OPERATOR 3 = (int4,int4), OPERATOR 4 >= (int4,int4),
|
||||
OPERATOR 5 > (int4,int4), FUNCTION 1 my_int4_sort(int4,int4);
|
||||
CREATE TABLE partkey_t (a int4) PARTITION BY RANGE (a test_int4_ops);
|
||||
CREATE TABLE partkey_t_1 PARTITION OF partkey_t FOR VALUES FROM (0) TO (1000);
|
||||
INSERT INTO partkey_t VALUES (100);
|
||||
INSERT INTO partkey_t VALUES (200);
|
||||
|
||||
-- cleanup
|
||||
DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3;
|
||||
DROP TABLE partkey_t;
|
||||
DROP OPERATOR CLASS test_int4_ops USING btree;
|
||||
DROP FUNCTION my_int4_sort(int4,int4);
|
||||
|
||||
-- comments on partitioned tables columns
|
||||
CREATE TABLE parted_col_comment (a int, b text) PARTITION BY LIST (a);
|
||||
|
Reference in New Issue
Block a user