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
This commit is contained in:
parent
1265776a4d
commit
d8f1c03a46
@ -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 &&
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user