Refactor code into new JsonbValueAsText, and use it more
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 20 Sep 2019 21:34:31 +0000 (18:34 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 20 Sep 2019 22:30:16 +0000 (19:30 -0300)
jsonb_object_field_text and jsonb_array_element_text both contained
identical copies of this code, so extract that into new routine
JsonbValueAsText.  This can also be used in other places, to measurable
performance benefit: the jsonb_each() and jsonb_array_elements()
functions can use it for outputting text forms instead of their less
efficient current implementation (because we no longer need to build
intermediate a jsonb representation of each value).

Author: Nikita Glukhov
Discussion: https://postgr.es/m/7c417f90-f95f-247e-ba63-d95e39c0ad14@postgrespro.ru

src/backend/utils/adt/jsonfuncs.c

index 667f9d9563e66c786057367cce244a607a8f3b81..01b44f5c754da350f335d7fb1e8c84237c33d7c4 100644 (file)
@@ -349,6 +349,7 @@ static Datum get_path_all(FunctionCallInfo fcinfo, bool as_text);
 static text *get_worker(text *json, char **tpath, int *ipath, int npath,
                        bool normalize_results);
 static Datum get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text);
+static text *JsonbValueAsText(JsonbValue *v);
 
 /* semantic action functions for json_array_length */
 static void alen_object_start(void *state);
@@ -761,39 +762,9 @@ jsonb_object_field_text(PG_FUNCTION_ARGS)
                                       VARDATA_ANY(key),
                                       VARSIZE_ANY_EXHDR(key));
 
-   if (v != NULL)
-   {
-       text       *result = NULL;
 
-       switch (v->type)
-       {
-           case jbvNull:
-               break;
-           case jbvBool:
-               result = cstring_to_text(v->val.boolean ? "true" : "false");
-               break;
-           case jbvString:
-               result = cstring_to_text_with_len(v->val.string.val, v->val.string.len);
-               break;
-           case jbvNumeric:
-               result = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out,
-                                                                            PointerGetDatum(v->val.numeric))));
-               break;
-           case jbvBinary:
-               {
-                   StringInfo  jtext = makeStringInfo();
-
-                   (void) JsonbToCString(jtext, v->val.binary.data, -1);
-                   result = cstring_to_text_with_len(jtext->data, jtext->len);
-               }
-               break;
-           default:
-               elog(ERROR, "unrecognized jsonb type: %d", (int) v->type);
-       }
-
-       if (result)
-           PG_RETURN_TEXT_P(result);
-   }
+   if (v != NULL && v->type != jbvNull)
+       PG_RETURN_TEXT_P(JsonbValueAsText(v));
 
    PG_RETURN_NULL();
 }
@@ -878,39 +849,9 @@ jsonb_array_element_text(PG_FUNCTION_ARGS)
    }
 
    v = getIthJsonbValueFromContainer(&jb->root, element);
-   if (v != NULL)
-   {
-       text       *result = NULL;
-
-       switch (v->type)
-       {
-           case jbvNull:
-               break;
-           case jbvBool:
-               result = cstring_to_text(v->val.boolean ? "true" : "false");
-               break;
-           case jbvString:
-               result = cstring_to_text_with_len(v->val.string.val, v->val.string.len);
-               break;
-           case jbvNumeric:
-               result = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out,
-                                                                            PointerGetDatum(v->val.numeric))));
-               break;
-           case jbvBinary:
-               {
-                   StringInfo  jtext = makeStringInfo();
 
-                   (void) JsonbToCString(jtext, v->val.binary.data, -1);
-                   result = cstring_to_text_with_len(jtext->data, jtext->len);
-               }
-               break;
-           default:
-               elog(ERROR, "unrecognized jsonb type: %d", (int) v->type);
-       }
-
-       if (result)
-           PG_RETURN_TEXT_P(result);
-   }
+   if (v != NULL && v->type != jbvNull)
+       PG_RETURN_TEXT_P(JsonbValueAsText(v));
 
    PG_RETURN_NULL();
 }
@@ -1548,6 +1489,53 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
    }
 }
 
