BUG #19745: tsquery input: 33 nested "!" raise XX000 via elog(), escaping pg_input_is_valid() - Mailing list pgsql-bugs

From PG Bug reporting form
Subject BUG #19745: tsquery input: 33 nested "!" raise XX000 via elog(), escaping pg_input_is_valid()
Date
Msg-id 19745-39cd7b93a505f9a9@postgresql.org
Whole thread
Responses Re: BUG #19745: tsquery input: 33 nested "!" raise XX000 via elog(), escaping pg_input_is_valid()
List pgsql-bugs
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
https://www.postgresql.org/message-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.





pgsql-bugs by date:

Previous
From: PG Bug reporting form
Date:
Subject: BUG #19744: contrib/seg output truncates to 6 significant digits, breaking dump/restore
Next
From: PG Bug reporting form
Date:
Subject: BUG #19746: interval with INT64_MIN microseconds prints a value interval_in rejects