1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Fix unlikely incremental tuple deform bug with missing attrs

The code added in c456e3911 added populate_isnull_array() to bulk
populate the slot's tts_isnull array 8 elements at a time.  When tuples
don't have an exact multiple-of-eight number of attributes, this will
lead to populating the tts_isnull elements for attributes that don't
exist in the tuple.  This is ok as the array is large enough.  However,
if we perform tuple deforming in two passes, and on the first pass
deform *some* of the attributes with slot_getmissingattrs() then later
when we deform the remaining missing attributes, the subsequent call to
populate_isnull_array() would overwrite the tts_isnull values previously
set by slot_getmissingattrs(), and since that function only continues
where it left off, it wouldn't reapply the previously set values and those
would be left as NULLs, as populate_isnull_array() would have set them.

Here, we fix by passing the tuple's natts to slot_getmissingattrs()
rather than the attnum we're deforming from.  This means we apply all
the missing attribute values each deform pass, so slightly more work,
but deforming several missing values in different deform passes is
likely exceedingly rare.  Doing that seems much better than adding
overhead in the happy path to check for this and skip the subsequent call
to populate_isnull_array().

Author: David Rowley <dgrowleyml@gmail.com>
Reported-by: Peter Geoghegan <pg@bowt.ie>
Discussion: https://postgr.es/m/CAH2-WznHo4b+6AmAj0GZ0jXqDSK69MfHe8fAQwuY_01y7cVNdw@mail.gmail.com
Backpatch-through: 19
This commit is contained in:
David Rowley
2026-08-06 17:42:44 +12:00
parent 1200dfd60c
commit d260d7d003
3 changed files with 36 additions and 1 deletions

View File

@@ -1254,7 +1254,17 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
* to implement a tail-call optimization
*/
*offp = off;
slot_getmissingattrs(slot, attnum, reqnatts);
Assert(HeapTupleHeaderGetNatts(tup) <= attnum);
/*
* Fetch all missing attributes. We pass natts rather than attnum as
* if we're deforming attributes after having already deformed some
* missing attributes, then the call to populate_isnull_array() may
* have overwritten the previous tts_isnull values from what was
* stored in the previous call to slot_getmissingattrs().
*/
slot_getmissingattrs(slot, HeapTupleHeaderGetNatts(tup), reqnatts);
return;
}
done:

View File

@@ -944,6 +944,21 @@ SELECT * FROM t ORDER BY id;
(3 rows)
DROP TABLE t;
-- Ensure defaults are correctly applied during tuple deformation for whole
-- row vars when the defaults are deformed incrementally.
CREATE TABLE t_missing_wholerow (a int NOT NULL, b int);
INSERT INTO t_missing_wholerow (a, b) VALUES (1, null); -- with NULL bitmap
INSERT INTO t_missing_wholerow (a, b) VALUES (2, 3); -- without NULL bitmap
ALTER TABLE t_missing_wholerow ADD COLUMN c int NOT NULL DEFAULT 15;
ALTER TABLE t_missing_wholerow ADD COLUMN d bigint NOT NULL DEFAULT 25;
SELECT a, b, c, t FROM t_missing_wholerow t;
a | b | c | t
---+---+----+-------------
1 | | 15 | (1,,15,25)
2 | 3 | 15 | (2,3,15,25)
(2 rows)
DROP TABLE t_missing_wholerow;
-- cleanup
DROP FOREIGN TABLE ft1;
DROP SERVER s0;

View File

@@ -618,6 +618,16 @@ REPACK t;
SELECT * FROM t ORDER BY id;
DROP TABLE t;
-- Ensure defaults are correctly applied during tuple deformation for whole
-- row vars when the defaults are deformed incrementally.
CREATE TABLE t_missing_wholerow (a int NOT NULL, b int);
INSERT INTO t_missing_wholerow (a, b) VALUES (1, null); -- with NULL bitmap
INSERT INTO t_missing_wholerow (a, b) VALUES (2, 3); -- without NULL bitmap
ALTER TABLE t_missing_wholerow ADD COLUMN c int NOT NULL DEFAULT 15;
ALTER TABLE t_missing_wholerow ADD COLUMN d bigint NOT NULL DEFAULT 25;
SELECT a, b, c, t FROM t_missing_wholerow t;
DROP TABLE t_missing_wholerow;
-- cleanup
DROP FOREIGN TABLE ft1;
DROP SERVER s0;