From a612e1bc77603cd808a5cc528b50047f13bb2c2b Mon Sep 17 00:00:00 2001 From: Artyom Ivanov Date: Tue, 27 Oct 2026 11:05:25 +0300 Subject: [PATCH 1/4] fix(gstat): Use correct RHD, RHDE and RHDF based on header flags in gstat - Changes in dpm.epp and sqz.cpp are related to the edge case where record size is big for RHDE (cannot store it with this header), but is enough for RHD, so the whole record will be put in tail, the head will not contain any data; --- src/jrd/dpm.epp | 1 - src/jrd/sqz.cpp | 4 ++-- src/utilities/gstat/dba.epp | 14 +++++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/jrd/dpm.epp b/src/jrd/dpm.epp index 733d3ed49d1..296ad1873c2 100644 --- a/src/jrd/dpm.epp +++ b/src/jrd/dpm.epp @@ -4182,7 +4182,6 @@ static void store_big_record(thread_db* tdbb, // Fill up data pages tail first until what's left fits on a single page. auto size = dcc.getPackedLength(); - fb_assert(size > max_data); do { diff --git a/src/jrd/sqz.cpp b/src/jrd/sqz.cpp index 5ebcc7db49d..eade6f262b6 100644 --- a/src/jrd/sqz.cpp +++ b/src/jrd/sqz.cpp @@ -344,7 +344,7 @@ ULONG Compressor::truncateTail(ULONG outLength) * Return the number of trailing input bytes that fit the given output length. * **************************************/ - fb_assert(m_length > outLength); + [[maybe_unused]] const bool truncateAll = m_length <= outLength; if (m_runs.isEmpty()) { @@ -394,7 +394,7 @@ ULONG Compressor::truncateTail(ULONG outLength) m_runs.pop(); } - fb_assert(m_runs.hasData()); + fb_assert(truncateAll || m_runs.hasData()); if (m_allowUnpacked) { diff --git a/src/utilities/gstat/dba.epp b/src/utilities/gstat/dba.epp index 01ceaa869ec..8abd3a80dfa 100644 --- a/src/utilities/gstat/dba.epp +++ b/src/utilities/gstat/dba.epp @@ -1557,6 +1557,10 @@ static bool analyze_data_page( dba_rel* relation, const data_page* page, bool sw relation->rel_record_space -= RHDF_SIZE; relation->rel_record_space += analyze_fragments(relation, header); } + else if (header->rhdf_flags & rhd_long_tranum) + { + relation->rel_record_space -= RHDE_SIZE; + } else { relation->rel_record_space -= RHD_SIZE; @@ -1663,8 +1667,14 @@ static ULONG analyze_fragments(dba_rel* relation, const rhdf* header) if (!index->dpg_offset) break; space += index->dpg_length; - space -= RHDF_SIZE; + header = (const rhdf*) ((SCHAR *) page + index->dpg_offset); + if (header->rhdf_flags & rhd_incomplete) + space -= RHDF_SIZE; + else if (header->rhdf_flags & rhd_long_tranum) + space -= RHDE_SIZE; + else + space -= RHD_SIZE; fragments++; } @@ -1868,6 +1878,8 @@ static ULONG analyze_versions( dba_rel* relation, const rhdf* header) space -= RHDF_SIZE; space += analyze_fragments(relation, header); } + else if (header->rhdf_flags & rhd_long_tranum) + space -= RHDE_SIZE; else space -= RHD_SIZE; } From 530f2ef1481b4525e8fe515a0397299e818c7e70 Mon Sep 17 00:00:00 2001 From: Artyom Ivanov Date: Tue, 27 Oct 2026 15:25:21 +0300 Subject: [PATCH 2/4] Add assert to check size of record --- src/jrd/dpm.epp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jrd/dpm.epp b/src/jrd/dpm.epp index 296ad1873c2..f3f77ccb65d 100644 --- a/src/jrd/dpm.epp +++ b/src/jrd/dpm.epp @@ -4182,6 +4182,7 @@ static void store_big_record(thread_db* tdbb, // Fill up data pages tail first until what's left fits on a single page. auto size = dcc.getPackedLength(); + fb_assert(size > max_data - (RHDE_SIZE - RHD_SIZE)); do { From ec38d95c7c776d666f1f1fc5e065598c44e1467e Mon Sep 17 00:00:00 2001 From: Artyom Ivanov Date: Thu, 29 Oct 2026 17:44:56 +0300 Subject: [PATCH 3/4] fix(sqz): Do not overrun input in `Compressor::truncateTail()` when everything fits --- src/jrd/sqz.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/jrd/sqz.cpp b/src/jrd/sqz.cpp index eade6f262b6..d21d4b66d26 100644 --- a/src/jrd/sqz.cpp +++ b/src/jrd/sqz.cpp @@ -346,14 +346,16 @@ ULONG Compressor::truncateTail(ULONG outLength) **************************************/ [[maybe_unused]] const bool truncateAll = m_length <= outLength; + ULONG inLength = 0; + if (m_runs.isEmpty()) { - m_length -= outLength; - return outLength; + inLength = MIN(outLength, m_length); + m_length -= inLength; + return inLength; } auto space = (int) outLength; - ULONG inLength = 0; while (m_runs.hasData()) { From 8a5c0792cee7326fe9ad93246378dd5d62ccc047 Mon Sep 17 00:00:00 2001 From: Artyom Ivanov Date: Thu, 29 Oct 2026 17:52:16 +0300 Subject: [PATCH 4/4] Fix assert to check size of record --- src/jrd/BulkInsert.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/BulkInsert.cpp b/src/jrd/BulkInsert.cpp index a86f407d3f5..e1a88e1990d 100644 --- a/src/jrd/BulkInsert.cpp +++ b/src/jrd/BulkInsert.cpp @@ -219,7 +219,7 @@ void BulkInsert::Buffer::fragmentRecord(thread_db* tdbb, record_param* rpb, Comp // Fill up data pages tail first until what's left fits on a single page. auto size = dcc->getPackedLength(); - fb_assert(size > max_data); + fb_assert(size > max_data - (RHDE_SIZE - RHD_SIZE)); do {