]> 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 70edd42c8d697cfa1ed44737d4fd8eae7ef87963..3553a304b8c1861e86f48f998163b35e6f2bd052 100644 (file)
@@ -3,7 +3,7 @@
  * jsonfuncs.c
  *             Functions to process JSON data types.
  *
- * Portions Copyright (c) 1996-2017, 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,23 +53,25 @@ 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 */
+       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 */
 } IterateJsonStringValuesState;
 
 /* state for transform_json_string_values function */
 typedef struct TransformJsonStringValuesState
 {
-       JsonLexContext                                  *lex;
-       StringInfo                                              strval;                 /* resulting json */
-       JsonTransformStringValuesAction action;                 /* an action that will be applied
-                                                                                                          to each json value */
-       void                                                    *action_state;  /* any necessary context for transformation */
+       JsonLexContext *lex;
+       StringInfo      strval;                 /* resulting json */
+       JsonTransformStringValuesAction action; /* an action that will be applied
+                                                                                        * to each json value */
+       void       *action_state;       /* any necessary context for transformation */
 } TransformJsonStringValuesState;
 
 /* state for json_get* functions */
@@ -84,7 +86,8 @@ typedef struct GetState
        char      **path_names;         /* field name(s) being sought */
        int                *path_indexes;       /* array index(es) being sought */
        bool       *pathok;                     /* is path matched to current depth? */
-       int                *array_cur_index;    /* current element index at each path level */
+       int                *array_cur_index;    /* current element index at each path
+                                                                        * level */
 } GetState;
 
 /* state for json_array_length */
@@ -122,7 +125,7 @@ typedef struct ElementsState
 } ElementsState;
 
 /* state for get_json_object_as_hash */
-typedef struct JhashState
+typedef struct JHashState
 {
        JsonLexContext *lex;
        const char *function_name;
@@ -135,7 +138,7 @@ typedef struct JhashState
 /* hashtable element */
 typedef struct JsonHashEntry
 {
-       char            fname[NAMEDATALEN];             /* hash key (MUST BE FIRST) */
+       char            fname[NAMEDATALEN]; /* hash key (MUST BE FIRST) */
        char       *val;
        JsonTokenType type;
 } JsonHashEntry;
@@ -154,29 +157,34 @@ typedef struct RecordIOData RecordIOData;
 /* structure to cache metadata needed for populate_array() */
 typedef struct ArrayIOData
 {
-       ColumnIOData   *element_info;   /* metadata cache */
-       Oid                             element_type;   /* array element type id */
-       int32                   element_typmod; /* array element type modifier */
+       ColumnIOData *element_info; /* metadata cache */
+       Oid                     element_type;   /* array element type id */
+       int32           element_typmod; /* array element type modifier */
 } ArrayIOData;
 
 /* structure to cache metadata needed for populate_composite() */
 typedef struct CompositeIOData
 {
        /*
-        * We use pointer to a RecordIOData here because variable-length
-        * struct RecordIOData can't be used directly in ColumnIOData.io union
+        * We use pointer to a RecordIOData here because variable-length struct
+        * RecordIOData can't be used directly in ColumnIOData.io union
         */
-       RecordIOData   *record_io;      /* metadata cache for populate_record() */
-       TupleDesc               tupdesc;        /* cached tuple descriptor */
+       RecordIOData *record_io;        /* metadata cache for populate_record() */
+       TupleDesc       tupdesc;                /* cached tuple descriptor */
+       /* these fields differ from target type only if domain over composite: */
+       Oid                     base_typid;             /* base type id */
+       int32           base_typmod;    /* base type modifier */
+       /* this field is used only if target type is domain over composite: */
+       void       *domain_info;        /* opaque cache for domain checks */
 } CompositeIOData;
 
 /* structure to cache metadata needed for populate_domain() */
 typedef struct DomainIOData
 {
-       ColumnIOData   *base_io;                /* metadata cache */
-       Oid                             base_typid;             /* base type id */
-       int32                   base_typmod;    /* base type modifier */
-       void               *domain_info;        /* opaque cache for domain checks */
+       ColumnIOData *base_io;          /* metadata cache */
+       Oid                     base_typid;             /* base type id */
+       int32           base_typmod;    /* base type modifier */
+       void       *domain_info;        /* opaque cache for domain checks */
 } DomainIOData;
 
 /* enumeration type categories */
@@ -185,7 +193,8 @@ typedef enum TypeCat
        TYPECAT_SCALAR = 's',
        TYPECAT_ARRAY = 'a',
        TYPECAT_COMPOSITE = 'c',
-       TYPECAT_DOMAIN = 'd',
+       TYPECAT_COMPOSITE_DOMAIN = 'C',
+       TYPECAT_DOMAIN = 'd'
 } TypeCat;
 
 /* these two are stolen from hstore / record_out, used in populate_record* */
@@ -193,17 +202,18 @@ typedef enum TypeCat
 /* structure to cache record metadata needed for populate_record_field() */
 struct ColumnIOData
 {
-       Oid                     typid;          /* column type id */
-       int32           typmod;         /* column type modifier */
-       TypeCat         typcat;         /* column type category */
-       ScalarIOData scalar_io; /* metadata cache for directi conversion
-                                                        * through input function */
+       Oid                     typid;                  /* column type id */
+       int32           typmod;                 /* column type modifier */
+       TypeCat         typcat;                 /* column type category */
+       ScalarIOData scalar_io;         /* metadata cache for directi conversion
+                                                                * through input function */
        union
        {
-               ArrayIOData             array;
-               CompositeIOData composite;
-               DomainIOData    domain;
-       } io;                   /* metadata cache for various column type categories */
+               ArrayIOData array;
+               CompositeIOData composite;
+               DomainIOData domain;
+       }                       io;                             /* metadata cache for various column type
+                                                                * categories */
 };
 
 /* structure to cache record metadata needed for populate_record() */
@@ -215,7 +225,15 @@ struct RecordIOData
        ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
 };
 
-/* state for populate_recordset */
+/* 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 */
+} PopulateRecordCache;
+
+/* per-call state for populate_recordset */
 typedef struct PopulateRecordsetState
 {
        JsonLexContext *lex;
@@ -225,40 +243,32 @@ typedef struct PopulateRecordsetState
        char       *save_json_start;
        JsonTokenType saved_token_type;
        Tuplestorestate *tuple_store;
-       TupleDesc       ret_tdesc;
        HeapTupleHeader rec;
-       RecordIOData **my_extra;
-       MemoryContext fn_mcxt;          /* used to stash IO funcs */
+       PopulateRecordCache *cache;
 } PopulateRecordsetState;
 
-/* structure to cache metadata needed for populate_record_worker() */
-typedef struct PopulateRecordCache
-{
-       Oid                             argtype;        /* verified row type of the first argument */
-       CompositeIOData io;                     /* metadata cache for populate_composite() */
-} PopulateRecordCache;
-
 /* common data for populate_array_json() and populate_array_dim_jsonb() */
 typedef struct PopulateArrayContext
 {
-       ArrayBuildState    *astate;             /* array build state */
-       ArrayIOData                *aio;                /* metadata cache */
-       MemoryContext           acxt;           /* array build memory context */
-       MemoryContext           mcxt;           /* cache memory context */
-       const char                 *colname;    /* for diagnostics only */
-       int                                *dims;               /* dimensions */
-       int                                *sizes;              /* current dimension counters */
-       int                                     ndims;          /* number of dimensions */
+       ArrayBuildState *astate;        /* array build state */
+       ArrayIOData *aio;                       /* metadata cache */
+       MemoryContext acxt;                     /* array build memory context */
+       MemoryContext mcxt;                     /* cache memory context */
+       const char *colname;            /* for diagnostics only */
+       int                *dims;                       /* dimensions */
+       int                *sizes;                      /* current dimension counters */
+       int                     ndims;                  /* number of dimensions */
 } PopulateArrayContext;
 
 /* state for populate_array_json() */
 typedef struct PopulateArrayState
 {
-       JsonLexContext     *lex;                        /* json lexer */
+       JsonLexContext *lex;            /* json lexer */
        PopulateArrayContext *ctx;      /* context */
-       char                       *element_start;      /* start of the current array element */
-       char                       *element_scalar;     /* current array element token if it is a scalar */
-       JsonTokenType           element_type;   /* current array element type */
+       char       *element_start;      /* start of the current array element */
+       char       *element_scalar; /* current array element token if it is a
+                                                                * scalar */
+       JsonTokenType element_type; /* current array element type */
 } PopulateArrayState;
 
 /* state for json_strip_nulls */
@@ -272,18 +282,18 @@ typedef struct StripnullState
 /* structure for generalized json/jsonb value passing */
 typedef struct JsValue
 {
-       bool is_json;                           /* json/jsonb */
+       bool            is_json;                /* json/jsonb */
        union
        {
                struct
                {
-                       char   *str;            /* json string */
-                       int             len;            /* json string length or -1 if null-terminated */
-                       JsonTokenType type;     /* json type */
-               } json;                                 /* json value */
+                       char       *str;        /* json string */
+                       int                     len;    /* json string length or -1 if null-terminated */
+                       JsonTokenType type; /* json type */
+               }                       json;           /* json value */
 
                JsonbValue *jsonb;              /* jsonb value */
-       } val;
+       }                       val;
 } JsValue;
 
 typedef struct JsObject
