From 0303b093bc23b97046456d112ac34a47b075f86a Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Mon, 11 Sep 2023 21:31:29 +0900
Subject: [PATCH v15 2/5] Add soft error handling to populate_record_field()

An uncoming patch would like the ability to call it from the
executor for some SQL/JSON expression nodes and ask to suppress any
errors that may occur.

This commit does two things mainly:

* It modifies the various interfaces internal to jsonfuncs.c, and in
  some cases, some external functions to pass the ErrorSaveContext
  around.

* Make necessary modifications to handle the cases where the
  processing is aborted partway through various functions that take
  an ErrorSaveContext when a soft error occurs.
---
 contrib/hstore/hstore_io.c               |   3 +-
 contrib/hstore/hstore_op.c               |   9 +-
 src/backend/catalog/objectaddress.c      |   3 +-
 src/backend/executor/execExprInterp.c    |   3 +-
 src/backend/utils/adt/array_userfuncs.c  |   5 +-
 src/backend/utils/adt/arrayfuncs.c       |  43 ++--
 src/backend/utils/adt/domains.c          |   5 +-
 src/backend/utils/adt/expandedrecord.c   |  12 +-
 src/backend/utils/adt/jsonfuncs.c        | 244 +++++++++++++++++------
 src/backend/utils/adt/orderedsetaggs.c   |   6 +-
 src/backend/utils/adt/regexp.c           |   2 +-
 src/include/utils/array.h                |   7 +-
 src/include/utils/builtins.h             |   3 +-
 src/pl/plperl/plperl.c                   |   7 +-
 src/pl/plpgsql/src/pl_exec.c             |   5 +-
 src/pl/plpython/plpy_typeio.c            |   5 +-
 src/pl/tcl/pltcl.c                       |   3 +-
 src/test/modules/test_regex/test_regex.c |   6 +-
 18 files changed, 266 insertions(+), 105 deletions(-)

diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 999ddad76d..fc145faa1b 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -1195,7 +1195,8 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 		domain_check(HeapTupleGetDatum(rettuple), false,
 					 argtype,
 					 &my_extra->domain_info,
-					 fcinfo->flinfo->fn_mcxt);
+					 fcinfo->flinfo->fn_mcxt,
+					 NULL);
 
 	ReleaseTupleDesc(tupdesc);
 
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 0d4ec16d1e..5d53695be1 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -619,7 +619,8 @@ hstore_slice_to_array(PG_FUNCTION_ARGS)
 							  ARR_NDIM(key_array),
 							  ARR_DIMS(key_array),
 							  ARR_LBOUND(key_array),
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false, TYPALIGN_INT,
+							  NULL);
 
 	PG_RETURN_POINTER(aout);
 }
@@ -762,7 +763,8 @@ hstore_avals(PG_FUNCTION_ARGS)
 	}
 
 	a = construct_md_array(d, nulls, 1, &count, &lb,
-						   TEXTOID, -1, false, TYPALIGN_INT);
+						   TEXTOID, -1, false, TYPALIGN_INT,
+						   NULL);
 
 	PG_RETURN_POINTER(a);
 }
@@ -814,7 +816,8 @@ hstore_to_array_internal(HStore *hs, int ndims)
 
 	return construct_md_array(out_datums, out_nulls,
 							  ndims, out_size, lb,
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false, TYPALIGN_INT,
+							  NULL);
 }
 
 PG_FUNCTION_INFO_V1(hstore_to_array);
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 715201f5a2..e8893613f2 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -6085,7 +6085,8 @@ strlist_to_textarray(List *list)
 
 	lb[0] = 1;
 	arr = construct_md_array(datums, nulls, 1, &j,
-							 lb, TEXTOID, -1, false, TYPALIGN_INT);
+							 lb, TEXTOID, -1, false, TYPALIGN_INT,
+							 NULL);
 
 	MemoryContextDelete(memcxt);
 
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index a18cf5c5c8..0ea173aff2 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2741,7 +2741,8 @@ ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
 									element_type,
 									op->d.arrayexpr.elemlength,
 									op->d.arrayexpr.elembyval,
-									op->d.arrayexpr.elemalign);
+									op->d.arrayexpr.elemalign,
+									NULL);
 	}
 	else
 	{
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 5c4fdcfba4..801c5efd87 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -857,7 +857,7 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
 	 */
 	result = makeMdArrayResult(state, 1, dims, lbs,
 							   CurrentMemoryContext,
-							   false);
+							   false, NULL);
 
 	PG_RETURN_DATUM(result);
 }
@@ -1622,7 +1622,8 @@ array_shuffle_n(ArrayType *array, int n, bool keep_lb,
 		rlbs[0] = 1;
 
 	result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
-								elmtyp, elmlen, elmbyval, elmalign);
+								elmtyp, elmlen, elmbyval, elmalign,
+								NULL);
 
 	pfree(elms);
 	pfree(nuls);
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 7828a6264b..02bc483ff6 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -21,6 +21,7 @@
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/pqformat.h"
+#include "nodes/miscnodes.h"
 #include "nodes/nodeFuncs.h"
 #include "nodes/supportnodes.h"
 #include "optimizer/optimizer.h"
