]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/jsonfuncs.c
Fix initialization of fake LSN for unlogged relations
[postgresql] / src / backend / utils / adt / jsonfuncs.c
index 2f12d0325abe97f1d0acc8fbf8b953aa7ed30cee..3553a304b8c1861e86f48f998163b35e6f2bd052 100644 (file)
@@ -3,7 +3,7 @@
  * jsonfuncs.c
  *             Functions to process JSON data types.
  *
- * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
@@ -53,14 +53,15 @@ typedef struct OkeysState
        int                     sent_count;
 } OkeysState;
 
-/* state for iterate_json_string_values function */
+/* state for iterate_json_values function */
 typedef struct IterateJsonStringValuesState
 {
        JsonLexContext *lex;
        JsonIterateStringValuesAction action;   /* an action that will be applied
                                                                                         * to each json value */
        void       *action_state;       /* any necessary context for iteration */
-       uint32      flags;                      /* what kind of elements from a json we want to iterate */
+       uint32          flags;                  /* what kind of elements from a json we want
+                                                                * to iterate */
 } IterateJsonStringValuesState;
 
 /* state for transform_json_string_values function */
@@ -224,13 +225,13 @@ struct RecordIOData
        ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
 };
 
-/* per-query cache for populate_recordset */
-typedef struct PopulateRecordsetCache
+/* per-query cache for populate_record_worker and populate_recordset_worker */
+typedef struct PopulateRecordCache
 {
        Oid                     argtype;                /* declared type of the record argument */
        ColumnIOData c;                         /* metadata cache for populate_composite() */
        MemoryContext fn_mcxt;          /* where this is stored */
-} PopulateRecordsetCache;
+} PopulateRecordCache;
 
 /* per-call state for populate_recordset */
 typedef struct PopulateRecordsetState
@@ -243,16 +244,9 @@ typedef struct PopulateRecordsetState
        JsonTokenType saved_token_type;
        Tuplestorestate *tuple_store;
        HeapTupleHeader rec;
-       PopulateRecordsetCache *cache;
+       PopulateRecordCache *cache;
 } PopulateRecordsetState;
 
-/* structure to cache metadata needed for populate_record_worker() */
-typedef struct PopulateRecordCache
-{
-       Oid                     argtype;                /* declared type of the record argument */
-       ColumnIOData c;                         /* metadata cache for populate_composite() */
-} PopulateRecordCache;
-
 /* common data for populate_array_json() and populate_array_dim_jsonb() */
 typedef struct PopulateArrayContext
 {
@@ -353,8 +347,9 @@ static void get_scalar(void *state, char *token, JsonTokenType tokentype);
 /* common worker function for json getter functions */
 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);
+                                               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);
@@ -364,7 +359,7 @@ static void alen_array_element_start(void *state, bool isnull);
 /* common workers for json{b}_each* functions */
 static Datum each_worker(FunctionCallInfo fcinfo, bool as_text);
 static Datum each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
-                                 bool as_text);
+                                                          bool as_text);
 
 /* semantic action functions for json_each */
 static void each_object_field_start(void *state, char *fname, bool isnull);
@@ -374,9 +369,9 @@ static void each_scalar(void *state, char *token, JsonTokenType tokentype);
 
 /* common workers for json{b}_array_elements_* functions */
 static Datum elements_worker(FunctionCallInfo fcinfo, const char *funcname,
-                               bool as_text);
+                                                        bool as_text);
 static Datum elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
-                                         bool as_text);
+                                                                  bool as_text);
 
 /* semantic action functions for json_array_elements */
 static void elements_object_start(void *state);
@@ -420,59 +415,59 @@ static void sn_scalar(void *state, char *token, JsonTokenType tokentype);
 
 /* worker functions for populate_record, to_record, populate_recordset and to_recordset */
 static Datum populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
-                                                 bool have_record_arg);
+                                                                          bool is_json, bool have_record_arg);
 static Datum populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
-                                          bool have_record_arg);
+                                                                       bool is_json, bool have_record_arg);
 
 /* helper functions for populate_record[set] */
 static HeapTupleHeader populate_record(TupleDesc tupdesc, RecordIOData **record_p,
-                               HeapTupleHeader defaultval, MemoryContext mcxt,
-                               JsObject *obj);
+                                                                          HeapTupleHeader defaultval, MemoryContext mcxt,
+                                                                          JsObject *obj);
+static void get_record_type_from_argument(FunctionCallInfo fcinfo,
+                                                                                 const char *funcname,
+                                                                                 PopulateRecordCache *cache);
+static void get_record_type_from_query(FunctionCallInfo fcinfo,
+                                                                          const char *funcname,
+                                                                          PopulateRecordCache *cache);
 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);