@@ -291,9 +301,9 @@ typedef struct JsObject
        bool            is_json;                /* json/jsonb */
        union
        {
-               HTAB               *json_hash;
+               HTAB       *json_hash;
                JsonbContainer *jsonb_cont;
-       } val;
+       }                       val;
 } JsObject;
 
 /* useful macros for testing JsValue properties */
@@ -306,14 +316,14 @@ typedef struct JsObject
        ((jsv)->is_json ? (jsv)->val.json.type == JSON_TOKEN_STRING \
                : ((jsv)->val.jsonb && (jsv)->val.jsonb->type == jbvString))
 
-#define JsObjectSize(jso) \
+#define JsObjectIsEmpty(jso) \
        ((jso)->is_json \
-               ? hash_get_num_entries((jso)->val.json_hash) \
-               : !(jso)->val.jsonb_cont || JsonContainerSize((jso)->val.jsonb_cont))
+               ? hash_get_num_entries((jso)->val.json_hash) == 0 \
+               : ((jso)->val.jsonb_cont == NULL || \
+                  JsonContainerSize((jso)->val.jsonb_cont) == 0))
 
-#define JsObjectIsEmpty(jso) (JsObjectSize(jso) == 0)
-
-#define JsObjectFree(jso) do { \
+#define JsObjectFree(jso) \
+       do { \
                if ((jso)->is_json) \
                        hash_destroy((jso)->val.json_hash); \
        } while (0)
@@ -337,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);
@@ -348,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);
@@ -358,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);
@@ -404,66 +415,64 @@ 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_info,
-                                                                          HeapTupleHeader template, MemoryContext mcxt,
+static HeapTupleHeader populate_record(TupleDesc tupdesc, RecordIOData **record_p,
+                                                                          HeapTupleHeader defaultval, MemoryContext mcxt,
                                                                           JsObject *obj);
-static Datum populate_record_field(ColumnIOData *col, Oid typid, int32 typmod,
-                                                                  const char *colname, MemoryContext mcxt,
-                                                                  Datum defaultval, JsValue *jsv, bool *isnull);
+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, int32 typmod,
+static Datum populate_composite(CompositeIOData *io, Oid typid,
                                                                const char *colname, MemoryContext mcxt,
-                                                               HeapTupleHeader defaultval, JsValue *jsv);
+                                                               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 json);
+                                                                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);
-static RecordIOData * allocate_record_info(MemoryContext mcxt, int ncolumns);
+static RecordIOData *allocate_record_info(MemoryContext mcxt, int ncolumns);
 static bool JsObjectGetField(JsObject *obj, char *field, JsValue *jsv);
 static void populate_recordset_record(PopulateRecordsetState *state, JsObject *obj);
 static void populate_array_json(PopulateArrayContext *ctx, char *json, int len);
-static void populate_array_dim_jsonb(PopulateArrayContext  *ctx, JsonbValue *jbv,
+static void populate_array_dim_jsonb(PopulateArrayContext *ctx, JsonbValue *jbv,
                                                                         int ndim);
 static void populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim);
 static void populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims);
 static void populate_array_check_dimension(PopulateArrayContext *ctx, int ndim);
 static void populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv);
-static Datum populate_array(ArrayIOData           *aio, const char *colname,
-                                                       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);
+static Datum populate_array(ArrayIOData *aio, const char *colname,
+                                                       MemoryContext mcxt, JsValue *jsv);
+static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
+                                                        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_string_values */
-static void iterate_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
+/* function supporting iterate_json_values */
+static void iterate_values_scalar(void *state, char *token, JsonTokenType tokentype);
+static void iterate_values_object_field_start(void *state, char *fname, bool isnull);
 
 /* functions supporting transform_json_string_values */
 static void transform_string_values_object_start(void *state);
@@ -496,7 +505,7 @@ jsonb_object_keys(PG_FUNCTION_ARGS)
        if (SRF_IS_FIRSTCALL())
        {
                MemoryContext oldcontext;
-               Jsonb      *jb = PG_GETARG_JSONB(0);
+               Jsonb      *jb = PG_GETARG_JSONB_P(0);
                bool            skipNested = false;
                JsonbIterator *it;
                JsonbValue      v;
@@ -700,19 +709,21 @@ json_object_field(PG_FUNCTION_ARGS)
 Datum
 jsonb_object_field(PG_FUNCTION_ARGS)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       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(JsonbValueToJsonb(v));
+               PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
 
        PG_RETURN_NULL();
 }
@@ -736,50 +747,21 @@ json_object_field_text(PG_FUNCTION_ARGS)
 Datum
 jsonb_object_field_text(PG_FUNCTION_ARGS)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       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();
 }
@@ -802,7 +784,7 @@ json_array_element(PG_FUNCTION_ARGS)
 Datum
 jsonb_array_element(PG_FUNCTION_ARGS)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       Jsonb      *jb = PG_GETARG_JSONB_P(0);
        int                     element = PG_GETARG_INT32(1);
        JsonbValue *v;
 
@@ -822,7 +804,7 @@ jsonb_array_element(PG_FUNCTION_ARGS)
 
        v = getIthJsonbValueFromContainer(&jb->root, element);
        if (v != NULL)
-               PG_RETURN_JSONB(JsonbValueToJsonb(v));
+               PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
 
        PG_RETURN_NULL();
 }
@@ -845,7 +827,7 @@ json_array_element_text(PG_FUNCTION_ARGS)
 Datum
 jsonb_array_element_text(PG_FUNCTION_ARGS)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       Jsonb      *jb = PG_GETARG_JSONB_P(0);
        int                     element = PG_GETARG_INT32(1);
        JsonbValue *v;
 
@@ -864,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();
 }
@@ -1160,7 +1112,7 @@ get_object_field_end(void *state, char *fname, bool isnull)
        if (get_last && _state->result_start != NULL)
        {
                /*
-                * make a text object from the string from the prevously noted json
+                * make a text object from the string from the previously noted json
                 * start up to the end of the previous token (the lexer is by now
                 * ahead of us on whatever came after what we're interested in).
                 */
@@ -1372,9 +1324,8 @@ jsonb_extract_path_text(PG_FUNCTION_ARGS)
 static Datum
 get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       Jsonb      *jb = PG_GETARG_JSONB_P(0);
        ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
-       Jsonb      *res;
        Datum      *pathtext;
        bool       *pathnulls;
        int                     npath;
@@ -1382,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;
 
        /*
@@ -1432,7 +1383,7 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
                else
                {
                        /* not text mode - just hand back the jsonb */
-                       PG_RETURN_JSONB(jb);
+                       PG_RETURN_JSONB_P(jb);
                }
        }
 
@@ -1440,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)
                {
@@ -1494,43 +1445,79 @@ 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(res);
+               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;
        }
 }
 
@@ -1568,7 +1555,7 @@ json_array_length(PG_FUNCTION_ARGS)
 Datum
 jsonb_array_length(PG_FUNCTION_ARGS)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       Jsonb      *jb = PG_GETARG_JSONB_P(0);
 
        if (JB_ROOT_IS_SCALAR(jb))
                ereport(ERROR,
@@ -1658,7 +1645,7 @@ jsonb_each_text(PG_FUNCTION_ARGS)
 static Datum
 each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       Jsonb      *jb = PG_GETARG_JSONB_P(0);
        ReturnSetInfo *rsi;
        Tuplestorestate *tuple_store;
        TupleDesc       tupdesc;
@@ -1731,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);
 
@@ -1743,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
                        {
@@ -1973,7 +1942,7 @@ static Datum
 elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
                                          bool as_text)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       Jsonb      *jb = PG_GETARG_JSONB_P(0);
        ReturnSetInfo *rsi;
        Tuplestorestate *tuple_store;
        TupleDesc       tupdesc;
@@ -2038,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)
                                {
@@ -2053,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);
@@ -2158,7 +2109,7 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
        state->next_scalar = false;
        state->lex = lex;
        state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
-                                                                                "json_array_elements temporary cxt",
+                                                                                  "json_array_elements temporary cxt",
                                                                                   ALLOCSET_DEFAULT_SIZES);
 
        pg_parse_json(lex, sem);
@@ -2280,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 */
@@ -2310,17 +2265,17 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
                if (ctx->colname)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("expected json array"),
-                                        errhint("see the value of key \"%s\"", ctx->colname)));
+                                        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
        {
-               StringInfoData  indices;
-               int                             i;
+               StringInfoData indices;
+               int                     i;
 
                initStringInfo(&indices);
 
@@ -2332,14 +2287,14 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
                if (ctx->colname)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("expected json array"),
-                                        errhint("see the array element %s of key \"%s\"",
+                                        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"),
-                                        errhint("see the array element %s",
+                                        errmsg("expected JSON array"),
+                                        errhint("See the array element %s.",
                                                         indices.data)));
        }
 }
@@ -2348,7 +2303,7 @@ populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
 static void
 populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims)
 {
-       int             i;
+       int                     i;
 
        Assert(ctx->ndims <= 0);
 
@@ -2360,21 +2315,21 @@ populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims)
        ctx->sizes = palloc0(sizeof(int) * ndims);
 
        for (i = 0; i < ndims; i++)
