mirror of
https://github.com/postgres/postgres.git
synced 2025-05-29 16:21:20 +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 when 8a0596cb656e ("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 aforementioned 8a0596cb656e and its followup be2343221fb7 ("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:
parent
08e6cda1c5
commit
5f11c6ec61
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user