diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index a69b078f91a..c48ec20eee3 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -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; } /* diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 72fce3038c0..7d44714718e 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -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 diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index f887781c6df..1e99c96703b 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -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'; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 9e8fc2f11f2..754184756c3 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -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);