Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5f11c6e

Browse files
committed
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 8a0596c ("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 8a0596c and its followup be23432 ("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
1 parent 08e6cda commit 5f11c6e

File tree

4 files changed

+60
-80
lines changed

4 files changed

+60
-80
lines changed

src/backend/utils/cache/relcache.c

Lines changed: 30 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,6 @@ static Relation AllocateRelationDesc(Form_pg_class relp);
266266
static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
267267
static void RelationBuildTupleDesc(Relation relation);
268268
static void RelationBuildPartitionKey(Relation relation);
269-
static PartitionKey copy_partition_key(PartitionKey fromkey);
270269
static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
271270
static void RelationInitPhysicalAddr(Relation relation);
272271
static void load_critical_index(Oid indexoid, Oid heapoid);
@@ -811,17 +810,16 @@ RelationBuildRuleLock(Relation relation)
811810
* RelationBuildPartitionKey
812811
* Build and attach to relcache partition key data of relation
813812
*
814-
* Partitioning key data is stored in CacheMemoryContext to ensure it survives
815-
* as long as the relcache. To avoid leaking memory in that context in case
816-
* of an error partway through this function, we build the structure in the
817-
* working context (which must be short-lived) and copy the completed
818-
* structure into the cache memory.
819-
*
820-
* Also, since the structure being created here is sufficiently complex, we
821-
* make a private child context of CacheMemoryContext for each relation that
822-
* has associated partition key information. That means no complicated logic
823-
* to free individual elements whenever the relcache entry is flushed - just
824-
* delete the context.
813+
* Partitioning key data is a complex structure; to avoid complicated logic to
814+
* free individual elements whenever the relcache entry is flushed, we give it
815+
* its own memory context, child of CacheMemoryContext, which can easily be
816+
* deleted on its own. To avoid leaking memory in that context in case of an
817+
* error partway through this function, the context is initially created as a
818+
* child of CurTransactionContext and only re-parented to CacheMemoryContext
819+
* at the end, when no further errors are possible. Also, we don't make this
820+
* context the current context except in very brief code sections, out of fear
821+
* that some of our callees allocate memory on their own which would be leaked
822+
* permanently.
825823
*/
826824
static void
827825
RelationBuildPartitionKey(Relation relation)
@@ -849,7 +847,12 @@ RelationBuildPartitionKey(Relation relation)
849847
if (!HeapTupleIsValid(tuple))
850848
return;
851849

852-
key = (PartitionKey) palloc0(sizeof(PartitionKeyData));
850+
partkeycxt = AllocSetContextCreate(CurTransactionContext,
851+
RelationGetRelationName(relation),
852+
ALLOCSET_SMALL_SIZES);
853+
854+
key = (PartitionKey) MemoryContextAllocZero(partkeycxt,
855+
sizeof(PartitionKeyData));
853856

854857
/* Fixed-length attributes */
855858
form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
@@ -896,13 +899,15 @@ RelationBuildPartitionKey(Relation relation)
896899
* expressions should be in canonical form already (ie, no need for
897900
* OR-merging or constant elimination).
898901
*/
899-
expr = eval_const_expressions(NULL, (Node *) expr);
902+
expr = eval_const_expressions(NULL, expr);
903+
fix_opfuncids(expr);
900904

901-
/* May as well fix opfuncids too */
902-
fix_opfuncids((Node *) expr);
903-
key->partexprs = (List *) expr;
905+
oldcxt = MemoryContextSwitchTo(partkeycxt);
906+
key->partexprs = (List *) copyObject(expr);
907+
MemoryContextSwitchTo(oldcxt);
904908
}
905909

910+
oldcxt = MemoryContextSwitchTo(partkeycxt);
906911
key->partattrs = (AttrNumber *) palloc0(key->partnatts * sizeof(AttrNumber));
907912
key->partopfamily = (Oid *) palloc0(key->partnatts * sizeof(Oid));
908913
key->partopcintype = (Oid *) palloc0(key->partnatts * sizeof(Oid));
@@ -917,6 +922,7 @@ RelationBuildPartitionKey(Relation relation)
917922
key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
918923
key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
919924
key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
925+
MemoryContextSwitchTo(oldcxt);
920926