-               ctx->dims[i] = -1; /* dimensions are unknown yet */
+               ctx->dims[i] = -1;              /* dimensions are unknown yet */
 }
 
 /* check the populated subarray dimension */
 static void
 populate_array_check_dimension(PopulateArrayContext *ctx, int ndim)
 {
-       int dim = ctx->sizes[ndim];     /* current dimension counter */
+       int                     dim = ctx->sizes[ndim]; /* current dimension counter */
 
        if (ctx->dims[ndim] == -1)
-               ctx->dims[ndim] = dim; /* assign dimension if not yet known */
+               ctx->dims[ndim] = dim;  /* assign dimension if not yet known */
        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.")));
 
@@ -2389,8 +2344,8 @@ populate_array_check_dimension(PopulateArrayContext *ctx, int ndim)
 static void
 populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv)
 {
-       Datum   element;
-       bool    element_isnull;
+       Datum           element;
+       bool            element_isnull;
 
        /* populate the array element */
        element = populate_record_field(ctx->aio->element_info,
@@ -2400,10 +2355,10 @@ populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv)
                                                                        jsv, &element_isnull);
 
        accumArrayResult(ctx->astate, element, element_isnull,
-                       ctx->aio->element_type, ctx->acxt);
+                                        ctx->aio->element_type, ctx->acxt);
 
        Assert(ndim > 0);
-       ctx->sizes[ndim - 1]++; /* increment current dimension counter */
+       ctx->sizes[ndim - 1]++;         /* increment current dimension counter */
 }
 
 /* json object start handler for populate_array_json() */