+                                                               const char *colname, MemoryContext mcxt,
+                                                               HeapTupleHeader defaultval, JsValue *jsv, bool isnull);
 static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv);
 static void prepare_column_cache(ColumnIOData *column, Oid typid, int32 typmod,
-                                        MemoryContext mcxt, bool need_scalar);
+                                                                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);
+                                                                  const char *colname, MemoryContext mcxt, Datum defaultval,
+                                                                  JsValue *jsv, bool *isnull);
 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,
-                                                int ndim);
+                                                                        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 Datum populate_array(ArrayIOData *aio, const char *colname,
-                          MemoryContext mcxt, JsValue *jsv);
+                                                       MemoryContext mcxt, JsValue *jsv);
 static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
-                               MemoryContext mcxt, JsValue *jsv, bool isnull);
-
-/* Worker that takes care of common setup for us */
-static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
-                                                          uint32 flags,
-                                                          char *key,
-                                                          uint32 keylen);
+                                                        MemoryContext mcxt, JsValue *jsv, bool isnull);
 
 /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
 static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
-                          JsonbParseState **state);
+                                                                 JsonbParseState **state);
 static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
-               bool *path_nulls, int path_len,
-               JsonbParseState **st, int level, Jsonb *newval,
-               int op_type);
+                                                  bool *path_nulls, int path_len,
+                                                  JsonbParseState **st, int level, Jsonb *newval,
+                                                  int op_type);
 static void setPathObject(JsonbIterator **it, Datum *path_elems,
-                         bool *path_nulls, int path_len, JsonbParseState **st,
-                         int level,
-                         Jsonb *newval, uint32 npairs, int op_type);
+                                                 bool *path_nulls, int path_len, JsonbParseState **st,
+                                                 int level,
+                                                 Jsonb *newval, uint32 npairs, int op_type);
 static void setPathArray(JsonbIterator **it, Datum *path_elems,
-                        bool *path_nulls, int path_len, JsonbParseState **st,
-                        int level, Jsonb *newval, uint32 nelems, int op_type);
+                                                bool *path_nulls, int path_len, JsonbParseState **st,
+                                                int level, Jsonb *newval, uint32 nelems, int op_type);
 static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
 
 /* function supporting iterate_json_values */
@@ -717,13 +712,15 @@ jsonb_object_field(PG_FUNCTION_ARGS)
        Jsonb      *jb = PG_GETARG_JSONB_P(0);
        text       *key = PG_GETARG_TEXT_PP(1);
        JsonbValue *v;
+       JsonbValue      vbuf;
 
        if (!JB_ROOT_IS_OBJECT(jb))
                PG_RETURN_NULL();
 
-       v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
-                                                                          VARDATA_ANY(key),
-                                                                          VARSIZE_ANY_EXHDR(key));
+       v = getKeyJsonValueFromContainer(&jb->root,
+                                                                        VARDATA_ANY(key),
+                                                                        VARSIZE_ANY_EXHDR(key),
+                                                                        &vbuf);
 
        if (v != NULL)
                PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
@@ -753,47 +750,18 @@ jsonb_object_field_text(PG_FUNCTION_ARGS)
        Jsonb      *jb = PG_GETARG_JSONB_P(0);
        text       *key = PG_GETARG_TEXT_PP(1);
        JsonbValue *v;
+       JsonbValue      vbuf;
 
        if (!JB_ROOT_IS_OBJECT(jb))
                PG_RETURN_NULL();
 
-       v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
-                                                                          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);
-               }
+       v = getKeyJsonValueFromContainer(&jb->root,
+                                                                        VARDATA_ANY(key),
+                                                                        VARSIZE_ANY_EXHDR(key),
+                                                                        &vbuf);
 
-               if (result)
-                       PG_RETURN_TEXT_P(result);
-       }
+       if (v != NULL && v->type != jbvNull)
+               PG_RETURN_TEXT_P(JsonbValueAsText(v));
 
        PG_RETURN_NULL();
 }
@@ -878,39 +846,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();
 }
