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))
|
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 */
|
/* store value directly in child pointer slot */
|
||||||
memcpy(slot, value_p, value_sz);
|
memcpy(slot, value_p, value_sz);
|
||||||
|
|
||||||
@ -1765,7 +1769,7 @@ have_slot:
|
|||||||
{
|
{
|
||||||
RT_CHILD_PTR leaf;
|
RT_CHILD_PTR leaf;
|
||||||
|
|
||||||
if (found)
|
if (found && !RT_CHILDPTR_IS_VALUE(*slot))
|
||||||
{
|
{
|
||||||
Assert(RT_PTR_ALLOC_IS_VALID(*slot));
|
Assert(RT_PTR_ALLOC_IS_VALID(*slot));
|
||||||
leaf.alloc = *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;
|
CREATE EXTENSION test_tidstore;
|
||||||
-- To hide the output of do_set_block_offsets()
|
-- To hide the output of do_set_block_offsets()
|
||||||
CREATE TEMP TABLE hideblocks(blockno bigint);
|
CREATE TEMP TABLE hideblocks(blockno bigint);
|
||||||
@ -79,6 +75,118 @@ SELECT test_destroy();
|
|||||||
|
|
||||||
(1 row)
|
(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,
|
-- Use shared memory this time. We can't do that in test_radixtree.sql,
|
||||||
-- because unused static functions would raise warnings there.
|
-- because unused static functions would raise warnings there.
|
||||||
SELECT test_create(true);
|
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;
|
CREATE EXTENSION test_tidstore;
|
||||||
|
|
||||||
-- To hide the output of do_set_block_offsets()
|
-- 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.
|
-- Re-create the TID store for randommized tests.
|
||||||
SELECT test_destroy();
|
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,
|
-- Use shared memory this time. We can't do that in test_radixtree.sql,
|
||||||
-- because unused static functions would raise warnings there.
|
-- because unused static functions would raise warnings there.
|
||||||
SELECT test_create(true);
|
SELECT test_create(true);
|
||||||
|
@ -146,6 +146,18 @@ sanity_check_array(ArrayType *ta)
|
|||||||
errmsg("argument must be empty or one-dimensional array")));
|
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 */
|
/* Set the given block and offsets pairs */
|
||||||
Datum
|
Datum
|
||||||
do_set_block_offsets(PG_FUNCTION_ARGS)
|
do_set_block_offsets(PG_FUNCTION_ARGS)
|
||||||
@ -165,6 +177,9 @@ do_set_block_offsets(PG_FUNCTION_ARGS)
|
|||||||
TidStoreSetBlockOffsets(tidstore, blkno, offs, noffs);
|
TidStoreSetBlockOffsets(tidstore, blkno, offs, noffs);
|
||||||
TidStoreUnlock(tidstore);
|
TidStoreUnlock(tidstore);
|
||||||
|
|
||||||
|
/* Remove the existing items of blkno from the verification array */
|
||||||
|
purge_from_verification_array(blkno);
|
||||||
|
|
||||||
/* Set TIDs in verification array */
|
/* Set TIDs in verification array */
|
||||||
for (int i = 0; i < noffs; i++)
|
for (int i = 0; i < noffs; i++)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user