921927
/* Copy partattrs and fill other per-attribute info */
922928
memcpy(key->partattrs, attrs, key->partnatts * sizeof(int16));
@@ -951,7 +957,7 @@ RelationBuildPartitionKey(Relation relation)
951957
BTORDER_PROC, opclassform->opcintype, opclassform->opcintype,
952958
opclassform->opcfamily);
953959

954-
fmgr_info(funcid, &key->partsupfunc[i]);
960+
fmgr_info_cxt(funcid, &key->partsupfunc[i], partkeycxt);
955961

956962
/* Collation */
957963
key->partcollation[i] = collation->values[i];
@@ -984,68 +990,13 @@ RelationBuildPartitionKey(Relation relation)
984990

985991
ReleaseSysCache(tuple);
986992

987-
/* Success --- now copy to the cache memory */
988-
partkeycxt = AllocSetContextCreate(CacheMemoryContext,
989-
RelationGetRelationName(relation),
990-
ALLOCSET_SMALL_SIZES);
993+
/*
994+
* Success --- reparent our context and make the relcache point to the
995+
* newly constructed key
996+
*/
997+
MemoryContextSetParent(partkeycxt, CacheMemoryContext);
991998
relation->rd_partkeycxt = partkeycxt;
992-
oldcxt = MemoryContextSwitchTo(relation->rd_partkeycxt);
993-
relation->rd_partkey = copy_partition_key(key);
994-
MemoryContextSwitchTo(oldcxt);
995-
}
996-
997-
/*
998-
* copy_partition_key
999-
*
1000-
* The copy is allocated in the current memory context.
1001-
*/
1002-
static PartitionKey
1003-
copy_partition_key(PartitionKey fromkey)
1004-
{
1005-
PartitionKey newkey;
1006-
int n;
1007-
1008-
newkey = (PartitionKey) palloc(sizeof(PartitionKeyData));
1009-
1010-
newkey->strategy = fromkey->strategy;
1011-
newkey->partnatts = n = fromkey->partnatts;
1012-
1013-
newkey->partattrs = (AttrNumber *) palloc(n * sizeof(AttrNumber));
1014-
memcpy(newkey->partattrs, fromkey->partattrs, n * sizeof(AttrNumber));
1015-
1016-
newkey->partexprs = copyObject(fromkey->partexprs);
1017-
1018-
newkey->partopfamily = (Oid *) palloc(n * sizeof(Oid));
1019-
memcpy(newkey->partopfamily, fromkey->partopfamily, n * sizeof(Oid));
1020-
1021-
newkey->partopcintype = (Oid *) palloc(n * sizeof(Oid));
1022-
memcpy(newkey->partopcintype, fromkey->partopcintype, n * sizeof(Oid));
1023-
1024-
newkey->partsupfunc = (FmgrInfo *) palloc(n * sizeof(FmgrInfo));
1025-
memcpy(newkey->partsupfunc, fromkey->partsupfunc, n * sizeof(FmgrInfo));
1026-
1027-
newkey->partcollation = (Oid *) palloc(n * sizeof(Oid));
1028-
memcpy(newkey->partcollation, fromkey->partcollation, n * sizeof(Oid));
1029-
1030-
newkey->parttypid = (Oid *) palloc(n * sizeof(Oid));
1031-
memcpy(newkey->parttypid, fromkey->parttypid, n * sizeof(Oid));
1032-
1033-
newkey->parttypmod = (int32 *) palloc(n * sizeof(int32));
1034-
memcpy(newkey->parttypmod, fromkey->parttypmod, n * sizeof(int32));
1035-
1036-
newkey->parttyplen = (int16 *) palloc(n * sizeof(int16));
1037-
memcpy(newkey->parttyplen, fromkey->parttyplen, n * sizeof(int16));
1038-
1039-
newkey->parttypbyval = (bool *) palloc(n * sizeof(bool));
1040-
memcpy(newkey->parttypbyval, fromkey->parttypbyval, n * sizeof(bool));
1041-
1042-
newkey->parttypalign = (char *) palloc(n * sizeof(bool));
1043-
memcpy(newkey->parttypalign, fromkey->parttypalign, n * sizeof(char));
1044-
1045-
newkey->parttypcoll = (Oid *) palloc(n * sizeof(Oid));
1046-
memcpy(newkey->parttypcoll, fromkey->parttypcoll, n * sizeof(Oid));
1047-
1048-
return newkey;
999+
relation->rd_partkey = key;
10491000
}
10501001