@@ -2321,7 +2322,8 @@ array_set_element(Datum arraydatum,
 		return PointerGetDatum(construct_md_array(&dataValue, &isNull,
 												  nSubscripts, dim, lb,
 												  elmtype,
-												  elmlen, elmbyval, elmalign));
+												  elmlen, elmbyval, elmalign,
+												  NULL));
 	}
 
 	if (ndim != nSubscripts)
@@ -2881,7 +2883,8 @@ array_set_slice(Datum arraydatum,
 
 		return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
 												  dim, lb, elmtype,
-												  elmlen, elmbyval, elmalign));
+												  elmlen, elmbyval, elmalign,
+												  NULL));
 	}
 
 	if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
@@ -3328,7 +3331,8 @@ construct_array(Datum *elems, int nelems,
 	lbs[0] = 1;
 
 	return construct_md_array(elems, NULL, 1, dims, lbs,
-							  elmtype, elmlen, elmbyval, elmalign);
+							  elmtype, elmlen, elmbyval, elmalign,
+							  NULL);
 }
 
 /*
@@ -3432,6 +3436,8 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
  * elem values will be copied into the object even if pass-by-ref type.
  * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
  *
+ * NULL is returned if an error occurs and escontext is valid.
+ *
  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
  * from the system catalogs, given the elmtype.  However, the caller is
  * in a better position to cache this info across multiple uses, or even
@@ -3443,7 +3449,8 @@ construct_md_array(Datum *elems,
 				   int ndims,
 				   int *dims,
 				   int *lbs,
-				   Oid elmtype, int elmlen, bool elmbyval, char elmalign)
+				   Oid elmtype, int elmlen, bool elmbyval, char elmalign,
+				   Node *escontext)
 {
 	ArrayType  *result;
 	bool		hasnulls;
@@ -3453,15 +3460,18 @@ construct_md_array(Datum *elems,
 	int			nelems;
 
 	if (ndims < 0)				/* we do allow zero-dimension arrays */
-		ereport(ERROR,
+		errsave(escontext,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("invalid number of dimensions: %d", ndims)));
 	if (ndims > MAXDIM)
-		ereport(ERROR,
+		errsave(escontext,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
 						ndims, MAXDIM)));
 
+	if (SOFT_ERROR_OCCURRED(escontext))
+		return NULL;
+
 	/* This checks for overflow of the array dimensions */
 	nelems = ArrayGetNItems(ndims, dims);
 	ArrayCheckBounds(ndims, dims, lbs);
@@ -3485,7 +3495,13 @@ construct_md_array(Datum *elems,
 			elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
 		nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
 		nbytes = att_align_nominal(nbytes, elmalign);
-		/* check for overflow of total request */
+		/*
+		 * check for overflow of total request
+		 *
+		 * The following should be perhaps be errsave() to respect caller's
+		 * wish to suppress the error, but it also doesn't seem like a good
+		 * idea to ignore the problem being reported here.
+		 */
 		if (!AllocSizeIsValid(nbytes))
 			ereport(ERROR,
 					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
@@ -4690,7 +4706,8 @@ array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
 									ARR_ELEMTYPE(iterator->arr),
 									iterator->typlen,
 									iterator->typbyval,
-									iterator->typalign);
+									iterator->typalign,
+									NULL);
 
 		*isnull = false;
 		*value = PointerGetDatum(result);
