diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index ce5a5d741de..452a7f3f953 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -572,6 +572,10 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, * are reliably identifiable only within a session, since the identity info * may use a typmod that is only locally assigned. The caller is expected * to know whether these cases are safe.) + * + * flags can also control the phrasing of the error messages. If + * CHKATYPE_IS_PARTKEY is specified, "attname" should be a partition key + * column number as text, not a real column name. * -------------------------------- */ void @@ -598,10 +602,19 @@ CheckAttributeType(const char *attname, if (!((atttypid == ANYARRAYOID && (flags & CHKATYPE_ANYARRAY)) || (atttypid == RECORDOID && (flags & CHKATYPE_ANYRECORD)) || (atttypid == RECORDARRAYOID && (flags & CHKATYPE_ANYRECORD)))) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("column \"%s\" has pseudo-type %s", - attname, format_type_be(atttypid)))); + { + if (flags & CHKATYPE_IS_PARTKEY) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + /* translator: first %s is an integer not a name */ + errmsg("partition key column %s has pseudo-type %s", + attname, format_type_be(atttypid)))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("column \"%s\" has pseudo-type %s", + attname, format_type_be(atttypid)))); + } } else if (att_typtype == TYPTYPE_DOMAIN) { @@ -648,7 +661,7 @@ CheckAttributeType(const char *attname, CheckAttributeType(NameStr(attr->attname), attr->atttypid, attr->attcollation, containing_rowtypes, - flags); + flags & ~CHKATYPE_IS_PARTKEY); } relation_close(relation, AccessShareLock); @@ -679,11 +692,21 @@ CheckAttributeType(const char *attname, * useless, and it cannot be dumped, so we must disallow it. */ if (!OidIsValid(attcollation) && type_is_collatable(atttypid)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("no collation was derived for column \"%s\" with collatable type %s", - attname, format_type_be(atttypid)), - errhint("Use the COLLATE clause to set the collation explicitly."))); + { + if (flags & CHKATYPE_IS_PARTKEY) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + /* translator: first %s is an integer not a name */ + errmsg("no collation was derived for partition key column %s with collatable type %s", + attname, format_type_be(atttypid)), + errhint("Use the COLLATE clause to set the collation explicitly."))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("no collation was derived for column \"%s\" with collatable type %s", + attname, format_type_be(atttypid)), + errhint("Use the COLLATE clause to set the collation explicitly."))); + } } /* diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index e8e004eef4d..53a8f1610a4 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -4899,8 +4899,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) /* * Set all columns in the new slot to NULL initially, to ensure - * columns added as part of the rewrite are initialized to - * NULL. That is necessary as tab->newvals will not contain an + * columns added as part of the rewrite are initialized to NULL. + * That is necessary as tab->newvals will not contain an * expression for columns with a NULL default, e.g. when adding a * column without a default together with a column with a default * requiring an actual rewrite. @@ -15096,11 +15096,23 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu { /* Expression */ Node *expr = pelem->expr; + char partattname[16]; Assert(expr != NULL); atttype = exprType(expr); attcollation = exprCollation(expr); + /* + * The expression must be of a storable type (e.g., not RECORD). + * The test is the same as for whether a table column is of a safe + * type (which is why we needn't check for the non-expression + * case). + */ + snprintf(partattname, sizeof(partattname), "%d", attn + 1); + CheckAttributeType(partattname, + atttype, attcollation, + NIL, CHKATYPE_IS_PARTKEY); + /* * Strip any top-level COLLATE clause. This ensures that we treat * "x COLLATE y" and "(x COLLATE y)" alike. diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index eec71c29d5b..2e631378971 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -22,6 +22,7 @@ /* flag bits for CheckAttributeType/CheckAttributeNamesTypes */ #define CHKATYPE_ANYARRAY 0x01 /* allow ANYARRAY */ #define CHKATYPE_ANYRECORD 0x02 /* allow RECORD and RECORD[] */ +#define CHKATYPE_IS_PARTKEY 0x04 /* attname is part key # not column */ typedef struct RawColumnDefault { diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index f63016871ca..50de4b380a5 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -375,7 +375,7 @@ CREATE TABLE partitioned ( ERROR: cannot use subquery in partition key expression CREATE TABLE partitioned ( a int -) PARTITION BY RANGE (('a')); +) PARTITION BY RANGE ((42)); ERROR: cannot use constant expression as partition key CREATE FUNCTION const_func () RETURNS int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE; CREATE TABLE partitioned ( @@ -402,6 +402,17 @@ CREATE TABLE partitioned ( ERROR: cannot use system column "xmin" in partition key LINE 3: ) PARTITION BY RANGE (xmin); ^ +-- cannot use pseudotypes +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE (((a, b))); +ERROR: partition key column 1 has pseudo-type record +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE (a, ('unknown')); +ERROR: partition key column 2 has pseudo-type unknown -- 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 e835b65ac44..9a40d7b8cdf 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -361,7 +361,7 @@ CREATE TABLE partitioned ( CREATE TABLE partitioned ( a int -) PARTITION BY RANGE (('a')); +) PARTITION BY RANGE ((42)); CREATE FUNCTION const_func () RETURNS int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE; CREATE TABLE partitioned ( @@ -384,6 +384,16 @@ CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (xmin); +-- cannot use pseudotypes +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE (((a, b))); +CREATE TABLE partitioned ( + a int, + b int +) PARTITION BY RANGE (a, ('unknown')); + -- functions in key must be immutable CREATE FUNCTION immut_func (a int) RETURNS int AS $$ SELECT a + random()::int; $$ LANGUAGE SQL; CREATE TABLE partitioned (