10511002
/*

src/include/access/hash.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ typedef HashMetaPageData *HashMetaPage;
290290

291291
/*
292292
* When a new operator class is declared, we require that the user supply
293-
* us with an amproc procudure for hashing a key of the new type.
293+
* us with an amproc procedure for hashing a key of the new type.
294294
* Since we only have one such proc in amproc, it's number 1.
295295
*/
296296
#define HASHPROC 1

src/test/regress/expected/create_table.out

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,8 +764,22 @@ Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MA
764764
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))
765765

766766
DROP TABLE range_parted4;
767+
-- user-defined operator class in partition key
768+
CREATE FUNCTION my_int4_sort(int4,int4) RETURNS int LANGUAGE sql
769+
AS $$ SELECT case WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN 1 ELSE -1 END; $$;
770+
CREATE OPERATOR CLASS test_int4_ops FOR TYPE int4 USING btree AS
771+
OPERATOR 1 < (int4,int4), OPERATOR 2 <= (int4,int4),
772+
OPERATOR 3 = (int4,int4), OPERATOR 4 >= (int4,int4),
773+
OPERATOR 5 > (int4,int4), FUNCTION 1 my_int4_sort(int4,int4);
774+
CREATE TABLE partkey_t (a int4) PARTITION BY RANGE (a test_int4_ops);
775+
CREATE TABLE partkey_t_1 PARTITION OF partkey_t FOR VALUES FROM (0) TO (1000);
776+
INSERT INTO partkey_t VALUES (100);
777+
INSERT INTO partkey_t VALUES (200);
767778
-- cleanup
768779
DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3;
780+
DROP TABLE partkey_t;
781+
DROP OPERATOR CLASS test_int4_ops USING btree;
782+
DROP FUNCTION my_int4_sort(int4,int4);
769783
-- comments on partitioned tables columns
770784
CREATE TABLE parted_col_comment (a int, b text) PARTITION BY LIST (a);
771785
COMMENT ON TABLE parted_col_comment IS 'Am partitioned table';

src/test/regress/sql/create_table.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,8 +637,23 @@ CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, M
637637
\d+ range_parted4_3
638638
DROP TABLE range_parted4;
639639

640+
-- user-defined operator class in partition key
641+
CREATE FUNCTION my_int4_sort(int4,int4) RETURNS int LANGUAGE sql
642+
AS $$ SELECT case WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN 1 ELSE -1 END; $$;
643+
CREATE OPERATOR CLASS test_int4_ops FOR TYPE int4 USING btree AS
644+
OPERATOR 1 < (int4,int4), OPERATOR 2 <= (int4,int4),
645+
OPERATOR 3 = (int4,int4), OPERATOR 4 >= (int4,int4),
646+
OPERATOR 5 > (int4,int4), FUNCTION 1 my_int4_sort(int4,int4);
647+
CREATE TABLE partkey_t (a int4) PARTITION BY RANGE (a test_int4_ops);
648+
CREATE TABLE partkey_t_1 PARTITION OF partkey_t FOR VALUES FROM (0) TO (1000);
649+
INSERT INTO partkey_t VALUES (100);
650+
INSERT INTO partkey_t VALUES (200);
651+
640652
-- cleanup
641653
DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3;
654+
DROP TABLE partkey_t;
655+
DROP OPERATOR CLASS test_int4_ops USING btree;
656+
DROP FUNCTION my_int4_sort(int4,int4);
642657

643658
-- comments on partitioned tables columns
644659
CREATE TABLE parted_col_comment (a int, b text) PARTITION BY LIST (a);

0 commit comments

Comments
 (0)