@@ -5377,7 +5394,7 @@ makeArrayResult(ArrayBuildState *astate,
 	lbs[0] = 1;
 
 	return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
-							 astate->private_cxt);
+							 astate->private_cxt, NULL);
 }
 
 /*
@@ -5401,7 +5418,8 @@ makeMdArrayResult(ArrayBuildState *astate,
 				  int *dims,
 				  int *lbs,
 				  MemoryContext rcontext,
-				  bool release)
+				  bool release,
+				  Node *escontext)
 {
 	ArrayType  *result;
 	MemoryContext oldcontext;
@@ -5417,7 +5435,8 @@ makeMdArrayResult(ArrayBuildState *astate,
 								astate->element_type,
 								astate->typlen,
 								astate->typbyval,
-								astate->typalign);
+								astate->typalign,
+								NULL);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -5817,7 +5836,7 @@ makeArrayResultAny(ArrayBuildStateAny *astate,
 		lbs[0] = 1;
 
 		result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
-								   rcontext, release);
+								   rcontext, release, NULL);
 	}
 	else
 	{
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
index 8d766f68e3..f8669ee1b5 100644
--- a/src/backend/utils/adt/domains.c
+++ b/src/backend/utils/adt/domains.c
@@ -341,7 +341,8 @@ domain_recv(PG_FUNCTION_ARGS)
  */
 void
 domain_check(Datum value, bool isnull, Oid domainType,
-			 void **extra, MemoryContext mcxt)
+			 void **extra, MemoryContext mcxt,
+			 Node *escontext)
 {
 	DomainIOData *my_extra = NULL;
 
@@ -365,7 +366,7 @@ domain_check(Datum value, bool isnull, Oid domainType,
 	/*
 	 * Do the necessary checks to ensure it's a valid domain value.
 	 */
-	domain_check_input(value, isnull, my_extra, NULL);
+	domain_check_input(value, isnull, my_extra, escontext);
 }
 
 /*
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index c46e5aa36f..358ee015a5 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -1359,7 +1359,8 @@ expanded_record_set_fields(ExpandedRecordHeader *erh,
 		domain_check(ExpandedRecordGetRODatum(erh), false,
 					 erh->er_decltypeid,
 					 &erh->er_domaininfo,
-					 erh->hdr.eoh_context);
+					 erh->hdr.eoh_context,
+					 NULL);
 	}
 
 	MemoryContextSwitchTo(oldcxt);
@@ -1561,7 +1562,8 @@ check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber,
 	domain_check(ExpandedRecordGetRODatum(dummy_erh), false,
 				 erh->er_decltypeid,
 				 &erh->er_domaininfo,
-				 erh->hdr.eoh_context);
+				 erh->hdr.eoh_context,
+				 NULL);
 
 	MemoryContextSwitchTo(oldcxt);
 
@@ -1587,7 +1589,8 @@ check_domain_for_new_tuple(ExpandedRecordHeader *erh, HeapTuple tuple)
 		domain_check((Datum) 0, true,
 					 erh->er_decltypeid,
 					 &erh->er_domaininfo,
-					 erh->hdr.eoh_context);
+					 erh->hdr.eoh_context,
+					 NULL);
 
 		MemoryContextSwitchTo(oldcxt);
 
@@ -1624,7 +1627,8 @@ check_domain_for_new_tuple(ExpandedRecordHeader *erh, HeapTuple tuple)
 	domain_check(ExpandedRecordGetRODatum(dummy_erh), false,
 				 erh->er_decltypeid,
 				 &erh->er_domaininfo,
-				 erh->hdr.eoh_context);
+				 erh->hdr.eoh_context,
+				 NULL);
 
 	MemoryContextSwitchTo(oldcxt);
 
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index a4bfa5e404..a8b05f17c5 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -265,6 +265,7 @@ typedef struct PopulateArrayContext
 	int		   *dims;			/* dimensions */
 	int		   *sizes;			/* current dimension counters */
 	int			ndims;			/* number of dimensions */
+	Node	   *escontext;		/* For soft-error handling */
 } PopulateArrayContext;
 
 /* state for populate_array_json() */
@@ -431,7 +432,7 @@ static Datum populate_record_worker(FunctionCallInfo fcinfo, const char *funcnam
 /* helper functions for populate_record[set] */
 static HeapTupleHeader populate_record(TupleDesc tupdesc, RecordIOData **record_p,
 									   HeapTupleHeader defaultval, MemoryContext mcxt,
-									   JsObject *obj);
+									   JsObject *obj, Node *escontext);
 static void get_record_type_from_argument(FunctionCallInfo fcinfo,
 										  const char *funcname,
 										  PopulateRecordCache *cache);
@@ -441,27 +442,32 @@ static void get_record_type_from_query(FunctionCallInfo fcinfo,
 static void JsValueToJsObject(JsValue *jsv, JsObject *jso);
 static Datum populate_composite(CompositeIOData *io, Oid typid,
 								const char *colname, MemoryContext mcxt,
-								HeapTupleHeader defaultval, JsValue *jsv, bool isnull);
-static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv);
+								HeapTupleHeader defaultval, JsValue *jsv, bool isnull,
+								Node *escontext);
+static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
+							 bool *isnull, Node *escontext);
 static void prepare_column_cache(ColumnIOData *column, Oid typid, int32 typmod,
 								 MemoryContext mcxt, bool need_scalar);
 static Datum populate_record_field(ColumnIOData *col, Oid typid, int32 typmod,
 								   const char *colname, MemoryContext mcxt, Datum defaultval,
-								   JsValue *jsv, bool *isnull);
+								   JsValue *jsv, bool *isnull, Node *escontext);
 static RecordIOData *allocate_record_info(MemoryContext mcxt, int ncolumns);
 static bool JsObjectGetField(JsObject *obj, char *field, JsValue *jsv);
 static void populate_recordset_record(PopulateRecordsetState *state, JsObject *obj);
-static void populate_array_json(PopulateArrayContext *ctx, char *json, int len);
-static void populate_array_dim_jsonb(PopulateArrayContext *ctx, JsonbValue *jbv,
+static bool populate_array_json(PopulateArrayContext *ctx, char *json, int len);
+static bool populate_array_dim_jsonb(PopulateArrayContext *ctx, JsonbValue *jbv,
 									 int ndim);
 static void populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim);
-static void populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims);
-static void populate_array_check_dimension(PopulateArrayContext *ctx, int ndim);
-static void populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv);
+static bool populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims);
+static bool populate_array_check_dimension(PopulateArrayContext *ctx, int ndim);
+static bool populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv);
 static Datum populate_array(ArrayIOData *aio, const char *colname,
-							MemoryContext mcxt, JsValue *jsv);
+							MemoryContext mcxt, JsValue *jsv,
+							bool *isnull,
+							Node *escontext);
 static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