@@ -1388,7 +1326,6 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
 {
        Jsonb      *jb = PG_GETARG_JSONB_P(0);
        ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
-       Jsonb      *res;
        Datum      *pathtext;
        bool       *pathnulls;
        int                     npath;
@@ -1396,7 +1333,7 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
        bool            have_object = false,
                                have_array = false;
        JsonbValue *jbvp = NULL;
-       JsonbValue      tv;
+       JsonbValue      jbvbuf;
        JsonbContainer *container;
 
        /*
@@ -1454,10 +1391,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
        {
                if (have_object)
                {
-                       jbvp = findJsonbValueFromContainerLen(container,
-                                                                                                 JB_FOBJECT,
-                                                                                                 VARDATA(pathtext[i]),
-                                                                                                 VARSIZE(pathtext[i]) - VARHDRSZ);
+                       jbvp = getKeyJsonValueFromContainer(container,
+                                                                                               VARDATA(pathtext[i]),
+                                                                                               VARSIZE(pathtext[i]) - VARHDRSZ,
+                                                                                               &jbvbuf);
                }
                else if (have_array)
                {
@@ -1508,46 +1445,82 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
 
                if (jbvp->type == jbvBinary)
                {
-                       JsonbIterator *it = JsonbIteratorInit((JsonbContainer *) jbvp->val.binary.data);
-                       JsonbIteratorToken r;
-
-                       r = JsonbIteratorNext(&it, &tv, true);
-                       container = (JsonbContainer *) jbvp->val.binary.data;
-                       have_object = r == WJB_BEGIN_OBJECT;
-                       have_array = r == WJB_BEGIN_ARRAY;
+                       container = jbvp->val.binary.data;
+                       have_object = JsonContainerIsObject(container);
+                       have_array = JsonContainerIsArray(container);
+                       Assert(!JsonContainerIsScalar(container));
                }
                else
                {
-                       have_object = jbvp->type == jbvObject;
-                       have_array = jbvp->type == jbvArray;
+                       Assert(IsAJsonbScalar(jbvp));
+                       have_object = false;
+                       have_array = false;
                }
        }
 
        if (as_text)
        {
-               /* special-case outputs for string and null values */
-               if (jbvp->type == jbvString)
-                       PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
-                                                                                                         jbvp->val.string.len));
                if (jbvp->type == jbvNull)
                        PG_RETURN_NULL();
-       }
-
-       res = JsonbValueToJsonb(jbvp);
 
-       if (as_text)
-       {
-               PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
-                                                                                                               &res->root,
-                                                                                                               VARSIZE(res))));
+               PG_RETURN_TEXT_P(JsonbValueAsText(jbvp));
        }
        else
        {
+               Jsonb      *res = JsonbValueToJsonb(jbvp);
+
                /* not text mode - just hand back the jsonb */
                PG_RETURN_JSONB_P(res);
        }
 }
 
+/*
+ * 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
  */
@@ -1745,6 +1718,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
                         * matter what shape it is.
                         */
                        r = JsonbIteratorNext(&it, &v, skipNested);
+                       Assert(r != WJB_DONE);
 
                        values[0] = PointerGetDatum(key);
 
@@ -1757,26 +1731,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
                        {
@@ -2052,13 +2007,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)
                                {
@@ -2067,26 +2016,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);
@@ -2294,25 +2231,29 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
 Datum
 jsonb_populate_record(PG_FUNCTION_ARGS)
 {
-       return populate_record_worker(fcinfo, "jsonb_populate_record", true);
+       return populate_record_worker(fcinfo, "jsonb_populate_record",
+                                                                 false, true);
 }
 
 Datum
 jsonb_to_record(PG_FUNCTION_ARGS)
 {
-       return populate_record_worker(fcinfo, "jsonb_to_record", false);
+       return populate_record_worker(fcinfo, "jsonb_to_record",
+                                                                 false, false);
 }
 
 Datum
 json_populate_record(PG_FUNCTION_ARGS)
 {
-       return populate_record_worker(fcinfo, "json_populate_record", true);
+       return populate_record_worker(fcinfo, "json_populate_record",
+                                                                 true, true);
 }
 
 Datum
 json_to_record(PG_FUNCTION_ARGS)
 {
-       return populate_record_worker(fcinfo, "json_to_record", false);
+       return populate_record_worker(fcinfo, "json_to_record",
+                                                                 true, false);
 }
 
 /* helper function for diagnostics */
@@ -2324,12 +2265,12 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
                if (ctx->colname)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("expected json array"),
+                                        errmsg("expected JSON array"),
                                         errhint("See the value of key \"%s\".", ctx->colname)));
                else
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("expected json array")));
+                                        errmsg("expected JSON array")));
        }
        else
        {
@@ -2346,13 +2287,13 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
                if (ctx->colname)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("expected json array"),
+                                        errmsg("expected JSON array"),
                                         errhint("See the array element %s of key \"%s\".",
                                                         indices.data, ctx->colname)));
                else
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("expected json array"),
+                                        errmsg("expected JSON array"),
                                         errhint("See the array element %s.",
                                                         indices.data)));
        }
@@ -2388,7 +2329,7 @@ populate_array_check_dimension(PopulateArrayContext *ctx, int ndim)
        else if (ctx->dims[ndim] != dim)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                errmsg("malformed json array"),
+                                errmsg("malformed JSON array"),
                                 errdetail("Multidimensional arrays must have "
                                                   "sub-arrays with matching dimensions.")));
 
@@ -2797,26 +2738,7 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv)
 
                json = jsv->val.json.str;
                Assert(json);
