mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Tighten check for generated column in partition key expression
A generated column may end up being part of the partition key expression, if it's specified as an expression e.g. "(<generated column name>)" or if the partition key expression contains a whole-row reference, even though we do not allow a generated column to be part of partition key expression. Fix this hole. Co-authored-by: jian he <jian.universality@gmail.com> Co-authored-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com> Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com> Discussion: https://www.postgresql.org/message-id/flat/CACJufxF%3DWDGthXSAQr9thYUsfx_1_t9E6N8tE3B8EqXcVoVfQw%40mail.gmail.com
This commit is contained in:
@@ -19835,6 +19835,8 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu
|
|||||||
/* Expression */
|
/* Expression */
|
||||||
Node *expr = pelem->expr;
|
Node *expr = pelem->expr;
|
||||||
char partattname[16];
|
char partattname[16];
|
||||||
|
Bitmapset *expr_attrs = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
Assert(expr != NULL);
|
Assert(expr != NULL);
|
||||||
atttype = exprType(expr);
|
atttype = exprType(expr);
|
||||||
@@ -19858,43 +19860,36 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu
|
|||||||
while (IsA(expr, CollateExpr))
|
while (IsA(expr, CollateExpr))
|
||||||
expr = (Node *) ((CollateExpr *) expr)->arg;
|
expr = (Node *) ((CollateExpr *) expr)->arg;
|
||||||
|
|
||||||
if (IsA(expr, Var) &&
|
/*
|
||||||
((Var *) expr)->varattno > 0)
|
* Examine all the columns in the partition key expression. When
|
||||||
|
* the whole-row reference is present, examine all the columns of
|
||||||
|
* the partitioned table.
|
||||||
|
*/
|
||||||
|
pull_varattnos(expr, 1, &expr_attrs);
|
||||||
|
if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
|
||||||
{
|
{
|
||||||
/*
|
expr_attrs = bms_add_range(expr_attrs,
|
||||||
* User wrote "(column)" or "(column COLLATE something)".
|
1 - FirstLowInvalidHeapAttributeNumber,
|
||||||
* Treat it like simple attribute anyway.
|
RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
|
||||||
*/
|
expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
|
||||||
partattrs[attn] = ((Var *) expr)->varattno;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
i = -1;
|
||||||
|
while ((i = bms_next_member(expr_attrs, i)) >= 0)
|
||||||
{
|
{
|
||||||
Bitmapset *expr_attrs = NULL;
|
AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
|
||||||
int i;
|
|
||||||
|
|
||||||
partattrs[attn] = 0; /* marks the column as expression */
|
Assert(attno != 0);
|
||||||
*partexprs = lappend(*partexprs, expr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* transformPartitionSpec() should have already rejected
|
|
||||||
* subqueries, aggregates, window functions, and SRFs, based
|
|
||||||
* on the EXPR_KIND_ for partition expressions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cannot allow system column references, since that would
|
* Cannot allow system column references, since that would
|
||||||
* make partition routing impossible: their values won't be
|
* make partition routing impossible: their values won't be
|
||||||
* known yet when we need to do that.
|
* known yet when we need to do that.
|
||||||
*/
|
*/
|
||||||
pull_varattnos(expr, 1, &expr_attrs);
|
if (attno < 0)
|
||||||
for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
|
ereport(ERROR,
|
||||||
{
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
|
errmsg("partition key expressions cannot contain system column references")));
|
||||||
expr_attrs))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
||||||
errmsg("partition key expressions cannot contain system column references")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stored generated columns cannot work: They are computed
|
* Stored generated columns cannot work: They are computed
|
||||||
@@ -19904,20 +19899,35 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu
|
|||||||
* SET EXPRESSION would need to check whether the column is
|
* SET EXPRESSION would need to check whether the column is
|
||||||
* used in partition keys). Seems safer to prohibit for now.
|
* used in partition keys). Seems safer to prohibit for now.
|
||||||
*/
|
*/
|
||||||
i = -1;
|
if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
|
||||||
while ((i = bms_next_member(expr_attrs, i)) >= 0)
|
ereport(ERROR,
|
||||||
{
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
|
errmsg("cannot use generated column in partition key"),
|
||||||
|
errdetail("Column \"%s\" is a generated column.",
|
||||||
|
get_attname(RelationGetRelid(rel), attno, false)),
|
||||||
|
parser_errposition(pstate, pelem->location)));
|
||||||
|
}
|
||||||
|
|
||||||
if (attno > 0 &&
|
if (IsA(expr, Var) &&
|
||||||
TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
|
((Var *) expr)->varattno > 0)
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
||||||
errmsg("cannot use generated column in partition key"),
|
/*
|
||||||
errdetail("Column \"%s\" is a generated column.",
|
* User wrote "(column)" or "(column COLLATE something)".
|
||||||
get_attname(RelationGetRelid(rel), attno, false)),
|
* Treat it like simple attribute anyway.
|
||||||
parser_errposition(pstate, pelem->location)));
|
*/
|
||||||
}
|
partattrs[attn] = ((Var *) expr)->varattno;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
partattrs[attn] = 0; /* marks the column as expression */
|
||||||
|
*partexprs = lappend(*partexprs, expr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* transformPartitionSpec() should have already rejected
|
||||||
|
* subqueries, aggregates, window functions, and SRFs, based
|
||||||
|
* on the EXPR_KIND_ for partition expressions.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Preprocess the expression before checking for mutability.
|
* Preprocess the expression before checking for mutability.
|
||||||
|
|||||||
@@ -1074,11 +1074,26 @@ ERROR: cannot use generated column in partition key
|
|||||||
LINE 1: ...ENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
|
LINE 1: ...ENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
|
||||||
^
|
^
|
||||||
DETAIL: Column "f3" is a generated column.
|
DETAIL: Column "f3" is a generated column.
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3));
|
||||||
|
ERROR: cannot use generated column in partition key
|
||||||
|
LINE 1: ...ERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3));
|
||||||
|
^
|
||||||
|
DETAIL: Column "f3" is a generated column.
|
||||||
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
|
||||||
ERROR: cannot use generated column in partition key
|
ERROR: cannot use generated column in partition key
|
||||||
LINE 1: ...ED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
|
LINE 1: ...ED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
|
||||||
^
|
^
|
||||||
DETAIL: Column "f3" is a generated column.
|
DETAIL: Column "f3" is a generated column.
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((gtest_part_key));
|
||||||
|
ERROR: cannot use generated column in partition key
|
||||||
|
LINE 1: ...ED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((gtest_par...
|
||||||
|
^
|
||||||
|
DETAIL: Column "f3" is a generated column.
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((gtest_part_key is not null));
|
||||||
|
ERROR: cannot use generated column in partition key
|
||||||
|
LINE 1: ...ED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((gtest_par...
|
||||||
|
^
|
||||||
|
DETAIL: Column "f3" is a generated column.
|
||||||
-- ALTER TABLE ... ADD COLUMN
|
-- ALTER TABLE ... ADD COLUMN
|
||||||
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
||||||
INSERT INTO gtest25 VALUES (3), (4);
|
INSERT INTO gtest25 VALUES (3), (4);
|
||||||
|
|||||||
@@ -1036,11 +1036,26 @@ ERROR: cannot use generated column in partition key
|
|||||||
LINE 1: ...NERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE (f3);
|
LINE 1: ...NERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE (f3);
|
||||||
^
|
^
|
||||||
DETAIL: Column "f3" is a generated column.
|
DETAIL: Column "f3" is a generated column.
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3));
|
||||||
|
ERROR: cannot use generated column in partition key
|
||||||
|
LINE 1: ...RATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3));
|
||||||
|
^
|
||||||
|
DETAIL: Column "f3" is a generated column.
|
||||||
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3 * 3));
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3 * 3));
|
||||||
ERROR: cannot use generated column in partition key
|
ERROR: cannot use generated column in partition key
|
||||||
LINE 1: ...D ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3 * 3));
|
LINE 1: ...D ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3 * 3));
|
||||||
^
|
^
|
||||||
DETAIL: Column "f3" is a generated column.
|
DETAIL: Column "f3" is a generated column.
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((gtest_part_key));
|
||||||
|
ERROR: cannot use generated column in partition key
|
||||||
|
LINE 1: ...D ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((gtest_par...
|
||||||
|
^
|
||||||
|
DETAIL: Column "f3" is a generated column.
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((gtest_part_key is not null));
|
||||||
|
ERROR: cannot use generated column in partition key
|
||||||
|
LINE 1: ...D ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((gtest_par...
|
||||||
|
^
|
||||||
|
DETAIL: Column "f3" is a generated column.
|
||||||
-- ALTER TABLE ... ADD COLUMN
|
-- ALTER TABLE ... ADD COLUMN
|
||||||
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
||||||
INSERT INTO gtest25 VALUES (3), (4);
|
INSERT INTO gtest25 VALUES (3), (4);
|
||||||
|
|||||||
@@ -500,7 +500,10 @@ SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
|||||||
|
|
||||||
-- generated columns in partition key (not allowed)
|
-- generated columns in partition key (not allowed)
|
||||||
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3));
|
||||||
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((gtest_part_key));
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((gtest_part_key is not null));
|
||||||
|
|
||||||
-- ALTER TABLE ... ADD COLUMN
|
-- ALTER TABLE ... ADD COLUMN
|
||||||
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
||||||
|
|||||||
@@ -543,7 +543,10 @@ SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
|||||||
|
|
||||||
-- generated columns in partition key (not allowed)
|
-- generated columns in partition key (not allowed)
|
||||||
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE (f3);
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE (f3);
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3));
|
||||||
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3 * 3));
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3 * 3));
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((gtest_part_key));
|
||||||
|
CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((gtest_part_key is not null));
|
||||||
|
|
||||||
-- ALTER TABLE ... ADD COLUMN
|
-- ALTER TABLE ... ADD COLUMN
|
||||||
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
||||||
|
|||||||
Reference in New Issue
Block a user