-							 MemoryContext mcxt, JsValue *jsv, bool isnull);
+							 MemoryContext mcxt, JsValue *jsv, bool isnull,
+							 Node *escontext);
 
 /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
 static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
@@ -2477,19 +2483,23 @@ json_to_record(PG_FUNCTION_ARGS)
 								  true, false);
 }
 
-/* helper function for diagnostics */
+/*
+ * Helper function for diagnostics
+ *
+ * Returns false if the input is erratic.
+ */
 static void
 populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
 {
 	if (ndim <= 0)
 	{
 		if (ctx->colname)
-			ereport(ERROR,
+			errsave(ctx->escontext,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("expected JSON array"),
 					 errhint("See the value of key \"%s\".", ctx->colname)));
 		else
-			ereport(ERROR,
+			errsave(ctx->escontext,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("expected JSON array")));
 	}
@@ -2506,13 +2516,13 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
 			appendStringInfo(&indices, "[%d]", ctx->sizes[i]);
 
 		if (ctx->colname)
-			ereport(ERROR,
+			errsave(ctx->escontext,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("expected JSON array"),
 					 errhint("See the array element %s of key \"%s\".",
 							 indices.data, ctx->colname)));
 		else
-			ereport(ERROR,
+			errsave(ctx->escontext,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("expected JSON array"),
 					 errhint("See the array element %s.",
@@ -2520,8 +2530,12 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
 	}
 }
 
-/* set the number of dimensions of the populated array when it becomes known */
-static void
+/*
+ * Set the number of dimensions of the populated array when it becomes known.
+ *
+ * Returns false if the input (ndims) is erratic.
+ */
+static bool
 populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims)
 {
 	int			i;
@@ -2529,7 +2543,12 @@ populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims)
 	Assert(ctx->ndims <= 0);
 
 	if (ndims <= 0)
+	{
 		populate_array_report_expected_array(ctx, ndims);
+		/* Getting here means the error was reported softly. */
+		Assert(SOFT_ERROR_OCCURRED(ctx->escontext));
+		return false;
+	}
 
 	ctx->ndims = ndims;
 	ctx->dims = palloc(sizeof(int) * ndims);
@@ -2537,10 +2556,16 @@ populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims)
 
 	for (i = 0; i < ndims; i++)
 		ctx->dims[i] = -1;		/* dimensions are unknown yet */
+
+	return true;
 }
 
-/* check the populated subarray dimension */
-static void
+/*
+ * Check the populated subarray dimension
+ *
+ * Returns false if the input (ndims) is erratic.
+ */
+static bool
 populate_array_check_dimension(PopulateArrayContext *ctx, int ndim)
 {
 	int			dim = ctx->sizes[ndim]; /* current dimension counter */
@@ -2548,21 +2573,31 @@ populate_array_check_dimension(PopulateArrayContext *ctx, int ndim)
 	if (ctx->dims[ndim] == -1)
 		ctx->dims[ndim] = dim;	/* assign dimension if not yet known */
 	else if (ctx->dims[ndim] != dim)
-		ereport(ERROR,
+		errsave(ctx->escontext,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("malformed JSON array"),
 				 errdetail("Multidimensional arrays must have "
 						   "sub-arrays with matching dimensions.")));
 
+	/* Nothing to do on an error. */
+	if (SOFT_ERROR_OCCURRED(ctx->escontext))
+		return false;
+
 	/* reset the current array dimension size counter */
 	ctx->sizes[ndim] = 0;
 
 	/* increment the parent dimension counter if it is a nested sub-array */
 	if (ndim > 0)
 		ctx->sizes[ndim - 1]++;
+
+	return true;
 }
 
-static void
+/*
+ * Returns true if the array element value was successfully extracted from jsv
+ * and added to ctx->astate.  False if an error occurred when doing so.
+ */
+static bool
 populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv)
 {
 	Datum		element;
@@ -2573,13 +2608,18 @@ populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv)
 									ctx->aio->element_type,
 									ctx->aio->element_typmod,
 									NULL, ctx->mcxt, PointerGetDatum(NULL),
-									jsv, &element_isnull);
+									jsv, &element_isnull, ctx->escontext);
+	/* Nothing to do on an error. */
+	if (SOFT_ERROR_OCCURRED(ctx->escontext))
+		return false;
 
 	accumArrayResult(ctx->astate, element, element_isnull,
 					 ctx->aio->element_type, ctx->acxt);
 
 	Assert(ndim > 0);
 	ctx->sizes[ndim - 1]++;		/* increment current dimension counter */
+
+	return true;
 }
 
 /* json object start handler for populate_array_json() */
@@ -2594,6 +2634,10 @@ populate_array_object_start(void *_state)
 	else if (ndim < state->ctx->ndims)
 		populate_array_report_expected_array(state->ctx, ndim);
 
+	/* Report if an error occurred. */
+	if (SOFT_ERROR_OCCURRED(state->ctx->escontext))
+		return JSON_SEM_ACTION_FAILED;
+
 	return JSON_SUCCESS;
 }
 