-
-               /* already done the hard work in the json case */
-               if ((typid == JSONOID || typid == JSONBOID) &&
-                       jsv->val.json.type == JSON_TOKEN_STRING)
-               {
-                       /*
-                        * Add quotes around string value (should be already escaped) if
-                        * converting to json/jsonb.
-                        */
-
-                       if (len < 0)
-                               len = strlen(json);
-
-                       str = palloc(len + sizeof(char) * 3);
-                       str[0] = '"';
-                       memcpy(&str[1], json, len);
-                       str[len + 1] = '"';
-                       str[len + 2] = '\0';
-               }
-               else if (len >= 0)
+               if (len >= 0)
                {
                        /* Need to copy non-null-terminated string */
                        str = palloc(len + 1 * sizeof(char));
@@ -2824,7 +2746,21 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv)
                        str[len] = '\0';
                }
                else
-                       str = json;                     /* null-terminated string */
+                       str = json;                     /* string is already null-terminated */
+
+               /* If converting to json/jsonb, make string into valid JSON literal */
+               if ((typid == JSONOID || typid == JSONBOID) &&
+                       jsv->val.json.type == JSON_TOKEN_STRING)
+               {
+                       StringInfoData buf;
+
+                       initStringInfo(&buf);
+                       escape_json(&buf, str);
+                       /* free temporary buffer */
+                       if (str != json)
+                               pfree(str);
+                       str = buf.data;
+               }
        }
        else
        {
@@ -3085,8 +3021,8 @@ JsObjectGetField(JsObject *obj, char *field, JsValue *jsv)
        else
        {
                jsv->val.jsonb = !obj->val.jsonb_cont ? NULL :
-                       findJsonbValueFromContainerLen(obj->val.jsonb_cont, JB_FOBJECT,
-                                                                                  field, strlen(field));
+                       getKeyJsonValueFromContainer(obj->val.jsonb_cont, field, strlen(field),
+                                                                                NULL);
 
                return jsv->val.jsonb != NULL;
        }
@@ -3201,12 +3137,79 @@ populate_record(TupleDesc tupdesc,
        return res->t_data;
 }
 
+/*
+ * Setup for json{b}_populate_record{set}: result type will be same as first
+ * argument's type --- unless first argument is "null::record", which we can't
+ * extract type info from; we handle that later.
+ */
+static void
+get_record_type_from_argument(FunctionCallInfo fcinfo,
+                                                         const char *funcname,
+                                                         PopulateRecordCache *cache)
+{
+       cache->argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+       prepare_column_cache(&cache->c,
+                                                cache->argtype, -1,
+                                                cache->fn_mcxt, false);
+       if (cache->c.typcat != TYPECAT_COMPOSITE &&
+               cache->c.typcat != TYPECAT_COMPOSITE_DOMAIN)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+               /* translator: %s is a function name, eg json_to_record */
+                                errmsg("first argument of %s must be a row type",
+                                               funcname)));
+}
+
+/*
+ * Setup for json{b}_to_record{set}: result type is specified by calling
+ * query.  We'll also use this code for json{b}_populate_record{set},
+ * if we discover that the first argument is a null of type RECORD.
+ *
+ * Here it is syntactically impossible to specify the target type
+ * as domain-over-composite.
+ */
+static void
+get_record_type_from_query(FunctionCallInfo fcinfo,
+                                                  const char *funcname,
+                                                  PopulateRecordCache *cache)
+{
+       TupleDesc       tupdesc;
+       MemoryContext old_cxt;
+
+       if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+               /* translator: %s is a function name, eg json_to_record */
+                                errmsg("could not determine row type for result of %s",
+                                               funcname),
+                                errhint("Provide a non-null record argument, "
+                                                "or call the function in the FROM clause "
+                                                "using a column definition list.")));
+
+       Assert(tupdesc);
+       cache->argtype = tupdesc->tdtypeid;
+
+       /* If we go through this more than once, avoid memory leak */
+       if (cache->c.io.composite.tupdesc)
+               FreeTupleDesc(cache->c.io.composite.tupdesc);
+
+       /* Save identified tupdesc */
+       old_cxt = MemoryContextSwitchTo(cache->fn_mcxt);
+       cache->c.io.composite.tupdesc = CreateTupleDescCopy(tupdesc);
+       cache->c.io.composite.base_typid = tupdesc->tdtypeid;
+       cache->c.io.composite.base_typmod = tupdesc->tdtypmod;
+       MemoryContextSwitchTo(old_cxt);
+}
+
+/*
+ * common worker for json{b}_populate_record() and json{b}_to_record()
+ * is_json and have_record_arg identify the specific function
+ */
 static Datum
 populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
