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.96) (envelope-from ) id 1ww3Op-001iDl-24 for pgsql-bugs@arkaria.postgresql.org; Mon, 17 Aug 2026 19:53:35 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1ww3On-00CKV0-1f for pgsql-bugs@arkaria.postgresql.org; Mon, 17 Aug 2026 19:53:34 +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.96) (envelope-from ) id 1wvxxb-00AvzZ-2m for pgsql-bugs@lists.postgresql.org; Mon, 17 Aug 2026 14:05:08 +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 1wvxxa-0000000143q-3993 for pgsql-bugs@lists.postgresql.org; Mon, 17 Aug 2026 14:05: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=N0th+FgMbZrmdlpcpMUMX9rpEf3BtJmlEd48BAwVqqU=; b=NWefMnxZBGrDCDUEOyq4xyald1 6KXNdeiCHLoTvaez8EwCca4ak5tiOhApYrlnVzG2+aNihtvEQWQDmRVDfXZhj4H8TzvudUlIc5m/o jI+MOfLMztUqHIOFh5fFMlPbBmaWAIgNBDpPnMo6V9j6RHdu7ZLNGvyhzvd5NcDZGFo8Rurmo6M/y Rh8oXnoiTYc5vOlMxw4Ue6XTboG8EhaUerbdliRSSQve1lH8fnnNTsfYjJpPd7+cbFlbjy3OqfJsz nF5stf0bOSOebQz7ulNl/Sern6kUYAdFUwDWz6DYfDnjEDo8v8Pz870Po6FAMUc0qlOqj7T5ZRnK9 48ec3i4w==; 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 1wvxxZ-003Gj1-0o for pgsql-bugs@lists.postgresql.org; Mon, 17 Aug 2026 14:05:06 +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 1wvxxX-00000008mbq-3vTH for pgsql-bugs@lists.postgresql.org; Mon, 17 Aug 2026 14:05:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19625: SQL/JSON boolean DEFAULT expression silently replaced with 'false' To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: zengman@halodbtech.com Reply-To: zengman@halodbtech.com, pgsql-bugs@lists.postgresql.org Date: Mon, 17 Aug 2026 14:04:21 +0000 Message-ID: <19625-683b498c92087bc8@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: 19625 Logged by: Man Zeng Email address: zengman@halodbtech.com PostgreSQL version: 19beta3 Operating system: 24.04.1-Ubuntu Description: =20 Hi all, A boolean DEFAULT expression in the SQL/JSON functions can be silently replaced with 'false', discarding the user's expression at parse time. For example: ```sql postgres=3D# SELECT JSON_VALUE('{"a":"abc"}', 'strict $.b' DEFAULT true ON ERROR); -- expected true json_value ------------ false (1 row) postgres=3D# SELECT JSON_VALUE('{"a":"abc"}', 'strict $.b' DEFAULT (1=3D1) = ON ERROR); -- expected true json_value ------------ false (1 row) postgres=3D# SELECT JSON_QUERY('{"a":"abc"}', 'strict $.b' DEFAULT false ON ERROR); json_query ------------ false (1 row) ``` The user asked for true, so the result is wrong. A boolean expression such as DEFAULT (1=3D1) is affected in the same way, and so is JSON_QUERY(). Non-boolean DEFAULT expressions (e.g. DEFAULT 'ok') return the correct value, and so do boolean ones when the RETURNING type is boolean or integer. The problem is in transformJsonBehavior() in parse_expr.c. When the DEFAULT expression is boolean-valued and the RETURNING type is neither boolean nor integer (or a domain over integer), the code guesses the jsonb value from the behavior type: ```c char *val =3D btype =3D=3D JSON_BEHAVIOR_TRUE ? "true" : "false"; ``` This only works for the canned TRUE/FALSE constants. A user-supplied DEFAULT clause has btype =3D=3D JSON_BEHAVIOR_DEFAULT, so the condition is always false and the expression is replaced with jsonb 'false' without being evaluated. I think we can use to_jsonb() to handle all of these cases -- whether a canned TRUE/FALSE constant or a user-supplied boolean DEFAULT expression, the value is converted per the expression's actual evaluation (there is no bool->jsonb cast, hence the explicit function call). Below is my change: ```c diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 30c889f505f..175b3d3ee69 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -4956,20 +4956,17 @@ transformJsonBehavior(ParseState *pstate, JsonExpr *jsexpr, coerce_at_runtime =3D true; =20 /* - * json_populate_type() expects to be passed a jsonb value, so gin - * up a Const containing the appropriate boolean value represented - * as jsonb, discarding the original Const containing a plain - * boolean. + * json_populate_type() only takes a jsonb value, so convert a + * boolean to jsonb by calling to_jsonb() on it. That way any + * boolean-valued expression -- whether a canned TRUE/FALSE + * constant or a user-supplied DEFAULT expression -- is converted + * according to the value it actually evaluates to. */ if (exprType(expr) =3D=3D BOOLOID) - { - char *val =3D btype =3D=3D JSON_BEHAVIOR_TRUE ? "true" : "false"; - - expr =3D (Node *) makeConst(JSONBOID, -1, InvalidOid, -1, - DirectFunctionCall1(jsonb_in, - CStringGetDatum(val)), - false, false); - } + expr =3D (Node *) makeFuncExpr(F_TO_JSONB, JSONBOID, + list_make1(expr), + InvalidOid, InvalidOid, + COERCE_EXPLICIT_CALL); } else { ``` Thoughts? -- Regards, Man Zeng