@@ -2411,7 +2366,7 @@ static void
 populate_array_object_start(void *_state)
 {
        PopulateArrayState *state = (PopulateArrayState *) _state;
-       int                                     ndim = state->lex->lex_level;
+       int                     ndim = state->lex->lex_level;
 
        if (state->ctx->ndims <= 0)
                populate_array_assign_ndims(state->ctx, ndim);
@@ -2423,9 +2378,9 @@ populate_array_object_start(void *_state)
 static void
 populate_array_array_end(void *_state)
 {
-       PopulateArrayState         *state = (PopulateArrayState *) _state;
-       PopulateArrayContext   *ctx = state->ctx;
-       int                                             ndim = state->lex->lex_level;
+       PopulateArrayState *state = (PopulateArrayState *) _state;
+       PopulateArrayContext *ctx = state->ctx;
+       int                     ndim = state->lex->lex_level;
 
        if (ctx->ndims <= 0)
                populate_array_assign_ndims(ctx, ndim + 1);
@@ -2439,7 +2394,7 @@ static void
 populate_array_element_start(void *_state, bool isnull)
 {
        PopulateArrayState *state = (PopulateArrayState *) _state;
-       int                                     ndim = state->lex->lex_level;
+       int                     ndim = state->lex->lex_level;
 
        if (state->ctx->ndims <= 0 || ndim == state->ctx->ndims)
        {
@@ -2454,9 +2409,9 @@ populate_array_element_start(void *_state, bool isnull)
 static void
 populate_array_element_end(void *_state, bool isnull)
 {
-       PopulateArrayState         *state = (PopulateArrayState *) _state;
-       PopulateArrayContext   *ctx = state->ctx;
-       int                                             ndim = state->lex->lex_level;
+       PopulateArrayState *state = (PopulateArrayState *) _state;
+       PopulateArrayContext *ctx = state->ctx;
+       int                     ndim = state->lex->lex_level;
 
        Assert(ctx->ndims > 0);
 
@@ -2493,9 +2448,9 @@ populate_array_element_end(void *_state, bool isnull)
 static void
 populate_array_scalar(void *_state, char *token, JsonTokenType tokentype)
 {
-       PopulateArrayState         *state = (PopulateArrayState *) _state;
-       PopulateArrayContext   *ctx = state->ctx;
-       int                                             ndim = state->lex->lex_level;
+       PopulateArrayState *state = (PopulateArrayState *) _state;
+       PopulateArrayContext *ctx = state->ctx;
+       int                     ndim = state->lex->lex_level;
 
        if (ctx->ndims <= 0)
                populate_array_assign_ndims(ctx, ndim);
@@ -2515,8 +2470,8 @@ populate_array_scalar(void *_state, char *token, JsonTokenType tokentype)
 static void
 populate_array_json(PopulateArrayContext *ctx, char *json, int len)
 {
-       PopulateArrayState      state;
-       JsonSemAction           sem;
+       PopulateArrayState state;
+       JsonSemAction sem;
 
        state.lex = makeJsonLexContextCstringLen(json, len, true);
        state.ctx = ctx;
@@ -2539,18 +2494,18 @@ populate_array_json(PopulateArrayContext *ctx, char *json, int len)
 
 /*
  * populate_array_dim_jsonb() -- Iterate recursively through jsonb sub-array
- *             elements and accumulate result using given ArrayBuildState.
+ *             elements and accumulate result using given ArrayBuildState.
  */
 static void
-populate_array_dim_jsonb(PopulateArrayContext  *ctx,   /* context */
-                                                JsonbValue                        *jbv,        /* jsonb sub-array */
-                                                int                                    ndim)   /* current dimension */
+populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
+                                                JsonbValue *jbv,       /* jsonb sub-array */
+                                                int ndim)      /* current dimension */
 {
-       JsonbContainer     *jbc = jbv->val.binary.data;
-       JsonbIterator      *it;
-       JsonbIteratorToken      tok;
-       JsonbValue                      val;
-       JsValue                         jsv;
+       JsonbContainer *jbc = jbv->val.binary.data;
+       JsonbIterator *it;
+       JsonbIteratorToken tok;
+       JsonbValue      val;
+       JsValue         jsv;
 
        check_stack_depth();
 
@@ -2567,9 +2522,9 @@ populate_array_dim_jsonb(PopulateArrayContext  *ctx,      /* context */
        tok = JsonbIteratorNext(&it, &val, true);
 
        /*
-        * If the number of dimensions is not yet known and
-        * we have found end of the array, or the first child element is not
-        *  an array, then assign the number of dimensions now.
+        * If the number of dimensions is not yet known and we have found end of
+        * the array, or the first child element is not an array, then assign the
+        * number of dimensions now.
         */
        if (ctx->ndims <= 0 &&
                (tok == WJB_END_ARRAY ||
@@ -2585,8 +2540,8 @@ populate_array_dim_jsonb(PopulateArrayContext  *ctx,      /* context */
        while (tok == WJB_ELEM)
        {
                /*
-                * Recurse only if the dimensions of dimensions is still unknown or
-                * if it is not the innermost dimension.
+                * Recurse only if the dimensions of dimensions is still unknown or if
+                * it is not the innermost dimension.
                 */
                if (ctx->ndims > 0 && ndim >= ctx->ndims)
                        populate_array_element(ctx, ndim, &jsv);
@@ -2613,29 +2568,29 @@ populate_array_dim_jsonb(PopulateArrayContext  *ctx,    /* context */
 
 /* recursively populate an array from json/jsonb */
 static Datum
-populate_array(ArrayIOData        *aio,
-                          const char      *colname,
-                          MemoryContext        mcxt,
-                          JsValue                 *jsv)
-{
-       PopulateArrayContext    ctx;
-       Datum                                   result;
-       int                                        *lbs;
-       int                                             i;
+populate_array(ArrayIOData *aio,
+                          const char *colname,
+                          MemoryContext mcxt,
+                          JsValue *jsv)
+{
+       PopulateArrayContext ctx;
+       Datum           result;
+       int                *lbs;
+       int                     i;
 
        ctx.aio = aio;
        ctx.mcxt = mcxt;
        ctx.acxt = CurrentMemoryContext;
        ctx.astate = initArrayResult(aio->element_type, ctx.acxt, true);
        ctx.colname = colname;
-       ctx.ndims = 0; /* unknown yet */
+       ctx.ndims = 0;                          /* unknown yet */
        ctx.dims = NULL;
        ctx.sizes = NULL;
 
        if (jsv->is_json)
                populate_array_json(&ctx, jsv->val.json.str,
                                                        jsv->val.json.len >= 0 ? jsv->val.json.len
-                                                                                                  : strlen(jsv->val.json.str));
+                                                       : strlen(jsv->val.json.str));
        else
        {
                populate_array_dim_jsonb(&ctx, jsv->val.jsonb, 1);
@@ -2644,7 +2599,7 @@ populate_array(ArrayIOData           *aio,
 
        Assert(ctx.ndims > 0);
 
-       lbs  = palloc(sizeof(int) * ctx.ndims);
+       lbs = palloc(sizeof(int) * ctx.ndims);
 
        for (i = 0; i < ctx.ndims; i++)
                lbs[i] = 1;
@@ -2668,11 +2623,11 @@ JsValueToJsObject(JsValue *jsv, JsObject *jso)
        {
                /* convert plain-text json into a hash table */
                jso->val.json_hash =
-                               get_json_object_as_hash(jsv->val.json.str,
-                                                                               jsv->val.json.len >= 0
-                                                                                        ? jsv->val.json.len
-                                                                                        : strlen(jsv->val.json.str),
-                                                                               "populate_composite");
+                       get_json_object_as_hash(jsv->val.json.str,
+                                                                       jsv->val.json.len >= 0
+                                                                       ? jsv->val.json.len
+                                                                       : strlen(jsv->val.json.str),
+                                                                       "populate_composite");
        }
        else
        {
@@ -2680,32 +2635,38 @@ JsValueToJsObject(JsValue *jsv, JsObject *jso)
 
                if (jbv->type == jbvBinary &&
                        JsonContainerIsObject(jbv->val.binary.data))
+               {
                        jso->val.jsonb_cont = jbv->val.binary.data;
+               }
                else
-                       jso->val.jsonb_cont = NULL;
+               {
+                       bool            is_scalar;
+
+                       is_scalar = IsAJsonbScalar(jbv) ||
+                               (jbv->type == jbvBinary &&
+                                JsonContainerIsScalar(jbv->val.binary.data));
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        is_scalar
+                                        ? errmsg("cannot call %s on a scalar",
+                                                         "populate_composite")
+                                        : errmsg("cannot call %s on an array",
+                                                         "populate_composite")));
+               }
        }
 }
 
-/* recursively populate a composite (row type) value from json/jsonb */
-static Datum
-populate_composite(CompositeIOData *io,
-                                  Oid                          typid,
-                                  int32                        typmod,
-                                  const char      *colname,
-                                  MemoryContext        mcxt,
-                                  HeapTupleHeader      defaultval,
-                                  JsValue                 *jsv)
+/* acquire or update cached tuple descriptor for a composite type */
+static void
+update_cached_tupdesc(CompositeIOData *io, MemoryContext mcxt)
 {
-       HeapTupleHeader tuple;
-       JsObject                jso;
-
-       /* acquire cached tuple descriptor */
        if (!io->tupdesc ||
-               io->tupdesc->tdtypeid != typid ||
-               io->tupdesc->tdtypmod != typmod)
+               io->tupdesc->tdtypeid != io->base_typid ||
+               io->tupdesc->tdtypmod != io->base_typmod)
        {
-               TupleDesc               tupdesc = lookup_rowtype_tupdesc(typid, typmod);
-               MemoryContext   oldcxt;
+               TupleDesc       tupdesc = lookup_rowtype_tupdesc(io->base_typid,
+                                                                                                        io->base_typmod);
+               MemoryContext oldcxt;
 
                if (io->tupdesc)
                        FreeTupleDesc(io->tupdesc);
@@ -2717,17 +2678,50 @@ populate_composite(CompositeIOData *io,
 
                ReleaseTupleDesc(tupdesc);
        }
+}
+
+/* recursively populate a composite (row type) value from json/jsonb */
+static Datum
+populate_composite(CompositeIOData *io,
+                                  Oid typid,
+                                  const char *colname,
+                                  MemoryContext mcxt,
+                                  HeapTupleHeader defaultval,
+                                  JsValue *jsv,
+                                  bool isnull)
+{
+       Datum           result;
+
+       /* acquire/update cached tuple descriptor */
+       update_cached_tupdesc(io, mcxt);
 
-       /* prepare input value */
-       JsValueToJsObject(jsv, &jso);
+       if (isnull)
+               result = (Datum) 0;
+       else
+       {
+               HeapTupleHeader tuple;
+               JsObject        jso;
 
-       /* populate resulting record tuple */
-       tuple = populate_record(io->tupdesc, &io->record_io,
-                                                       defaultval, mcxt, &jso);
+               /* prepare input value */
+               JsValueToJsObject(jsv, &jso);
+
+               /* populate resulting record tuple */
+               tuple = populate_record(io->tupdesc, &io->record_io,
+                                                               defaultval, mcxt, &jso);
+               result = HeapTupleHeaderGetDatum(tuple);
+
+               JsObjectFree(&jso);
+       }
 
-       JsObjectFree(&jso);
+       /*
+        * If it's domain over composite, check domain constraints.  (This should
+        * probably get refactored so that we can see the TYPECAT value, but for
+        * now, we can tell by comparing typid to base_typid.)
+        */
+       if (typid != io->base_typid && typid != RECORDOID)
+               domain_check(result, isnull, typid, &io->domain_info, mcxt);
 
-       return HeapTupleHeaderGetDatum(tuple);
+       return result;
 }
 
 /* populate non-null scalar value from json/jsonb value */
@@ -2744,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));
@@ -2771,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
        {
@@ -2779,8 +2768,9 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv)
 
                if (typid == JSONBOID)
                {
-                       Jsonb *jsonb = JsonbValueToJsonb(jbv); /* directly use jsonb */
-                       return JsonbGetDatum(jsonb);
+                       Jsonb      *jsonb = JsonbValueToJsonb(jbv); /* directly use jsonb */
+
+                       return JsonbPGetDatum(jsonb);
                }
                /* convert jsonb to string for typio call */
                else if (typid == JSONOID && jbv->type != jbvBinary)
@@ -2789,19 +2779,20 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv)
                         * Convert scalar jsonb (non-scalars are passed here as jbvBinary)
                         * to json string, preserving quotes around top-level strings.
                         */
-                       Jsonb *jsonb = JsonbValueToJsonb(jbv);
+                       Jsonb      *jsonb = JsonbValueToJsonb(jbv);
+
                        str = JsonbToCString(NULL, &jsonb->root, VARSIZE(jsonb));
                }
-               else if (jbv->type == jbvString) /* quotes are stripped */
+               else if (jbv->type == jbvString)        /* quotes are stripped */
                        str = pnstrdup(jbv->val.string.val, jbv->val.string.len);
                else if (jbv->type == jbvBool)
                        str = pstrdup(jbv->val.boolean ? "true" : "false");
                else if (jbv->type == jbvNumeric)
                        str = DatumGetCString(DirectFunctionCall1(numeric_out,
-                                                                               PointerGetDatum(jbv->val.numeric)));
+                                                                                                         PointerGetDatum(jbv->val.numeric)));
                else if (jbv->type == jbvBinary)
                        str = JsonbToCString(NULL, jbv->val.binary.data,
-                                                                          jbv->val.binary.len);
+                                                                jbv->val.binary.len);
                else
                        elog(ERROR, "unrecognized jsonb type: %d", (int) jbv->type);
        }
@@ -2816,12 +2807,12 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv)
 }
 
 static Datum
-populate_domain(DomainIOData   *io,
-                               Oid                             typid,
-                               const char         *colname,
-                               MemoryContext   mcxt,
-                               JsValue            *jsv,
-                               bool                    isnull)
+populate_domain(DomainIOData *io,
+                               Oid typid,
+                               const char *colname,
+                               MemoryContext mcxt,
+                               JsValue *jsv,
+                               bool isnull)
 {
        Datum           res;
 
@@ -2843,14 +2834,14 @@ populate_domain(DomainIOData   *io,
 
 /* prepare column metadata cache for the given type */
 static void
-prepare_column_cache(ColumnIOData  *column,
-                                        Oid                    typid,
-                                        int32                  typmod,
-                                        MemoryContext  mcxt,
-                                        bool                   json)
+prepare_column_cache(ColumnIOData *column,
+                                        Oid typid,
+                                        int32 typmod,
+                                        MemoryContext mcxt,
+                                        bool need_scalar)
 {
-       HeapTuple               tup;
-       Form_pg_type    type;
+       HeapTuple       tup;
+       Form_pg_type type;
 
        column->typid = typid;
        column->typmod = typmod;
@@ -2863,35 +2854,63 @@ prepare_column_cache(ColumnIOData  *column,
 
        if (type->typtype == TYPTYPE_DOMAIN)
        {
-               column->typcat = TYPECAT_DOMAIN;
-               column->io.domain.base_typid = type->typbasetype;
-               column->io.domain.base_typmod = type->typtypmod;
-               column->io.domain.base_io = MemoryContextAllocZero(mcxt,
-                                                                                                         sizeof(ColumnIOData));
-               column->io.domain.domain_info = NULL;
+               /*
+                * We can move directly to the bottom base type; domain_check() will
+                * take care of checking all constraints for a stack of domains.
+                */
+               Oid                     base_typid;
+               int32           base_typmod = typmod;
+
+               base_typid = getBaseTypeAndTypmod(typid, &base_typmod);
+               if (get_typtype(base_typid) == TYPTYPE_COMPOSITE)
+               {
+                       /* domain over composite has its own code path */
+                       column->typcat = TYPECAT_COMPOSITE_DOMAIN;
+                       column->io.composite.record_io = NULL;
+                       column->io.composite.tupdesc = NULL;
+                       column->io.composite.base_typid = base_typid;
+                       column->io.composite.base_typmod = base_typmod;
+                       column->io.composite.domain_info = NULL;
+               }
+               else
+               {
+                       /* domain over anything else */
+                       column->typcat = TYPECAT_DOMAIN;
+                       column->io.domain.base_typid = base_typid;
+                       column->io.domain.base_typmod = base_typmod;
+                       column->io.domain.base_io =
+                               MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
+                       column->io.domain.domain_info = NULL;
+               }
        }
        else if (type->typtype == TYPTYPE_COMPOSITE || typid == RECORDOID)
        {
                column->typcat = TYPECAT_COMPOSITE;
                column->io.composite.record_io = NULL;
                column->io.composite.tupdesc = NULL;
+               column->io.composite.base_typid = typid;
+               column->io.composite.base_typmod = typmod;
+               column->io.composite.domain_info = NULL;
        }
        else if (type->typlen == -1 && OidIsValid(type->typelem))
        {
                column->typcat = TYPECAT_ARRAY;
                column->io.array.element_info = MemoryContextAllocZero(mcxt,
-                                                                                                               sizeof(ColumnIOData));
+                                                                                                                          sizeof(ColumnIOData));
                column->io.array.element_type = type->typelem;
                /* array element typemod stored in attribute's typmod */
                column->io.array.element_typmod = typmod;
        }
        else
+       {
                column->typcat = TYPECAT_SCALAR;
+               need_scalar = true;
+       }
 
-       /* don't need input function when converting from jsonb to jsonb */
-       if (json || typid != JSONBOID)
+       /* caller can force us to look up scalar_io info even for non-scalars */
+       if (need_scalar)
        {
-               Oid             typioproc;
+               Oid                     typioproc;
 
                getTypeInputInfo(typid, &typioproc, &column->scalar_io.typioparam);
                fmgr_info_cxt(typioproc, &column->scalar_io.typiofunc, mcxt);
@@ -2903,21 +2922,24 @@ prepare_column_cache(ColumnIOData  *column,
 /* recursively populate a record field or an array element from a json/jsonb value */
 static Datum
 populate_record_field(ColumnIOData *col,
-                                         Oid                   typid,
-                                         int32                 typmod,
-                                         const char   *colname,
-                                         MemoryContext mcxt,
-                                         Datum                 defaultval,
-                                         JsValue          *jsv,
-                                         bool             *isnull)
+                                         Oid typid,
+                                         int32 typmod,
+                                         const char *colname,
+                                         MemoryContext mcxt,
+                                         Datum defaultval,
+                                         JsValue *jsv,
+                                         bool *isnull)
 {
        TypeCat         typcat;
 
        check_stack_depth();
 
-       /* prepare column metadata cache for the given type */
+       /*
+        * Prepare column metadata cache for the given type.  Force lookup of the
+        * scalar_io data so that the json string hack below will work.
+        */
        if (col->typid != typid || col->typmod != typmod)
-               prepare_column_cache(col, typid, typmod, mcxt, jsv->is_json);
+               prepare_column_cache(col, typid, typmod, mcxt, true);
 
        *isnull = JsValueIsNull(jsv);
 
@@ -2925,11 +2947,15 @@ populate_record_field(ColumnIOData *col,
 
        /* try to convert json string to a non-scalar type through input function */
        if (JsValueIsString(jsv) &&
-               (typcat == TYPECAT_ARRAY || typcat == TYPECAT_COMPOSITE))
+               (typcat == TYPECAT_ARRAY ||
+                typcat == TYPECAT_COMPOSITE ||
+                typcat == TYPECAT_COMPOSITE_DOMAIN))
                typcat = TYPECAT_SCALAR;
 
-       /* we must perform domain checks for NULLs */
-       if (*isnull && typcat != TYPECAT_DOMAIN)
+       /* we must perform domain checks for NULLs, otherwise exit immediately */
+       if (*isnull &&
+               typcat != TYPECAT_DOMAIN &&
+               typcat != TYPECAT_COMPOSITE_DOMAIN)
                return (Datum) 0;
 
        switch (typcat)
@@ -2941,12 +2967,13 @@ populate_record_field(ColumnIOData *col,
                        return populate_array(&col->io.array, colname, mcxt, jsv);
 
                case TYPECAT_COMPOSITE:
-                       return populate_composite(&col->io.composite, typid, typmod,
+               case TYPECAT_COMPOSITE_DOMAIN:
+                       return populate_composite(&col->io.composite, typid,
                                                                          colname, mcxt,
                                                                          DatumGetPointer(defaultval)
                                                                          ? DatumGetHeapTupleHeader(defaultval)
                                                                          : NULL,
-                                                                         jsv);
+                                                                         jsv, *isnull);
 
                case TYPECAT_DOMAIN:
                        return populate_domain(&col->io.domain, typid, colname, mcxt,
@@ -2962,9 +2989,9 @@ static RecordIOData *
 allocate_record_info(MemoryContext mcxt, int ncolumns)
 {
        RecordIOData *data = (RecordIOData *)
-                       MemoryContextAlloc(mcxt,
-                                                               offsetof(RecordIOData, columns) +
-                                                               ncolumns * sizeof(ColumnIOData));
+       MemoryContextAlloc(mcxt,
+                                          offsetof(RecordIOData, columns) +
+                                          ncolumns * sizeof(ColumnIOData));
 
        data->record_type = InvalidOid;
        data->record_typmod = 0;
@@ -2986,7 +3013,7 @@ JsObjectGetField(JsObject *obj, char *field, JsValue *jsv)
 
                jsv->val.json.type = hashentry ? hashentry->type : JSON_TOKEN_NULL;
                jsv->val.json.str = jsv->val.json.type == JSON_TOKEN_NULL ? NULL :
-                                                                                                                       hashentry->val;
+                       hashentry->val;
                jsv->val.json.len = jsv->val.json.str ? -1 : 0; /* null-terminated */
 
                return hashentry != NULL;
@@ -2994,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;
        }
@@ -3003,23 +3030,23 @@ JsObjectGetField(JsObject *obj, char *field, JsValue *jsv)
 
 /* populate a record tuple from json/jsonb value */
 static HeapTupleHeader
-populate_record(TupleDesc              tupdesc,
-                               RecordIOData  **precord,
-                               HeapTupleHeader defaultval,
-                               MemoryContext   mcxt,
-                               JsObject           *obj)
-{
-       RecordIOData   *record = *precord;
-       Datum              *values;
-       bool               *nulls;
-       HeapTuple               res;
-       int                             ncolumns = tupdesc->natts;
-       int                             i;
+populate_record(TupleDesc tupdesc,
+                               RecordIOData **record_p,
+                               HeapTupleHeader defaultval,
+                               MemoryContext mcxt,
+                               JsObject *obj)
+{
+       RecordIOData *record = *record_p;
+       Datum      *values;
+       bool       *nulls;
+       HeapTuple       res;
+       int                     ncolumns = tupdesc->natts;
+       int                     i;
 
        /*
-        * if the input json is empty, we can only skip the rest if we were
-        * passed in a non-null record, since otherwise there may be issues
-        * with domain nulls.
+        * if the input json is empty, we can only skip the rest if we were passed
+        * in a non-null record, since otherwise there may be issues with domain
+        * nulls.
         */
        if (defaultval && JsObjectIsEmpty(obj))
                return defaultval;
@@ -3027,14 +3054,14 @@ populate_record(TupleDesc               tupdesc,
        /* (re)allocate metadata cache */
        if (record == NULL ||
                record->ncolumns != ncolumns)
-               *precord = record = allocate_record_info(mcxt, ncolumns);
+               *record_p = record = allocate_record_info(mcxt, ncolumns);
 
        /* invalidate metadata cache if the record type has changed */
        if (record->record_type != tupdesc->tdtypeid ||
                record->record_typmod != tupdesc->tdtypmod)
        {
                MemSet(record, 0, offsetof(RecordIOData, columns) +
-                                                       ncolumns * sizeof(ColumnIOData));
+                          ncolumns * sizeof(ColumnIOData));
                record->record_type = tupdesc->tdtypeid;
                record->record_typmod = tupdesc->tdtypmod;
                record->ncolumns = ncolumns;
@@ -3067,10 +3094,10 @@ populate_record(TupleDesc               tupdesc,
 
        for (i = 0; i < ncolumns; ++i)
        {
-               Form_pg_attribute       att = tupdesc->attrs[i];
-               char                       *colname = NameStr(att->attname);
-               JsValue                         field = { 0 };
-               bool                            found;
+               Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+               char       *colname = NameStr(att->attname);
+               JsValue         field = {0};
+               bool            found;
 
                /* Ignore dropped columns in datatype */
                if (att->attisdropped)
@@ -3110,111 +3137,159 @@ 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 = NULL;
-       Oid                     tupType;
-       int32           tupTypmod;
-       TupleDesc       tupdesc = NULL;
+       JsValue         jsv = {0};
+       HeapTupleHeader rec;
        Datum           rettuple;
        JsonbValue      jbv;
        MemoryContext fnmcxt = fcinfo->flinfo->fn_mcxt;
        PopulateRecordCache *cache = fcinfo->flinfo->fn_extra;
 
-       Assert(jtype == JSONOID || jtype == JSONBOID);
-
        /*
-        * We arrange to look up the needed I/O info just once per series of
-        * calls, assuming the record type doesn't change underneath us.
+        * If first time through, identify input/result record type.  Note that
+        * this stanza looks only at fcinfo context, which can't change during the
+        * query; so we may not be able to fully resolve a RECORD input type yet.
         */
        if (!cache)
-               fcinfo->flinfo->fn_extra = cache =
-                               MemoryContextAllocZero(fnmcxt, sizeof(*cache));
-
-       if (have_record_arg)
        {
-               Oid                     argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
-
-               if (cache->argtype != argtype)
-               {
-                       if (!type_is_rowtype(argtype))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                errmsg("first argument of %s must be a row type",
-                                                               funcname)));
-
-                       cache->argtype = argtype;
-               }
-
-               if (PG_ARGISNULL(0))
-               {
-                       if (PG_ARGISNULL(1))
-                               PG_RETURN_NULL();
+               fcinfo->flinfo->fn_extra = cache =
+                       MemoryContextAllocZero(fnmcxt, sizeof(*cache));
+               cache->fn_mcxt = fnmcxt;
 
-                       /*
-                        * We have no tuple to look at, so the only source of type info is
-                        * the argtype. The lookup_rowtype_tupdesc call below will error
-                        * out if we don't have a known composite type oid here.
-                        */
-                       tupType = argtype;
-                       tupTypmod = -1;
-               }
+               if (have_record_arg)
+                       get_record_type_from_argument(fcinfo, funcname, cache);
                else
-               {
-                       rec = PG_GETARG_HEAPTUPLEHEADER(0);
+                       get_record_type_from_query(fcinfo, funcname, cache);
+       }
 
-                       if (PG_ARGISNULL(1))
-                               PG_RETURN_POINTER(rec);
+       /* Collect record arg if we have one */
+       if (!have_record_arg)
+               rec = NULL;                             /* it's json{b}_to_record() */
+       else if (!PG_ARGISNULL(0))
+       {
+               rec = PG_GETARG_HEAPTUPLEHEADER(0);
 
-                       /* Extract type info from the tuple itself */
-                       tupType = HeapTupleHeaderGetTypeId(rec);
-                       tupTypmod = HeapTupleHeaderGetTypMod(rec);
+               /*
+                * When declared arg type is RECORD, identify actual record type from
+                * the tuple itself.
+                */
+               if (cache->argtype == RECORDOID)
+               {
+                       cache->c.io.composite.base_typid = HeapTupleHeaderGetTypeId(rec);
+                       cache->c.io.composite.base_typmod = HeapTupleHeaderGetTypMod(rec);
                }
        }
        else
        {
-               /* json{b}_to_record case */
-               if (PG_ARGISNULL(0))
-                       PG_RETURN_NULL();
-
-               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);
+               rec = NULL;
 
                /*
-                * Add tupdesc to the cache and set the appropriate values of
-                * tupType/tupTypmod for proper cache usage in populate_composite().
+                * When declared arg type is RECORD, identify actual record type from
+                * calling query, or fail if we can't.
                 */
-               cache->io.tupdesc = tupdesc;
+               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);
+               }
+       }
 
-               tupType = tupdesc->tdtypeid;
-               tupTypmod = tupdesc->tdtypmod;
+       /* If no JSON argument, just return the record (if any) unchanged */
+       if (PG_ARGISNULL(json_arg_num))
+       {
+               if (rec)
+                       PG_RETURN_POINTER(rec);
+               else
+                       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);
 
                jsv.val.json.str = VARDATA_ANY(json);
                jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
-               jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in populate_composite() */
+               jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in
+                                                                                                * populate_composite() */
        }
        else
        {
-               Jsonb      *jb = PG_GETARG_JSONB(json_arg_num);
+               Jsonb      *jb = PG_GETARG_JSONB_P(json_arg_num);
 
                jsv.val.jsonb = &jbv;
 
@@ -3224,14 +3299,8 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
                jbv.val.binary.len = VARSIZE(jb) - VARHDRSZ;
        }
 
-       rettuple = populate_composite(&cache->io, tupType, tupTypmod,
-                                                                 NULL, fnmcxt, rec, &jsv);
-
-       if (tupdesc)
-       {
-               cache->io.tupdesc = NULL;
-               ReleaseTupleDesc(tupdesc);
-       }
+       rettuple = populate_composite(&cache->c.io.composite, cache->argtype,
+                                                                 NULL, fnmcxt, rec, &jsv, false);
 
        PG_RETURN_DATUM(rettuple);
 }
@@ -3358,7 +3427,7 @@ hash_array_start(void *state)
        if (_state->lex->lex_level == 0)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                          errmsg("cannot call %s on an array", _state->function_name)));
+                                errmsg("cannot call %s on an array", _state->function_name)));
 }
 
 static void
@@ -3369,7 +3438,7 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
        if (_state->lex->lex_level == 0)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                          errmsg("cannot call %s on a scalar", _state->function_name)));
+                                errmsg("cannot call %s on a scalar", _state->function_name)));
 
        if (_state->lex->lex_level == 1)
        {
@@ -3393,37 +3462,56 @@ 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)
 {
-       HeapTupleData   tuple;
-       HeapTupleHeader tuphead = populate_record(state->ret_tdesc,
-                                                                                         state->my_extra,
-                                                                                         state->rec,
-                                                                                         state->fn_mcxt,
-                                                                                         obj);
+       PopulateRecordCache *cache = state->cache;
+       HeapTupleHeader tuphead;
+       HeapTupleData tuple;
+
+       /* acquire/update cached tuple descriptor */
+       update_cached_tupdesc(&cache->c.io.composite, cache->fn_mcxt);
+
+       /* replace record fields from json */
+       tuphead = populate_record(cache->c.io.composite.tupdesc,
+                                                         &cache->c.io.composite.record_io,
+                                                         state->rec,
+                                                         cache->fn_mcxt,
+                                                         obj);
 
+       /* if it's domain over composite, check domain constraints */
+       if (cache->c.typcat == TYPECAT_COMPOSITE_DOMAIN)
+               domain_check(HeapTupleHeaderGetDatum(tuphead), false,
+                                        cache->argtype,
+                                        &cache->c.io.composite.domain_info,
+                                        cache->fn_mcxt);
+
+       /* ok, save into tuplestore */
        tuple.t_len = HeapTupleHeaderGetDatumLength(tuphead);
        ItemPointerSetInvalid(&(tuple.t_self));
        tuple.t_tableOid = InvalidOid;
@@ -3433,36 +3521,24 @@ 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;
-       TupleDesc       tupdesc;
+       PopulateRecordCache *cache = fcinfo->flinfo->fn_extra;
        PopulateRecordsetState *state;
 
-       if (have_record_arg)
-       {
-               Oid                     argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
-
-               if (!type_is_rowtype(argtype))
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                        errmsg("first argument of %s must be a row type",
-                                                       funcname)));
-       }
-
        rsi = (ReturnSetInfo *) fcinfo->resultinfo;
 
        if (!rsi || !IsA(rsi, ReturnSetInfo) ||
-               (rsi->allowedModes & SFRM_Materialize) == 0 ||
-               rsi->expectedDesc == NULL)
+               (rsi->allowedModes & SFRM_Materialize) == 0)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("set-valued function called in context that "
@@ -3471,42 +3547,79 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
        rsi->returnMode = SFRM_Materialize;
 
        /*
-        * get the tupdesc from the result set info - it must be a record type
-        * because we already checked that arg1 is a record type, or we're in a
-        * to_record function which returns a setof record.
+        * If first time through, identify input/result record type.  Note that
+        * this stanza looks only at fcinfo context, which can't change during the
+        * query; so we may not be able to fully resolve a RECORD input type yet.
         */
-       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")));
+       if (!cache)
+       {
+               fcinfo->flinfo->fn_extra = cache =
+                       MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt, sizeof(*cache));
+               cache->fn_mcxt = fcinfo->flinfo->fn_mcxt;
+
+               if (have_record_arg)
+                       get_record_type_from_argument(fcinfo, funcname, cache);
+               else
+                       get_record_type_from_query(fcinfo, funcname, cache);
+       }
+
+       /* Collect record arg if we have one */
+       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.
+                */
+               if (cache->argtype == RECORDOID)
+               {
+                       cache->c.io.composite.base_typid = HeapTupleHeaderGetTypeId(rec);
+                       cache->c.io.composite.base_typmod = HeapTupleHeaderGetTypMod(rec);
+               }
+       }
+       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();
 
