diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 2d0d2f4b32..a40382b146 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -149,8 +149,18 @@ convert_tuples_by_position(TupleDesc indesc, { for (i = 0; i < n; 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) + { + same = false; + break; + } if (attrMap[i] == (i + 1)) continue; @@ -160,8 +170,6 @@ convert_tuples_by_position(TupleDesc indesc, * also dropped, we needn't convert. However, attlen and attalign * must agree. */ - inatt = TupleDescAttr(indesc, i); - outatt = TupleDescAttr(outdesc, i); if (attrMap[i] == 0 && inatt->attisdropped && inatt->attlen == outatt->attlen && @@ -233,8 +241,18 @@ convert_tuples_by_name(TupleDesc indesc, same = true; for (i = 0; i < n; 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) + { + same = false; + break; + } if (attrMap[i] == (i + 1)) continue; @@ -244,8 +262,6 @@ convert_tuples_by_name(TupleDesc indesc, * also dropped, we needn't convert. However, attlen and attalign * must agree. */ - inatt = TupleDescAttr(indesc, i); - outatt = TupleDescAttr(outdesc, i); if (attrMap[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 44c178ff4a..4e9c15bbd4 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -4174,3 +4174,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 c65791d4d3..cfc09e859b 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -2763,3 +2763,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 */