mirror of https://github.com/postgres/postgres
The comment did not match what the code actually did for integers with the 43rd bit set. You get an integer like that, if you have a posting list with two adjacent TIDs that are more than 2^31 blocks apart. According to the comment, we would store that in 6 bytes, with no continuation bit on the 6th byte, but in reality, the code encodes it using 7 bytes, with a continuation bit on the 6th byte as normal. The decoding routine also handled these 7-byte integers correctly, except for an overflow check that assumed that one integer needs at most 6 bytes. Fix the overflow check, and fix the comment to match what the code actually does. Also fix the comment that claimed that there are 17 unused bits in the 64-bit representation of an item pointer. In reality, there are 64-32-11=21. Fitting any item pointer into max 6 bytes was an important property when this was written, because in the old pre-9.4 format, item pointers were stored as plain arrays, with 6 bytes for every item pointer. The maximum of 6 bytes per integer in the new format guaranteed that we could convert any page from the old format to the new format after upgrade, so that the new format was never larger than the old format. But we hardly need to worry about that anymore, and running into that problem during upgrade, where an item pointer is expanded from 6 to 7 bytes such that the data doesn't fit on a page anymore, is implausible in practice anyway. Backpatch to all supported versions. This also includes a little test module to test these large distances between item pointers, without requiring a 16 TB table. It is not backpatched, I'm including it more for the benefit of future development of new posting list formats. Discussion: https://www.postgresql.org/message-id/33bfc20a-5c86-f50c-f5a5-58e9925d05ff%40iki.fi Reviewed-by: Masahiko Sawada, Alexander Korotkovpull/47/head
parent
720b59b55b
commit
bde7493d10
@ -0,0 +1,21 @@ |
|||||||
|
# src/test/modules/test_ginpostinglist/Makefile
|
||||||
|
|
||||||
|
MODULE_big = test_ginpostinglist
|
||||||
|
OBJS = test_ginpostinglist.o $(WIN32RES)
|
||||||
|
PGFILEDESC = "test_ginpostinglist - test code for src/backend/access/gin//ginpostinglist.c"
|
||||||
|
|
||||||
|
EXTENSION = test_ginpostinglist
|
||||||
|
DATA = test_ginpostinglist--1.0.sql
|
||||||
|
|
||||||
|
REGRESS = test_ginpostinglist
|
||||||
|
|
||||||
|
ifdef USE_PGXS |
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS) |
||||||
|
else |
||||||
|
subdir = src/test/modules/test_ginpostinglist
|
||||||
|
top_builddir = ../../../..
|
||||||
|
include $(top_builddir)/src/Makefile.global |
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk |
||||||
|
endif |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
test_ginpostinglist contains unit tests for the GIN posting list code in |
||||||
|
src/backend/access/gin/ginpostinglist.c. |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
CREATE EXTENSION test_ginpostinglist; |
||||||
|
-- |
||||||
|
-- All the logic is in the test_ginpostinglist() function. It will throw |
||||||
|
-- a error if something fails. |
||||||
|
-- |
||||||
|
SELECT test_ginpostinglist(); |
||||||
|
NOTICE: testing with (0, 1), (0, 2), max 14 bytes |
||||||
|
NOTICE: encoded 2 item pointers to 10 bytes |
||||||
|
NOTICE: testing with (0, 1), (0, 291), max 14 bytes |
||||||
|
NOTICE: encoded 2 item pointers to 10 bytes |
||||||
|
NOTICE: testing with (0, 1), (4294967294, 291), max 14 bytes |
||||||
|
NOTICE: encoded 1 item pointers to 8 bytes |
||||||
|
NOTICE: testing with (0, 1), (4294967294, 291), max 16 bytes |
||||||
|
NOTICE: encoded 2 item pointers to 16 bytes |
||||||
|
test_ginpostinglist |
||||||
|
--------------------- |
||||||
|
|
||||||
|
(1 row) |
||||||
|
|
||||||
@ -0,0 +1,7 @@ |
|||||||
|
CREATE EXTENSION test_ginpostinglist; |
||||||
|
|
||||||
|
-- |
||||||
|
-- All the logic is in the test_ginpostinglist() function. It will throw |
||||||
|
-- a error if something fails. |
||||||
|
-- |
||||||
|
SELECT test_ginpostinglist(); |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
/* src/test/modules/test_ginpostinglist/test_ginpostinglist--1.0.sql */ |
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION |
||||||
|
\echo Use "CREATE EXTENSION test_ginpostinglist" to load this file. \quit |
||||||
|
|
||||||
|
CREATE FUNCTION test_ginpostinglist() |
||||||
|
RETURNS pg_catalog.void STRICT |
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C; |
||||||
@ -0,0 +1,96 @@ |
|||||||
|
/*--------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* test_ginpostinglist.c |
||||||
|
* Test varbyte-encoding in ginpostinglist.c |
||||||
|
* |
||||||
|
* Copyright (c) 2019, PostgreSQL Global Development Group |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* src/test/modules/test_ginpostinglist/test_ginpostinglist.c |
||||||
|
* |
||||||
|
* ------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include "fmgr.h" |
||||||
|
#include "access/ginblock.h" |
||||||
|
#include "access/gin_private.h" |
||||||
|
#include "access/htup_details.h" |
||||||
|
|
||||||
|
PG_MODULE_MAGIC; |
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(test_ginpostinglist); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Encodes a pair of TIDs, and decodes it back. The first TID is always |
||||||
|
* (0, 1), the second one is formed from the blk/off arguments. The 'maxsize' |
||||||
|
* argument is passed to ginCompressPostingList(); it can be used to test the |
||||||
|
* overflow checks. |
||||||
|
* |
||||||
|
* The reason that we test a pair, instead of just a single TID, is that |
||||||
|
* the GinPostingList stores the first TID as is, and the varbyte-encoding |
||||||
|
* is only used for the deltas between TIDs. So testing a single TID would |
||||||
|
* not exercise the varbyte encoding at all. |
||||||
|
* |
||||||
|
* This function prints NOTICEs to describe what is tested, and how large the |
||||||
|
* resulting GinPostingList is. Any incorrect results, e.g. if the encode + |
||||||
|
* decode round trip doesn't return the original input, are reported as |
||||||
|
* ERRORs. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
test_itemptr_pair(BlockNumber blk, OffsetNumber off, int maxsize) |
||||||
|
{ |
||||||
|
ItemPointerData orig_itemptrs[2]; |
||||||
|
ItemPointer decoded_itemptrs; |
||||||
|
GinPostingList *pl; |
||||||
|
int nwritten; |
||||||
|
int ndecoded; |
||||||
|
|
||||||
|
elog(NOTICE, "testing with (%u, %d), (%u, %d), max %d bytes", |
||||||
|
0, 1, blk, off, maxsize); |
||||||
|
ItemPointerSet(&orig_itemptrs[0], 0, 1); |
||||||
|
ItemPointerSet(&orig_itemptrs[1], blk, off); |
||||||
|
|
||||||
|
/* Encode, and decode it back */ |
||||||
|
pl = ginCompressPostingList(orig_itemptrs, 2, maxsize, &nwritten); |
||||||
|
elog(NOTICE, "encoded %d item pointers to %zu bytes", |
||||||
|
nwritten, SizeOfGinPostingList(pl)); |
||||||
|
|
||||||
|
if (SizeOfGinPostingList(pl) > maxsize) |
||||||
|
elog(ERROR, "overflow: result was %zu bytes, max %d", |
||||||
|
SizeOfGinPostingList(pl), maxsize); |
||||||
|
|
||||||
|
decoded_itemptrs = ginPostingListDecode(pl, &ndecoded); |
||||||
|
if (nwritten != ndecoded) |
||||||
|
elog(NOTICE, "encoded %d itemptrs, %d came back", nwritten, ndecoded); |
||||||
|
|
||||||
|
/* Check the result */ |
||||||
|
if (!ItemPointerEquals(&orig_itemptrs[0], &decoded_itemptrs[0])) |
||||||
|
elog(ERROR, "mismatch on first itemptr: (%u, %d) vs (%u, %d)", |
||||||
|
0, 1, |
||||||
|
ItemPointerGetBlockNumber(&decoded_itemptrs[0]), |
||||||
|
ItemPointerGetOffsetNumber(&decoded_itemptrs[0])); |
||||||
|
|
||||||
|
if (ndecoded == 2 && |
||||||
|
!ItemPointerEquals(&orig_itemptrs[0], &decoded_itemptrs[0])) |
||||||
|
{ |
||||||
|
elog(ERROR, "mismatch on second itemptr: (%u, %d) vs (%u, %d)", |
||||||
|
0, 1, |
||||||
|
ItemPointerGetBlockNumber(&decoded_itemptrs[0]), |
||||||
|
ItemPointerGetOffsetNumber(&decoded_itemptrs[0])); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL-callable entry point to perform all tests. |
||||||
|
*/ |
||||||
|
Datum |
||||||
|
test_ginpostinglist(PG_FUNCTION_ARGS) |
||||||
|
{ |
||||||
|
test_itemptr_pair(0, 2, 14); |
||||||
|
test_itemptr_pair(0, MaxHeapTuplesPerPage, 14); |
||||||
|
test_itemptr_pair(MaxBlockNumber, MaxHeapTuplesPerPage, 14); |
||||||
|
test_itemptr_pair(MaxBlockNumber, MaxHeapTuplesPerPage, 16); |
||||||
|
|
||||||
|
PG_RETURN_VOID(); |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
comment = 'Test code for ginpostinglist.c' |
||||||
|
default_version = '1.0' |
||||||
|
module_pathname = '$libdir/test_ginpostinglist' |
||||||
|
relocatable = true |
||||||
Loading…
Reference in new issue