-       if (!have_record_arg || PG_ARGISNULL(0))
-               rec = NULL;
-       else
-               rec = PG_GETARG_HEAPTUPLEHEADER(0);
+       /*
+        * 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 these in a sufficiently long-lived memory context */
+       /* make tuplestore in a sufficiently long-lived memory context */
        old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-       state->ret_tdesc = CreateTupleDescCopy(tupdesc);
-       BlessTupleDesc(state->ret_tdesc);
        state->tuple_store = tuplestore_begin_heap(rsi->allowedModes &
                                                                                           SFRM_Materialize_Random,
                                                                                           false, work_mem);
        MemoryContextSwitchTo(old_cxt);
 
        state->function_name = funcname;
-       state->my_extra = (RecordIOData **) &fcinfo->flinfo->fn_extra;
+       state->cache = cache;
        state->rec = rec;
-       state->fn_mcxt = fcinfo->flinfo->fn_mcxt;
 
-       if (jtype == JSONOID)
+       if (is_json)
        {
                text       *json = PG_GETARG_TEXT_PP(json_arg_num);
                JsonLexContext *lex;
@@ -3531,14 +3644,12 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
        }
        else
        {
-               Jsonb      *jb = PG_GETARG_JSONB(json_arg_num);
+               Jsonb      *jb = PG_GETARG_JSONB_P(json_arg_num);
                JsonbIterator *it;
                JsonbValue      v;
                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),