@@ -2609,7 +2653,11 @@ populate_array_array_end(void *_state)
 		populate_array_assign_ndims(ctx, ndim + 1);
 
 	if (ndim < ctx->ndims)
-		populate_array_check_dimension(ctx, ndim);
+	{
+		/* Report if an error occurred. */
+		if (!populate_array_check_dimension(ctx, ndim))
+			return JSON_SEM_ACTION_FAILED;
+	}
 
 	return JSON_SUCCESS;
 }
@@ -2667,7 +2715,8 @@ populate_array_element_end(void *_state, bool isnull)
 								state->element_start) * sizeof(char);
 		}
 
-		populate_array_element(ctx, ndim, &jsv);
+		if (!populate_array_element(ctx, ndim, &jsv))
+			return JSON_SEM_ACTION_FAILED;
 	}
 
 	return JSON_SUCCESS;
@@ -2686,6 +2735,10 @@ populate_array_scalar(void *_state, char *token, JsonTokenType tokentype)
 	else if (ndim < ctx->ndims)
 		populate_array_report_expected_array(ctx, ndim);
 
+	/* Report if an error occurred. */
+	if (SOFT_ERROR_OCCURRED(ctx->escontext))
+		return JSON_SEM_ACTION_FAILED;
+
 	if (ndim == ctx->ndims)
 	{
 		/* remember the scalar element token */
@@ -2697,8 +2750,12 @@ populate_array_scalar(void *_state, char *token, JsonTokenType tokentype)
 	return JSON_SUCCESS;
 }
 
-/* parse a json array and populate array */
-static void
+/*
+ * Parse a json array and populate array
+ *
+ * Returns false if an error occurs when parsing.
+ */
+static bool
 populate_array_json(PopulateArrayContext *ctx, char *json, int len)
 {
 	PopulateArrayState state;
@@ -2715,19 +2772,25 @@ populate_array_json(PopulateArrayContext *ctx, char *json, int len)
 	sem.array_element_end = populate_array_element_end;
 	sem.scalar = populate_array_scalar;
 
-	pg_parse_json_or_ereport(state.lex, &sem);
-
-	/* number of dimensions should be already known */
-	Assert(ctx->ndims > 0 && ctx->dims);
+	if (pg_parse_json_or_errsave(state.lex, &sem, ctx->escontext))
+	{
+		/* number of dimensions should be already known */
+		Assert(ctx->ndims > 0 && ctx->dims);
+	}
 
 	pfree(state.lex);
+
+	return !SOFT_ERROR_OCCURRED(ctx->escontext);
 }
 
 /*
  * populate_array_dim_jsonb() -- Iterate recursively through jsonb sub-array
  *		elements and accumulate result using given ArrayBuildState.
+ *
+ * Returns false if we return partway through because of an error in a
+ * subroutine.
  */
-static void
+static bool
 populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
 						 JsonbValue *jbv,	/* jsonb sub-array */
 						 int ndim)	/* current dimension */
@@ -2741,7 +2804,12 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
 	check_stack_depth();
 
 	if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc))
+	{
 		populate_array_report_expected_array(ctx, ndim - 1);
+		/* Getting here means the error was reported softly. */
+		Assert(SOFT_ERROR_OCCURRED(ctx->escontext));
+		return false;
+	}
 
 	Assert(!JsonContainerIsScalar(jbc));
 
@@ -2762,7 +2830,10 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
 		 (tok == WJB_ELEM &&
 		  (val.type != jbvBinary ||
 		   !JsonContainerIsArray(val.val.binary.data)))))
-		populate_array_assign_ndims(ctx, ndim);
+	{
+		if (!populate_array_assign_ndims(ctx, ndim))
+			return false;
+	}
 
 	jsv.is_json = false;
 	jsv.val.jsonb = &val;
@@ -2775,16 +2846,21 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
 		 * it is not the innermost dimension.
 		 */
 		if (ctx->ndims > 0 && ndim >= ctx->ndims)
-			populate_array_element(ctx, ndim, &jsv);
+		{
+			if (!populate_array_element(ctx, ndim, &jsv))
+				return false;
+		}
 		else
 		{
-			/* populate child sub-array */
-			populate_array_dim_jsonb(ctx, &val, ndim + 1);
+			/* populate child sub-array; nothing to do on an error. */
+			if (!populate_array_dim_jsonb(ctx, &val, ndim + 1))
+				return false;
 
 			/* number of dimensions should be already known */
 			Assert(ctx->ndims > 0 && ctx->dims);
 
-			populate_array_check_dimension(ctx, ndim);
+			if (!populate_array_check_dimension(ctx, ndim))
+				return false;
 		}
 
 		tok = JsonbIteratorNext(&it, &val, true);
@@ -2795,14 +2871,22 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
 	/* free iterator, iterating until WJB_DONE */
 	tok = JsonbIteratorNext(&it, &val, true);
 	Assert(tok == WJB_DONE && !it);
+
+	return true;
 }
 
