diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index a074852a015..2cd16d7eafb 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -294,8 +294,14 @@ check_attrmap_match(TupleDesc indesc, for (i = 0; i < attrMap->maplen; i++) { - Form_pg_attribute inatt; - Form_pg_attribute outatt; + Form_pg_attribute inatt = TupleDescAttr(indesc, i); + Form_pg_attribute outatt = TupleDescAttr(outdesc, i); + + /* + * If the input column has a missing attribute, we need a conversion. + */ + if (inatt->atthasmissing) + return false; if (attrMap->attnums[i] == (i + 1)) continue; @@ -305,8 +311,6 @@ check_attrmap_match(TupleDesc indesc, * dropped, we don't need a conversion. However, attlen and attalign * must agree. */ - inatt = TupleDescAttr(indesc, i); - outatt = TupleDescAttr(outdesc, i); if (attrMap->attnums[i] == 0 && inatt->attisdropped && inatt->attlen == outatt->attlen && diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index e73ce4b6f3c..fb6d86a2690 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -4258,3 +4258,41 @@ alter table at_test_sql_partop attach partition at_test_sql_partop_1 for values drop table at_test_sql_partop; drop operator class at_test_sql_partop using btree; drop function at_test_sql_partop; +/* Test case for bug #16242 */ +-- We create a parent and child where the child has missing +-- non-null attribute values, and arrange to pass them through +-- tuple conversion from the child to the parent tupdesc +create table bar1 (a integer, b integer not null default 1) + partition by range (a); +create table bar2 (a integer); +insert into bar2 values (1); +alter table bar2 add column b integer not null default 1; +-- (at this point bar2 contains tuple with natts=1) +alter table bar1 attach partition bar2 default; +-- this works: +select * from bar1; + a | b +---+--- + 1 | 1 +(1 row) + +-- this exercises tuple conversion: +create function xtrig() + returns trigger language plpgsql +as $$ + declare + r record; + begin + for r in select * from old loop + raise info 'a=%, b=%', r.a, r.b; + end loop; + return NULL; + end; +$$; +create trigger xtrig + after update on bar1 + referencing old table as old + for each statement execute procedure xtrig(); +update bar1 set a = a + 1; +INFO: a=1, b=1 +/* End test case for bug #16242 */ diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index a16e4c9a293..3801f19c58e 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -2801,3 +2801,42 @@ alter table at_test_sql_partop attach partition at_test_sql_partop_1 for values drop table at_test_sql_partop; drop operator class at_test_sql_partop using btree; drop function at_test_sql_partop; + + +/* Test case for bug #16242 */ + +-- We create a parent and child where the child has missing +-- non-null attribute values, and arrange to pass them through +-- tuple conversion from the child to the parent tupdesc +create table bar1 (a integer, b integer not null default 1) + partition by range (a); +create table bar2 (a integer); +insert into bar2 values (1); +alter table bar2 add column b integer not null default 1; +-- (at this point bar2 contains tuple with natts=1) +alter table bar1 attach partition bar2 default; + +-- this works: +select * from bar1; + +-- this exercises tuple conversion: +create function xtrig() + returns trigger language plpgsql +as $$ + declare + r record; + begin + for r in select * from old loop + raise info 'a=%, b=%', r.a, r.b; + end loop; + return NULL; + end; +$$; +create trigger xtrig + after update on bar1 + referencing old table as old + for each statement execute procedure xtrig(); + +update bar1 set a = a + 1; + +/* End test case for bug #16242 */