@@ -3559,8 +3670,8 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
                                        !JsonContainerIsObject(v.val.binary.data))
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("argument of %s must be an array of objects",
-                                                               funcname)));
+                                                        errmsg("argument of %s must be an array of objects",
+                                                                       funcname)));
 
                                obj.is_json = false;
                                obj.val.jsonb_cont = v.val.binary.data;
@@ -3570,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 = state->ret_tdesc;
+       rsi->setDesc = CreateTupleDescCopy(cache->c.io.composite.tupdesc);
 
        PG_RETURN_NULL();
 }
@@ -3730,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.
  *
@@ -3883,7 +3983,7 @@ json_strip_nulls(PG_FUNCTION_ARGS)
 Datum
 jsonb_strip_nulls(PG_FUNCTION_ARGS)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       Jsonb      *jb = PG_GETARG_JSONB_P(0);
        JsonbIterator *it;
        JsonbParseState *parseState = NULL;
        JsonbValue *res = NULL;
@@ -3956,8 +4056,9 @@ addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
 
        if (JB_ROOT_IS_SCALAR(jb))
        {
-               (void) JsonbIteratorNext(&it, &v, false);               /* skip array header */
-               (void) JsonbIteratorNext(&it, &v, false);               /* fetch scalar value */
+               (void) JsonbIteratorNext(&it, &v, false);       /* skip array header */
+               Assert(v.type == jbvArray);
+               (void) JsonbIteratorNext(&it, &v, false);       /* fetch scalar value */
 
                switch (o->type)
                {
@@ -3992,7 +4093,7 @@ addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
 Datum
 jsonb_pretty(PG_FUNCTION_ARGS)
 {
-       Jsonb      *jb = PG_GETARG_JSONB(0);
+       Jsonb      *jb = PG_GETARG_JSONB_P(0);
        StringInfo      str = makeStringInfo();
 
        JsonbToCStringIndent(str, &jb->root, VARSIZE(jb));
@@ -4008,8 +4109,8 @@ jsonb_pretty(PG_FUNCTION_ARGS)
 Datum
 jsonb_concat(PG_FUNCTION_ARGS)
 {
-       Jsonb      *jb1 = PG_GETARG_JSONB(0);
-       Jsonb      *jb2 = PG_GETARG_JSONB(1);
+       Jsonb      *jb1 = PG_GETARG_JSONB_P(0);
+       Jsonb      *jb2 = PG_GETARG_JSONB_P(1);
        JsonbParseState *state = NULL;
        JsonbValue *res;
        JsonbIterator *it1,
@@ -4024,9 +4125,9 @@ jsonb_concat(PG_FUNCTION_ARGS)
        if (JB_ROOT_IS_OBJECT(jb1) == JB_ROOT_IS_OBJECT(jb2))
        {
                if (JB_ROOT_COUNT(jb1) == 0 && !JB_ROOT_IS_SCALAR(jb2))
-                       PG_RETURN_JSONB(jb2);
+                       PG_RETURN_JSONB_P(jb2);
                else if (JB_ROOT_COUNT(jb2) == 0 && !JB_ROOT_IS_SCALAR(jb1))
-                       PG_RETURN_JSONB(jb1);
+                       PG_RETURN_JSONB_P(jb1);
        }
 
        it1 = JsonbIteratorInit(&jb1->root);
@@ -4036,7 +4137,7 @@ jsonb_concat(PG_FUNCTION_ARGS)
 
        Assert(res != NULL);
 
-       PG_RETURN_JSONB(JsonbValueToJsonb(res));
+       PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
 }
 
 
@@ -4049,7 +4150,7 @@ jsonb_concat(PG_FUNCTION_ARGS)
 Datum
 jsonb_delete(PG_FUNCTION_ARGS)
 {
-       Jsonb      *in = PG_GETARG_JSONB(0);
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
        text       *key = PG_GETARG_TEXT_PP(1);
        char       *keyptr = VARDATA_ANY(key);
        int                     keylen = VARSIZE_ANY_EXHDR(key);
@@ -4066,11 +4167,11 @@ jsonb_delete(PG_FUNCTION_ARGS)
                                 errmsg("cannot delete from scalar")));
 
        if (JB_ROOT_COUNT(in) == 0)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        it = JsonbIteratorInit(&in->root);
 
-       while ((r = JsonbIteratorNext(&it, &v, skipNested)) != 0)
+       while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
        {
                skipNested = true;
 
@@ -4080,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;
                }
@@ -4090,7 +4191,7 @@ jsonb_delete(PG_FUNCTION_ARGS)
 
        Assert(res != NULL);
 
-       PG_RETURN_JSONB(JsonbValueToJsonb(res));
+       PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
 }
 
 /*
@@ -4102,7 +4203,7 @@ jsonb_delete(PG_FUNCTION_ARGS)
 Datum
 jsonb_delete_array(PG_FUNCTION_ARGS)
 {
-       Jsonb      *in = PG_GETARG_JSONB(0);
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
        ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
        Datum      *keys_elems;
        bool       *keys_nulls;
@@ -4125,17 +4226,17 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
                                 errmsg("cannot delete from scalar")));
 
        if (JB_ROOT_COUNT(in) == 0)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        deconstruct_array(keys, TEXTOID, -1, false, 'i',
                                          &keys_elems, &keys_nulls, &keys_len);
 
        if (keys_len == 0)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        it = JsonbIteratorInit(&in->root);
 
-       while ((r = JsonbIteratorNext(&it, &v, skipNested)) != 0)
+       while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
        {
                skipNested = true;
 
@@ -4165,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;
                        }
@@ -4176,7 +4277,7 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
 
        Assert(res != NULL);
 
-       PG_RETURN_JSONB(JsonbValueToJsonb(res));
+       PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
 }
 
 /*
@@ -4189,7 +4290,7 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
 Datum
 jsonb_delete_idx(PG_FUNCTION_ARGS)
 {
-       Jsonb      *in = PG_GETARG_JSONB(0);
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
        int                     idx = PG_GETARG_INT32(1);
        JsonbParseState *state = NULL;
        JsonbIterator *it;
@@ -4210,7 +4311,7 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
                                 errmsg("cannot delete from object using integer index")));
 
        if (JB_ROOT_COUNT(in) == 0)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        it = JsonbIteratorInit(&in->root);
 
@@ -4227,11 +4328,11 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
        }
 
        if (idx >= n)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        pushJsonbValue(&state, r, NULL);
 
-       while ((r = JsonbIteratorNext(&it, &v, true)) != 0)
+       while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
        {
                if (r == WJB_ELEM)
                {
@@ -4244,19 +4345,18 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
 
        Assert(res != NULL);
 
-       PG_RETURN_JSONB(JsonbValueToJsonb(res));
+       PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
 }
 
 /*
  * SQL function jsonb_set(jsonb, text[], jsonb, boolean)
- *
  */
 Datum
 jsonb_set(PG_FUNCTION_ARGS)
 {
-       Jsonb      *in = PG_GETARG_JSONB(0);
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
        ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
-       Jsonb      *newval = PG_GETARG_JSONB(2);
+       Jsonb      *newval = PG_GETARG_JSONB_P(2);
        bool            create = PG_GETARG_BOOL(3);
        JsonbValue *res = NULL;
        Datum      *path_elems;
@@ -4276,13 +4376,13 @@ jsonb_set(PG_FUNCTION_ARGS)
                                 errmsg("cannot set path in scalar")));
 
        if (JB_ROOT_COUNT(in) == 0 && !create)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        deconstruct_array(path, TEXTOID, -1, false, 'i',
                                          &path_elems, &path_nulls, &path_len);
 
        if (path_len == 0)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        it = JsonbIteratorInit(&in->root);
 
