From 910a60c7167b9d9c086a2183063cf0605ec31f8d Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Wed, 22 Jan 2020 13:28:03 -0500
Subject: [PATCH v3 5/5] Move some code from jsonapi.c to jsonfuncs.c.

Specifically, move all of the functions that depend on the backend's
error-reporting infrastructure from jsonapi.c to jsonfuncs.c, in
preparation for allowing jsonapi.c to be used from frontend code.

Well, not quite all of them. A few cases where elog(ERROR, ...)
is used for can't-happen conditions are left alone; we can handle
those in some other way in frontend code.
---
 src/backend/utils/adt/json.c      |   2 +-
 src/backend/utils/adt/jsonapi.c   | 127 +-----------------------------
 src/backend/utils/adt/jsonb.c     |   2 +-
 src/backend/utils/adt/jsonfuncs.c | 126 +++++++++++++++++++++++++++++
 src/include/utils/jsonapi.h       |  15 +---
 src/include/utils/jsonfuncs.h     |   9 +++
 6 files changed, 140 insertions(+), 141 deletions(-)

diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index e73a60ece8..f6cd2b9911 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -23,7 +23,7 @@
 #include "utils/date.h"
 #include "utils/datetime.h"
 #include "utils/json.h"
-#include "utils/jsonapi.h"
+#include "utils/jsonfuncs.h"
 #include "utils/lsyscache.h"
 #include "utils/typcache.h"
 
diff --git a/src/backend/utils/adt/jsonapi.c b/src/backend/utils/adt/jsonapi.c
index 129fbd65d5..1ac3b7beda 100644
--- a/src/backend/utils/adt/jsonapi.c
+++ b/src/backend/utils/adt/jsonapi.c
@@ -44,7 +44,6 @@ static JsonParseErrorType parse_object(JsonLexContext *lex, JsonSemAction *sem);
 static JsonParseErrorType parse_array_element(JsonLexContext *lex, JsonSemAction *sem);
 static JsonParseErrorType parse_array(JsonLexContext *lex, JsonSemAction *sem);
 static JsonParseErrorType report_parse_error(JsonParseContext ctx, JsonLexContext *lex);
-static int	report_json_context(JsonLexContext *lex);
 static char *extract_token(JsonLexContext *lex);
 
 /* the null action object used for pure validation */
@@ -128,25 +127,13 @@ IsValidJsonNumber(const char *str, int len)
 }
 
 /*
- * makeJsonLexContext
+ * makeJsonLexContextCstringLen
  *
- * lex constructor, with or without StringInfo object
- * for de-escaped lexemes.
+ * lex constructor, with or without StringInfo object for de-escaped lexemes.
  *
  * Without is better as it makes the processing faster, so only make one
  * if really required.
- *
- * If you already have the json as a text* value, use the first of these
- * functions, otherwise use  makeJsonLexContextCstringLen().
  */
-JsonLexContext *
-makeJsonLexContext(text *json, bool need_escapes)
-{
-	return makeJsonLexContextCstringLen(VARDATA_ANY(json),
-										VARSIZE_ANY_EXHDR(json),
-										need_escapes);
-}
-
 JsonLexContext *
 makeJsonLexContextCstringLen(char *json, int len, bool need_escapes)
 {
@@ -202,23 +189,6 @@ pg_parse_json(JsonLexContext *lex, JsonSemAction *sem)
 	return result;
 }
 
-/*
- * pg_parse_json_or_ereport
- *
- * This fuction is like pg_parse_json, except that it does not return a
- * JsonParseErrorType. Instead, in case of any failure, this function will
- * ereport(ERROR).
- */
-void
-pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem)
-{
-	JsonParseErrorType	result;
-
-	result = pg_parse_json(lex, sem);
-	if (result != JSON_SUCCESS)
-		json_ereport_error(result, lex);
-}
-
 /*
  * json_count_array_elements
  *
@@ -1038,27 +1008,6 @@ report_parse_error(JsonParseContext ctx, JsonLexContext *lex)
 	}
 }
 
-/*
- * Report a JSON error.
- */
-void
-json_ereport_error(JsonParseErrorType error, JsonLexContext *lex)
-{
-	if (error == JSON_UNICODE_HIGH_ESCAPE ||
-		error == JSON_UNICODE_CODE_POINT_ZERO)
-		ereport(ERROR,
-				(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
-				 errmsg("unsupported Unicode escape sequence"),
-				 errdetail("%s", json_errdetail(error, lex)),
-				 report_json_context(lex)));
-	else
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type %s", "json"),
-				 errdetail("%s", json_errdetail(error, lex)),
-				 report_json_context(lex)));
-}
-
 /*
  * Construct a detail message for a JSON error.
  */
@@ -1118,78 +1067,6 @@ json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
 	}
 }
 
