Move some code from jsonapi.c to jsonfuncs.c.
authorRobert Haas <rhaas@postgresql.org>
Mon, 27 Jan 2020 16:22:13 +0000 (11:22 -0500)
committerRobert Haas <rhaas@postgresql.org>
Mon, 27 Jan 2020 16:22:13 +0000 (11:22 -0500)
Specifically, move those functions that depend on ereport()
from jsonapi.c to jsonfuncs.c, in preparation for allowing
jsonapi.c to be used from frontend code.

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.

Reviewed by Mark Dilger and Andrew Dunstan.

Discussion: http://postgr.es/m/CA+TgmoYfOXhd27MUDGioVh6QtpD0C1K-f6ObSA10AWiHBAL5bA@mail.gmail.com

src/backend/utils/adt/json.c
src/backend/utils/adt/jsonapi.c
src/backend/utils/adt/jsonb.c
src/backend/utils/adt/jsonfuncs.c
src/include/utils/jsonapi.h
src/include/utils/jsonfuncs.h

index e73a60ece8a6d11f8d1ffe3987497744bc824367..f6cd2b991159e797cf971ea0f2803c633fc88a2f 100644 (file)
@@ -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"
 
index 129fbd65d5149f8eb94e44bf7a45aff26b06e4ff..1ac3b7beda87a36eac0ec09ea5bf5b1dd4c4d25f 100644 (file)
@@ -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.
  */
index 83d7f68b8219cb982d4937f78332a1899d1c8120..c912f8932df6bc4c34bc942f398ac6012a12699a 100644 (file)
@@ -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"
index 9eff506855303e58a445fae340a41498d315054d..66ea11b971ca01c47fee50daf219413a18163a00 100644 (file)
@@ -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)
index 74dc35c41c627e792ea0e94d94e1a592474a0bec..4d69b184951efb06d41a3e902d3888da58aae2d6 100644 (file)
@@ -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);
 
index 19f087ccae9d0bbf5cbc07788f94a3daee312343..b993f38409f4859e48bf6d60065c236207310fc7 100644 (file)
@@ -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);