@@ -4291,7 +4391,7 @@ jsonb_set(PG_FUNCTION_ARGS)
 
        Assert(res != NULL);
 
-       PG_RETURN_JSONB(JsonbValueToJsonb(res));
+       PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
 }
 
 
@@ -4301,7 +4401,7 @@ jsonb_set(PG_FUNCTION_ARGS)
 Datum
 jsonb_delete_path(PG_FUNCTION_ARGS)
 {
-       Jsonb      *in = PG_GETARG_JSONB(0);
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
        ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
        JsonbValue *res = NULL;
        Datum      *path_elems;
@@ -4321,13 +4421,13 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
                                 errmsg("cannot delete path in scalar")));
 
        if (JB_ROOT_COUNT(in) == 0)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        deconstruct_array(path, TEXTOID, -1, false, 'i',
                                          &path_elems, &path_nulls, &path_len);
 
        if (path_len == 0)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        it = JsonbIteratorInit(&in->root);
 
@@ -4336,19 +4436,18 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
 
        Assert(res != NULL);
 
-       PG_RETURN_JSONB(JsonbValueToJsonb(res));
+       PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
 }
 
 /*
  * SQL function jsonb_insert(jsonb, text[], jsonb, boolean)
- *
  */
 Datum
 jsonb_insert(PG_FUNCTION_ARGS)
 {
-       Jsonb      *in = PG_GETARG_JSONB(0);
+       Jsonb      *in = PG_GETARG_JSONB_P(0);
        ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
-       Jsonb      *newval = PG_GETARG_JSONB(2);
+       Jsonb      *newval = PG_GETARG_JSONB_P(2);
        bool            after = PG_GETARG_BOOL(3);
        JsonbValue *res = NULL;
        Datum      *path_elems;
@@ -4371,7 +4470,7 @@ jsonb_insert(PG_FUNCTION_ARGS)
                                          &path_elems, &path_nulls, &path_len);
 
        if (path_len == 0)