-/*
- * Report a CONTEXT line for bogus JSON input.
- *
- * lex->token_terminator must be set to identify the spot where we detected
- * the error.  Note that lex->token_start might be NULL, in case we recognized
- * error at EOF.
- *
- * The return value isn't meaningful, but we make it non-void so that this
- * can be invoked inside ereport().
- */
-static int
-report_json_context(JsonLexContext *lex)
-{
-	const char *context_start;
-	const char *context_end;
-	const char *line_start;
-	int			line_number;
-	char	   *ctxt;
-	int			ctxtlen;
-	const char *prefix;
-	const char *suffix;
-
-	/* Choose boundaries for the part of the input we will display */
-	context_start = lex->input;
-	context_end = lex->token_terminator;
-	line_start = context_start;
-	line_number = 1;
-	for (;;)
-	{
-		/* Always advance over newlines */
-		if (context_start < context_end && *context_start == '\n')
-		{
-			context_start++;
-			line_start = context_start;
-			line_number++;
-			continue;
-		}
-		/* Otherwise, done as soon as we are close enough to context_end */
-		if (context_end - context_start < 50)
-			break;
-		/* Advance to next multibyte character */
-		if (IS_HIGHBIT_SET(*context_start))
-			context_start += pg_mblen(context_start);
-		else
-			context_start++;
-	}
-
-	/*
-	 * We add "..." to indicate that the excerpt doesn't start at the
-	 * beginning of the line ... but if we're within 3 characters of the
-	 * beginning of the line, we might as well just show the whole line.
-	 */
-	if (context_start - line_start <= 3)
-		context_start = line_start;
-
-	/* Get a null-terminated copy of the data to present */
-	ctxtlen = context_end - context_start;
-	ctxt = palloc(ctxtlen + 1);
-	memcpy(ctxt, context_start, ctxtlen);
-	ctxt[ctxtlen] = '\0';
-
-	/*
-	 * Show the context, prefixing "..." if not starting at start of line, and
-	 * suffixing "..." if not ending at end of line.
-	 */
-	prefix = (context_start > line_start) ? "..." : "";
-	suffix = (lex->token_type != JSON_TOKEN_END && context_end - lex->input < lex->input_length && *context_end != '\n' && *context_end != '\r') ? "..." : "";
-
-	return errcontext("JSON data, line %d: %s%s%s",
-					  line_number, prefix, ctxt, suffix);
-}
-
 /*
  * Extract the current token from a lexing context, for error reporting.
  */
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 83d7f68b82..c912f8932d 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -23,8 +23,8 @@
 #include "utils/date.h"
 #include "utils/datetime.h"
 #include "utils/json.h"
-#include "utils/jsonapi.h"
 #include "utils/jsonb.h"
+#include "utils/jsonfuncs.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 9eff506855..66ea11b971 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -329,6 +329,8 @@ typedef struct JsObject
 			hash_destroy((jso)->val.json_hash); \
 	} while (0)
 
+static int	report_json_context(JsonLexContext *lex);
+
 /* semantic action functions for json_object_keys */
 static void okeys_object_field_start(void *state, char *fname, bool isnull);
 static void okeys_array_start(void *state);
