Skip to content

Commit 0e37489

Browse files
committed
Force tuple conversion when the source has missing attributes.
Tuple conversion incorrectly concluded that no conversion was needed as long as all the attributes lined up. But if the source tuple has a missing attribute (from addition of a column with default), then the destination tupdesc might not reflect the same default. The typical symptom was that the affected columns would be unexpectedly NULL. Repair by always forcing conversion if the source has missing attributes, which will be filled in by the deform operation. (In theory we could optimize for when the destination has the same default, but that seemed overkill.) Backpatch to 11 where missing attributes were added. Per bug #16242. Vik Fearing (discovery, code, testing) and me (analysis, testcase). Discussion: https://postgr.es/m/16242-d1c9fca28445966b@postgresql.org
1 parent 308724b commit 0e37489

File tree

3 files changed

+101
-8
lines changed

3 files changed

+101
-8
lines changed

src/backend/access/common/tupconvert.c

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,18 @@ convert_tuples_by_position(TupleDesc indesc,
144144
{
145145
for (i = 0; i < n; i++)
146146
{
147-
Form_pg_attribute inatt;
148-
Form_pg_attribute outatt;
147+
Form_pg_attribute inatt = TupleDescAttr(indesc, i);
148+
Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
149+
150+
/*
151+
* If the input column has a missing attribute, we need a
152+
* conversion.
153+
*/
154+
if (inatt->atthasmissing)
155+
{
156+
same = false;
157+
break;
158+
}
149159

150160
if (attrMap[i] == (i + 1))
151161
continue;
@@ -155,8 +165,6 @@ convert_tuples_by_position(TupleDesc indesc,
155165
* also dropped, we needn't convert. However, attlen and attalign
156166
* must agree.
157167
*/
158-
inatt = TupleDescAttr(indesc, i);
159-
outatt = TupleDescAttr(outdesc, i);
160168
if (attrMap[i] == 0 &&
161169
inatt->attisdropped &&
162170
inatt->attlen == outatt->attlen &&
@@ -347,8 +355,18 @@ convert_tuples_by_name_map_if_req(TupleDesc indesc,
347355
same = true;
348356
for (i = 0; i < n; i++)
349357
{
350-
Form_pg_attribute inatt;
351-
Form_pg_attribute outatt;
358+
Form_pg_attribute inatt = TupleDescAttr(indesc, i);
359+
Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
360+
361+
/*
362+
* If the input column has a missing attribute, we need a
363+
* conversion.
364+
*/
365+
if (inatt->atthasmissing)
366+
{
367+
same = false;
368+
break;
369+
}
352370

353371
if (attrMap[i] == (i + 1))
354372
continue;
@@ -358,8 +376,6 @@ convert_tuples_by_name_map_if_req(TupleDesc indesc,
358376
* also dropped, we needn't convert. However, attlen and attalign
359377
* must agree.
360378
*/
361-
inatt = TupleDescAttr(indesc, i);
362-
outatt = TupleDescAttr(outdesc, i);
363379
if (attrMap[i] == 0 &&
364380
inatt->attisdropped &&
365381
inatt->attlen == outatt->attlen &&

src/test/regress/expected/alter_table.out

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4148,3 +4148,41 @@ alter table at_test_sql_partop attach partition at_test_sql_partop_1 for values
41484148
drop table at_test_sql_partop;
41494149
drop operator class at_test_sql_partop using btree;
41504150
drop function at_test_sql_partop;
4151+
/* Test case for bug #16242 */
4152+
-- We create a parent and child where the child has missing
4153+
-- non-null attribute values, and arrange to pass them through
4154+
-- tuple conversion from the child to the parent tupdesc
4155+
create table bar1 (a integer, b integer not null default 1)
4156+
partition by range (a);
4157+
create table bar2 (a integer);
4158+
insert into bar2 values (1);
4159+
alter table bar2 add column b integer not null default 1;
4160+
-- (at this point bar2 contains tuple with natts=1)
4161+
alter table bar1 attach partition bar2 default;
4162+
-- this works:
4163+
select * from bar1;
4164+
a | b
4165+
---+---
4166+
1 | 1
4167+
(1 row)
4168+
4169+
-- this exercises tuple conversion:
4170+
create function xtrig()
4171+
returns trigger language plpgsql
4172+
as $$
4173+
declare
4174+
r record;
4175+
begin
4176+
for r in select * from old loop
4177+
raise info 'a=%, b=%', r.a, r.b;
4178+
end loop;
4179+
return NULL;
4180+
end;
4181+
$$;
4182+
create trigger xtrig
4183+
after update on bar1
4184+
referencing old table as old
4185+
for each statement execute procedure xtrig();
4186+
update bar1 set a = a + 1;
4187+
INFO: a=1, b=1
4188+
/* End test case for bug #16242 */

src/test/regress/sql/alter_table.sql

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,3 +2775,42 @@ alter table at_test_sql_partop attach partition at_test_sql_partop_1 for values
27752775
drop table at_test_sql_partop;
27762776
drop operator class at_test_sql_partop using btree;
27772777
drop function at_test_sql_partop;
2778+
2779+
2780+
/* Test case for bug #16242 */
2781+
2782+
-- We create a parent and child where the child has missing
2783+
-- non-null attribute values, and arrange to pass them through
2784+
-- tuple conversion from the child to the parent tupdesc
2785+
create table bar1 (a integer, b integer not null default 1)
2786+
partition by range (a);
2787+
create table bar2 (a integer);
2788+
insert into bar2 values (1);
2789+
alter table bar2 add column b integer not null default 1;
2790+
-- (at this point bar2 contains tuple with natts=1)
2791+
alter table bar1 attach partition bar2 default;
2792+
2793+
-- this works:
2794+
select * from bar1;
2795+
2796+
-- this exercises tuple conversion:
2797+
create function xtrig()
2798+
returns trigger language plpgsql
2799+
as $$
2800+
declare
2801+
r record;
2802+
begin
2803+
for r in select * from old loop
2804+
raise info 'a=%, b=%', r.a, r.b;
2805+
end loop;
2806+
return NULL;
2807+
end;
2808+
$$;
2809+
create trigger xtrig
2810+
after update on bar1
2811+
referencing old table as old
2812+
for each statement execute procedure xtrig();
2813+
2814+
update bar1 set a = a + 1;
2815+
2816+
/* End test case for bug #16242 */

0 commit comments

Comments
 (0)