-/* recursively populate an array from json/jsonb */
+/*
+ * Recursively populate an array from json/jsonb
+ *
+ * *isnull is set to true if an error is reported during parsing.
+ */
 static Datum
 populate_array(ArrayIOData *aio,
 			   const char *colname,
 			   MemoryContext mcxt,
-			   JsValue *jsv)
+			   JsValue *jsv,
+			   bool *isnull,
+			   Node *escontext)
 {
 	PopulateArrayContext ctx;
 	Datum		result;
@@ -2817,14 +2901,26 @@ populate_array(ArrayIOData *aio,
 	ctx.ndims = 0;				/* unknown yet */
 	ctx.dims = NULL;
 	ctx.sizes = NULL;
+	ctx.escontext = escontext;
 
 	if (jsv->is_json)
-		populate_array_json(&ctx, jsv->val.json.str,
-							jsv->val.json.len >= 0 ? jsv->val.json.len
-							: strlen(jsv->val.json.str));
+	{
+		if (!populate_array_json(&ctx, jsv->val.json.str,
+								 jsv->val.json.len >= 0 ? jsv->val.json.len
+								 : strlen(jsv->val.json.str)))
+		{
+			*isnull = true;
+			return (Datum) 0;
+		}
+	}
 	else
 	{
-		populate_array_dim_jsonb(&ctx, jsv->val.jsonb, 1);
+		/* Nothing to do on an error. */
+		if (!populate_array_dim_jsonb(&ctx, jsv->val.jsonb, 1))
+		{
+			*isnull = true;
+			return (Datum) 0;
+		}
 		ctx.dims[0] = ctx.sizes[0];
 	}
 
@@ -2836,12 +2932,13 @@ populate_array(ArrayIOData *aio,
 		lbs[i] = 1;
 
 	result = makeMdArrayResult(ctx.astate, ctx.ndims, ctx.dims, lbs,
-							   ctx.acxt, true);
+							   ctx.acxt, true, escontext);
 
 	pfree(ctx.dims);
 	pfree(ctx.sizes);
 	pfree(lbs);
 
+	*isnull = false;
 	return result;
 }
 
@@ -2919,7 +3016,8 @@ populate_composite(CompositeIOData *io,
 				   MemoryContext mcxt,
 				   HeapTupleHeader defaultval,
 				   JsValue *jsv,
-				   bool isnull)
+				   bool isnull,
+				   Node *escontext)
 {
 	Datum		result;
 
@@ -2938,7 +3036,7 @@ populate_composite(CompositeIOData *io,
 
 		/* populate resulting record tuple */
 		tuple = populate_record(io->tupdesc, &io->record_io,
-								defaultval, mcxt, &jso);
+								defaultval, mcxt, &jso, escontext);
 		result = HeapTupleHeaderGetDatum(tuple);
 
 		JsObjectFree(&jso);
@@ -2950,14 +3048,20 @@ populate_composite(CompositeIOData *io,
 	 * now, we can tell by comparing typid to base_typid.)
 	 */
 	if (typid != io->base_typid && typid != RECORDOID)
-		domain_check(result, isnull, typid, &io->domain_info, mcxt);
+		domain_check(result, isnull, typid, &io->domain_info, mcxt, escontext);
 
 	return result;
 }
 
-/* populate non-null scalar value from json/jsonb value */
+/*
+ * Populate non-null scalar value from json/jsonb value.
+ *
+ * Returns null if an error occurs during the call to type input function,
+ * provided escontext is valid.
+ */
 static Datum
-populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv)
+populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
+				bool *isnull, Node *escontext)
 {
 	Datum		res;
 	char	   *str = NULL;
@@ -3028,7 +3132,12 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv)
 			elog(ERROR, "unrecognized jsonb type: %d", (int) jbv->type);
 	}
 
-	res = InputFunctionCall(&io->typiofunc, str, io->typioparam, typmod);
+	if (!InputFunctionCallSafe(&io->typiofunc, str, io->typioparam, typmod,
+							   escontext, &res))
+	{
+		res = (Datum) 0;
+		*isnull = true;
+	}
 
 	/* free temporary buffer */
 	if (str != json)
@@ -3043,7 +3152,8 @@ populate_domain(DomainIOData *io,
 				const char *colname,
 				MemoryContext mcxt,
 				JsValue *jsv,
-				bool isnull)
+				bool isnull,
+				Node *escontext)
 {
 	Datum		res;
 
@@ -3054,11 +3164,11 @@ populate_domain(DomainIOData *io,
 		res = populate_record_field(io->base_io,
 									io->base_typid, io->base_typmod,
 									colname, mcxt, PointerGetDatum(NULL),
-									jsv, &isnull);
+									jsv, &isnull, escontext);
 		Assert(!isnull);
 	}
 
-	domain_check(res, isnull, typid, &io->domain_info, mcxt);
+	domain_check(res, isnull, typid, &io->domain_info, mcxt, escontext);
 
 	return res;
 }
