new file mode 100644
@@ -0,0 +1,37 @@
+From 1a0f6530c8f506f953d1b4ffdfa84ae7ad72f7df Mon Sep 17 00:00:00 2001
+From: alhudz <al.hudz.k@gmail.com>
+Date: Mon, 1 Jun 2026 19:05:57 +0530
+Subject: [PATCH] set group value from child tokens to avoid quadratic grouping
+
+(cherry picked from commit 939b129e24c0ad5d51368b1aa72fffcaca76f06f)
+
+CVE: CVE-2026-54284
+Upstream-Status: Backport [https://github.com/andialbrecht/sqlparse/commit/939b129e24c0ad5d51368b1aa72fffcaca76f06f]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ sqlparse/sql.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/sqlparse/sql.py b/sqlparse/sql.py
+index 831dfb9..0163ff1 100644
+--- a/sqlparse/sql.py
++++ b/sqlparse/sql.py
+@@ -159,7 +159,7 @@ class TokenList(Token):
+ def __init__(self, tokens=None):
+ self.tokens = tokens or []
+ [setattr(token, 'parent', self) for token in self.tokens]
+- super().__init__(None, str(self))
++ super().__init__(None, ''.join(token.value for token in self.tokens))
+ self.is_group = True
+
+ def __str__(self):
+@@ -322,7 +322,7 @@ class TokenList(Token):
+ grp = start
+ grp.tokens.extend(subtokens)
+ del self.tokens[start_idx + 1:end_idx]
+- grp.value = str(start)
++ grp.value += ''.join(token.value for token in subtokens)
+ else:
+ subtokens = self.tokens[start_idx:end_idx]
+ grp = grp_cls(subtokens)
new file mode 100644
@@ -0,0 +1,144 @@
+From 6416e171b41da8939f391c0492b355d3b6cb8c11 Mon Sep 17 00:00:00 2001
+From: Andi Albrecht <albrecht.andi@gmail.com>
+Date: Sat, 6 Jun 2026 07:12:43 +0200
+Subject: [PATCH] Add tests from PR, update CHANGELOG and AUTHORS.
+
+(cherry picked from commit f80af6a4007f11ada847218df8c29dc859238290)
+
+CVE: CVE-2026-54284
+Upstream-Status: Backport [https://github.com/andialbrecht/sqlparse/commit/f80af6a4007f11ada847218df8c29dc859238290]
+
+Dropped changes to the CHANGELOG file.
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ AUTHORS | 2 ++
+ benchmarks/bench_grouping.py | 59 ++++++++++++++++++++++++++++++++++++
+ tests/test_dos_prevention.py | 28 +++++++++++++++++
+ 3 files changed, 89 insertions(+)
+ create mode 100644 benchmarks/bench_grouping.py
+
+diff --git a/AUTHORS b/AUTHORS
+index 24ca667..872c700 100644
+--- a/AUTHORS
++++ b/AUTHORS
+@@ -12,6 +12,7 @@ Alphabetical list of contributors:
+ * Aki Ariga <chezou+github@gmail.com>
+ * Alexander Beedie <ayembee@gmail.com>
+ * Alexey Malyshev <nostrict@gmail.com>
++* alhudz <al.hudz.k@gmail.com>
+ * ali-tny <aliteeney@googlemail.com>
+ * andrew deryabin <github@djsf.com>
+ * Andrew Tipton <andrew.tipton@compareglobalgroup.com>
+@@ -77,6 +78,7 @@ Alphabetical list of contributors:
+ * Tao Wang <twang2218@gmail.com>
+ * Tenghuan <tenghuanhe@gmail.com>
+ * Tim Graham <timograham@gmail.com>
++* tonghuaroot <tonghuaroot@users.noreply.github.com>
+ * Victor Hahn <info@victor-hahn.de>
+ * Victor Uriarte <vmuriart@gmail.com>
+ * Ville Skyttä <ville.skytta@iki.fi>
+diff --git a/benchmarks/bench_grouping.py b/benchmarks/bench_grouping.py
+new file mode 100644
+index 0000000..245ea0e
+--- /dev/null
++++ b/benchmarks/bench_grouping.py
+@@ -0,0 +1,59 @@
++"""Grouping performance benchmarks.
++
++Measures parse time for SQL patterns that stress the grouping engine:
++- Deeply nested parentheses
++- Deeply nested CASE WHEN expressions
++- Wide column lists (tests O(N) identifier grouping, fixed in PR848)
++
++Run with: python benchmarks/bench_grouping.py
++"""
++
++import signal
++import time
++
++import sqlparse
++
++
++def _alarm_handler(signum, frame):
++ raise TimeoutError()
++
++
++signal.signal(signal.SIGALRM, _alarm_handler)
++
++
++def measure(label, sql, fn):
++ signal.alarm(30)
++ t0 = time.perf_counter()
++ status = 'OK'
++ try:
++ fn(sql)
++ except sqlparse.exceptions.SQLParseError:
++ status = 'CAP'
++ except TimeoutError:
++ status = 'TIMEOUT'
++ finally:
++ signal.alarm(0)
++ dt = (time.perf_counter() - t0) * 1000
++ print(f' {status:8} {dt:8.1f} ms {label} ({len(sql)} B)')
++
++
++# Vector 1: deeply nested parentheses
++print('Nested parentheses:')
++for n in (200, 500, 1000, 2000):
++ sql = 'SELECT ' + '(' * n + '1' + ')' * n
++ measure(f'nested-paren n={n}', sql, sqlparse.parse)
++
++# Vector 2: deeply nested CASE WHEN
++print('Nested CASE WHEN:')
++for n in (100, 200, 400):
++ case = '1'
++ for i in range(n):
++ case = f'CASE WHEN x={i} THEN {case} ELSE NULL END'
++ measure(f'CASE-nested n={n}', f'SELECT {case} FROM t', sqlparse.parse)
++
++# Vector 3: wide column lists (O(N) grouping, regression fixed in PR848)
++print('Wide column lists:')
++for n in (500, 1000, 2000, 4000):
++ cols = ', '.join(f'col_{i}' for i in range(n))
++ sql = f'SELECT {cols} FROM t'
++ measure(f'wide-select n={n}', sql, sqlparse.parse)
+diff --git a/tests/test_dos_prevention.py b/tests/test_dos_prevention.py
+index 4e826c5..1753c05 100644
+--- a/tests/test_dos_prevention.py
++++ b/tests/test_dos_prevention.py
+@@ -50,6 +50,34 @@ class TestDoSPrevention:
+ with pytest.raises(SQLParseError, match="Maximum number of tokens exceeded"):
+ sqlparse.format(sql, reindent=True)
+
++ def test_nested_paren_within_cap_under_1s(self):
++ """Reaching MAX_GROUPING_DEPTH must not require multi-second CPU.
++
++ Before the TokenList.__init__ fix, a 1 KB payload of 500 nested
++ parens took ~1.3 s and a 2 KB payload of 1000 nested parens took
++ ~11 s before the depth cap raised SQLParseError, because each
++ TokenList materialised its ``value`` via ``str(self)`` which
++ recursed over the full subtree (O(n * depth)).
++ """
++ sql = 'SELECT ' + '(' * 1000 + '1' + ')' * 1000
++ t0 = time.perf_counter()
++ with pytest.raises(SQLParseError, match='Maximum grouping depth'):
++ sqlparse.parse(sql)
++ dt = time.perf_counter() - t0
++ assert dt < 1.0, f'parse took {dt:.2f}s, expected sub-second'
++
++ def test_nested_case_within_cap_under_1s(self):
++ """Same invariant as nested parentheses, exercised via CASE WHEN."""
++ case = '1'
++ for i in range(400):
++ case = f'CASE WHEN x={i} THEN {case} ELSE NULL END'
++ sql = f'SELECT {case} FROM t'
++ t0 = time.perf_counter()
++ with pytest.raises(SQLParseError, match='Maximum grouping depth'):
++ sqlparse.parse(sql)
++ dt = time.perf_counter() - t0
++ assert dt < 1.0, f'parse took {dt:.2f}s, expected sub-second'
++
+ def test_normal_sql_still_works(self):
+ """Test that normal SQL still works correctly after DoS protections."""
+ sql = """
@@ -6,6 +6,10 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=2b136f573f5386001ea3b7b9016222fc"
SRC_URI[sha256sum] = "e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e"
+SRC_URI += "file://CVE-2026-54284-1.patch \
+ file://CVE-2026-54284-2.patch \
+"
+
CVE_PRODUCT = "sqlparse"
export BUILD_SYS