-                                          bool have_record_arg)
+                                          bool is_json, bool have_record_arg)
 {
        int                     json_arg_num = have_record_arg ? 1 : 0;
-       Oid                     jtype = get_fn_expr_argtype(fcinfo->flinfo, json_arg_num);
        JsValue         jsv = {0};
        HeapTupleHeader rec;
        Datum           rettuple;
@@ -3214,8 +3217,6 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
        MemoryContext fnmcxt = fcinfo->flinfo->fn_mcxt;
        PopulateRecordCache *cache = fcinfo->flinfo->fn_extra;
 
-       Assert(jtype == JSONOID || jtype == JSONBOID);
-
        /*
         * If first time through, identify input/result record type.  Note that
         * this stanza looks only at fcinfo context, which can't change during the
@@ -3225,63 +3226,24 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
        {
                fcinfo->flinfo->fn_extra = cache =
                        MemoryContextAllocZero(fnmcxt, sizeof(*cache));
+               cache->fn_mcxt = fnmcxt;
 
                if (have_record_arg)
-               {
-                       /*
-                        * json{b}_populate_record case: result type will be same as first
-                        * argument's.
-                        */
-                       cache->argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
-                       prepare_column_cache(&cache->c,
-                                                                cache->argtype, -1,
-                                                                fnmcxt, false);
-                       if (cache->c.typcat != TYPECAT_COMPOSITE &&
-                               cache->c.typcat != TYPECAT_COMPOSITE_DOMAIN)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                errmsg("first argument of %s must be a row type",
-                                                               funcname)));
-               }
+                       get_record_type_from_argument(fcinfo, funcname, cache);
                else
-               {
-                       /*
-                        * json{b}_to_record case: result type is specified by calling
-                        * query.  Here it is syntactically impossible to specify the
-                        * target type as domain-over-composite.
-                        */
-                       TupleDesc       tupdesc;
-                       MemoryContext old_cxt;
-
-                       if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("function returning record called in context "
-                                                               "that cannot accept type record"),
-                                                errhint("Try calling the function in the FROM clause "
-                                                                "using a column definition list.")));
-
-                       Assert(tupdesc);
-                       cache->argtype = tupdesc->tdtypeid;
-
-                       /* Save identified tupdesc */
-                       old_cxt = MemoryContextSwitchTo(fnmcxt);
-                       cache->c.io.composite.tupdesc = CreateTupleDescCopy(tupdesc);
-                       cache->c.io.composite.base_typid = tupdesc->tdtypeid;
-                       cache->c.io.composite.base_typmod = tupdesc->tdtypmod;
-                       MemoryContextSwitchTo(old_cxt);
-               }
+                       get_record_type_from_query(fcinfo, funcname, cache);
        }
 
        /* Collect record arg if we have one */
-       if (have_record_arg && !PG_ARGISNULL(0))
+       if (!have_record_arg)
+               rec = NULL;                             /* it's json{b}_to_record() */
+       else if (!PG_ARGISNULL(0))
        {
                rec = PG_GETARG_HEAPTUPLEHEADER(0);
 
                /*
                 * When declared arg type is RECORD, identify actual record type from
-                * the tuple itself.  Note the lookup_rowtype_tupdesc call in
-                * update_cached_tupdesc will fail if we're unable to do this.
+                * the tuple itself.
                 */
                if (cache->argtype == RECORDOID)
                {
@@ -3290,8 +3252,21 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
                }
        }
        else
+       {
                rec = NULL;
 
+               /*
+                * When declared arg type is RECORD, identify actual record type from
+                * calling query, or fail if we can't.
+                */
+               if (cache->argtype == RECORDOID)
+               {
+                       get_record_type_from_query(fcinfo, funcname, cache);
+                       /* This can't change argtype, which is important for next time */
+                       Assert(cache->argtype == RECORDOID);
+               }
+       }
+
        /* If no JSON argument, just return the record (if any) unchanged */
        if (PG_ARGISNULL(json_arg_num))
        {
@@ -3301,9 +3276,9 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
                        PG_RETURN_NULL();
        }
 
-       jsv.is_json = jtype == JSONOID;
+       jsv.is_json = is_json;
 
