On Wed, 14 Jan 2026 at 15:26, Roman Khapov <[email protected]> wrote:
>> Hi! Thank you 🙏 for your effort. 0003 looks good to me
>>
>> --
>> Best regards,
>> Kirill Reshke
>
> Hi! Thanks for the patch!
>
> I'v been reviewing your patch, and noticed there are some code logic
> that handles NULL values, but tests doesn't cover this scenarios.
>
> So, I added simple line at contrib/pageinspect/sql/gin.sql:
>
> INSERT INTO test1 VALUES (1, ARRAY[11, 111], ARRAY['a', 'b', 'c']);
> +INSERT INTO test1 VALUES (1, ARRAY[NULL, 222], ARRAY['d', NULL]);
> CREATE INDEX test1_y_idx ON test1 USING gin (y) WITH (fastupdate = off);
>
> And got unexpected result...
> As far as I understand, we shouldn't get error for the whole
> gin_entrypage_items executions when there are NULL-values columns at index
> key, but the
> output contains only the next error:
>
> SELECT * FROM gin_entrypage_items(get_raw_page('test1_y_idx', 1),
> 'test1_y_idx'::regclass);
> --[ RECORD 1 ]--------------
> -itemoffset | 1
> -downlink | (2147483664,1)
> -tids | {"(0,1)"}
> -keys | y=11
> --[ RECORD 2 ]--------------
> -itemoffset | 2
> -downlink | (2147483664,1)
> -tids | {"(0,1)"}
> -keys | y=111
> -
> +ERROR: invalid gin entry page tuple at offset 4
>
> Is the NULL values handle correct?
>
Nice catch!
I agree — the NULL handling seems wrong.
Here's a quick patch to fix it. What do you think?
--
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.
diff --git a/contrib/pageinspect/expected/gin.out b/contrib/pageinspect/expected/gin.out
index 5018ef76aa7..b27cc582273 100644
--- a/contrib/pageinspect/expected/gin.out
+++ b/contrib/pageinspect/expected/gin.out
@@ -1,5 +1,6 @@
CREATE TABLE test1 (x int, y int[], z text[]);
INSERT INTO test1 VALUES (1, ARRAY[11, 111], ARRAY['a', 'b', 'c']);
+INSERT INTO test1 VALUES (2, NULL, ARRAY['d']);
CREATE INDEX test1_y_idx ON test1 USING gin (y) WITH (fastupdate = off);
CREATE INDEX test2_y_z_idx ON test1 USING gin (y, z) WITH (fastupdate = off);
CREATE INDEX test3_y_z_idx ON test1 USING gin (y, z) WITH (fastupdate = on);
@@ -14,7 +15,7 @@ n_pending_tuples | 0
n_total_pages | 2
n_entry_pages | 1
n_data_pages | 0
-n_entries | 2
+n_entries | 3
version | 2
SELECT * FROM gin_metapage_info(get_raw_page('test1_y_idx', 1));
@@ -40,6 +41,11 @@ itemoffset | 2
downlink | (2147483664,1)
tids | {"(0,1)"}
keys | y=111
+-[ RECORD 3 ]--------------
+itemoffset | 3
+downlink | (2147483666,1)
+tids | {"(0,2)"}
+keys | y=NULL
SELECT * FROM gin_entrypage_items(get_raw_page('test2_y_z_idx', 1), 'test2_y_z_idx'::regclass);
-[ RECORD 1 ]--------------
@@ -54,19 +60,29 @@ tids | {"(0,1)"}
keys | y=111
-[ RECORD 3 ]--------------
itemoffset | 3
-downlink | (2147483664,1)
-tids | {"(0,1)"}
-keys | z=a
+downlink | (2147483672,1)
+tids | {"(0,2)"}
+keys | y=NULL
-[ RECORD 4 ]--------------
itemoffset | 4
downlink | (2147483664,1)
tids | {"(0,1)"}
-keys | z=b
+keys | z=a
-[ RECORD 5 ]--------------
itemoffset | 5
downlink | (2147483664,1)
tids | {"(0,1)"}
+keys | z=b
+-[ RECORD 6 ]--------------
+itemoffset | 6
+downlink | (2147483664,1)
+tids | {"(0,1)"}
keys | z=c
+-[ RECORD 7 ]--------------
+itemoffset | 7
+downlink | (2147483664,1)
+tids | {"(0,2)"}
+keys | z=d
INSERT INTO test1 SELECT x, ARRAY[1,10] FROM generate_series(2,10000) x;
SELECT COUNT(*) > 0
diff --git a/contrib/pageinspect/ginfuncs.c b/contrib/pageinspect/ginfuncs.c
index 4c0bac683ea..8a51e7b5d9a 100644
--- a/contrib/pageinspect/ginfuncs.c
+++ b/contrib/pageinspect/ginfuncs.c
@@ -284,10 +284,6 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
/* Here we can safely reuse any tuple descriptor. */
attrVal = index_getattr(idxtuple, FirstOffsetNumber, tupdesc, &isnull);
- if (isnull)
- ereport(ERROR,
- errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("invalid gin entry page tuple at offset %u", offset));
}
else
{
diff --git a/contrib/pageinspect/sql/gin.sql b/contrib/pageinspect/sql/gin.sql
index 1a9eaebeebc..65e791f829c 100644
--- a/contrib/pageinspect/sql/gin.sql
+++ b/contrib/pageinspect/sql/gin.sql
@@ -1,5 +1,6 @@
CREATE TABLE test1 (x int, y int[], z text[]);
INSERT INTO test1 VALUES (1, ARRAY[11, 111], ARRAY['a', 'b', 'c']);
+INSERT INTO test1 VALUES (2, NULL, ARRAY['d']);
CREATE INDEX test1_y_idx ON test1 USING gin (y) WITH (fastupdate = off);
CREATE INDEX test2_y_z_idx ON test1 USING gin (y, z) WITH (fastupdate = off);
CREATE INDEX test3_y_z_idx ON test1 USING gin (y, z) WITH (fastupdate = on);