Re: BUG #19822: DEFAULT boolean expressions in SQL/JSON functions are re-encoded as JSON booleans and always yield f

From: Amit Langote <amitlangote09(at)gmail(dot)com>
To: 303677365(at)qq(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org
Subject: Re: BUG #19822: DEFAULT boolean expressions in SQL/JSON functions are re-encoded as JSON booleans and always yield f
Date: 2026-11-12 01:25:21
Message-ID: CA+HiwqHLKoRy4O5ENVu2RaLRdy4LDb5T-HMW5AUBhUJcguc70A@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

Hi,

On Mon, Nov 2, 2026 at 8:04 PM PG Bug reporting form
<noreply(at)postgresql(dot)org> wrote:
>
> The following bug has been logged on the website:
>
> Bug reference: 19822
> Logged by: chunling qin
> Email address: 303677365(at)qq(dot)com
> PostgreSQL version: 18.6
> Operating system: Linux (TencentOS 4.4, x86_64)
> Description:
>
> Hello,
>
> In transformJsonBehavior() (src/backend/parser/parse_expr.c), the
> "coerce at runtime" branch replaces a boolean DEFAULT expression with a
> JSON boolean constant using the *clause behavior type* instead of the
> expression's actual value:
>
> char *val = btype == JSON_BEHAVIOR_TRUE ? "true" : "false";
>
> For a DEFAULT clause, btype is JSON_BEHAVIOR_DEFAULT, so the constant is
> always "false", regardless of whether the expression evaluates to true,
> false, or NULL. The replacement is also not guarded by !isnull, so a
> NULL boolean constant becomes false as well.
>
> The trigger is: the DEFAULT expression's type is boolean, and the
> RETURNING type's base type is not int4 (the ordinary bool->int4 cast
> path is unaffected).
>
> Reproducer (self-contained, on 18.6 and on 20devel):
>
> -- G1: DEFAULT true must yield 'true ' (padded to char(5))
> SELECT JSON_VALUE(jsonb '"xyzzy!"', '$'
> RETURNING char(5) DEFAULT true ON ERROR);
> -- actual: false <- the boolean true was replaced by JSON false
> -- expected: 'true ' (SQL boolean cast to bpchar(5))
>
> -- G4: same with a domain over bool
> CREATE DOMAIN dbool AS bool;
> SELECT JSON_VALUE(jsonb '"xyzzy!"', '$'
> RETURNING dbool DEFAULT true ON ERROR);
> -- actual: f -- expected: t
>
> -- H1: a NULL boolean constant is also replaced (no !isnull guard)
> SELECT JSON_VALUE(jsonb '"xyzzy!"', '$'
> RETURNING char(5) DEFAULT NULL::bool ON ERROR);
> -- actual: false -- expected: NULL
>
> -- Controls that show the normal paths are fine:
> SELECT JSON_VALUE(jsonb '"xyzzy!"', '$'
> RETURNING char(5) DEFAULT false ON ERROR); -- false (correct by
> accident)
> SELECT JSON_VALUE(jsonb '"xyzzy!"', '$'
> RETURNING int DEFAULT true ON ERROR); -- 1 (bool->int4 path,
> correct)
>
> Expected: the value of a DEFAULT expression should be determined by the
> expression itself (true -> "true", NULL -> NULL), not by the clause
> behavior type; JSON boolean encoding should only apply to an explicit
> FORMAT JSON.
>
> Actual: DEFAULT true/false/NULL all come back as JSON false whenever the
> runtime-coercion path is taken, silently and with no warning. A common
> use of DEFAULT true is a fallback flag (feature switch, approval marker);
> getting false there instead of true produces wrong business decisions
> with no error anywhere.
>
> Reproduced on:
> - PostgreSQL 18.6 (REL_18_STABLE, self-built, snapshot 2026-10-26)
> - PostgreSQL 20devel (snapshot of 2026-10-29)
>
> A draft fix location would be transformJsonBehavior()
> (src/backend/parser/parse_expr.c): use the actual constant value for
> boolean DEFAULT expressions (and guard the replacement with !isnull).

Thanks for the report.

This looks to be the same as bug #19625. Please check the patch I
just posted to that thread [1].

--
Thanks, Amit Langote

[1] https://postgr.es/m/19625-683b498c92087bc8@postgresql.org

In response to

Browse pgsql-bugs by date

  From Date Subject
Next Message Michael Paquier 2026-11-12 01:26:00 Re: BUG #19857: Stats restore rejects empty arrays, losing range and array stats
Previous Message Amit Langote 2026-11-12 01:23:15 Re: BUG #19625: SQL/JSON boolean DEFAULT expression silently replaced with 'false'