Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1x7YRk-00000000TfQ-3VYT for pgsql-bugs@arkaria.postgresql.org; Fri, 18 Sep 2026 13:16:09 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.98.2) (envelope-from ) id 1x7YRj-00000001Dlv-4ATd for pgsql-bugs@arkaria.postgresql.org; Fri, 18 Sep 2026 13:16:08 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1x7OWH-00000007P8z-0HlZ for pgsql-bugs@lists.postgresql.org; Fri, 18 Sep 2026 02:40:09 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1x7OWE-00000000FSO-0wYC for pgsql-bugs@lists.postgresql.org; Fri, 18 Sep 2026 02:40:08 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=postgresql.org; s=20171124; h=Message-ID:Date:Reply-To:Cc:From:To:Subject: Content-Transfer-Encoding:MIME-Version:Content-Type:Sender:Content-ID: Content-Description:In-Reply-To:References; bh=0xmCrjgJcA2aDvH4PHsm25n282YWqxu6vzsbSS4hROg=; b=fVkolArz+PZiy5v0VUw0Y+NQMh MBhl5qbU4+FRxw4qbVJbmGJQg9iPrSnpeLMKidz6TPObQsJpHIfxE4A6lXHQ2H5zWb5MtOckBbuSw zqTA19ucks+5JIv+qZkZ/SwMuM58FWSj+cOJH1vnH6sOUjrS5ClXWKsV3JV5bZ8UzUnTifha/wZFm CluLbo6WdyzK1uYiMOLsan8GwH5YPNkVrXsK22WgnTyNMALb71wx5j1SgE1VjPSolM8HUfx+ECg/u qu/PLLodqBXxSTLkLqfF9Av+zx9atmaF1s03QHRZ4gwUOC/301GQPv+bDwya0mdv1x1ee2UqTHl86 B9e3z2pw==; Received: from wrigleys.postgresql.org ([2a02:16a8:dc51::60]) by mahout.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x7OWD-001pU7-1D for pgsql-bugs@lists.postgresql.org; Fri, 18 Sep 2026 02:40:05 +0000 Received: from localhost ([127.0.0.1] helo=wrigleys.postgresql.org) by wrigleys.postgresql.org with esmtp (Exim 4.98.2) (envelope-from ) id 1x7OWB-00000006odH-1WZa for pgsql-bugs@lists.postgresql.org; Fri, 18 Sep 2026 02:40:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19693: JSON_VALUE/JSON_QUERY PASSING a toasted text value reads the toast pointer instead of the text To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: chaitanyyachoudhary@gmail.com Reply-To: chaitanyyachoudhary@gmail.com, pgsql-bugs@lists.postgresql.org Date: Fri, 18 Sep 2026 02:39:08 +0000 Message-ID: <19693-2ecd2b52c838b3e5@postgresql.org> X-Auto-Response-Suppress: All Auto-Submitted: auto-generated List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk The following bug has been logged on the website: Bug reference: 19693 Logged by: Chaitanya Choudhary Email address: chaitanyyachoudhary@gmail.com PostgreSQL version: 18.6 Operating system: macOS 26 (aarch64), Homebrew build of 18.6 Description: =20 When a text or varchar column value is passed to a SQL/JSON query function through PASSING and the value is stored out of line (TOAST), the jsonpath variable does not contain the text. It contains a few bytes of the toast pointer. Steps to reproduce: CREATE TABLE t (c text); ALTER TABLE t ALTER COLUMN c SET STORAGE EXTERNAL; INSERT INTO t VALUES (repeat('x', 10000)); SELECT length(c) AS stored, length(JSON_VALUE('{}', '$x' PASSING c AS x)) AS via_passing FROM t; Result: stored | via_passing --------+------------- 10000 | 3 Expected: 10000 in both columns. The 3-character result is not part of the stored value: SELECT JSON_VALUE('{}', '$x' PASSING c AS x) =3D c FROM t; -- f SELECT left(JSON_VALUE('{}', '$x' PASSING c AS x), 20) FROM t; -- \x12\x14' Forcing a detoast before PASSING gives the right answer: SELECT length(JSON_VALUE('{}', '$x' PASSING (c || '') AS x)) FROM t; -- 10000 A short value, which is stored inline, also works. JSON_QUERY and JSON_EXISTS are affected the same way; for example SELECT JSON_EXISTS('{}', '$x ? (@ starts with "xxxxxxxxxx")' PASSING c AS x) FROM t; returns false for a value of ten thousand x's. Cause: In src/backend/utils/adt/jsonpath_exec.c, JsonItemFromDatum() handles TEXTOID and VARCHAROID by reading the datum directly: case TEXTOID: case VARCHAROID: res->type =3D jbvString; res->val.string.val =3D VARDATA_ANY(val); res->val.string.len =3D VARSIZE_ANY_EXHDR(val); break; The datum is never detoasted, so for an out-of-line value the macros read the toast pointer's own bytes. The value comes from the PASSING argument via GetJsonPathVar() -> JsonItemFromDatum(var->value, ...) and nothing on that path detoasts it either. The other varlena cases in this function (JSONB, and the datetime types through their output paths) go through code that detoasts. Fix: detoast the datum in that case, for example text *txt =3D DatumGetTextPP(val); res->val.string.val =3D VARDATA_ANY(txt); res->val.string.len =3D VARSIZE_ANY_EXHDR(txt); The same code is present on REL_18_STABLE and master as of 2026-09-17.