-               PG_RETURN_JSONB(in);
+               PG_RETURN_JSONB_P(in);
 
        it = JsonbIteratorInit(&in->root);
 
@@ -4380,7 +4479,7 @@ jsonb_insert(PG_FUNCTION_ARGS)
 
        Assert(res != NULL);
 
-       PG_RETURN_JSONB(JsonbValueToJsonb(res));
+       PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
 }
 
 /*
@@ -4422,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);
        }
 
@@ -4462,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
@@ -4474,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);
@@ -4691,8 +4790,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
                        lindex < INT_MIN)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                         errmsg("path element at position %d is not an integer: \"%s\"",
-                                        level + 1, c)));
+                                        errmsg("path element at position %d is not an integer: \"%s\"",
+                                                       level + 1, c)));
                idx = lindex;
        }
        else
@@ -4787,58 +4886,207 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 }
 
 /*
- * Iterate over jsonb string values or elements, and pass them together with an
- * iteration state to a specified JsonIterateStringValuesAction.
+ * Parse information about what elements of a jsonb document we want to iterate
+ * in functions iterate_json(b)_values. This information is presented in jsonb
+ * format, so that it can be easily extended in the future.
+ */
+uint32
+parse_jsonb_index_flags(Jsonb *jb)
+{
+       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 names are
+        * chosen the same as jsonb_typeof uses.
+        */
+       if (type != WJB_BEGIN_ARRAY)
+               ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                               errmsg("wrong flag type, only arrays and scalars are allowed")));
+
+       while ((type = JsonbIteratorNext(&it, &v, false)) == WJB_ELEM)
+       {
+               if (v.type != jbvString)
+                       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\".")));
+
+               if (v.val.string.len == 3 &&
+                       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)
+                       flags |= jtiKey;
+               else if (v.val.string.len == 6 &&
+                                pg_strncasecmp(v.val.string.val, "string", 5) == 0)
+                       flags |= jtiString;
+               else if (v.val.string.len == 7 &&
+                                pg_strncasecmp(v.val.string.val, "numeric", 7) == 0)
+                       flags |= jtiNumeric;
+               else if (v.val.string.len == 7 &&
+                                pg_strncasecmp(v.val.string.val, "boolean", 7) == 0)
+                       flags |= jtiBool;
+               else
+                       ereport(ERROR,
+                                       (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\".")));
+       }
+
+       /* expect end of array now */
+       if (type != WJB_END_ARRAY)
+               elog(ERROR, "unexpected end of flag array");
+
+       /* get final WJB_DONE and free iterator */
+       type = JsonbIteratorNext(&it, &v, false);
+       if (type != WJB_DONE)
+               elog(ERROR, "unexpected end of flag array");
+
+       return flags;
+}
+
+/*
+ * Iterate over jsonb values or elements, specified by flags, and pass them
+ * together with an iteration state to a specified JsonIterateStringValuesAction.
  */
 void
-iterate_jsonb_string_values(Jsonb *jb, void *state, JsonIterateStringValuesAction action)
+iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
+                                        JsonIterateStringValuesAction action)
 {
-       JsonbIterator           *it;
-       JsonbValue                      v;
-       JsonbIteratorToken      type;
+       JsonbIterator *it;
+       JsonbValue      v;
+       JsonbIteratorToken type;
 
        it = JsonbIteratorInit(&jb->root);
 
+       /*
+        * Just recursively iterating over jsonb and call callback on all
+        * corresponding elements
+        */
        while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
        {
-               if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
+               if (type == WJB_KEY)
+               {
+                       if (flags & jtiKey)
+                               action(state, v.val.string.val, v.val.string.len);
+
+                       continue;
+               }
+               else if (!(type == WJB_VALUE || type == WJB_ELEM))
+               {
+                       /* do not call callback for composite JsonbValue */
+                       continue;
+               }
+
+               /* JsonbValue is a value of object or element of array */
+               switch (v.type)
                {
-                       action(state, v.val.string.val, v.val.string.len);
+                       case jbvString:
+                               if (flags & jtiString)
+                                       action(state, v.val.string.val, v.val.string.len);
+                               break;
+                       case jbvNumeric:
+                               if (flags & jtiNumeric)
+                               {
+                                       char       *val;
+
+                                       val = DatumGetCString(DirectFunctionCall1(numeric_out,
+                                                                                                                         NumericGetDatum(v.val.numeric)));
+
+                                       action(state, val, strlen(val));
+                                       pfree(val);
+                               }
+                               break;
+                       case jbvBool:
+                               if (flags & jtiBool)
+                               {
+                                       if (v.val.boolean)
+                                               action(state, "true", 4);
+                                       else
+                                               action(state, "false", 5);
+                               }
+                               break;
+                       default:
+                               /* do not call callback for composite JsonbValue */
+                               break;
                }
        }
 }
 
 /*
- * Iterate over json string values or elements, and pass them together with an
- * iteration state to a specified JsonIterateStringValuesAction.
+ * Iterate over json values and elements, specified by flags, and pass them
+ * together with an iteration state to a specified JsonIterateStringValuesAction.
  */
 void
-iterate_json_string_values(text *json, void *action_state, JsonIterateStringValuesAction action)
+iterate_json_values(text *json, uint32 flags, void *action_state,
+                                       JsonIterateStringValuesAction action)
 {
        JsonLexContext *lex = makeJsonLexContext(json, true);
        JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
-       IterateJsonStringValuesState   *state = palloc0(sizeof(IterateJsonStringValuesState));
+       IterateJsonStringValuesState *state = palloc0(sizeof(IterateJsonStringValuesState));
 
        state->lex = lex;
        state->action = action;
        state->action_state = action_state;
+       state->flags = flags;
 
        sem->semstate = (void *) state;
-       sem->scalar = iterate_string_values_scalar;
+       sem->scalar = iterate_values_scalar;
+       sem->object_field_start = iterate_values_object_field_start;
 
        pg_parse_json(lex, sem);
 }
 
 /*
- * An auxiliary function for iterate_json_string_values to invoke a specified
- * JsonIterateStringValuesAction.
+ * An auxiliary function for iterate_json_values to invoke a specified
+ * JsonIterateStringValuesAction for specified values.
  */
 static void
-iterate_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
+iterate_values_scalar(void *state, char *token, JsonTokenType tokentype)
 {
-       IterateJsonStringValuesState   *_state = (IterateJsonStringValuesState *) state;
-       if (tokentype == JSON_TOKEN_STRING)
-               (*_state->action) (_state->action_state, token, strlen(token));
+       IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
+
+       switch (tokentype)
+       {
+               case JSON_TOKEN_STRING:
+                       if (_state->flags & jtiString)
+                               _state->action(_state->action_state, token, strlen(token));
+                       break;
+               case JSON_TOKEN_NUMBER:
+                       if (_state->flags & jtiNumeric)
+                               _state->action(_state->action_state, token, strlen(token));
+                       break;
+               case JSON_TOKEN_TRUE:
+               case JSON_TOKEN_FALSE:
+                       if (_state->flags & jtiBool)
+                               _state->action(_state->action_state, token, strlen(token));
+                       break;
+               default:
+                       /* do not call callback for any other token */
+                       break;
+       }
+}
+
+static void
+iterate_values_object_field_start(void *state, char *fname, bool isnull)
+{
+       IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
+
+       if (_state->flags & jtiKey)
+       {
+               char       *val = pstrdup(fname);
+
+               _state->action(_state->action_state, val, strlen(val));
+       }
 }
 
 /*
@@ -4851,12 +5099,13 @@ Jsonb *
 transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
                                                          JsonTransformStringValuesAction transform_action)
 {
-       JsonbIterator           *it;
-       JsonbValue                      v, *res = NULL;
-       JsonbIteratorToken      type;
-       JsonbParseState         *st = NULL;
-       text                            *out;
-       bool                            is_scalar = false;
+       JsonbIterator *it;
+       JsonbValue      v,
+                          *res = NULL;
+       JsonbIteratorToken type;
+       JsonbParseState *st = NULL;
+       text       *out;
+       bool            is_scalar = false;
 
        it = JsonbIteratorInit(&jsonb->root);
        is_scalar = it->isScalar;
@@ -4928,6 +5177,7 @@ static void
 transform_string_values_object_start(void *state)
 {
        TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
+
        appendStringInfoCharMacro(_state->strval, '{');
 }
 
@@ -4935,6 +5185,7 @@ static void
 transform_string_values_object_end(void *state)
 {
        TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
+
        appendStringInfoCharMacro(_state->strval, '}');
 }
 
@@ -4942,6 +5193,7 @@ static void
 transform_string_values_array_start(void *state)
 {
        TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
+
        appendStringInfoCharMacro(_state->strval, '[');
 }
 
@@ -4949,6 +5201,7 @@ static void
 transform_string_values_array_end(void *state)
 {
        TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
+
        appendStringInfoCharMacro(_state->strval, ']');
 }
 
@@ -4984,7 +5237,8 @@ transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype
 
        if (tokentype == JSON_TOKEN_STRING)
        {
-               text *out = (*_state->action) (_state->action_state, token, strlen(token));
+               text       *out = _state->action(_state->action_state, token, strlen(token));
+
                escape_json(_state->strval, text_to_cstring(out));
        }
        else