From d2934f97683e857efb1262d65b4fd0e58642a28e Mon Sep 17 00:00:00 2001
From: Shveta Malik <shveta.malik@gmail.com>
Date: Tue, 1 Sep 2026 14:38:51 +0530
Subject: [PATCH] reentrant json fix

---
 src/backend/utils/adt/json.c | 96 ++++++++++++++++++++++++++++--------
 1 file changed, 75 insertions(+), 21 deletions(-)

diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index babbaed6392..aa41665a2e6 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -348,34 +348,72 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result,
 			}
 			break;
 		case JSONTYPE_JSON:
-
-			/*
-			 * JSON and JSONB output are already escaped, so we can call their
-			 * output functions directly without extra escaping. Check the
-			 * rendered length before appending to result.
-			 */
-			outputstr = OidOutputFunctionCall(outfuncoid, val);
-			if (json_size_would_exceed(result->len, strlen(outputstr)))
 			{
+				Size		saved_limit = json_size_limit;
+				bool		saved_hit = json_size_limit_hit;
+
+				/*
+				 * Suspend the size limit before invoking type output or cast
+				 * functions. This prevents intermediate internal operations
+				 * from prematurely triggering the limit, while the final
+				 * returned output is still strictly measured against the
+				 * threshold immediately after.
+				 */
+				json_set_size_limit(0);
+
+				/*
+				 * JSON and JSONB output are already escaped, so we can call
+				 * their output functions directly without extra escaping.
+				 * Check the rendered length before appending to result.
+				 */
+				outputstr = OidOutputFunctionCall(outfuncoid, val);
+
+				json_size_limit = saved_limit;
+				json_size_limit_hit = saved_hit;
+
+				if (json_size_would_exceed(result->len, strlen(outputstr)))
+				{
+					pfree(outputstr);
+					return;
+				}
+
+				appendStringInfoString(result, outputstr);
 				pfree(outputstr);
-				return;
+				break;
 			}
-
-			appendStringInfoString(result, outputstr);
-			pfree(outputstr);
-			break;
 		case JSONTYPE_CAST:
-			/* outfuncoid refers to a cast function, not an output function */
-			jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
-			if (json_size_would_exceed(result->len, VARSIZE_ANY_EXHDR(jsontext)))
 			{
+				Size		saved_limit = json_size_limit;
+				bool		saved_hit = json_size_limit_hit;
+
+				/*
+				 * Suspend the size limit before invoking type output or cast
+				 * functions. This prevents intermediate internal operations
+				 * from prematurely triggering the limit, while the final
+				 * returned output is still strictly measured against the
+				 * threshold immediately after.
+				 */
+				json_set_size_limit(0);
+
+				/*
+				 * outfuncoid refers to a cast function, not an output
+				 * function
+				 */
+				jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
+
+				json_size_limit = saved_limit;
+				json_size_limit_hit = saved_hit;
+
+				if (json_size_would_exceed(result->len, VARSIZE_ANY_EXHDR(jsontext)))
+				{
+					pfree(jsontext);
+					return;
+				}
+				appendBinaryStringInfo(result, VARDATA_ANY(jsontext),
+									   VARSIZE_ANY_EXHDR(jsontext));
 				pfree(jsontext);
-				return;
+				break;
 			}
-			appendBinaryStringInfo(result, VARDATA_ANY(jsontext),
-								   VARSIZE_ANY_EXHDR(jsontext));
-			pfree(jsontext);
-			break;
 		default:
 			/* special-case text types to save useless palloc/memcpy cycles */
 			if (outfuncoid == F_TEXTOUT || outfuncoid == F_VARCHAROUT ||
@@ -397,7 +435,23 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result,
 			}
 			else
 			{
+				Size		saved_limit = json_size_limit;
+				bool		saved_hit = json_size_limit_hit;
+
+				/*
+				 * Suspend the size limit before invoking type output or cast
+				 * functions. This prevents intermediate internal operations
+				 * from prematurely triggering the limit, while the final
+				 * returned output is still strictly measured against the
+				 * threshold immediately after.
+				 */
+				json_set_size_limit(0);
+
 				outputstr = OidOutputFunctionCall(outfuncoid, val);
+
+				json_size_limit = saved_limit;
+				json_size_limit_hit = saved_hit;
+
 				if (json_size_would_exceed(result->len, 6 * strlen(outputstr)))
 				{
 					pfree(outputstr);
-- 
2.34.1