-       if (jsv.is_json)
+       if (is_json)
        {
                text       *json = PG_GETARG_TEXT_PP(json_arg_num);
 
@@ -3487,31 +3462,35 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
 Datum
 jsonb_populate_recordset(PG_FUNCTION_ARGS)
 {
-       return populate_recordset_worker(fcinfo, "jsonb_populate_recordset", true);
+       return populate_recordset_worker(fcinfo, "jsonb_populate_recordset",
+                                                                        false, true);
 }
 
 Datum
 jsonb_to_recordset(PG_FUNCTION_ARGS)
 {
-       return populate_recordset_worker(fcinfo, "jsonb_to_recordset", false);
+       return populate_recordset_worker(fcinfo, "jsonb_to_recordset",
+                                                                        false, false);
 }
 
 Datum
 json_populate_recordset(PG_FUNCTION_ARGS)
 {
-       return populate_recordset_worker(fcinfo, "json_populate_recordset", true);
+       return populate_recordset_worker(fcinfo, "json_populate_recordset",
+                                                                        true, true);
 }
 
 Datum
 json_to_recordset(PG_FUNCTION_ARGS)
 {
-       return populate_recordset_worker(fcinfo, "json_to_recordset", false);
+       return populate_recordset_worker(fcinfo, "json_to_recordset",
+                                                                        true, false);
 }
 
 static void
 populate_recordset_record(PopulateRecordsetState *state, JsObject *obj)
 {
-       PopulateRecordsetCache *cache = state->cache;
+       PopulateRecordCache *cache = state->cache;
        HeapTupleHeader tuphead;
        HeapTupleData tuple;
 
@@ -3542,18 +3521,18 @@ populate_recordset_record(PopulateRecordsetState *state, JsObject *obj)
 }
 
 /*
- * common worker for json_populate_recordset() and json_to_recordset()
+ * common worker for json{b}_populate_recordset() and json{b}_to_recordset()
+ * is_json and have_record_arg identify the specific function
  */
 static Datum
 populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
-                                                 bool have_record_arg)
+                                                 bool is_json, bool have_record_arg)
 {
        int                     json_arg_num = have_record_arg ? 1 : 0;
-       Oid                     jtype = get_fn_expr_argtype(fcinfo->flinfo, json_arg_num);
        ReturnSetInfo *rsi;
        MemoryContext old_cxt;
        HeapTupleHeader rec;
-       PopulateRecordsetCache *cache = fcinfo->flinfo->fn_extra;
+       PopulateRecordCache *cache = fcinfo->flinfo->fn_extra;
        PopulateRecordsetState *state;
 
        rsi = (ReturnSetInfo *) fcinfo->resultinfo;
@@ -3579,60 +3558,21 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
                cache->fn_mcxt = fcinfo->flinfo->fn_mcxt;
 
                if (have_record_arg)
-               {
-                       /*
-                        * json{b}_populate_recordset case: result type will be same as
-                        * first argument's.
-                        */
-                       cache->argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
-                       prepare_column_cache(&cache->c,
-                                                                cache->argtype, -1,
-                                                                cache->fn_mcxt, false);
-                       if (cache->c.typcat != TYPECAT_COMPOSITE &&
-                               cache->c.typcat != TYPECAT_COMPOSITE_DOMAIN)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                errmsg("first argument of %s must be a row type",
-                                                               funcname)));
-               }
+                       get_record_type_from_argument(fcinfo, funcname, cache);
                else
-               {
-                       /*
-                        * json{b}_to_recordset case: result type is specified by calling
-                        * query.  Here it is syntactically impossible to specify the
-                        * target type as domain-over-composite.
-                        */
-                       TupleDesc       tupdesc;
-
-                       if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("function returning record called in context "
-                                                               "that cannot accept type record"),
-                                                errhint("Try calling the function in the FROM clause "
-                                                                "using a column definition list.")));
-
-                       Assert(tupdesc);
-                       cache->argtype = tupdesc->tdtypeid;
-
-                       /* Save identified tupdesc */
-                       old_cxt = MemoryContextSwitchTo(cache->fn_mcxt);
-                       cache->c.io.composite.tupdesc = CreateTupleDescCopy(tupdesc);
-                       cache->c.io.composite.base_typid = tupdesc->tdtypeid;
-                       cache->c.io.composite.base_typmod = tupdesc->tdtypmod;
-                       MemoryContextSwitchTo(old_cxt);
-               }
+                       get_record_type_from_query(fcinfo, funcname, cache);
        }
 
        /* Collect record arg if we have one */
-       if (have_record_arg && !PG_ARGISNULL(0))
+       if (!have_record_arg)
+               rec = NULL;                             /* it's json{b}_to_recordset() */
+       else if (!PG_ARGISNULL(0))
        {
                rec = PG_GETARG_HEAPTUPLEHEADER(0);
 
                /*
                 * When declared arg type is RECORD, identify actual record type from
-                * the tuple itself.  Note the lookup_rowtype_tupdesc call in
-                * update_cached_tupdesc will fail if we're unable to do this.
+                * the tuple itself.
                 */
                if (cache->argtype == RECORDOID)
                {
@@ -3641,12 +3581,31 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
                }
        }
        else
+       {
                rec = NULL;
 
+               /*
+                * When declared arg type is RECORD, identify actual record type from
+                * calling query, or fail if we can't.
+                */
+               if (cache->argtype == RECORDOID)
+               {
+                       get_record_type_from_query(fcinfo, funcname, cache);
+                       /* This can't change argtype, which is important for next time */
+                       Assert(cache->argtype == RECORDOID);
+               }
+       }
+
        /* if the json is null send back an empty set */
        if (PG_ARGISNULL(json_arg_num))
                PG_RETURN_NULL();
 
+       /*
+        * Forcibly update the cached tupdesc, to ensure we have the right tupdesc
+        * to return even if the JSON contains no rows.
+        */
+       update_cached_tupdesc(&cache->c.io.composite, cache->fn_mcxt);
+
        state = palloc0(sizeof(PopulateRecordsetState));
 
        /* make tuplestore in a sufficiently long-lived memory context */
@@ -3660,7 +3619,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
        state->cache = cache;
        state->rec = rec;
 
-       if (jtype == JSONOID)
+       if (is_json)
        {
                text       *json = PG_GETARG_TEXT_PP(json_arg_num);
                JsonLexContext *lex;
@@ -3691,8 +3650,6 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
                bool            skipNested = false;
                JsonbIteratorToken r;
 
-               Assert(jtype == JSONBOID);
-
                if (JB_ROOT_IS_SCALAR(jb) || !JB_ROOT_IS_ARRAY(jb))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -3724,8 +3681,13 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
                }
        }
 
+       /*
+        * Note: we must copy the cached tupdesc because the executor will free
+        * the passed-back setDesc, but we want to hang onto the cache in case
+        * we're called again in the same query.
+        */
        rsi->setResult = state->tuple_store;
-       rsi->setDesc = cache->c.io.composite.tupdesc;
+       rsi->setDesc = CreateTupleDescCopy(cache->c.io.composite.tupdesc);
 
        PG_RETURN_NULL();
 }
@@ -3884,22 +3846,6 @@ populate_recordset_object_field_end(void *state, char *fname, bool isnull)
        }
 }
 
-/*
- * findJsonbValueFromContainer() wrapper that sets up JsonbValue key string.
- */
-static JsonbValue *
-findJsonbValueFromContainerLen(JsonbContainer *container, uint32 flags,
-                                                          char *key, uint32 keylen)
-{
-       JsonbValue      k;
-
-       k.type = jbvString;
-       k.val.string.val = key;
-       k.val.string.len = keylen;
-
-       return findJsonbValueFromContainer(container, flags, &k);
-}
-
 /*
  * Semantic actions for json_strip_nulls.
  *
@@ -4111,6 +4057,7 @@ addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
        if (JB_ROOT_IS_SCALAR(jb))
        {
                (void) JsonbIteratorNext(&it, &v, false);       /* skip array header */
+               Assert(v.type == jbvArray);
                (void) JsonbIteratorNext(&it, &v, false);       /* fetch scalar value */
 
                switch (o->type)
@@ -4224,7 +4171,7 @@ jsonb_delete(PG_FUNCTION_ARGS)
 
        it = JsonbIteratorInit(&in->root);
 
-       while ((r = JsonbIteratorNext(&it, &v, skipNested)) != 0)
+       while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
        {
                skipNested = true;
 
@@ -4234,7 +4181,7 @@ jsonb_delete(PG_FUNCTION_ARGS)
                {
                        /* skip corresponding value as well */
                        if (r == WJB_KEY)
-                               JsonbIteratorNext(&it, &v, true);
+                               (void) JsonbIteratorNext(&it, &v, true);
 
                        continue;
                }
@@ -4289,7 +4236,7 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
 
        it = JsonbIteratorInit(&in->root);
 
-       while ((r = JsonbIteratorNext(&it, &v, skipNested)) != 0)
+       while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
        {
                skipNested = true;
 
@@ -4319,7 +4266,7 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
                        {
                                /* skip corresponding value as well */
                                if (r == WJB_KEY)
-                                       JsonbIteratorNext(&it, &v, true);
+                                       (void) JsonbIteratorNext(&it, &v, true);
 
                                continue;
                        }
@@ -4385,7 +4332,7 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
 
        pushJsonbValue(&state, r, NULL);
 
-       while ((r = JsonbIteratorNext(&it, &v, true)) != 0)
+       while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
        {
                if (r == WJB_ELEM)
                {
@@ -4403,7 +4350,6 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
 
 /*
  * SQL function jsonb_set(jsonb, text[], jsonb, boolean)
- *
  */
 Datum
 jsonb_set(PG_FUNCTION_ARGS)
@@ -4495,7 +4441,6 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
 
 /*
  * SQL function jsonb_insert(jsonb, text[], jsonb, boolean)
- *
  */
 Datum
 jsonb_insert(PG_FUNCTION_ARGS)
@@ -4576,7 +4521,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
                 * Append the all tokens from v2 to res, include last WJB_END_OBJECT
                 * (the concatenation will be completed).
                 */
-               while ((r2 = JsonbIteratorNext(it2, &v2, true)) != 0)
+               while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
                        res = pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
        }
 
@@ -4616,10 +4561,10 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
                if (prepend)
                {
                        pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
-                       while ((r1 = JsonbIteratorNext(it_object, &v1, true)) != 0)
+                       while ((r1 = JsonbIteratorNext(it_object, &v1, true)) != WJB_DONE)
                                pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
 
-                       while ((r2 = JsonbIteratorNext(it_array, &v2, true)) != 0)
+                       while ((r2 = JsonbIteratorNext(it_array, &v2, true)) != WJB_DONE)
                                res = pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
                }
                else
@@ -4628,7 +4573,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
                                pushJsonbValue(state, r1, &v1);
 
                        pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
-                       while ((r2 = JsonbIteratorNext(it_object, &v2, true)) != 0)
+                       while ((r2 = JsonbIteratorNext(it_object, &v2, true)) != WJB_DONE)
                                pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
 
                        res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
@@ -4948,19 +4893,19 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 uint32
 parse_jsonb_index_flags(Jsonb *jb)
 {
-       JsonbIterator      *it;
-       JsonbValue                      v;
-       JsonbIteratorToken      type;
-       uint32                          flags = 0;
+       JsonbIterator *it;
+       JsonbValue      v;
+       JsonbIteratorToken type;
+       uint32          flags = 0;
 
        it = JsonbIteratorInit(&jb->root);
 
        type = JsonbIteratorNext(&it, &v, false);
 
        /*
-        * We iterate over array (scalar internally is represented as array, so, we
-        * will accept it too) to check all its elements. Flag's names are choosen
-        * the same as jsonb_typeof uses.
+        * We iterate over array (scalar internally is represented as array, so,
+        * we will accept it too) to check all its elements.  Flag names are
+        * chosen the same as jsonb_typeof uses.
         */
        if (type != WJB_BEGIN_ARRAY)
                ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -4972,10 +4917,10 @@ parse_jsonb_index_flags(Jsonb *jb)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("flag array element is not a string"),
-                                        errhint("Possible values are: \"string\", \"numeric\", \"boolean\", \"key\" and \"all\"")));
+                                        errhint("Possible values are: \"string\", \"numeric\", \"boolean\", \"key\", and \"all\".")));
 
                if (v.val.string.len == 3 &&
-                                pg_strncasecmp(v.val.string.val, "all", 3) == 0)
+                       pg_strncasecmp(v.val.string.val, "all", 3) == 0)
                        flags |= jtiAll;
                else if (v.val.string.len == 3 &&
                                 pg_strncasecmp(v.val.string.val, "key", 3) == 0)
@@ -4994,15 +4939,17 @@ parse_jsonb_index_flags(Jsonb *jb)
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("wrong flag in flag array: \"%s\"",
                                                        pnstrdup(v.val.string.val, v.val.string.len)),
-                                        errhint("Possible values are: \"string\", \"numeric\", \"boolean\", \"key\" and \"all\"")));
+                                        errhint("Possible values are: \"string\", \"numeric\", \"boolean\", \"key\", and \"all\".")));
        }
 
-       /* user should not get it */
+       /* expect end of array now */
        if (type != WJB_END_ARRAY)
                elog(ERROR, "unexpected end of flag array");
 
        /* get final WJB_DONE and free iterator */
-       JsonbIteratorNext(&it, &v, false);
+       type = JsonbIteratorNext(&it, &v, false);
+       if (type != WJB_DONE)
+               elog(ERROR, "unexpected end of flag array");
 
        return flags;
 }
@@ -5023,7 +4970,7 @@ iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
 
        /*
         * Just recursively iterating over jsonb and call callback on all
-        * correspoding elements
+        * corresponding elements
         */
        while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
        {
@@ -5041,7 +4988,7 @@ iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
                }
 
                /* JsonbValue is a value of object or element of array */
-               switch(v.type)
+               switch (v.type)
                {
                        case jbvString:
                                if (flags & jtiString)
@@ -5050,10 +4997,10 @@ iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
                        case jbvNumeric:
                                if (flags & jtiNumeric)
                                {
-                                       char *val;
+                                       char       *val;
 
                                        val = DatumGetCString(DirectFunctionCall1(numeric_out,
-                                                               NumericGetDatum(v.val.numeric)));
+                                                                                                                         NumericGetDatum(v.val.numeric)));
 
                                        action(state, val, strlen(val));
                                        pfree(val);
@@ -5108,7 +5055,7 @@ iterate_values_scalar(void *state, char *token, JsonTokenType tokentype)
 {
        IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
 
-       switch(tokentype)
+       switch (tokentype)
        {
                case JSON_TOKEN_STRING:
                        if (_state->flags & jtiString)
@@ -5136,7 +5083,8 @@ iterate_values_object_field_start(void *state, char *fname, bool isnull)
 
        if (_state->flags & jtiKey)
        {
-               char *val = pstrdup(fname);
+               char       *val = pstrdup(fname);
+
                _state->action(_state->action_state, val, strlen(val));
        }
 }