Re: Missing update of all_hasnulls in BRIN opclasses
Tomas Vondra <tomas.vondra@enterprisedb.com>
From: Tomas Vondra <tomas.vondra@enterprisedb.com>
To: Matthias van de Meent <boekewurm+postgres@gmail.com>
Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Date: 2022-10-22T00:30:48Z
Lists: pgsql-hackers
Attachments
- 0001-fixup-brin-has_nulls.patch (text/x-patch) patch 0001
- 0002-fixup-brin-has_nulls-2.patch (text/x-patch) patch 0002
On 10/21/22 18:44, Tomas Vondra wrote: > > ... > >> Apart from that, this patch looks good. >> Sadly, I don't think we can fix it like this :-( The problem is that all ranges start with all_nulls=true, because the new range gets initialized by brin_memtuple_initialize() like that. But this happens for *every* range before we even start processing the rows. So this way all the ranges would end up with has_nulls=true, making that flag pretty useless. Actually, even just doing "truncate" on the table creates such all-nulls range for the first range, and serializes it to disk. I wondered why we even write such tuples for "empty" ranges to disk, for example after "TRUNCATE" - the table is empty by definition, so how come we write all-nulls brin summary for the first range? For example brininsert() checks if the brin tuple was modified and needs to be written back, but brinbuild() just ignores that, and initializes (and writes) writes the tuple to disk anyway. I think we should not do that - there should be a flag in BrinBuildState, tracking if the BRIN tuple was modified, and we should only write it if it's true. That means we should never get an on-disk summary representing nothing. That doesn't fix the issue, though, because we still need to pass the memtuple tuple to the add_value opclass procedure, and whether it sets the has_nulls flag depends on whether it's a new tuple representing no other rows (in which case has_nulls remains false) or whether it was read from disk (in which case it needs to be flipped to 'true'). But the opclass has no way to tell the difference at the moment - it just gets the BrinMemTuple. So we'd have to extend this, somehow. I wonder how to do this in a back-patchable way - we can't add parameters to the opclass procedure, and the other solution seems to be storing it right in the BrinMemTuple, somehow. But that's likely an ABI break :-( The only solution I can think of is actually passing it using all_nulls and has_nulls - we could set both flags to true (which we never do now) and teach the opclass that it signifies "empty" (and thus not to update has_nulls after resetting all_nulls). Something like the attached (I haven't added any more tests, not sure what would those look like - I can't think of a query testing this, although maybe we could check how the flags change using pageinspect). regards -- Tomas Vondra EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
Commits
-
Show empty BRIN ranges in brin_page_items
- 428c0cae929b 16.0 landed
-
Fix handling of empty ranges and NULLs in BRIN
- 3581cbdcd64f 16.0 landed
- fc7dc728d1a6 11.21 landed
- d78a66d92c73 12.16 landed
- e187693239ad 15.4 landed
- 40d465cafce0 14.9 landed
- 6c512fc6e90d 13.12 landed
-
Fix handling of NULLs when merging BRIN summaries
- b511d7323df6 11.21 landed
- d42ffda685f5 12.16 landed
- 2b1ab28b9dc9 13.12 landed
- 3f1356e5d60a 14.9 landed
- 80f64b90088a 15.4 landed
- 3ec8a3bfb547 16.0 landed
-
Move IS [NOT] NULL handling from BRIN support functions
- 72ccf55cb99c 14.0 cited