+/*
+ * Return the text representation of the given JsonbValue.
+ */
+static text *
+JsonbValueAsText(JsonbValue *v)
+{
+   switch (v->type)
+   {
+       case jbvNull:
+           return NULL;
+
+       case jbvBool:
+           return v->val.boolean ?
+               cstring_to_text_with_len("true", 4) :
+               cstring_to_text_with_len("false", 5);
+
+       case jbvString:
+           return cstring_to_text_with_len(v->val.string.val,
+                                           v->val.string.len);
+
+       case jbvNumeric:
+           {
+               Datum       cstr;
+
+               cstr = DirectFunctionCall1(numeric_out,
+                                          PointerGetDatum(v->val.numeric));
+
+               return cstring_to_text(DatumGetCString(cstr));
+           }
+
+       case jbvBinary:
+           {
+               StringInfoData jtext;
+
+               initStringInfo(&jtext);
+               (void) JsonbToCString(&jtext, v->val.binary.data,
+                                     v->val.binary.len);
+
+               return cstring_to_text_with_len(jtext.data, jtext.len);
+           }
+
+       default:
+           elog(ERROR, "unrecognized jsonb type: %d", (int) v->type);
+           return NULL;
+   }
+}
+
 /*
  * SQL function json_array_length(json) -> int
  */
@@ -1758,26 +1746,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
                    values[1] = (Datum) NULL;
                }
                else
-               {
-                   text       *sv;
-
-                   if (v.type == jbvString)
-                   {
-                       /* In text mode, scalar strings should be dequoted */
-                       sv = cstring_to_text_with_len(v.val.string.val, v.val.string.len);
-                   }
-                   else
-                   {
-                       /* Turn anything else into a json string */
-                       StringInfo  jtext = makeStringInfo();
-                       Jsonb      *jb = JsonbValueToJsonb(&v);
-
-                       (void) JsonbToCString(jtext, &jb->root, 0);
-                       sv = cstring_to_text_with_len(jtext->data, jtext->len);
-                   }
-
-                   values[1] = PointerGetDatum(sv);
-               }
+                   values[1] = PointerGetDatum(JsonbValueAsText(&v));
            }
            else
            {
@@ -2053,13 +2022,7 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
            /* use the tmp context so we can clean up after each tuple is done */
            old_cxt = MemoryContextSwitchTo(tmp_cxt);
 
-           if (!as_text)
-           {
-               Jsonb      *val = JsonbValueToJsonb(&v);
-
-               values[0] = PointerGetDatum(val);
-           }
-           else
+           if (as_text)
            {
                if (v.type == jbvNull)
                {
@@ -2068,26 +2031,14 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
                    values[0] = (Datum) NULL;
                }
                else
-               {
-                   text       *sv;
-
-                   if (v.type == jbvString)
-                   {
-                       /* in text mode scalar strings should be dequoted */
-                       sv = cstring_to_text_with_len(v.val.string.val, v.val.string.len);
-                   }
-                   else
-                   {
-                       /* turn anything else into a json string */
-                       StringInfo  jtext = makeStringInfo();
-                       Jsonb      *jb = JsonbValueToJsonb(&v);
-
-                       (void) JsonbToCString(jtext, &jb->root, 0);
-                       sv = cstring_to_text_with_len(jtext->data, jtext->len);
-                   }
+                   values[0] = PointerGetDatum(JsonbValueAsText(&v));
+           }
+           else
+           {
+               /* Not in text mode, just return the Jsonb */
+               Jsonb      *val = JsonbValueToJsonb(&v);
 
-                   values[0] = PointerGetDatum(sv);
-               }
+               values[0] = PointerGetDatum(val);
            }
 
            tuple = heap_form_tuple(ret_tdesc, values, nulls);
@@ -4430,7 +4381,6 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
 
 /*
  * SQL function jsonb_set(jsonb, text[], jsonb, boolean)
- *
  */
 Datum
 jsonb_set(PG_FUNCTION_ARGS)
@@ -4522,7 +4472,6 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
 
 /*
  * SQL function jsonb_insert(jsonb, text[], jsonb, boolean)
- *
  */
 Datum
 jsonb_insert(PG_FUNCTION_ARGS)