@@ -3159,7 +3269,8 @@ populate_record_field(ColumnIOData *col,
 					  MemoryContext mcxt,
 					  Datum defaultval,
 					  JsValue *jsv,
-					  bool *isnull)
+					  bool *isnull,
+					  Node *escontext)
 {
 	TypeCat		typcat;
 
@@ -3192,10 +3303,12 @@ populate_record_field(ColumnIOData *col,
 	switch (typcat)
 	{
 		case TYPECAT_SCALAR:
-			return populate_scalar(&col->scalar_io, typid, typmod, jsv);
+			return populate_scalar(&col->scalar_io, typid, typmod, jsv,
+								   isnull, escontext);
 
 		case TYPECAT_ARRAY:
-			return populate_array(&col->io.array, colname, mcxt, jsv);
+			return populate_array(&col->io.array, colname, mcxt, jsv,
+								  isnull, escontext);
 
 		case TYPECAT_COMPOSITE:
 		case TYPECAT_COMPOSITE_DOMAIN:
@@ -3204,11 +3317,12 @@ populate_record_field(ColumnIOData *col,
 									  DatumGetPointer(defaultval)
 									  ? DatumGetHeapTupleHeader(defaultval)
 									  : NULL,
-									  jsv, *isnull);
+									  jsv, *isnull,
+									  escontext);
 
 		case TYPECAT_DOMAIN:
 			return populate_domain(&col->io.domain, typid, colname, mcxt,
-								   jsv, *isnull);
+								   jsv, *isnull, escontext);
 
 		default:
 			elog(ERROR, "unrecognized type category '%c'", typcat);
@@ -3265,7 +3379,8 @@ populate_record(TupleDesc tupdesc,
 				RecordIOData **record_p,
 				HeapTupleHeader defaultval,
 				MemoryContext mcxt,
-				JsObject *obj)
+				JsObject *obj,
+				Node *escontext)
 {
 	RecordIOData *record = *record_p;
 	Datum	   *values;
@@ -3357,7 +3472,8 @@ populate_record(TupleDesc tupdesc,
 										  mcxt,
 										  nulls[i] ? (Datum) 0 : values[i],
 										  &field,
-										  &nulls[i]);
+										  &nulls[i],
+										  escontext);
 	}
 
 	res = heap_form_tuple(tupdesc, values, nulls);
@@ -3531,7 +3647,8 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
 	}
 
 	rettuple = populate_composite(&cache->c.io.composite, cache->argtype,
-								  NULL, fnmcxt, rec, &jsv, false);
+								  NULL, fnmcxt, rec, &jsv, false,
+								  NULL);
 
 	PG_RETURN_DATUM(rettuple);
 }
@@ -3740,14 +3857,15 @@ populate_recordset_record(PopulateRecordsetState *state, JsObject *obj)
 							  &cache->c.io.composite.record_io,
 							  state->rec,
 							  cache->fn_mcxt,
-							  obj);
+							  obj,
+							  NULL);
 
 	/* if it's domain over composite, check domain constraints */
 	if (cache->c.typcat == TYPECAT_COMPOSITE_DOMAIN)
 		domain_check(HeapTupleHeaderGetDatum(tuphead), false,
 					 cache->argtype,
 					 &cache->c.io.composite.domain_info,
-					 cache->fn_mcxt);
+					 cache->fn_mcxt, NULL);
 
 	/* ok, save into tuplestore */
 	tuple.t_len = HeapTupleHeaderGetDatumLength(tuphead);
diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c
index 2582a5cf45..d90ec30a02 100644
--- a/src/backend/utils/adt/orderedsetaggs.c
+++ b/src/backend/utils/adt/orderedsetaggs.c
@@ -840,7 +840,8 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS)
 										 osastate->qstate->sortColType,
 										 osastate->qstate->typLen,
 										 osastate->qstate->typByVal,
-										 osastate->qstate->typAlign));
+										 osastate->qstate->typAlign,
+										 NULL));
 }
 
 /*
@@ -996,7 +997,8 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
 										 expect_type,
 										 typLen,
 										 typByVal,
-										 typAlign));
+										 typAlign,
+										 NULL));
 }
 
 /*
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index 702cd52b6d..e507f43c8d 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -1665,7 +1665,7 @@ build_regexp_match_result(regexp_matches_ctx *matchctx)
 	lbs[0] = 1;
 	/* XXX: this hardcodes assumptions about the text type */
 	return construct_md_array(elems, nulls, 1, dims, lbs,
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false, TYPALIGN_INT, NULL);
 }
 
 /*
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index b13dfb345e..c1abecd3fc 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -62,6 +62,7 @@
 #define ARRAY_H
 
 #include "fmgr.h"
+#include "nodes/nodes.h"
 #include "utils/expandeddatum.h"
 
 /* avoid including execnodes.h here */