@@ -484,6 +486,37 @@ static void transform_string_values_object_field_start(void *state, char *fname,
 static void transform_string_values_array_element_start(void *state, bool isnull);
 static void transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
 
+/*
+ * pg_parse_json_or_ereport
+ *
+ * This fuction is like pg_parse_json, except that it does not return a
+ * JsonParseErrorType. Instead, in case of any failure, this function will
+ * ereport(ERROR).
+ */
+void
+pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem)
+{
+	JsonParseErrorType	result;
+
+	result = pg_parse_json(lex, sem);
+	if (result != JSON_SUCCESS)
+		json_ereport_error(result, lex);
+}
+
+/*
+ * makeJsonLexContext
+ *
+ * This is like makeJsonLexContextCstringLen, but it accepts a text value
+ * directly.
+ */
+JsonLexContext *
+makeJsonLexContext(text *json, bool need_escapes)
+{
+	return makeJsonLexContextCstringLen(VARDATA_ANY(json),
+										VARSIZE_ANY_EXHDR(json),
+										need_escapes);
+}
+
 /*
  * SQL function json_object_keys
  *
@@ -573,6 +606,99 @@ jsonb_object_keys(PG_FUNCTION_ARGS)
 	SRF_RETURN_DONE(funcctx);
 }
 
+/*
+ * Report a JSON error.
+ */
+void
+json_ereport_error(JsonParseErrorType error, JsonLexContext *lex)
+{
+	if (error == JSON_UNICODE_HIGH_ESCAPE ||
+		error == JSON_UNICODE_CODE_POINT_ZERO)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
+				 errmsg("unsupported Unicode escape sequence"),
+				 errdetail("%s", json_errdetail(error, lex)),
+				 report_json_context(lex)));
+	else
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for type %s", "json"),
+				 errdetail("%s", json_errdetail(error, lex)),
+				 report_json_context(lex)));
+}
+
+/*
+ * Report a CONTEXT line for bogus JSON input.
+ *
+ * lex->token_terminator must be set to identify the spot where we detected
+ * the error.  Note that lex->token_start might be NULL, in case we recognized
+ * error at EOF.
+ *
+ * The return value isn't meaningful, but we make it non-void so that this
+ * can be invoked inside ereport().
+ */
+static int
+report_json_context(JsonLexContext *lex)
+{
+	const char *context_start;
+	const char *context_end;
+	const char *line_start;
+	int			line_number;
+	char	   *ctxt;
+	int			ctxtlen;
+	const char *prefix;
+	const char *suffix;
+
+	/* Choose boundaries for the part of the input we will display */
+	context_start = lex->input;
+	context_end = lex->token_terminator;
+	line_start = context_start;
+	line_number = 1;
+	for (;;)
+	{
+		/* Always advance over newlines */
+		if (context_start < context_end && *context_start == '\n')
+		{
+			context_start++;
+			line_start = context_start;
+			line_number++;
+			continue;
+		}
+		/* Otherwise, done as soon as we are close enough to context_end */
+		if (context_end - context_start < 50)
+			break;
+		/* Advance to next multibyte character */
+		if (IS_HIGHBIT_SET(*context_start))
+			context_start += pg_mblen(context_start);
+		else
+			context_start++;
+	}
+
+	/*
+	 * We add "..." to indicate that the excerpt doesn't start at the
+	 * beginning of the line ... but if we're within 3 characters of the
+	 * beginning of the line, we might as well just show the whole line.
+	 */
+	if (context_start - line_start <= 3)
+		context_start = line_start;
+
+	/* Get a null-terminated copy of the data to present */
+	ctxtlen = context_end - context_start;
+	ctxt = palloc(ctxtlen + 1);
+	memcpy(ctxt, context_start, ctxtlen);
+	ctxt[ctxtlen] = '\0';
+
+	/*
+	 * Show the context, prefixing "..." if not starting at start of line, and
+	 * suffixing "..." if not ending at end of line.
+	 */
+	prefix = (context_start > line_start) ? "..." : "";
+	suffix = (lex->token_type != JSON_TOKEN_END && context_end - lex->input < lex->input_length && *context_end != '\n' && *context_end != '\r') ? "..." : "";
+
+	return errcontext("JSON data, line %d: %s%s%s",
+					  line_number, prefix, ctxt, suffix);
+}
+
 
 Datum
 json_object_keys(PG_FUNCTION_ARGS)
diff --git a/src/include/utils/jsonapi.h b/src/include/utils/jsonapi.h
index 74dc35c41c..4d69b18495 100644
--- a/src/include/utils/jsonapi.h
+++ b/src/include/utils/jsonapi.h
@@ -126,12 +126,6 @@ typedef struct JsonSemAction
 extern JsonParseErrorType pg_parse_json(JsonLexContext *lex,
 										JsonSemAction *sem);
 
-/*
- * Same thing, but signal errors via ereport(ERROR) instead of returning
- * a result code.
- */
-extern void pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem);
-
 /* the null action object used for pure validation */
 extern JsonSemAction nullSemAction;
 
@@ -148,15 +142,11 @@ extern JsonParseErrorType json_count_array_elements(JsonLexContext *lex,
 													int *elements);
 
 /*
- * constructors for JsonLexContext, with or without strval element.
+ * constructor for JsonLexContext, with or without strval element.
  * If supplied, the strval element will contain a de-escaped version of
  * the lexeme. However, doing this imposes a performance penalty, so
  * it should be avoided if the de-escaped lexeme is not required.
- *
- * If you already have the json as a text* value, use the first of these
- * functions, otherwise use  makeJsonLexContextCstringLen().
  */
-extern JsonLexContext *makeJsonLexContext(text *json, bool need_escapes);
 extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
 													int len,
 													bool need_escapes);
@@ -164,9 +154,6 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
 /* lex one token */
 extern JsonParseErrorType json_lex(JsonLexContext *lex);
 
-/* report an error during json lexing or parsing */
-extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
-
 /* construct an error detail string for a json error */
 extern char *json_errdetail(JsonParseErrorType error, JsonLexContext *lex);
 
diff --git a/src/include/utils/jsonfuncs.h b/src/include/utils/jsonfuncs.h
index 19f087ccae..b993f38409 100644
--- a/src/include/utils/jsonfuncs.h
+++ b/src/include/utils/jsonfuncs.h
@@ -36,6 +36,15 @@ typedef void (*JsonIterateStringValuesAction) (void *state, char *elem_value, in
 /* an action that will be applied to each value in transform_json(b)_values functions */
 typedef text *(*JsonTransformStringValuesAction) (void *state, char *elem_value, int elem_len);
 
+/* build a JsonLexContext from a text datum */
+extern JsonLexContext *makeJsonLexContext(text *json, bool need_escapes);
+
+/* try to parse json, and ereport(ERROR) on failure */
+extern void pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem);
+
+/* report an error during json lexing or parsing */
+extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
+
 extern uint32 parse_jsonb_index_flags(Jsonb *jb);
 extern void iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
 								 JsonIterateStringValuesAction action);
-- 
2.17.2 (Apple Git-113)

