Currently, the following query
SELECT q.b = row(2)
FROM unnest(ARRAY[row(1, row(2))]) AS q(a int, b record);
would fail with
ERROR: column "b" has pseudo-type record
This is due to CheckAttributeNamesTypes() being used on a function
coldeflist as if it was a real relation definition. But in the context
of a query there seems to be no harm in allowing this, as other ways of
manipulating anonymous rowtypes work well, e.g.:
SELECT (ARRAY[ROW(1, ROW(2))])[1];
Elvis
>From 873ecd6b31abc28c787f398d78ba2511c6e712a2 Mon Sep 17 00:00:00 2001
From: Elvis Pranskevichus <[email protected]>
Date: Thu, 6 Dec 2018 17:16:28 -0500
Subject: [PATCH] Allow anonymous rowtypes in function return column definition
Currently, the following query
SELECT q.b = row(2)
FROM unnest(ARRAY[row(1, row(2))]) AS q(a int, b record);
would fail with
ERROR: column "b" has pseudo-type record
This is due to CheckAttributeNamesTypes() being used on a function
coldeflist as if it was a real relation definition. But in the context
of a query there seems to be no harm in allowing this, as other ways of
manipulating anonymous rowtypes work well, e.g.:
SELECT (ARRAY[ROW(1, ROW(2))])[1];
---
src/backend/catalog/heap.c | 23 +++++++++++++++--------
src/backend/catalog/index.c | 2 +-
src/backend/commands/tablecmds.c | 4 ++--
src/backend/parser/parse_relation.c | 2 +-
src/include/catalog/heap.h | 6 ++++--
src/test/regress/expected/rowtypes.out | 7 +++++++
src/test/regress/sql/rowtypes.sql | 2 ++
7 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 8c52a1543d..ab9cb600f1 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -412,7 +412,8 @@ heap_create(const char *relname,
*/
void
CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
- bool allow_system_table_mods)
+ bool allow_system_table_mods,
+ bool allow_anonymous_records)
{
int i;
int j;
@@ -471,7 +472,8 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
TupleDescAttr(tupdesc, i)->atttypid,
TupleDescAttr(tupdesc, i)->attcollation,
NIL, /* assume we're creating a new rowtype */
- allow_system_table_mods);
+ allow_system_table_mods,
+ allow_anonymous_records);
}
}
@@ -494,7 +496,8 @@ void
CheckAttributeType(const char *attname,
Oid atttypid, Oid attcollation,
List *containing_rowtypes,
- bool allow_system_table_mods)
+ bool allow_system_table_mods,
+ bool allow_anonymous_records)
{
char att_typtype = get_typtype(atttypid);
Oid att_typelem;
@@ -507,7 +510,8 @@ CheckAttributeType(const char *attname,
* catalogs (this allows creating pg_statistic and cloning it during
* VACUUM FULL)
*/
- if (atttypid != ANYARRAYOID || !allow_system_table_mods)
+ if (!((atttypid == ANYARRAYOID && allow_system_table_mods) ||
+ (atttypid == RECORDOID && allow_anonymous_records)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("column \"%s\" has pseudo-type %s",
@@ -520,7 +524,8 @@ CheckAttributeType(const char *attname,
*/
CheckAttributeType(attname, getBaseType(atttypid), attcollation,
containing_rowtypes,
- allow_system_table_mods);
+ allow_system_table_mods,
+ allow_anonymous_records);
}
else if (att_typtype == TYPTYPE_COMPOSITE)
{
@@ -558,7 +563,8 @@ CheckAttributeType(const char *attname,
CheckAttributeType(NameStr(attr->attname),
attr->atttypid, attr->attcollation,
containing_rowtypes,
- allow_system_table_mods);
+ allow_system_table_mods,
+ allow_anonymous_records);
}
relation_close(relation, AccessShareLock);
@@ -572,7 +578,8 @@ CheckAttributeType(const char *attname,
*/
CheckAttributeType(attname, att_typelem, attcollation,
containing_rowtypes,
- allow_system_table_mods);
+ allow_system_table_mods,
+ allow_anonymous_records);
}
/*
@@ -1063,7 +1070,7 @@ heap_create_with_catalog(const char *relname,
*/
Assert(IsNormalProcessingMode() || IsBootstrapProcessingMode());
- CheckAttributeNamesTypes(tupdesc, relkind, allow_system_table_mods);
+ CheckAttributeNamesTypes(tupdesc, relkind, allow_system_table_mods, false);
/*
* This would fail later on anyway, if the relation already exists. But
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 44625a507b..ac5a285be7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -435,7 +435,7 @@ ConstructTupleDescriptor(Relation heapRelation,
*/
CheckAttributeType(NameStr(to->attname),
to->atttypid, to->attcollation,
- NIL, false);
+ NIL, false, false);
}
/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 357c73073d..0a19209519 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5488,7 +5488,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* make sure datatype is legal for a column */
CheckAttributeType(colDef->colname, typeOid, collOid,
list_make1_oid(rel->rd_rel->reltype),
- false);
+ false, false);
/* construct new attribute's pg_attribute entry */
attribute.attrelid = myrelid;
@@ -9126,7 +9126,7 @@ ATPrepAlterColumnType(List **wqueue,
/* make sure datatype is legal for a column */
CheckAttributeType(colName, targettype, targetcollid,
list_make1_oid(rel->rd_rel->reltype),
- false);
+ false, false);
if (tab->relkind == RELKIND_RELATION ||
tab->relkind == RELKIND_PARTITIONED_TABLE)
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index bf5df26009..0f224e7718 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1567,7 +1567,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
* Ensure that the coldeflist defines a legal set of names (no
* duplicates) and datatypes (no pseudo-types, for instance).
*/
- CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false);
+ CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false, true);
}
else
ereport(ERROR,
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index c5e40ff017..9ee4824af8 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -133,12 +133,14 @@ extern Form_pg_attribute SystemAttributeByName(const char *attname,
bool relhasoids);
extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
- bool allow_system_table_mods);
+ bool allow_system_table_mods,
+ bool allow_anonymous_records);
extern void CheckAttributeType(const char *attname,
Oid atttypid, Oid attcollation,
List *containing_rowtypes,
- bool allow_system_table_mods);
+ bool allow_system_table_mods,
+ bool allow_anonymous_records);
/* pg_partitioned_table catalog manipulation functions */
extern void StorePartitionKey(Relation rel,
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index 30053d07df..0a98725d98 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -668,6 +668,13 @@ select row(1, '(1,2)')::testtype6 *<> row(1, '(1,3)')::testtype6;
(1 row)
drop type testtype1, testtype2, testtype3, testtype4, testtype5, testtype6;
+-- Test anonymous rowtype in coldeflist
+SELECT q.b = row(2) FROM unnest(ARRAY[row(1, row(2))]) AS q(a int, b record);
+ ?column?
+----------
+ t
+(1 row)
+
--
-- Test case derived from bug #5716: check multiple uses of a rowtype result
--
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index faf2e108d6..08d320426a 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -261,6 +261,8 @@ select row(1, '(1,2)')::testtype6 *<> row(1, '(1,3)')::testtype6;
drop type testtype1, testtype2, testtype3, testtype4, testtype5, testtype6;
+-- Test anonymous rowtype in coldeflist
+SELECT q.b = row(2) FROM unnest(ARRAY[row(1, row(2))]) AS q(a int, b record);
--
-- Test case derived from bug #5716: check multiple uses of a rowtype result
--
2.19.2