public inbox for [email protected]
help / color / mirror / Atom feedFrom: Andrei Lepikhov <[email protected]>
To: Masahiko Sawada <[email protected]>
Cc: PostgreSQL mailing lists <[email protected]>
Subject: Re: TRAP: failed Assert("offsets[i] > offsets[i - 1]"), File: "tidstore.c"
Date: Thu, 16 Apr 2026 09:13:41 +0200
Message-ID: <[email protected]> (raw)
In-Reply-To: <CAD21AoBVEcC5stzLr80RgaWuBh0EoyRQys_aeOz0ceogMVREcQ@mail.gmail.com>
References: <[email protected]>
<CAD21AoBVEcC5stzLr80RgaWuBh0EoyRQys_aeOz0ceogMVREcQ@mail.gmail.com>
On 15/04/2026 22:50, Masahiko Sawada wrote:
> On Wed, Apr 15, 2026 at 5:48 AM Andrei Lepikhov <[email protected]> wrote:
> Could you provide the reproducer of the assertion failure? IIRC there
> have not been such reports on the community so far and the test should
> be included in the patch anyway.
Sure! See in attachment.
--
regards, Andrei Lepikhov,
pgEdge
From 2b04d6476c1d4e75e0caab57dbe61ed817a1fac2 Mon Sep 17 00:00:00 2001
From: "Andrei V. Lepikhov" <[email protected]>
Date: Wed, 15 Apr 2026 14:41:51 +0200
Subject: [PATCH v1] Sort offsets in test_tidstore's do_set_block_offsets().
TidStoreSetBlockOffsets() requires its offsets[] argument to be
strictly ascending, and asserts the precondition.
The test harness was forwarding the OffsetNumber array straight from
SQL, which works only because the current planner happens to present
array_agg() input in the order the VALUES clause was written. That
ordering is not a SQL guarantee: array_agg() without ORDER BY is
unordered by specification, and any plan change that reshuffles the
Cartesian product used in test_tidstore.sql -- parallel aggregation,
a different join order, or a randomised planner -- can deliver the
offsets out of order and cause the Assert.
Fix by sorting offs in place inside do_set_block_offsets() before
calling TidStoreSetBlockOffsets(). Duplicates are intentionally left to fail
the strict-inequality Assert.
---
.../test_tidstore/expected/test_tidstore.out | 8 ++++++++
.../modules/test_tidstore/sql/test_tidstore.sql | 4 ++++
src/test/modules/test_tidstore/test_tidstore.c | 16 ++++++++++++++++
3 files changed, 28 insertions(+)
diff --git a/src/test/modules/test_tidstore/expected/test_tidstore.out b/src/test/modules/test_tidstore/expected/test_tidstore.out
index cbcacfd26e1..0ba1ec76f4c 100644
--- a/src/test/modules/test_tidstore/expected/test_tidstore.out
+++ b/src/test/modules/test_tidstore/expected/test_tidstore.out
@@ -32,6 +32,14 @@ SELECT do_set_block_offsets(blk, array_agg(off)::int2[])
(VALUES (0), (1), (:maxblkno / 2), (:maxblkno - 1), (:maxblkno)) AS blocks(blk),
(VALUES (1), (2), (:maxoffset / 2), (:maxoffset - 1), (:maxoffset)) AS offsets(off)
GROUP BY blk;
+SELECT do_set_block_offsets(1, array_agg(off ORDER BY off DESC)::int2[])
+ FROM
+ (VALUES (1), (2)) AS offsets(off);
+ do_set_block_offsets
+----------------------
+ 1
+(1 row)
+
-- Test offsets embedded in the bitmap header.
SELECT do_set_block_offsets(501, array[greatest((random() * :maxoffset)::int, 1)]::int2[]);
do_set_block_offsets
diff --git a/src/test/modules/test_tidstore/sql/test_tidstore.sql b/src/test/modules/test_tidstore/sql/test_tidstore.sql
index a29e4ec1c55..f1b184656c4 100644
--- a/src/test/modules/test_tidstore/sql/test_tidstore.sql
+++ b/src/test/modules/test_tidstore/sql/test_tidstore.sql
@@ -23,6 +23,10 @@ SELECT do_set_block_offsets(blk, array_agg(off)::int2[])
(VALUES (1), (2), (:maxoffset / 2), (:maxoffset - 1), (:maxoffset)) AS offsets(off)
GROUP BY blk;
+SELECT do_set_block_offsets(1, array_agg(off ORDER BY off DESC)::int2[])
+ FROM
+ (VALUES (1), (2)) AS offsets(off);
+
-- Test offsets embedded in the bitmap header.
SELECT do_set_block_offsets(501, array[greatest((random() * :maxoffset)::int, 1)]::int2[]);
SELECT do_set_block_offsets(502, array_agg(DISTINCT greatest((random() * :maxoffset)::int, 1))::int2[])
diff --git a/src/test/modules/test_tidstore/test_tidstore.c b/src/test/modules/test_tidstore/test_tidstore.c
index c9a035fa494..bfc40bce24c 100644
--- a/src/test/modules/test_tidstore/test_tidstore.c
+++ b/src/test/modules/test_tidstore/test_tidstore.c
@@ -75,6 +75,19 @@ itemptr_cmp(const void *left, const void *right)
return 0;
}
+static int
+offsetnumber_cmp(const void *a, const void *b)
+{
+ OffsetNumber l = *(const OffsetNumber *) a;
+ OffsetNumber r = *(const OffsetNumber *) b;
+
+ if (l < r)
+ return -1;
+ else if (l > r)
+ return 1;
+ return 0;
+}
+
/*
* Create a TidStore. If shared is false, the tidstore is created
* on TopMemoryContext, otherwise on DSA. Although the tidstore
@@ -178,6 +191,9 @@ do_set_block_offsets(PG_FUNCTION_ARGS)
noffs = ArrayGetNItems(ARR_NDIM(ta), ARR_DIMS(ta));
offs = ((OffsetNumber *) ARR_DATA_PTR(ta));
+ /* TidStoreSetBlockOffsets() requires offsets to be strictly ascending. */
+ qsort(offs, noffs, sizeof(OffsetNumber), offsetnumber_cmp);
+
/* Set TIDs in the store */
TidStoreLockExclusive(tidstore);
TidStoreSetBlockOffsets(tidstore, blkno, offs, noffs);
--
2.53.0
Attachments:
[text/plain] v1-0001-Sort-offsets-in-test_tidstore-s-do_set_block_offs.patch (4.1K, 2-v1-0001-Sort-offsets-in-test_tidstore-s-do_set_block_offs.patch)
download | inline diff:
From 2b04d6476c1d4e75e0caab57dbe61ed817a1fac2 Mon Sep 17 00:00:00 2001
From: "Andrei V. Lepikhov" <[email protected]>
Date: Wed, 15 Apr 2026 14:41:51 +0200
Subject: [PATCH v1] Sort offsets in test_tidstore's do_set_block_offsets().
TidStoreSetBlockOffsets() requires its offsets[] argument to be
strictly ascending, and asserts the precondition.
The test harness was forwarding the OffsetNumber array straight from
SQL, which works only because the current planner happens to present
array_agg() input in the order the VALUES clause was written. That
ordering is not a SQL guarantee: array_agg() without ORDER BY is
unordered by specification, and any plan change that reshuffles the
Cartesian product used in test_tidstore.sql -- parallel aggregation,
a different join order, or a randomised planner -- can deliver the
offsets out of order and cause the Assert.
Fix by sorting offs in place inside do_set_block_offsets() before
calling TidStoreSetBlockOffsets(). Duplicates are intentionally left to fail
the strict-inequality Assert.
---
.../test_tidstore/expected/test_tidstore.out | 8 ++++++++
.../modules/test_tidstore/sql/test_tidstore.sql | 4 ++++
src/test/modules/test_tidstore/test_tidstore.c | 16 ++++++++++++++++
3 files changed, 28 insertions(+)
diff --git a/src/test/modules/test_tidstore/expected/test_tidstore.out b/src/test/modules/test_tidstore/expected/test_tidstore.out
index cbcacfd26e1..0ba1ec76f4c 100644
--- a/src/test/modules/test_tidstore/expected/test_tidstore.out
+++ b/src/test/modules/test_tidstore/expected/test_tidstore.out
@@ -32,6 +32,14 @@ SELECT do_set_block_offsets(blk, array_agg(off)::int2[])
(VALUES (0), (1), (:maxblkno / 2), (:maxblkno - 1), (:maxblkno)) AS blocks(blk),
(VALUES (1), (2), (:maxoffset / 2), (:maxoffset - 1), (:maxoffset)) AS offsets(off)
GROUP BY blk;
+SELECT do_set_block_offsets(1, array_agg(off ORDER BY off DESC)::int2[])
+ FROM
+ (VALUES (1), (2)) AS offsets(off);
+ do_set_block_offsets
+----------------------
+ 1
+(1 row)
+
-- Test offsets embedded in the bitmap header.
SELECT do_set_block_offsets(501, array[greatest((random() * :maxoffset)::int, 1)]::int2[]);
do_set_block_offsets
diff --git a/src/test/modules/test_tidstore/sql/test_tidstore.sql b/src/test/modules/test_tidstore/sql/test_tidstore.sql
index a29e4ec1c55..f1b184656c4 100644
--- a/src/test/modules/test_tidstore/sql/test_tidstore.sql
+++ b/src/test/modules/test_tidstore/sql/test_tidstore.sql
@@ -23,6 +23,10 @@ SELECT do_set_block_offsets(blk, array_agg(off)::int2[])
(VALUES (1), (2), (:maxoffset / 2), (:maxoffset - 1), (:maxoffset)) AS offsets(off)
GROUP BY blk;
+SELECT do_set_block_offsets(1, array_agg(off ORDER BY off DESC)::int2[])
+ FROM
+ (VALUES (1), (2)) AS offsets(off);
+
-- Test offsets embedded in the bitmap header.
SELECT do_set_block_offsets(501, array[greatest((random() * :maxoffset)::int, 1)]::int2[]);
SELECT do_set_block_offsets(502, array_agg(DISTINCT greatest((random() * :maxoffset)::int, 1))::int2[])
diff --git a/src/test/modules/test_tidstore/test_tidstore.c b/src/test/modules/test_tidstore/test_tidstore.c
index c9a035fa494..bfc40bce24c 100644
--- a/src/test/modules/test_tidstore/test_tidstore.c
+++ b/src/test/modules/test_tidstore/test_tidstore.c
@@ -75,6 +75,19 @@ itemptr_cmp(const void *left, const void *right)
return 0;
}
+static int
+offsetnumber_cmp(const void *a, const void *b)
+{
+ OffsetNumber l = *(const OffsetNumber *) a;
+ OffsetNumber r = *(const OffsetNumber *) b;
+
+ if (l < r)
+ return -1;
+ else if (l > r)
+ return 1;
+ return 0;
+}
+
/*
* Create a TidStore. If shared is false, the tidstore is created
* on TopMemoryContext, otherwise on DSA. Although the tidstore
@@ -178,6 +191,9 @@ do_set_block_offsets(PG_FUNCTION_ARGS)
noffs = ArrayGetNItems(ARR_NDIM(ta), ARR_DIMS(ta));
offs = ((OffsetNumber *) ARR_DATA_PTR(ta));
+ /* TidStoreSetBlockOffsets() requires offsets to be strictly ascending. */
+ qsort(offs, noffs, sizeof(OffsetNumber), offsetnumber_cmp);
+
/* Set TIDs in the store */
TidStoreLockExclusive(tidstore);
TidStoreSetBlockOffsets(tidstore, blkno, offs, noffs);
--
2.53.0
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected]
Subject: Re: TRAP: failed Assert("offsets[i] > offsets[i - 1]"), File: "tidstore.c"
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox