Hi, It seems to me that we currently allow expressions that are anonymous and self-referencing composite type records as partition key, but shouldn't. Allowing them leads to this:
create table foo (a int) partition by list ((row(a, b))); create table foo1 partition of foo for values in ('(1)'::foo); create table foo2 partition of foo for values in ('(2)'::foo); explain select * from foo where row(a) = '(1)'::foo; ERROR: stack depth limit exceeded Stack trace is this: #0 errfinish (dummy=0) at elog.c:442 #1 0x0000000000911a51 in check_stack_depth () at postgres.c:3288 #2 0x00000000007970e6 in expression_tree_mutator (node=0x31890a0, mutator=0x82095f <eval_const_expressions_mutator>, context=0x7fff0578ef60) at nodeFuncs.c:2526 #3 0x000000000082340b in eval_const_expressions_mutator (node=0x31890a0, context=0x7fff0578ef60) at clauses.c:3605 #4 0x000000000079875c in expression_tree_mutator (node=0x31890f8, mutator=0x82095f <eval_const_expressions_mutator>, context=0x7fff0578ef60) at nodeFuncs.c:2996 #5 0x000000000082340b in eval_const_expressions_mutator (node=0x31890f8, context=0x7fff0578ef60) at clauses.c:3605 #6 0x000000000079810c in expression_tree_mutator (node=0x3188cc8, mutator=0x82095f <eval_const_expressions_mutator>, context=0x7fff0578ef60) at nodeFuncs.c:2863 #7 0x000000000082225d in eval_const_expressions_mutator (node=0x3188cc8, context=0x7fff0578ef60) at clauses.c:3154 #8 0x000000000079875c in expression_tree_mutator (node=0x3189240, mutator=0x82095f <eval_const_expressions_mutator>, context=0x7fff0578ef60) at nodeFuncs.c:2996 #9 0x000000000082340b in eval_const_expressions_mutator (node=0x3189240, context=0x7fff0578ef60) at clauses.c:3605 #10 0x000000000082090c in eval_const_expressions (root=0x0, node=0x3189240) at clauses.c:2265 #11 0x0000000000a75169 in RelationBuildPartitionKey (relation=0x7f5ca3e479a8) at partcache.c:139 #12 0x0000000000a7aa5e in RelationBuildDesc (targetRelId=17178, insertIt=true) at relcache.c:1171 #13 0x0000000000a7c975 in RelationIdGetRelation (relationId=17178) at relcache.c:2035 #14 0x000000000048e0c0 in relation_open (relationId=17178, lockmode=1) at relation.c:59 #15 0x0000000000a8a4f7 in load_typcache_tupdesc (typentry=0x1c16bc0) at typcache.c:793 #16 0x0000000000a8a3bb in lookup_type_cache (type_id=17180, flags=256) at typcache.c:748 #17 0x0000000000a8bba4 in lookup_rowtype_tupdesc_internal (type_id=17180, typmod=-1, noError=false) at typcache.c:1570 #18 0x0000000000a8be43 in lookup_rowtype_tupdesc (type_id=17180, typmod=-1) at typcache.c:1656 #19 0x0000000000a0713f in record_cmp (fcinfo=0x7fff0578f4d0) at rowtypes.c:815 #20 0x0000000000a083e2 in btrecordcmp (fcinfo=0x7fff0578f4d0) at rowtypes.c:1276 #21 0x0000000000a97bd9 in FunctionCall2Coll (flinfo=0x2bb4a98, collation=0, arg1=51939144, arg2=51940000) at fmgr.c:1162 #22 0x00000000008443f6 in qsort_partition_list_value_cmp (a=0x3188c50, b=0x3188c58, arg=0x2bb46c0) at partbounds.c:1769 #23 0x0000000000af9dc6 in qsort_arg (a=0x3188c50, n=2, es=8, cmp=0x84439a <qsort_partition_list_value_cmp>, arg=0x2bb46c0) at qsort_arg.c:132 #24 0x000000000084186a in create_list_bounds (boundspecs=0x3188650, nparts=2, key=0x2bb46c0, mapping=0x7fff0578f7d8) at partbounds.c:396 #25 0x00000000008410ec in partition_bounds_create (boundspecs=0x3188650, nparts=2, key=0x2bb46c0, mapping=0x7fff0578f7d8) at partbounds.c:206 #26 0x0000000000847622 in RelationBuildPartitionDesc (rel=0x7f5ca3e47560) at partdesc.c:205 #27 0x0000000000a7aa6a in RelationBuildDesc (targetRelId=17178, insertIt=true) at relcache.c:1172 Also: create table foo (a int) partition by list ((row(a))); create table foo1 partition of foo for values in (row(1)); create table foo2 partition of foo for values in (row(2)); explain select * from foo where row(a) = '(1)'::foo; QUERY PLAN ---------------------------------------------------------- Seq Scan on foo1 foo (cost=0.00..41.88 rows=13 width=4) Filter: (ROW(a) = '(1)'::foo) (2 rows) explain select * from foo where row(a) = '(2)'::foo; QUERY PLAN ---------------------------------------------------------- Seq Scan on foo2 foo (cost=0.00..41.88 rows=13 width=4) Filter: (ROW(a) = '(2)'::foo) (2 rows) -- another session explain select * from foo where row(a) = '(1)'::foo; ERROR: record type has not been registered LINE 1: explain select * from foo where row(a) = '(1)'::foo; Attached a patch to fix that. Thanks, Amit
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index daa80ec4aa..8e5e3296df 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -15157,8 +15157,9 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu */ /* - * Cannot have expressions containing whole-row references or - * system column references. + * Cannot have expressions containing whole-row references, + * system column references, or anonymous and self-referencing + * records. */ pull_varattnos(expr, 1, &expr_attrs); if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, @@ -15175,6 +15176,13 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu errmsg("partition key expressions cannot contain system column references"))); } + if (atttype == RECORDOID || + (type_is_rowtype(atttype) && atttype == rel->rd_rel->reltype)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("partition key cannot be of anonymous or self-referencing composite type"), + parser_errposition(pstate, pelem->location))); + /* * Generated columns cannot work: They are computed after * BEFORE triggers, but partition routing is done before all diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index f63016871c..9fd772d4f8 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -402,6 +402,28 @@ CREATE TABLE partitioned ( ERROR: cannot use system column "xmin" in partition key LINE 3: ) PARTITION BY RANGE (xmin); ^ +-- cannot use anonymous or self-referencing records +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE (((a, b))); +ERROR: partition key cannot be of anonymous or self-referencing composite type +LINE 4: ) PARTITION BY RANGE (((a, b))); + ^ +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE ((row(a, b))); +ERROR: partition key cannot be of anonymous or self-referencing composite type +LINE 4: ) PARTITION BY RANGE ((row(a, b))); + ^ +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE ((row(a, b)::partitioned)); +ERROR: partition key cannot be of anonymous or self-referencing composite type +LINE 4: ) PARTITION BY RANGE ((row(a, b)::partitioned)); + ^ -- functions in key must be immutable CREATE FUNCTION immut_func (a int) RETURNS int AS $$ SELECT a + random()::int; $$ LANGUAGE SQL; CREATE TABLE partitioned ( diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index e835b65ac4..abf4d6e093 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -384,6 +384,20 @@ CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (xmin); +-- cannot use anonymous or self-referencing records +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE (((a, b))); +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE ((row(a, b))); +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE ((row(a, b)::partitioned)); + -- functions in key must be immutable CREATE FUNCTION immut_func (a int) RETURNS int AS $$ SELECT a + random()::int; $$ LANGUAGE SQL; CREATE TABLE partitioned (