@@ -393,7 +394,8 @@ extern ArrayType *construct_md_array(Datum *elems,
 									 int ndims,
 									 int *dims,
 									 int *lbs,
-									 Oid elmtype, int elmlen, bool elmbyval, char elmalign);
+									 Oid elmtype, int elmlen, bool elmbyval, char elmalign,
+									 Node *escontext);
 extern ArrayType *construct_empty_array(Oid elmtype);
 extern ExpandedArrayHeader *construct_empty_expanded_array(Oid element_type,
 														   MemoryContext parentcontext,
@@ -419,7 +421,8 @@ extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
 extern Datum makeArrayResult(ArrayBuildState *astate,
 							 MemoryContext rcontext);
 extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
-							   int *dims, int *lbs, MemoryContext rcontext, bool release);
+							   int *dims, int *lbs, MemoryContext rcontext, bool release,
+							   Node *escontext);
 
 extern ArrayBuildStateArr *initArrayResultArr(Oid array_type, Oid element_type,
 											  MemoryContext rcontext, bool subcontext);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2f8b46d6da..4e1e655652 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -27,7 +27,8 @@ extern bool parse_bool_with_len(const char *value, size_t len, bool *result);
 
 /* domains.c */
 extern void domain_check(Datum value, bool isnull, Oid domainType,
-						 void **extra, MemoryContext mcxt);
+						 void **extra, MemoryContext mcxt,
+						 Node *escontext);
 extern int	errdatatype(Oid datatypeOid);
 extern int	errdomainconstraint(Oid datatypeOid, const char *conname);
 
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 863864253f..5410a37068 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1292,7 +1292,7 @@ plperl_array_to_datum(SV *src, Oid typid, int32 typmod)
 		lbs[i] = 1;
 
 	return makeMdArrayResult(astate, ndims, dims, lbs,
-							 CurrentMemoryContext, true);
+							 CurrentMemoryContext, true, NULL);
 }
 
 /* Get the information needed to convert data to the specified PG type */
@@ -1403,7 +1403,7 @@ plperl_sv_to_datum(SV *sv, Oid typid, int32 typmod,
 			ret = plperl_hash_to_datum(sv, td);
 
 			if (isdomain)
-				domain_check(ret, false, typid, NULL, NULL);
+				domain_check(ret, false, typid, NULL, NULL, NULL);
 
 			/* Release on the result of get_call_result_type is harmless */
 			ReleaseTupleDesc(td);
@@ -3373,7 +3373,8 @@ plperl_return_next_internal(SV *sv)
 			domain_check(HeapTupleGetDatum(tuple), false,
 						 current_call_data->cdomain_oid,
 						 &current_call_data->cdomain_info,
-						 rsi->econtext->ecxt_per_query_memory);
+						 rsi->econtext->ecxt_per_query_memory,
+						 NULL);
 
 		tuplestore_puttuple(current_call_data->tuple_store, tuple);
 	}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 4b76f7699a..0c5d5263c5 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -713,7 +713,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 						/* and check domain constraints */
 						/* XXX allowing caching here would be good, too */
 						domain_check(estate.retval, false, resultTypeId,
-									 NULL, NULL);
+									 NULL, NULL, NULL);
 						break;
 					case TYPEFUNC_RECORD:
 
@@ -1494,7 +1494,8 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
 								  PointerGetDatum(construct_md_array(elems, NULL,
 																	 1, dims, lbs,
 																	 TEXTOID,
-																	 -1, false, TYPALIGN_INT)),
+																	 -1, false, TYPALIGN_INT,
+																	 NULL)),
 								  false, true);
 			}
 			else
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index db14c5f8da..886118ce01 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -1104,7 +1104,8 @@ PLyObject_ToDomain(PLyObToDatum *arg, PyObject *plrv,
 
 	result = base->func(base, plrv, isnull, inarray);
 	domain_check(result, *isnull, arg->typoid,
-				 &arg->u.domain.domain_info, arg->mcxt);
+				 &arg->u.domain.domain_info, arg->mcxt,
+				 NULL);
 	return result;
 }
 
@@ -1177,7 +1178,7 @@ PLySequence_ToArray(PLyObToDatum *arg, PyObject *plrv,
 		lbs[i] = 1;
 
 	return makeMdArrayResult(astate, ndims, dims, lbs,
-							 CurrentMemoryContext, true);
+							 CurrentMemoryContext, true, NULL);
 }
 
 /*
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index e8f9d7b289..a7adb42ff0 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -3240,7 +3240,8 @@ pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
 		domain_check(HeapTupleGetDatum(tuple), false,
 					 call_state->prodesc->result_typid,
 					 &call_state->prodesc->domain_info,
-					 call_state->prodesc->fn_cxt);
+					 call_state->prodesc->fn_cxt,
+					 NULL);
 
 	return tuple;
 }
diff --git a/src/test/modules/test_regex/test_regex.c b/src/test/modules/test_regex/test_regex.c
index d1dd48a993..fb8a22f891 100644
--- a/src/test/modules/test_regex/test_regex.c
+++ b/src/test/modules/test_regex/test_regex.c
@@ -679,7 +679,8 @@ build_test_info_result(regex_t *cpattern, test_re_flags *flags)
 	lbs[0] = 1;
 	/* XXX: this hardcodes assumptions about the text type */
 	return construct_md_array(elems, NULL, 1, dims, lbs,
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false, TYPALIGN_INT,
+							  NULL);
 }
 
 /*
@@ -759,5 +760,6 @@ build_test_match_result(test_regex_ctx *matchctx)
 	lbs[0] = 1;
 	/* XXX: this hardcodes assumptions about the text type */
 	return construct_md_array(elems, nulls, 1, dims, lbs,
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false, TYPALIGN_INT,
+							  NULL);
 }
-- 
2.35.3

