Skip to content

Commit 6292cde

Browse files
committed
Fix overflow check and comment in GIN posting list encoding.
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 Korotkov
1 parent fe50481 commit 6292cde

File tree

1 file changed

+26
-9
lines changed

1 file changed

+26
-9
lines changed

src/backend/access/gin/ginpostinglist.c

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,32 @@
2323
/*
2424
* For encoding purposes, item pointers are represented as 64-bit unsigned
2525
* integers. The lowest 11 bits represent the offset number, and the next
26-
* lowest 32 bits are the block number. That leaves 17 bits unused, ie.
26+
* lowest 32 bits are the block number. That leaves 21 bits unused, i.e.
2727
* only 43 low bits are used.
2828
*
29+
* 11 bits is enough for the offset number, because MaxHeapTuplesPerPage <
30+
* 2^11 on all supported block sizes. We are frugal with the bits, because
31+
* smaller integers use fewer bytes in the varbyte encoding, saving disk
32+
* space. (If we get a new table AM in the future that wants to use the full
33+
* range of possible offset numbers, we'll need to change this.)
34+
*
2935
* These 43-bit integers are encoded using varbyte encoding. In each byte,
3036
* the 7 low bits contain data, while the highest bit is a continuation bit.
3137
* When the continuation bit is set, the next byte is part of the same
32-
* integer, otherwise this is the last byte of this integer. 43 bits fit
33-
* conveniently in at most 6 bytes when varbyte encoded (the 6th byte does
34-
* not need a continuation bit, because we know the max size to be 43 bits):
38+
* integer, otherwise this is the last byte of this integer. 43 bits need
39+
* at most 7 bytes in this encoding:
3540
*
3641
* 0XXXXXXX
3742
* 1XXXXXXX 0XXXXYYY
3843
* 1XXXXXXX 1XXXXYYY 0YYYYYYY
3944
* 1XXXXXXX 1XXXXYYY 1YYYYYYY 0YYYYYYY
4045
* 1XXXXXXX 1XXXXYYY 1YYYYYYY 1YYYYYYY 0YYYYYYY
41-
* 1XXXXXXX 1XXXXYYY 1YYYYYYY 1YYYYYYY 1YYYYYYY YYYYYYYY
46+
* 1XXXXXXX 1XXXXYYY 1YYYYYYY 1YYYYYYY 1YYYYYYY 0YYYYYYY
47+
* 1XXXXXXX 1XXXXYYY 1YYYYYYY 1YYYYYYY 1YYYYYYY 1YYYYYYY 0uuuuuuY
4248
*
4349
* X = bits used for offset number
4450
* Y = bits used for block number
51+
* u = unused bit
4552
*
4653
* The bytes are in stored in little-endian order.
4754
*
@@ -73,6 +80,9 @@
7380
*/
7481
#define MaxHeapTuplesPerPageBits 11
7582

83+
/* Max. number of bytes needed to encode the largest supported integer. */
84+
#define MaxBytesPerInteger 7
85+
7686
static inline uint64
7787
itemptr_to_uint64(const ItemPointer iptr)
7888
{
@@ -130,33 +140,40 @@ decode_varbyte(unsigned char **ptr)
130140
unsigned char *p = *ptr;
131141
uint64 c;
132142

143+
/* 1st byte */
133144
c = *(p++);
134145
val = c & 0x7F;
135146
if (c & 0x80)
136147
{
148+
/* 2nd byte */
137149
c = *(p++);
138150
val |= (c & 0x7F) << 7;
139151
if (c & 0x80)
140152
{
153+
/* 3rd byte */
141154
c = *(p++);
142155
val |= (c & 0x7F) << 14;
143156
if (c & 0x80)
144157
{
158+
/* 4th byte */
145159
c = *(p++);
146160
val |= (c & 0x7F) << 21;
147161
if (c & 0x80)
148162
{
163+
/* 5th byte */
149164
c = *(p++);
150165
val |= (c & 0x7F) << 28;
151166
if (c & 0x80)
152167
{
168+
/* 6th byte */
153169
c = *(p++);
154170
val |= (c & 0x7F) << 35;
155171
if (c & 0x80)
156172
{
157-
/* last byte, no continuation bit */
173+
/* 7th byte, should not have continuation bit */
158174
c = *(p++);
159175
val |= c << 42;
176+
Assert((c & 0x80) == 0);
160177
}
161178
}
162179
}
@@ -212,15 +229,15 @@ ginCompressPostingList(const ItemPointer ipd, int nipd, int maxsize,
212229

213230
Assert(val > prev);
214231

215-
if (endptr - ptr >= 6)
232+
if (endptr - ptr >= MaxBytesPerInteger)
216233
encode_varbyte(delta, &ptr);
217234
else
218235
{
219236
/*
220-
* There are less than 6 bytes left. Have to check if the next
237+
* There are less than 7 bytes left. Have to check if the next
221238
* item fits in that space before writing it out.
222239
*/
223-
unsigned char buf[6];
240+
unsigned char buf[MaxBytesPerInteger];
224241
unsigned char *p = buf;
225242

226243
encode_varbyte(delta, &p);

0 commit comments

Comments
 (0)