mirror of
https://github.com/postgres/postgres.git
synced 2025-05-17 06:41:24 +03:00
radixtree: Fix SIGSEGV at update of embeddable value to non-embeddable.
Also, fix a memory leak when updating from non-embeddable to embeddable. Both were unreachable without adding C code. Reported-by: Noah Misch Author: Noah Misch Reviewed-by: Masahiko Sawada, John Naylor Discussion: https://postgr.es/m/20240424210319.4c.nmisch%40google.com
This commit is contained in:
parent
e03ec4641d
commit
bb7f195ff7
@ -1749,6 +1749,10 @@ have_slot:
|
||||
|
||||
if (RT_VALUE_IS_EMBEDDABLE(value_p))
|
||||
{
|
||||
/* free the existing leaf */
|
||||
if (found && !RT_CHILDPTR_IS_VALUE(*slot))
|
||||
RT_FREE_LEAF(tree, *slot);
|
||||
|
||||
/* store value directly in child pointer slot */
|
||||
memcpy(slot, value_p, value_sz);
|
||||
|
||||
@ -1765,7 +1769,7 @@ have_slot:
|
||||
{
|
||||
RT_CHILD_PTR leaf;
|
||||
|
||||
if (found)
|
||||
if (found && !RT_CHILDPTR_IS_VALUE(*slot))
|
||||
{
|
||||
Assert(RT_PTR_ALLOC_IS_VALID(*slot));
|
||||
leaf.alloc = *slot;
|
||||
|
@ -1,7 +1,3 @@
|
||||
-- Note: The test code use an array of TIDs for verification similar
|
||||
-- to vacuum's dead item array pre-PG17. To avoid adding duplicates,
|
||||
-- each call to do_set_block_offsets() should use different block
|
||||
-- numbers.
|
||||
CREATE EXTENSION test_tidstore;
|
||||
-- To hide the output of do_set_block_offsets()
|
||||
CREATE TEMP TABLE hideblocks(blockno bigint);
|
||||
@ -79,6 +75,118 @@ SELECT test_destroy();
|
||||
|
||||
(1 row)
|
||||
|
||||
-- Test replacements crossing RT_CHILDPTR_IS_VALUE in both directions
|
||||
SELECT test_create(false);
|
||||
test_create
|
||||
-------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1,2]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1,2,3]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1,2,3,4]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1,2,3,4,100]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1,2,3,4]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1,2,3]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1,2]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT do_set_block_offsets(1, array[1]::int2[]); SELECT check_set_block_offsets();
|
||||
do_set_block_offsets
|
||||
----------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
check_set_block_offsets
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT test_destroy();
|
||||
test_destroy
|
||||
--------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- Use shared memory this time. We can't do that in test_radixtree.sql,
|
||||
-- because unused static functions would raise warnings there.
|
||||
SELECT test_create(true);
|
||||
|
@ -1,8 +1,3 @@
|
||||
-- Note: The test code use an array of TIDs for verification similar
|
||||
-- to vacuum's dead item array pre-PG17. To avoid adding duplicates,
|
||||
-- each call to do_set_block_offsets() should use different block
|
||||
-- numbers.
|
||||
|
||||
CREATE EXTENSION test_tidstore;
|
||||
|
||||
-- To hide the output of do_set_block_offsets()
|
||||
@ -50,6 +45,22 @@ SELECT test_is_full();
|
||||
|
||||
-- Re-create the TID store for randommized tests.
|
||||
SELECT test_destroy();
|
||||
|
||||
|
||||
-- Test replacements crossing RT_CHILDPTR_IS_VALUE in both directions
|
||||
SELECT test_create(false);
|
||||
SELECT do_set_block_offsets(1, array[1]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT do_set_block_offsets(1, array[1,2]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT do_set_block_offsets(1, array[1,2,3]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT do_set_block_offsets(1, array[1,2,3,4]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT do_set_block_offsets(1, array[1,2,3,4,100]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT do_set_block_offsets(1, array[1,2,3,4]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT do_set_block_offsets(1, array[1,2,3]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT do_set_block_offsets(1, array[1,2]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT do_set_block_offsets(1, array[1]::int2[]); SELECT check_set_block_offsets();
|
||||
SELECT test_destroy();
|
||||
|
||||
|
||||
-- Use shared memory this time. We can't do that in test_radixtree.sql,
|
||||
-- because unused static functions would raise warnings there.
|
||||
SELECT test_create(true);
|
||||
|
@ -146,6 +146,18 @@ sanity_check_array(ArrayType *ta)
|
||||
errmsg("argument must be empty or one-dimensional array")));
|
||||
}
|
||||
|
||||
static void
|
||||
purge_from_verification_array(BlockNumber blkno)
|
||||
{
|
||||
int dst = 0;
|
||||
|
||||
for (int src = 0; src < items.num_tids; src++)
|
||||
if (ItemPointerGetBlockNumber(&items.insert_tids[src]) != blkno)
|
||||
items.insert_tids[dst++] = items.insert_tids[src];
|
||||
items.num_tids = dst;
|
||||
}
|
||||
|
||||
|
||||
/* Set the given block and offsets pairs */
|
||||
Datum
|
||||
do_set_block_offsets(PG_FUNCTION_ARGS)
|
||||
@ -165,6 +177,9 @@ do_set_block_offsets(PG_FUNCTION_ARGS)
|
||||
TidStoreSetBlockOffsets(tidstore, blkno, offs, noffs);
|
||||
TidStoreUnlock(tidstore);
|
||||
|
||||
/* Remove the existing items of blkno from the verification array */
|
||||
purge_from_verification_array(blkno);
|
||||
|
||||
/* Set TIDs in verification array */
|
||||
for (int i = 0; i < noffs; i++)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user