BUG #19745: tsquery input: 33 nested "!" raise XX000 via elog(), escaping pg_input_is_valid()

Started by PG Bug reporting form19 days ago2 messagesbugs
Jump to latest
#1PG Bug reporting form
noreply@postgresql.org

The following bug has been logged on the website:

Bug reference: 19745
Logged by: Ke
Email address: kehan5800@gmail.com
PostgreSQL version: 18.6
Operating system: Ubuntu 22.04.2 x86_64
Description:

makepol() in src/backend/utils/adt/tsquery.c keeps a fixed operator stack
(STACKDEPTH = 32, tsquery.c:627), and pushOpStack() reports overflow as an
internal error:

static void
pushOpStack(OperatorElement *stack, int *lenstack, int8 op, int16
distance)
{
if (*lenstack == STACKDEPTH) /* internal error */
elog(ERROR, "tsquery stack too small");

(tsquery.c:638-639). The prefix operator "!" pushes an entry per occurrence,
so the condition is reachable from plain user input:

SELECT (repeat('!', 32) || 'a')::tsquery; -- ok
SELECT (repeat('!', 33) || 'a')::tsquery;
ERROR: XX000: tsquery stack too small
LOCATION: pushOpStack, tsquery.c:639

SELECT websearch_to_tsquery('simple', repeat('-', 33) || 'a');
ERROR: XX000: tsquery stack too small

SELECT to_tsquery('simple', repeat('!', 33) || 'a');
ERROR: XX000: tsquery stack too small

Because this is elog() rather than ereturn(escontext, ...), it bypasses the
soft-error machinery used since 16:

SELECT pg_input_is_valid(repeat('!', 32) || 'a', 'tsquery'); -- t
SELECT pg_input_is_valid(repeat('!', 33) || 'a', 'tsquery');
ERROR: XX000: tsquery stack too small

and COPY ... (ON_ERROR ignore) aborts the whole load instead of skipping
the row:

CREATE TABLE onerr (q tsquery);
-- onerr.txt: a & b
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!a (33 "!")
-- c & d
\copy onerr from 'onerr.txt' (on_error ignore)
ERROR: XX000: tsquery stack too small
CONTEXT: COPY onerr, line 2, column q:
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!a"
SELECT count(*) FROM onerr; -- 0

Expected: an ordinary user-facing error with a proper SQLSTATE (e.g.
54001 statement_too_complex or 54000 program_limit_exceeded), raised through
the soft-error path so that pg_input_is_valid() returns false and
ON_ERROR ignore skips the row. websearch_to_tsquery() is documented for raw
user search input, so an XX000 from 33 hyphens typed into a search box is
the wrong category.

Actual: XX000 internal_error, not catchable by pg_input_is_valid() or
ON_ERROR ignore.

For comparison, intarray's copy of the same parser (contrib/intarray/
_int_bool.c:181-184) was converted when soft errors were introduced:

if (lenstack == STACKDEPTH)
ereturn(state->escontext, ERR,
(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
errmsg("statement too complex")));

SELECT pg_input_is_valid(repeat('!', 17) || '1', 'query_int'); -- f

The third copy, contrib/ltree/ltxtquery_io.c:252 (elog(ERROR, "stack too
short")), behaves like tsquery: 33 stacked "!" give XX000 and
pg_input_is_valid(..., 'ltxtquery') raises. That one was noted earlier in
/messages/by-id/749c63a4-ff67-76a8-e6d4-53b293931c81@gmail.com

Suggested fix: pass the TSQueryParserState to pushOpStack() and report the
overflow with errsave(state->escontext, ...) as intarray does (e.g.
ERRCODE_STATEMENT_TOO_COMPLEX, "tsquery is too complex", errdetail naming
the limit), then return; makepol() already checks
SOFT_ERROR_OCCURRED(state->escontext) after each token and returns, so no
other plumbing is needed. The same change applies to ltxtquery_io.c's
makepol().
Raising STACKDEPTH alone would not address the error class or the
soft-error escape.

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: PG Bug reporting form (#1)
Re: BUG #19745: tsquery input: 33 nested "!" raise XX000 via elog(), escaping pg_input_is_valid()

PG Bug reporting form <noreply@postgresql.org> writes:

makepol() in src/backend/utils/adt/tsquery.c keeps a fixed operator stack
(STACKDEPTH = 32, tsquery.c:627), and pushOpStack() reports overflow as an
internal error:

Hmm. I'm not terribly concerned by which SQLSTATE gets reported,
but I do agree that it's bad that this isn't trappable as a soft
error. Will fix, thanks for report.

regards, tom lane