| From: | Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com> |
|---|---|
| To: | roji(at)roji(dot)org, pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Subject: | Re: BUG #19778: Empty tsquery cannot roundtrip through binary input |
| Date: | 2026-10-22 16:52:37 |
| Message-ID: | CAJTYsWW2ESUM2km5sAMoMHRXREkKWa7o-f5U363m2NSOabJunw@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
Hi,
On Thu, 22 Oct 2026 at 11:58, PG Bug reporting form
<noreply(at)postgresql(dot)org> wrote:
>
> The following bug has been logged on the website:
>
> Bug reference: 19778
> Logged by: Shay Rojansky
> Email address: roji(at)roji(dot)org
> PostgreSQL version: 18.4
> Operating system: macOS Tahoe 26.7.1
> Description:
>
> It appears that an empty binary tsquery cannot be written to PostgreSQL.
> Writing a non-empty binary tsquery works, as well as writing an empty text
> tsquery.
>
> Minimal repro:
>
> #include <libpq-fe.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
>
> static void require_row(PGresult *result)
> {
> if (PQresultStatus(result) != PGRES_TUPLES_OK || PQntuples(result) != 1)
> {
> fprintf(stderr, "Unexpected result: %s",
> PQresultErrorMessage(result));
> exit(1);
> }
> }
>
> int main(int argc, char **argv)
> {
> PGconn *conn = PQconnectdb(argc > 1 ? argv[1] : "dbname=postgres");
> if (PQstatus(conn) != CONNECTION_OK)
> {
> fprintf(stderr, "%s", PQerrorMessage(conn));
> PQfinish(conn);
> return 1;
> }
> printf("libpq=%d, PostgreSQL=%d\n", PQlibVersion(),
> PQserverVersion(conn));
>
> /* Obtain both binary values from PostgreSQL, without constructing
> either. */
> PGresult *values = PQexecParams(
> conn, "SELECT ''::tsquery, 'foo'::tsquery",
> 0, NULL, NULL, NULL, NULL, 1);
> require_row(values);
> const Oid types[] = {PQftype(values, 0)};
> const char *params[] = {PQgetvalue(values, 0, 1)};
> int lengths[] = {PQgetlength(values, 0, 1)};
> int formats[] = {1};
>
> PGresult *result = PQexecParams(conn, "SELECT $1", 1, types, params,
> lengths, formats, 0);
> require_row(result);
> if (PQgetisnull(result, 0, 0) || strcmp(PQgetvalue(result, 0, 0),
> "'foo'") != 0)
> return 1;
> printf("Binary non-empty tsquery roundtrip: %s\n", PQgetvalue(result, 0,
> 0));
> PQclear(result);
>
> params[0] = "";
> lengths[0] = 0;
> formats[0] = 0;
> result = PQexecParams(conn, "SELECT $1", 1, types, params, lengths,
> formats, 0);
> require_row(result);
> if (PQgetisnull(result, 0, 0) || PQgetlength(result, 0, 0) != 0)
> return 1;
> puts("Text empty tsquery roundtrip: OK");
> PQclear(result);
>
> params[0] = PQgetvalue(values, 0, 0);
> lengths[0] = PQgetlength(values, 0, 0);
> formats[0] = 1;
> printf("Server-produced empty tsquery: type=%u, format=%d, length=%d,
> hex=",
> types[0], PQfformat(values, 0), lengths[0]);
> for (int i = 0; i < lengths[0]; i++)
> printf("%02x", (unsigned char)params[0][i]);
> putchar('\n');
>
> /* Send the server's bytes back unchanged, with the server's type OID.
> */
> result = PQexecParams(conn, "SELECT $1", 1, types, params, lengths,
> formats, 0);
> const char *state = PQresultErrorField(result, PG_DIAG_SQLSTATE);
> const char *message = PQresultErrorField(result,
> PG_DIAG_MESSAGE_PRIMARY);
> const char *routine = PQresultErrorField(result,
> PG_DIAG_SOURCE_FUNCTION);
> int reproduced = PQresultStatus(result) == PGRES_FATAL_ERROR
> && state && strcmp(state, "XX000") == 0
> && message && strcmp(message, "malformed tsquery: operand not
> found") == 0
> && routine && strcmp(routine, "findoprnd_recurse") == 0;
> if (reproduced)
> printf("REPRODUCED: %s: %s (%s)\n", state, message, routine);
> else
> fprintf(stderr, "Expected server error was not reproduced: %s\n",
> PQresultErrorMessage(result));
>
> PQclear(result);
> PQclear(values);
> PQfinish(conn);
> return reproduced ? 0 : 1;
> }
Thanks for the report, I can reproduce this on master too.
tsqueryrecv() calls findoprnd() even for an empty query, and that
fails with "malformed tsquery: operand not found". The text input
doesn't have this problem since parse_tsquery() returns early for an
empty query.
Attached is a patch that skips findoprnd() when size is 0.
IIUC, this goes back to all supported branches, and logical
replication with binary = true would hit it too. Which is why I'm
somewhat in favour of backpatching for this.
Regards,
Ayush
| Attachment | Content-Type | Size |
|---|---|---|
| v1-0001-Accept-empty-tsquery-in-binary-input.patch | application/octet-stream | 1.4 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Tom Lane | 2026-10-22 17:34:58 | Re: BUG #19778: Empty tsquery cannot roundtrip through binary input |
| Previous Message | PG Bug reporting form | 2026-10-22 14:07:10 | BUG #19791: ALTER COLUMN TYPE fails on an INCLUDE column of an index used by a foreign key |