}
/*
- * map_partition_varattnos - maps varattno of any Vars in expr from the
- * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which
- * may be either a leaf partition or a partitioned table, but both of which
- * must be from the same partitioning hierarchy.
+ * map_partition_varattnos - maps varattnos of all Vars in 'expr' (that have
+ * varno 'fromrel_varno') from the attnums of 'from_rel' to the attnums of
+ * 'to_rel', each of which may be either a leaf partition or a partitioned
+ * table, but both of which must be from the same partitioning hierarchy.
*
- * Even though all of the same column names must be present in all relations
- * in the hierarchy, and they must also have the same types, the attnos may
- * be different.
- *
- * If found_whole_row is not NULL, *found_whole_row returns whether a
- * whole-row variable was found in the input expression.
+ * We need this because even though all of the same column names must be
+ * present in all relations in the hierarchy, and they must also have the
+ * same types, the attnums may be different.
*
* Note: this will work on any node tree, so really the argument and result
* should be declared "Node *". But a substantial majority of the callers
*/
List *
map_partition_varattnos(List *expr, int fromrel_varno,
- Relation to_rel, Relation from_rel,
- bool *found_whole_row)
+ Relation to_rel, Relation from_rel)
{
- bool my_found_whole_row = false;
-
if (expr != NIL)
{
AttrMap *part_attmap;
+ bool found_whole_row;
part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel),
RelationGetDescr(from_rel));
fromrel_varno, 0,
part_attmap,
RelationGetForm(to_rel)->reltype,
- &my_found_whole_row);
+ &found_whole_row);
+ /* Since we provided a to_rowtype, we may ignore found_whole_row. */
}
- if (found_whole_row)
- *found_whole_row = my_found_whole_row;
-
return expr;
}
*/
/*
- * Cannot have expressions containing whole-row references or
- * system column references.
+ * Cannot allow system column references, since that would
+ * make partition routing impossible: their values won't be
+ * known yet when we need to do that.
*/
pull_varattnos(expr, 1, &expr_attrs);
- if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
- expr_attrs))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("partition key expressions cannot contain whole-row references")));
for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
{
if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
{
AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
- if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
+ if (attno > 0 &&
+ TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot use generated column in partition key"),
for (i = 0; i < partdesc->nparts; i++)
{
Relation part_rel;
- bool found_whole_row;
List *thisPartConstraint;
/*
*/
thisPartConstraint =
map_partition_varattnos(partConstraint, 1,
- part_rel, scanrel, &found_whole_row);
- /* There can never be a whole-row reference here */
- if (found_whole_row)
- elog(ERROR, "unexpected whole-row reference found in partition constraint");
+ part_rel, scanrel);
QueuePartitionConstraintValidation(wqueue, part_rel,
thisPartConstraint,
TupleDesc tupleDesc;
ObjectAddress address;
const char *trigger_name;
- bool found_whole_row;
Oid defaultPartOid;
List *partBoundConstraint;
* numbers.
*/
partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
- rel, &found_whole_row);
- /* There can never be a whole-row reference here */
- if (found_whole_row)
- elog(ERROR,
- "unexpected whole-row reference found in partition key");
+ rel);
/* Validate partition constraints against the table being attached. */
QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
*/
defPartConstraint =
map_partition_varattnos(defPartConstraint,
- 1, defaultrel, rel, NULL);
+ 1, defaultrel, rel);
QueuePartitionConstraintValidation(wqueue, defaultrel,
defPartConstraint, true);
RelationGetDescr(pg_trigger), &isnull);
if (!isnull)
{
- bool found_whole_row;
-
qual = stringToNode(TextDatumGetCString(value));
qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
- partition, parent,
- &found_whole_row);
- if (found_whole_row)
- elog(ERROR, "unexpected whole-row reference found in trigger WHEN clause");
+ partition, parent);
qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
- partition, parent,
- &found_whole_row);
- if (found_whole_row)
- elog(ERROR, "unexpected whole-row reference found in trigger WHEN clause");
+ partition, parent);
}
/*
CreateTrigStmt *childStmt;
Relation childTbl;
Node *qual;
- bool found_whole_row;
childTbl = table_open(partdesc->oids[i], ShareRowExclusiveLock);
qual = copyObject(whenClause);
qual = (Node *)
map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
- childTbl, rel,
- &found_whole_row);
- if (found_whole_row)
- elog(ERROR, "unexpected whole-row reference found in trigger WHEN clause");
+ childTbl, rel);
qual = (Node *)
map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
- childTbl, rel,
- &found_whole_row);
- if (found_whole_row)
- elog(ERROR, "unexpected whole-row reference found in trigger WHEN clause");
+ childTbl, rel);
CreateTrigger(childStmt, queryString,
partdesc->oids[i], refRelOid,
*/
def_part_constraints =
map_partition_varattnos(def_part_constraints, 1, default_rel,
- parent, NULL);
+ parent);
/*
* If the existing constraints on the default partition imply that it will
partition_constraint = make_ands_explicit(def_part_constraints);
partition_constraint = (Expr *)
map_partition_varattnos((List *) partition_constraint, 1,
- part_rel, default_rel, NULL);
+ part_rel, default_rel);
/*
* If the partition constraints on default partition child imply
List *my_qual = NIL,
*result = NIL;
Relation parent;
- bool found_whole_row;
/* Guard against stack overflow due to overly deep partition tree */
check_stack_depth();
* in it to bear this relation's attnos. It's safe to assume varno = 1
* here.
*/
- result = map_partition_varattnos(result, 1, rel, parent,
- &found_whole_row);
- /* There can never be a whole-row reference here */
- if (found_whole_row)
- elog(ERROR, "unexpected whole-row reference found in partition key");
+ result = map_partition_varattnos(result, 1, rel, parent);
/* Assert that we're not ing any old data during assignments below */
Assert(rel->rd_partcheckcxt == NULL);
extern List *get_partition_ancestors(Oid relid);
extern Oid index_get_partition(Relation partition, Oid indexId);
extern List *map_partition_varattnos(List *expr, int fromrel_varno,
- Relation to_rel, Relation from_rel,
- bool *found_whole_row);
+ Relation to_rel, Relation from_rel);
extern bool has_partition_attrs(Relation rel, Bitmapset *attnums,
bool *used_in_expr);
) PARTITION BY RANGE (immut_func(a));
ERROR: functions in partition key expression must be marked IMMUTABLE
DROP FUNCTION immut_func(int);
--- cannot contain whole-row references
-CREATE TABLE partitioned (
- a int
-) PARTITION BY RANGE ((partitioned));
-ERROR: partition key expressions cannot contain whole-row references
-- prevent using columns of unsupported types in key (type must have a btree operator class)
CREATE TABLE partitioned (
a point
Filter: (ROW(a, b)::partitioned = '(1,2)'::partitioned)
(2 rows)
+drop table partitioned;
+-- whole-row Var in partition key works too
+create table partitioned (a int, b int)
+ partition by list ((partitioned));
+create table partitioned1
+ partition of partitioned for values in ('(1,2)');
+create table partitioned2
+ partition of partitioned for values in ('(2,4)');
+explain (costs off)
+select * from partitioned where partitioned = '(1,2)'::partitioned;
+ QUERY PLAN
+-----------------------------------------------------------------
+ Seq Scan on partitioned1 partitioned
+ Filter: ((partitioned.*)::partitioned = '(1,2)'::partitioned)
+(2 rows)
+
+\d+ partitioned1
+ Table "public.partitioned1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+ b | integer | | | | plain | |
+Partition of: partitioned FOR VALUES IN ('(1,2)')
+Partition constraint: (((partitioned1.*)::partitioned IS DISTINCT FROM NULL) AND ((partitioned1.*)::partitioned = '(1,2)'::partitioned))
+
drop table partitioned;
-- check that dependencies of partition columns are handled correctly
create domain intdom1 as int;
) PARTITION BY RANGE (immut_func(a));
DROP FUNCTION immut_func(int);
--- cannot contain whole-row references
-CREATE TABLE partitioned (
- a int
-) PARTITION BY RANGE ((partitioned));
-
-- prevent using columns of unsupported types in key (type must have a btree operator class)
CREATE TABLE partitioned (
a point
select * from partitioned where row(a,b)::partitioned = '(1,2)'::partitioned;
drop table partitioned;
+-- whole-row Var in partition key works too
+create table partitioned (a int, b int)
+ partition by list ((partitioned));
+create table partitioned1
+ partition of partitioned for values in ('(1,2)');
+create table partitioned2
+ partition of partitioned for values in ('(2,4)');
+explain (costs off)
+select * from partitioned where partitioned = '(1,2)'::partitioned;
+\d+ partitioned1
+drop table partitioned;
+
-- check that dependencies of partition columns are handled correctly
create domain intdom1 as int;