Hi all,

Some of you may have noticed that some regression tests have been
removed due to some noise in the buildfarm, as of commit 906ea101d0d5.

We did not have time to do something for this release, unfortunately.
It is possible to reproduce the incompatibility by setting
max_stack_depth to a low value, where the first new query of ltree and
intarray would fail, when written in their original shape.

Tom had the idea to switch these two unstable tests to use a balanced
binary tree instead, so as they don't eat the stack still are able to
cover the recent fixes pushed into the tree.

And this investigation has led me to the attached, to-be-backpatched
down to v14.  Even under a low max_stack_depth, these new tests are
stable.  I could not see an issue for the two tests added at the
bottom of ltree.

Opinions or comments?

Thanks,
--
Michael
From d11a1dc0e8dc8d4838ca81fe8d4aa935cc1ed2df Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email protected]>
Date: Thu, 14 May 2026 13:40:00 +0900
Subject: [PATCH] Re-add regression tests for ltree and intarray

These tests have been removed by 906ea101d0d5, due to some of them being
unstable in the buildfarm with low max_stack_depth values.  These tests
are reworked to be more portable.

The tests to cover the findoprnd() overflows use a balanced tree to
avoid using too much stack, per a suggestion by Tom Lane.

Backpatch-through: 14
---
 contrib/intarray/expected/_int.out | 15 +++++++++++++++
 contrib/intarray/sql/_int.sql      | 13 +++++++++++++
 contrib/ltree/expected/ltree.out   | 24 ++++++++++++++++++++++++
 contrib/ltree/sql/ltree.sql        | 20 ++++++++++++++++++++
 4 files changed, 72 insertions(+)

diff --git a/contrib/intarray/expected/_int.out 
b/contrib/intarray/expected/_int.out
index d0e68d0447fb..fb4086a95caf 100644
--- a/contrib/intarray/expected/_int.out
+++ b/contrib/intarray/expected/_int.out
@@ -398,6 +398,21 @@ SELECT '1&(2&(4&(5|!6)))'::query_int;
  1 & 2 & 4 & ( 5 | !6 )
 (1 row)
 
+-- Test for overflow of the int16 "left" field in findoprnd().
+-- This query uses a balanced binary tree to avoid using too much stack.
+DO $$
+DECLARE
+  e text := '1';
+BEGIN
+  FOR i IN 1..15 LOOP
+    e := '(' || e || '&' || e || ')';
+  END LOOP;
+  PERFORM ('0|' || e)::query_int;
+END;
+$$;
+ERROR:  query_int expression is too complex
+CONTEXT:  SQL statement "SELECT ('0|' || e)::query_int"
+PL/pgSQL function inline_code_block line 8 at PERFORM
 -- test non-error-throwing input
 SELECT str as "query_int",
        pg_input_is_valid(str,'query_int') as ok,
diff --git a/contrib/intarray/sql/_int.sql b/contrib/intarray/sql/_int.sql
index 5668ab407045..0d30914725e1 100644
--- a/contrib/intarray/sql/_int.sql
+++ b/contrib/intarray/sql/_int.sql
@@ -75,6 +75,19 @@ SELECT '1&2&4&5&6'::query_int;
 SELECT '1&(2&(4&(5|6)))'::query_int;
 SELECT '1&(2&(4&(5|!6)))'::query_int;
 
+-- Test for overflow of the int16 "left" field in findoprnd().
+-- This query uses a balanced binary tree to avoid using too much stack.
+DO $$
+DECLARE
+  e text := '1';
+BEGIN
+  FOR i IN 1..15 LOOP
+    e := '(' || e || '&' || e || ')';
+  END LOOP;
+  PERFORM ('0|' || e)::query_int;
+END;
+$$;
+
 -- test non-error-throwing input
 
 SELECT str as "query_int",
diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out
index d2a566284755..15b9131a7506 100644
--- a/contrib/ltree/expected/ltree.out
+++ b/contrib/ltree/expected/ltree.out
@@ -1283,6 +1283,21 @@ SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & 
aw_rw%*'::ltxtquery;
  f
 (1 row)
 
+-- Test for overflow of the int16 "left" field in findoprnd().
+-- This query uses a balanced binary tree to avoid using too much stack.
+DO $$
+DECLARE
+  e text := 'a';
+BEGIN
+  FOR i IN 1..14 LOOP
+    e := '(' || e || '&' || e || ')';
+  END LOOP;
+  PERFORM ('b|' || e)::ltxtquery;
+END;
+$$;
+ERROR:  ltxtquery is too large
+CONTEXT:  SQL statement "SELECT ('b|' || e)::ltxtquery"
+PL/pgSQL function inline_code_block line 8 at PERFORM
 --arrays
 SELECT '{1.2.3}'::ltree[] @> '1.2.3.4';
  ?column? 
@@ -8202,3 +8217,12 @@ FROM (VALUES ('.2.3', 'ltree'),
  !tree & aWdf@* | ltxtquery | t  |                |                            
        |                          | 
 (8 rows)
 
+-- Test for overflow of lquery_level.totallen.
+SELECT (repeat('x', 255) || repeat('|' || repeat('x', 255), 256))::lquery;
+ERROR:  lquery level is too large
+DETAIL:  Total size of level exceeds the maximum allowed (65535 bytes).
+--- Test for overflow of lquery_level.numvar, with a set of single-char
+--- variants in one level.
+SELECT (repeat('a|', 65535) || 'a')::lquery;
+ERROR:  lquery level has too many variants
+DETAIL:  Number of variants exceeds the maximum allowed (65535).
diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql
index 77e6958c62a7..d0fade9d17d8 100644
--- a/contrib/ltree/sql/ltree.sql
+++ b/contrib/ltree/sql/ltree.sql
@@ -253,6 +253,19 @@ SELECT 'tree.awdfg'::ltree @ 'tree & aWdfg@'::ltxtquery;
 SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & aw_qw%*'::ltxtquery;
 SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & aw_rw%*'::ltxtquery;
 
+-- Test for overflow of the int16 "left" field in findoprnd().
+-- This query uses a balanced binary tree to avoid using too much stack.
+DO $$
+DECLARE
+  e text := 'a';
+BEGIN
+  FOR i IN 1..14 LOOP
+    e := '(' || e || '&' || e || ')';
+  END LOOP;
+  PERFORM ('b|' || e)::ltxtquery;
+END;
+$$;
+
 --arrays
 
 SELECT '{1.2.3}'::ltree[] @> '1.2.3.4';
@@ -457,3 +470,10 @@ FROM (VALUES ('.2.3', 'ltree'),
              ('!tree & aWdf@*','ltxtquery'))
       AS a(str,typ),
      LATERAL pg_input_error_info(a.str, a.typ) as errinfo;
+
+-- Test for overflow of lquery_level.totallen.
+SELECT (repeat('x', 255) || repeat('|' || repeat('x', 255), 256))::lquery;
+
+--- Test for overflow of lquery_level.numvar, with a set of single-char
+--- variants in one level.
+SELECT (repeat('a|', 65535) || 'a')::lquery;
-- 
2.54.0

Attachment: signature.asc
Description: PGP signature

Reply via email to