| From: | 王飞 <wf13934952799(at)gmail(dot)com> |
|---|---|
| To: | wf13934952799(at)gmail(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Subject: | Re: BUG #19829: BTREE pivot tuple length overflows t_info when BLCKSZ=32768 |
| Date: | 2026-11-03 08:10:18 |
| Message-ID: | CACbtDCcMMa=j-npEqY6Kp4OmpQ77frNyNVxjndofrET_oOOsRA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
CREATE EXTENSION amcheck;
-- One incompressible 8172-byte value. The resulting index tuple is
-- MAXALIGN(sizeof(IndexTupleData) + VARHDRSZ + 8172) =
MAXALIGN(8+4+8172) = 8184
-- bytes, which is the largest index tuple index_form_tuple() will ever
produce.
CREATE TABLE pad AS
SELECT substr((SELECT string_agg(md5(random()::text), '')
FROM generate_series(1, 300)), 1, 8172) AS v;
CREATE TABLE t (v text);
-- Must come before CREATE INDEX: the index inherits attstorage, and this
stops
-- TOAST_INDEX_HACK in index_form_tuple() from compressing the value.
ALTER TABLE t ALTER COLUMN v SET STORAGE EXTERNAL;
CREATE INDEX t_v_idx ON t (v);
-- Four identical keys. Three fill the leaf page; the fourth splits it.
Since
-- all keys are equal, _bt_truncate() cannot truncate away any key
attribute and
-- must append a tiebreaker heap TID: newsize = 8184 + 8 = 8192.
INSERT INTO t SELECT v FROM pad, generate_series(1, 4);
SELECT bt_index_check('t_v_idx', true);
ERROR: invalid line pointer storage in index "t_v_idx"
DETAIL: Index tid=(3,2) lp_off=32744, lp_len=0 lp_flags=1.
PG Bug reporting form <noreply(at)postgresql(dot)org> 于2026年11月3日周二 15:47写道:
> The following bug has been logged on the website:
>
> Bug reference: 19829
> Logged by: wangfei
> Email address: wf13934952799(at)gmail(dot)com
> PostgreSQL version: 18.6
> Operating system: ubuntu-24.04
> Description:
>
> PostgreSQL version: 19devel (also reproducible on 18.x)
> Operating system: Linux x86_64
> Configure options: --with-blocksize=32
>
> With BLCKSZ=32768, BTMaxItemSize() evaluates to roughly 10896 bytes, but
> the
> tuple length field reserved by INDEX_SIZE_MASK in IndexTupleData.t_info can
> only represent 8191 bytes. index_form_tuple() enforces the 8191 limit, so
> a
> leaf tuple is at most 8184 bytes after MAXALIGN. _bt_truncate() may then
> need
> to append a tiebreaker heap TID:
>
> newsize = MAXALIGN(IndexTupleSize(pivot)) +
> MAXALIGN(sizeof(ItemPointerData));
> ...
> tidpivot->t_info &= ~INDEX_SIZE_MASK;
> tidpivot->t_info |= newsize;
>
> For a maximally sized leaf tuple this is 8184 + 8 = 8192, i.e. exactly
> INDEX_SIZE_MASK + 1, so the length field wraps to zero. The result is a
> zero-length pivot tuple used as a leaf high key; PageAddItem() and
> XLogRegisterBufData() both accept it without complaint, so the corruption
> is
> written out and WAL-logged silently.
>
> BTMaxItemSize() is supposed to keep leaf tuples small enough that this
> cannot
> happen, but the one-third-of-a-page rule scales with BLCKSZ while
> INDEX_SIZE_MASK does not, so at BLCKSZ=32768 the one-third rule is no
> longer
> the binding constraint. Block sizes of 16384 and below are unaffected.
>
> Steps to reproduce:
> < SQL 脚本>
>
> Expected: either the insert is rejected, or the pivot tuple is stored
> correctly.
> Actual: high key with length 0 (amcheck reports: <具体报错>).
>
> A patch against master is attached. It takes Min() of the one-third rule
> and
> what t_info can represent in BTMaxItemSize(), and adds a defensive
> elog(ERROR)
> in _bt_truncate() so that a future miscalculation cannot corrupt an index
> silently. Since index_form_tuple() already caps index tuples at 8184
> bytes,
> lowering the BLCKSZ=32768 limit cannot invalidate any index that could
> actually exist on disk, so there is no on-disk compatibility concern.
>
>
>
| From | Date | Subject | |
|---|---|---|---|
| Next Message | 王飞 | 2026-11-03 08:12:54 | Re: BUG #19829: BTREE pivot tuple length overflows t_info when BLCKSZ=32768 |
| Previous Message | PG Bug reporting form | 2026-11-03 07:46:52 | BUG #19829: BTREE pivot tuple length overflows t_info when BLCKSZ=32768 |