LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonb.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 646 724 89.2 %
Date: 2017-09-29 13:40:31 Functions: 34 36 94.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * jsonb.c
       4             :  *      I/O routines for jsonb type
       5             :  *
       6             :  * Copyright (c) 2014-2017, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *    src/backend/utils/adt/jsonb.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : #include "postgres.h"
      14             : 
      15             : #include "miscadmin.h"
      16             : #include "access/htup_details.h"
      17             : #include "access/transam.h"
      18             : #include "catalog/pg_type.h"
      19             : #include "libpq/pqformat.h"
      20             : #include "parser/parse_coerce.h"
      21             : #include "utils/builtins.h"
      22             : #include "utils/date.h"
      23             : #include "utils/datetime.h"
      24             : #include "utils/lsyscache.h"
      25             : #include "utils/json.h"
      26             : #include "utils/jsonapi.h"
      27             : #include "utils/jsonb.h"
      28             : #include "utils/syscache.h"
      29             : #include "utils/typcache.h"
      30             : 
      31             : typedef struct JsonbInState
      32             : {
      33             :     JsonbParseState *parseState;
      34             :     JsonbValue *res;
      35             : } JsonbInState;
      36             : 
      37             : /* unlike with json categories, we need to treat json and jsonb differently */
      38             : typedef enum                    /* type categories for datum_to_jsonb */
      39             : {
      40             :     JSONBTYPE_NULL,             /* null, so we didn't bother to identify */
      41             :     JSONBTYPE_BOOL,             /* boolean (built-in types only) */
      42             :     JSONBTYPE_NUMERIC,          /* numeric (ditto) */
      43             :     JSONBTYPE_DATE,             /* we use special formatting for datetimes */
      44             :     JSONBTYPE_TIMESTAMP,        /* we use special formatting for timestamp */
      45             :     JSONBTYPE_TIMESTAMPTZ,      /* ... and timestamptz */
      46             :     JSONBTYPE_JSON,             /* JSON */
      47             :     JSONBTYPE_JSONB,            /* JSONB */
      48             :     JSONBTYPE_ARRAY,            /* array */
      49             :     JSONBTYPE_COMPOSITE,        /* composite */
      50             :     JSONBTYPE_JSONCAST,         /* something with an explicit cast to JSON */
      51             :     JSONBTYPE_OTHER             /* all else */
      52             : } JsonbTypeCategory;
      53             : 
      54             : typedef struct JsonbAggState
      55             : {
      56             :     JsonbInState *res;
      57             :     JsonbTypeCategory key_category;
      58             :     Oid         key_output_func;
      59             :     JsonbTypeCategory val_category;
      60             :     Oid         val_output_func;
      61             : } JsonbAggState;
      62             : 
      63             : static inline Datum jsonb_from_cstring(char *json, int len);
      64             : static size_t checkStringLen(size_t len);
      65             : static void jsonb_in_object_start(void *pstate);
      66             : static void jsonb_in_object_end(void *pstate);
      67             : static void jsonb_in_array_start(void *pstate);
      68             : static void jsonb_in_array_end(void *pstate);
      69             : static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
      70             : static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
      71             : static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
      72             : static void jsonb_categorize_type(Oid typoid,
      73             :                       JsonbTypeCategory *tcategory,
      74             :                       Oid *outfuncoid);
      75             : static void composite_to_jsonb(Datum composite, JsonbInState *result);
      76             : static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
      77             :                    Datum *vals, bool *nulls, int *valcount,
      78             :                    JsonbTypeCategory tcategory, Oid outfuncoid);
      79             : static void array_to_jsonb_internal(Datum array, JsonbInState *result);
      80             : static void jsonb_categorize_type(Oid typoid,
      81             :                       JsonbTypeCategory *tcategory,
      82             :                       Oid *outfuncoid);
      83             : static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
      84             :                JsonbTypeCategory tcategory, Oid outfuncoid,
      85             :                bool key_scalar);
      86             : static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
      87             :           Oid val_type, bool key_scalar);
      88             : static JsonbParseState *clone_parse_state(JsonbParseState *state);
      89             : static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
      90             : static void add_indent(StringInfo out, bool indent, int level);
      91             : 
      92             : /*
      93             :  * jsonb type input function
      94             :  */
      95             : Datum
      96        1758 : jsonb_in(PG_FUNCTION_ARGS)
      97             : {
      98        1758 :     char       *json = PG_GETARG_CSTRING(0);
      99             : 
     100        1758 :     return jsonb_from_cstring(json, strlen(json));
     101             : }
     102             : 
     103             : /*
     104             :  * jsonb type recv function
     105             :  *
     106             :  * The type is sent as text in binary mode, so this is almost the same
     107             :  * as the input function, but it's prefixed with a version number so we
     108             :  * can change the binary format sent in future if necessary. For now,
     109             :  * only version 1 is supported.
     110             :  */
     111             : Datum
     112           0 : jsonb_recv(PG_FUNCTION_ARGS)
     113             : {
     114           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     115           0 :     int         version = pq_getmsgint(buf, 1);
     116             :     char       *str;
     117             :     int         nbytes;
     118             : 
     119           0 :     if (version == 1)
     120           0 :         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     121             :     else
     122           0 :         elog(ERROR, "unsupported jsonb version number %d", version);
     123             : 
     124           0 :     return jsonb_from_cstring(str, nbytes);
     125             : }
     126             : 
     127             : /*
     128             :  * jsonb type output function
     129             :  */
     130             : Datum
     131         273 : jsonb_out(PG_FUNCTION_ARGS)
     132             : {
     133         273 :     Jsonb      *jb = PG_GETARG_JSONB(0);
     134             :     char       *out;
     135             : 
     136         273 :     out = JsonbToCString(NULL, &jb->root, VARSIZE(jb));
     137             : 
     138         273 :     PG_RETURN_CSTRING(out);
     139             : }
     140             : 
     141             : /*
     142             :  * jsonb type send function
     143             :  *
     144             :  * Just send jsonb as a version number, then a string of text
     145             :  */
     146             : Datum
     147           0 : jsonb_send(PG_FUNCTION_ARGS)
     148             : {
     149           0 :     Jsonb      *jb = PG_GETARG_JSONB(0);
     150             :     StringInfoData buf;
     151           0 :     StringInfo  jtext = makeStringInfo();
     152           0 :     int         version = 1;
     153             : 
     154           0 :     (void) JsonbToCString(jtext, &jb->root, VARSIZE(jb));
     155             : 
     156           0 :     pq_begintypsend(&buf);
     157           0 :     pq_sendint(&buf, version, 1);
     158           0 :     pq_sendtext(&buf, jtext->data, jtext->len);
     159           0 :     pfree(jtext->data);
     160           0 :     pfree(jtext);
     161             : 
     162           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     163             : }
     164             : 
     165             : /*
     166             :  * SQL function jsonb_typeof(jsonb) -> text
     167             :  *
     168             :  * This function is here because the analog json function is in json.c, since
     169             :  * it uses the json parser internals not exposed elsewhere.
     170             :  */
     171             : Datum
     172          15 : jsonb_typeof(PG_FUNCTION_ARGS)
     173             : {
     174          15 :     Jsonb      *in = PG_GETARG_JSONB(0);
     175             :     JsonbIterator *it;
     176             :     JsonbValue  v;
     177             :     char       *result;
     178             : 
     179          15 :     if (JB_ROOT_IS_OBJECT(in))
     180           2 :         result = "object";
     181          13 :     else if (JB_ROOT_IS_ARRAY(in) && !JB_ROOT_IS_SCALAR(in))
     182           2 :         result = "array";
     183             :     else
     184             :     {
     185          11 :         Assert(JB_ROOT_IS_SCALAR(in));
     186             : 
     187          11 :         it = JsonbIteratorInit(&in->root);
     188             : 
     189             :         /*
     190             :          * A root scalar is stored as an array of one element, so we get the
     191             :          * array and then its first (and only) member.
     192             :          */
     193          11 :         (void) JsonbIteratorNext(&it, &v, true);
     194          11 :         Assert(v.type == jbvArray);
     195          11 :         (void) JsonbIteratorNext(&it, &v, true);
     196          11 :         switch (v.type)
     197             :         {
     198             :             case jbvNull:
     199           1 :                 result = "null";
     200           1 :                 break;
     201             :             case jbvString:
     202           3 :                 result = "string";
     203           3 :                 break;
     204             :             case jbvNumeric:
     205           5 :                 result = "number";
     206           5 :                 break;
     207             :             case jbvBool:
     208           2 :                 result = "boolean";
     209           2 :                 break;
     210             :             default:
     211           0 :                 elog(ERROR, "unknown jsonb scalar type");
     212             :         }
     213             :     }
     214             : 
     215          15 :     PG_RETURN_TEXT_P(cstring_to_text(result));
     216             : }
     217             : 
     218             : /*
     219             :  * jsonb_from_cstring
     220             :  *
     221             :  * Turns json string into a jsonb Datum.
     222             :  *
     223             :  * Uses the json parser (with hooks) to construct a jsonb.
     224             :  */
     225             : static inline Datum
     226        1758 : jsonb_from_cstring(char *json, int len)
     227             : {
     228             :     JsonLexContext *lex;
     229             :     JsonbInState state;
     230             :     JsonSemAction sem;
     231             : 
     232        1758 :     memset(&state, 0, sizeof(state));
     233        1758 :     memset(&sem, 0, sizeof(sem));
     234        1758 :     lex = makeJsonLexContextCstringLen(json, len, true);
     235             : 
     236        1758 :     sem.semstate = (void *) &state;
     237             : 
     238        1758 :     sem.object_start = jsonb_in_object_start;
     239        1758 :     sem.array_start = jsonb_in_array_start;
     240        1758 :     sem.object_end = jsonb_in_object_end;
     241        1758 :     sem.array_end = jsonb_in_array_end;
     242        1758 :     sem.scalar = jsonb_in_scalar;
     243        1758 :     sem.object_field_start = jsonb_in_object_field_start;
     244             : 
     245        1758 :     pg_parse_json(lex, &sem);
     246             : 
     247             :     /* after parsing, the item member has the composed jsonb structure */
     248        1722 :     PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
     249             : }
     250             : 
     251             : static size_t
     252        9884 : checkStringLen(size_t len)
     253             : {
     254        9884 :     if (len > JENTRY_OFFLENMASK)
     255           0 :         ereport(ERROR,
     256             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     257             :                  errmsg("string too long to represent as jsonb string"),
     258             :                  errdetail("Due to an implementation restriction, jsonb strings cannot exceed %d bytes.",
     259             :                            JENTRY_OFFLENMASK)));
     260             : 
     261        9884 :     return len;
     262             : }
     263             : 
     264             : static void
     265        2696 : jsonb_in_object_start(void *pstate)
     266             : {
     267        2696 :     JsonbInState *_state = (JsonbInState *) pstate;
     268             : 
     269        2696 :     _state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL);
     270        2696 : }
     271             : 
     272             : static void
     273        1810 : jsonb_in_object_end(void *pstate)
     274             : {
     275        1810 :     JsonbInState *_state = (JsonbInState *) pstate;
     276             : 
     277        1810 :     _state->res = pushJsonbValue(&_state->parseState, WJB_END_OBJECT, NULL);
     278        1810 : }
     279             : 
     280             : static void
     281        1608 : jsonb_in_array_start(void *pstate)
     282             : {
     283        1608 :     JsonbInState *_state = (JsonbInState *) pstate;
     284             : 
     285        1608 :     _state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_ARRAY, NULL);
     286        1608 : }
     287             : 
     288             : static void
     289         587 : jsonb_in_array_end(void *pstate)
     290             : {
     291         587 :     JsonbInState *_state = (JsonbInState *) pstate;
     292             : 
     293         587 :     _state->res = pushJsonbValue(&_state->parseState, WJB_END_ARRAY, NULL);
     294         587 : }
     295             : 
     296             : static void
     297        6992 : jsonb_in_object_field_start(void *pstate, char *fname, bool isnull)
     298             : {
     299        6992 :     JsonbInState *_state = (JsonbInState *) pstate;
     300             :     JsonbValue  v;
     301             : 
     302        6992 :     Assert(fname != NULL);
     303        6992 :     v.type = jbvString;
     304        6992 :     v.val.string.len = checkStringLen(strlen(fname));
     305        6992 :     v.val.string.val = fname;
     306             : 
     307        6992 :     _state->res = pushJsonbValue(&_state->parseState, WJB_KEY, &v);
     308        6992 : }
     309             : 
     310             : static void
     311        1455 : jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal)
     312             : {
     313        1455 :     switch (scalarVal->type)
     314             :     {
     315             :         case jbvNull:
     316          42 :             appendBinaryStringInfo(out, "null", 4);
     317          42 :             break;
     318             :         case jbvString:
     319         797 :             escape_json(out, pnstrdup(scalarVal->val.string.val, scalarVal->val.string.len));
     320         797 :             break;
     321             :         case jbvNumeric:
     322         591 :             appendStringInfoString(out,
     323         591 :                                    DatumGetCString(DirectFunctionCall1(numeric_out,
     324             :                                                                        PointerGetDatum(scalarVal->val.numeric))));
     325         591 :             break;
     326             :         case jbvBool:
     327          25 :             if (scalarVal->val.boolean)
     328          14 :                 appendBinaryStringInfo(out, "true", 4);
     329             :             else
     330          11 :                 appendBinaryStringInfo(out, "false", 5);
     331          25 :             break;
     332             :         default:
     333           0 :             elog(ERROR, "unknown jsonb scalar type");
     334             :     }
     335        1455 : }
     336             : 
     337             : /*
     338             :  * For jsonb we always want the de-escaped value - that's what's in token
     339             :  */
     340             : static void
     341        6647 : jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
     342             : {
     343        6647 :     JsonbInState *_state = (JsonbInState *) pstate;
     344             :     JsonbValue  v;
     345             : 
     346        6647 :     switch (tokentype)
     347             :     {
     348             : 
     349             :         case JSON_TOKEN_STRING:
     350        2833 :             Assert(token != NULL);
     351        2833 :             v.type = jbvString;
     352        2833 :             v.val.string.len = checkStringLen(strlen(token));
     353        2833 :             v.val.string.val = token;
     354        2833 :             break;
     355             :         case JSON_TOKEN_NUMBER:
     356             : 
     357             :             /*
     358             :              * No need to check size of numeric values, because maximum
     359             :              * numeric size is well below the JsonbValue restriction
     360             :              */
     361        2663 :             Assert(token != NULL);
     362        2663 :             v.type = jbvNumeric;
     363        2663 :             v.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
     364             : 
     365        2663 :             break;
     366             :         case JSON_TOKEN_TRUE:
     367         483 :             v.type = jbvBool;
     368         483 :             v.val.boolean = true;
     369             : 
     370         483 :             break;
     371             :         case JSON_TOKEN_FALSE:
     372         473 :             v.type = jbvBool;
     373         473 :             v.val.boolean = false;
     374             : 
     375         473 :             break;
     376             :         case JSON_TOKEN_NULL:
     377         195 :             v.type = jbvNull;
     378         195 :             break;
     379             :         default:
     380             :             /* should not be possible */
     381           0 :             elog(ERROR, "invalid json token type");
     382             :             break;
     383             :     }
     384             : 
     385        6647 :     if (_state->parseState == NULL)
     386             :     {
     387             :         /* single scalar */
     388             :         JsonbValue  va;
     389             : 
     390         100 :         va.type = jbvArray;
     391         100 :         va.val.array.rawScalar = true;
     392         100 :         va.val.array.nElems = 1;
     393             : 
     394         100 :         _state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_ARRAY, &va);
     395         100 :         _state->res = pushJsonbValue(&_state->parseState, WJB_ELEM, &v);
     396         100 :         _state->res = pushJsonbValue(&_state->parseState, WJB_END_ARRAY, NULL);
     397             :     }
     398             :     else
     399             :     {
     400        6547 :         JsonbValue *o = &_state->parseState->contVal;
     401             : 
     402        6547 :         switch (o->type)
     403             :         {
     404             :             case jbvArray:
     405         878 :                 _state->res = pushJsonbValue(&_state->parseState, WJB_ELEM, &v);
     406         878 :                 break;
     407             :             case jbvObject:
     408        5669 :                 _state->res = pushJsonbValue(&_state->parseState, WJB_VALUE, &v);
     409        5669 :                 break;
     410             :             default:
     411           0 :                 elog(ERROR, "unexpected parent of nested structure");
     412             :         }
     413             :     }
     414        6647 : }
     415             : 
     416             : /*
     417             :  * JsonbToCString
     418             :  *     Converts jsonb value to a C-string.
     419             :  *
     420             :  * If 'out' argument is non-null, the resulting C-string is stored inside the
     421             :  * StringBuffer.  The resulting string is always returned.
     422             :  *
     423             :  * A typical case for passing the StringInfo in rather than NULL is where the
     424             :  * caller wants access to the len attribute without having to call strlen, e.g.
     425             :  * if they are converting it to a text* object.
     426             :  */
     427             : char *
     428         479 : JsonbToCString(StringInfo out, JsonbContainer *in, int estimated_len)
     429             : {
     430         479 :     return JsonbToCStringWorker(out, in, estimated_len, false);
     431             : }
     432             : 
     433             : /*
     434             :  * same thing but with indentation turned on
     435             :  */
     436             : char *
     437           3 : JsonbToCStringIndent(StringInfo out, JsonbContainer *in, int estimated_len)
     438             : {
     439           3 :     return JsonbToCStringWorker(out, in, estimated_len, true);
     440             : }
     441             : 
     442             : /*
     443             :  * common worker for above two functions
     444             :  */
     445             : static char *
     446         482 : JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent)
     447             : {
     448         482 :     bool        first = true;
     449             :     JsonbIterator *it;
     450             :     JsonbValue  v;
     451         482 :     JsonbIteratorToken type = WJB_DONE;
     452         482 :     int         level = 0;
     453         482 :     bool        redo_switch = false;
     454             : 
     455             :     /* If we are indenting, don't add a space after a comma */
     456         482 :     int         ispaces = indent ? 1 : 2;
     457             : 
     458             :     /*
     459             :      * Don't indent the very first item. This gets set to the indent flag at
     460             :      * the bottom of the loop.
     461             :      */
     462         482 :     bool        use_indent = false;
     463         482 :     bool        raw_scalar = false;
     464         482 :     bool        last_was_key = false;
     465             : 
     466         482 :     if (out == NULL)
     467         455 :         out = makeStringInfo();
     468             : 
     469         482 :     enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
     470             : 
     471         482 :     it = JsonbIteratorInit(in);
     472             : 
     473        3762 :     while (redo_switch ||
     474             :            ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE))
     475             :     {
     476        2798 :         redo_switch = false;
     477        2798 :         switch (type)
     478             :         {
     479             :             case WJB_BEGIN_ARRAY:
     480         556 :                 if (!first)
     481          15 :                     appendBinaryStringInfo(out, ", ", ispaces);
     482             : 
     483         556 :                 if (!v.val.array.rawScalar)
     484             :                 {
     485         295 :                     add_indent(out, use_indent && !last_was_key, level);
     486         295 :                     appendStringInfoCharMacro(out, '[');
     487             :                 }
     488             :                 else
     489         261 :                     raw_scalar = true;
     490             : 
     491         556 :                 first = true;
     492         556 :                 level++;
     493         556 :                 break;
     494             :             case WJB_BEGIN_OBJECT:
     495         268 :                 if (!first)
     496          32 :                     appendBinaryStringInfo(out, ", ", ispaces);
     497             : 
     498         268 :                 add_indent(out, use_indent && !last_was_key, level);
     499         268 :                 appendStringInfoCharMacro(out, '{');
     500             : 
     501         268 :                 first = true;
     502         268 :                 level++;
     503         268 :                 break;
     504             :             case WJB_KEY:
     505         475 :                 if (!first)
     506         219 :                     appendBinaryStringInfo(out, ", ", ispaces);
     507         475 :                 first = true;
     508             : 
     509         475 :                 add_indent(out, use_indent, level);
     510             : 
     511             :                 /* json rules guarantee this is a string */
     512         475 :                 jsonb_put_escaped_value(out, &v);
     513         475 :                 appendBinaryStringInfo(out, ": ", 2);
     514             : 
     515         475 :                 type = JsonbIteratorNext(&it, &v, false);
     516         475 :                 if (type == WJB_VALUE)
     517             :                 {
     518         305 :                     first = false;
     519         305 :                     jsonb_put_escaped_value(out, &v);
     520             :                 }
     521             :                 else
     522             :                 {
     523         170 :                     Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
     524             : 
     525             :                     /*
     526             :                      * We need to rerun the current switch() since we need to
     527             :                      * output the object which we just got from the iterator
     528             :                      * before calling the iterator again.
     529             :                      */
     530         170 :                     redo_switch = true;
     531             :                 }
     532         475 :                 break;
     533             :             case WJB_ELEM:
     534         675 :                 if (!first)
     535         252 :                     appendBinaryStringInfo(out, ", ", ispaces);
     536         675 :                 first = false;
     537             : 
     538         675 :                 if (!raw_scalar)
     539         414 :                     add_indent(out, use_indent, level);
     540         675 :                 jsonb_put_escaped_value(out, &v);
     541         675 :                 break;
     542             :             case WJB_END_ARRAY:
     543         556 :                 level--;
     544         556 :                 if (!raw_scalar)
     545             :                 {
     546         295 :                     add_indent(out, use_indent, level);
     547         295 :                     appendStringInfoCharMacro(out, ']');
     548             :                 }
     549         556 :                 first = false;
     550         556 :                 break;
     551             :             case WJB_END_OBJECT:
     552         268 :                 level--;
     553         268 :                 add_indent(out, use_indent, level);
     554         268 :                 appendStringInfoCharMacro(out, '}');
     555         268 :                 first = false;
     556         268 :                 break;
     557             :             default:
     558           0 :                 elog(ERROR, "unknown jsonb iterator token type");
     559             :         }
     560        2798 :         use_indent = indent;
     561        2798 :         last_was_key = redo_switch;
     562             :     }
     563             : 
     564         482 :     Assert(level == 0);
     565             : 
     566         482 :     return out->data;
     567             : }
     568             : 
     569             : static void
     570        2015 : add_indent(StringInfo out, bool indent, int level)
     571             : {
     572        2015 :     if (indent)
     573             :     {
     574             :         int         i;
     575             : 
     576          40 :         appendStringInfoCharMacro(out, '\n');
     577         104 :         for (i = 0; i < level; i++)
     578          64 :             appendBinaryStringInfo(out, "    ", 4);
     579             :     }
     580        2015 : }
     581             : 
     582             : 
     583             : /*
     584             :  * Determine how we want to render values of a given type in datum_to_jsonb.
     585             :  *
     586             :  * Given the datatype OID, return its JsonbTypeCategory, as well as the type's
     587             :  * output function OID.  If the returned category is JSONBTYPE_JSONCAST,
     588             :  * we return the OID of the relevant cast function instead.
     589             :  */
     590             : static void
     591         126 : jsonb_categorize_type(Oid typoid,
     592             :                       JsonbTypeCategory *tcategory,
     593             :                       Oid *outfuncoid)
     594             : {
     595             :     bool        typisvarlena;
     596             : 
     597             :     /* Look through any domain */
     598         126 :     typoid = getBaseType(typoid);
     599             : 
     600         126 :     *outfuncoid = InvalidOid;
     601             : 
     602             :     /*
     603             :      * We need to get the output function for everything except date and
     604             :      * timestamp types, booleans, array and composite types, json and jsonb,
     605             :      * and non-builtin types where there's a cast to json. In this last case
     606             :      * we return the oid of the cast function instead.
     607             :      */
     608             : 
     609         126 :     switch (typoid)
     610             :     {
     611             :         case BOOLOID:
     612           3 :             *tcategory = JSONBTYPE_BOOL;
     613           3 :             break;
     614             : 
     615             :         case INT2OID:
     616             :         case INT4OID:
     617             :         case INT8OID:
     618             :         case FLOAT4OID:
     619             :         case FLOAT8OID:
     620             :         case NUMERICOID:
     621          35 :             getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     622          35 :             *tcategory = JSONBTYPE_NUMERIC;
     623          35 :             break;
     624             : 
     625             :         case DATEOID:
     626           3 :             *tcategory = JSONBTYPE_DATE;
     627           3 :             break;
     628             : 
     629             :         case TIMESTAMPOID:
     630           3 :             *tcategory = JSONBTYPE_TIMESTAMP;
     631           3 :             break;
     632             : 
     633             :         case TIMESTAMPTZOID:
     634           4 :             *tcategory = JSONBTYPE_TIMESTAMPTZ;
     635           4 :             break;
     636             : 
     637             :         case JSONBOID:
     638           5 :             *tcategory = JSONBTYPE_JSONB;
     639           5 :             break;
     640             : 
     641             :         case JSONOID:
     642           4 :             *tcategory = JSONBTYPE_JSON;
     643           4 :             break;
     644             : 
     645             :         default:
     646             :             /* Check for arrays and composites */
     647          69 :             if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
     648          54 :                 || typoid == RECORDARRAYOID)
     649          15 :                 *tcategory = JSONBTYPE_ARRAY;
     650          54 :             else if (type_is_rowtype(typoid))   /* includes RECORDOID */
     651           8 :                 *tcategory = JSONBTYPE_COMPOSITE;
     652             :             else
     653             :             {
     654             :                 /* It's probably the general case ... */
     655          46 :                 *tcategory = JSONBTYPE_OTHER;
     656             : 
     657             :                 /*
     658             :                  * but first let's look for a cast to json (note: not to
     659             :                  * jsonb) if it's not built-in.
     660             :                  */
     661          46 :                 if (typoid >= FirstNormalObjectId)
     662             :                 {
     663             :                     Oid         castfunc;
     664             :                     CoercionPathType ctype;
     665             : 
     666           0 :                     ctype = find_coercion_pathway(JSONOID, typoid,
     667             :                                                   COERCION_EXPLICIT, &castfunc);
     668           0 :                     if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
     669             :                     {
     670           0 :                         *tcategory = JSONBTYPE_JSONCAST;
     671           0 :                         *outfuncoid = castfunc;
     672             :                     }
     673             :                     else
     674             :                     {
     675             :                         /* not a cast type, so just get the usual output func */
     676           0 :                         getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     677             :                     }
     678             :                 }
     679             :                 else
     680             :                 {
     681             :                     /* any other builtin type */
     682          46 :                     getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     683             :                 }
     684          46 :                 break;
     685             :             }
     686             :     }
     687         126 : }
     688             : 
     689             : /*
     690             :  * Turn a Datum into jsonb, adding it to the result JsonbInState.
     691             :  *
     692             :  * tcategory and outfuncoid are from a previous call to json_categorize_type,
     693             :  * except that if is_null is true then they can be invalid.
     694             :  *
     695             :  * If key_scalar is true, the value is stored as a key, so insist
     696             :  * it's of an acceptable type, and force it to be a jbvString.
     697             :  */
     698             : static void
     699         175 : datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
     700             :                JsonbTypeCategory tcategory, Oid outfuncoid,
     701             :                bool key_scalar)
     702             : {
     703             :     char       *outputstr;
     704             :     bool        numeric_error;
     705             :     JsonbValue  jb;
     706         175 :     bool        scalar_jsonb = false;
     707             : 
     708         175 :     check_stack_depth();
     709             : 
     710             :     /* Convert val to a JsonbValue in jb (in most cases) */
     711         175 :     if (is_null)
     712             :     {
     713           4 :         Assert(!key_scalar);
     714           4 :         jb.type = jbvNull;
     715             :     }
     716         171 :     else if (key_scalar &&
     717          31 :              (tcategory == JSONBTYPE_ARRAY ||
     718          30 :               tcategory == JSONBTYPE_COMPOSITE ||
     719          29 :               tcategory == JSONBTYPE_JSON ||
     720          29 :               tcategory == JSONBTYPE_JSONB ||
     721             :               tcategory == JSONBTYPE_JSONCAST))
     722             :     {
     723           3 :         ereport(ERROR,
     724             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     725             :                  errmsg("key value must be scalar, not array, composite, or json")));
     726             :     }
     727             :     else
     728             :     {
     729         168 :         if (tcategory == JSONBTYPE_JSONCAST)
     730           0 :             val = OidFunctionCall1(outfuncoid, val);
     731             : 
     732         168 :         switch (tcategory)
     733             :         {
     734             :             case JSONBTYPE_ARRAY:
     735          14 :                 array_to_jsonb_internal(val, result);
     736          14 :                 break;
     737             :             case JSONBTYPE_COMPOSITE:
     738          18 :                 composite_to_jsonb(val, result);
     739          18 :                 break;
     740             :             case JSONBTYPE_BOOL:
     741           3 :                 if (key_scalar)
     742             :                 {
     743           0 :                     outputstr = DatumGetBool(val) ? "true" : "false";
     744           0 :                     jb.type = jbvString;
     745           0 :                     jb.val.string.len = strlen(outputstr);
     746           0 :                     jb.val.string.val = outputstr;
     747             :                 }
     748             :                 else
     749             :                 {
     750           3 :                     jb.type = jbvBool;
     751           3 :                     jb.val.boolean = DatumGetBool(val);
     752             :                 }
     753           3 :                 break;
     754             :             case JSONBTYPE_NUMERIC:
     755          55 :                 outputstr = OidOutputFunctionCall(outfuncoid, val);
     756          55 :                 if (key_scalar)
     757             :                 {
     758             :                     /* always quote keys */
     759           5 :                     jb.type = jbvString;
     760           5 :                     jb.val.string.len = strlen(outputstr);
     761           5 :                     jb.val.string.val = outputstr;
     762             :                 }
     763             :                 else
     764             :                 {
     765             :                     /*
     766             :                      * Make it numeric if it's a valid JSON number, otherwise
     767             :                      * a string. Invalid numeric output will always have an
     768             :                      * 'N' or 'n' in it (I think).
     769             :                      */
     770         100 :                     numeric_error = (strchr(outputstr, 'N') != NULL ||
     771          50 :                                      strchr(outputstr, 'n') != NULL);
     772          50 :                     if (!numeric_error)
     773             :                     {
     774          50 :                         jb.type = jbvNumeric;
     775          50 :                         jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
     776             : 
     777          50 :                         pfree(outputstr);
     778             :                     }
     779             :                     else
     780             :                     {
     781           0 :                         jb.type = jbvString;
     782           0 :                         jb.val.string.len = strlen(outputstr);
     783           0 :                         jb.val.string.val = outputstr;
     784             :                     }
     785             :                 }
     786          55 :                 break;
     787             :             case JSONBTYPE_DATE:
     788             :                 {
     789             :                     DateADT     date;
     790             :                     struct pg_tm tm;
     791             :                     char        buf[MAXDATELEN + 1];
     792             : 
     793           3 :                     date = DatumGetDateADT(val);
     794             :                     /* Same as date_out(), but forcing DateStyle */
     795           3 :                     if (DATE_NOT_FINITE(date))
     796           2 :                         EncodeSpecialDate(date, buf);
     797             :                     else
     798             :                     {
     799           1 :                         j2date(date + POSTGRES_EPOCH_JDATE,
     800             :                                &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
     801           1 :                         EncodeDateOnly(&tm, USE_XSD_DATES, buf);
     802             :                     }
     803           3 :                     jb.type = jbvString;
     804           3 :                     jb.val.string.len = strlen(buf);
     805           3 :                     jb.val.string.val = pstrdup(buf);
     806             :                 }
     807           3 :                 break;
     808             :             case JSONBTYPE_TIMESTAMP:
     809             :                 {
     810             :                     Timestamp   timestamp;
     811             :                     struct pg_tm tm;
     812             :                     fsec_t      fsec;
     813             :                     char        buf[MAXDATELEN + 1];
     814             : 
     815           3 :                     timestamp = DatumGetTimestamp(val);
     816             :                     /* Same as timestamp_out(), but forcing DateStyle */
     817           3 :                     if (TIMESTAMP_NOT_FINITE(timestamp))
     818           2 :                         EncodeSpecialTimestamp(timestamp, buf);
     819           1 :                     else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
     820           1 :                         EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
     821             :                     else
     822           0 :                         ereport(ERROR,
     823             :                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
     824             :                                  errmsg("timestamp out of range")));
     825           3 :                     jb.type = jbvString;
     826           3 :                     jb.val.string.len = strlen(buf);
     827           3 :                     jb.val.string.val = pstrdup(buf);
     828             :                 }
     829           3 :                 break;
     830             :             case JSONBTYPE_TIMESTAMPTZ:
     831             :                 {
     832             :                     TimestampTz timestamp;
     833             :                     struct pg_tm tm;
     834             :                     int         tz;
     835             :                     fsec_t      fsec;
     836           4 :                     const char *tzn = NULL;
     837             :                     char        buf[MAXDATELEN + 1];
     838             : 
     839           4 :                     timestamp = DatumGetTimestampTz(val);
     840             :                     /* Same as timestamptz_out(), but forcing DateStyle */
     841           4 :                     if (TIMESTAMP_NOT_FINITE(timestamp))
     842           2 :                         EncodeSpecialTimestamp(timestamp, buf);
     843           2 :                     else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
     844           2 :                         EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
     845             :                     else
     846           0 :                         ereport(ERROR,
     847             :                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
     848             :                                  errmsg("timestamp out of range")));
     849           4 :                     jb.type = jbvString;
     850           4 :                     jb.val.string.len = strlen(buf);
     851           4 :                     jb.val.string.val = pstrdup(buf);
     852             :                 }
     853           4 :                 break;
     854             :             case JSONBTYPE_JSONCAST:
     855             :             case JSONBTYPE_JSON:
     856             :                 {
     857             :                     /* parse the json right into the existing result object */
     858             :                     JsonLexContext *lex;
     859             :                     JsonSemAction sem;
     860           3 :                     text       *json = DatumGetTextPP(val);
     861             : 
     862           3 :                     lex = makeJsonLexContext(json, true);
     863             : 
     864           3 :                     memset(&sem, 0, sizeof(sem));
     865             : 
     866           3 :                     sem.semstate = (void *) result;
     867             : 
     868           3 :                     sem.object_start = jsonb_in_object_start;
     869           3 :                     sem.array_start = jsonb_in_array_start;
     870           3 :                     sem.object_end = jsonb_in_object_end;
     871           3 :                     sem.array_end = jsonb_in_array_end;
     872           3 :                     sem.scalar = jsonb_in_scalar;
     873           3 :                     sem.object_field_start = jsonb_in_object_field_start;
     874             : 
     875           3 :                     pg_parse_json(lex, &sem);
     876             : 
     877             :                 }
     878           3 :                 break;
     879             :             case JSONBTYPE_JSONB:
     880             :                 {
     881           6 :                     Jsonb      *jsonb = DatumGetJsonb(val);
     882             :                     JsonbIterator *it;
     883             : 
     884           6 :                     it = JsonbIteratorInit(&jsonb->root);
     885             : 
     886           6 :                     if (JB_ROOT_IS_SCALAR(jsonb))
     887             :                     {
     888           0 :                         (void) JsonbIteratorNext(&it, &jb, true);
     889           0 :                         Assert(jb.type == jbvArray);
     890           0 :                         (void) JsonbIteratorNext(&it, &jb, true);
     891           0 :                         scalar_jsonb = true;
     892             :                     }
     893             :                     else
     894             :                     {
     895             :                         JsonbIteratorToken type;
     896             : 
     897          74 :                         while ((type = JsonbIteratorNext(&it, &jb, false))
     898             :                                != WJB_DONE)
     899             :                         {
     900          62 :                             if (type == WJB_END_ARRAY || type == WJB_END_OBJECT ||
     901          50 :                                 type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT)
     902          22 :                                 result->res = pushJsonbValue(&result->parseState,
     903             :                                                              type, NULL);
     904             :                             else
     905          40 :                                 result->res = pushJsonbValue(&result->parseState,
     906             :                                                              type, &jb);
     907             :                         }
     908             :                     }
     909             :                 }
     910           6 :                 break;
     911             :             default:
     912          59 :                 outputstr = OidOutputFunctionCall(outfuncoid, val);
     913          59 :                 jb.type = jbvString;
     914          59 :                 jb.val.string.len = checkStringLen(strlen(outputstr));
     915          59 :                 jb.val.string.val = outputstr;
     916          59 :                 break;
     917             :         }
     918             :     }
     919             : 
     920             :     /* Now insert jb into result, unless we did it recursively */
     921         172 :     if (!is_null && !scalar_jsonb &&
     922         100 :         tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST)
     923             :     {
     924             :         /* work has been done recursively */
     925         213 :         return;
     926             :     }
     927         131 :     else if (result->parseState == NULL)
     928             :     {
     929             :         /* single root scalar */
     930             :         JsonbValue  va;
     931             : 
     932          27 :         va.type = jbvArray;
     933          27 :         va.val.array.rawScalar = true;
     934          27 :         va.val.array.nElems = 1;
     935             : 
     936          27 :         result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, &va);
     937          27 :         result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
     938          27 :         result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
     939             :     }
     940             :     else
     941             :     {
     942         104 :         JsonbValue *o = &result->parseState->contVal;
     943             : 
     944         104 :         switch (o->type)
     945             :         {
     946             :             case jbvArray:
     947          44 :                 result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
     948          44 :                 break;
     949             :             case jbvObject:
     950          60 :                 result->res = pushJsonbValue(&result->parseState,
     951             :                                              key_scalar ? WJB_KEY : WJB_VALUE,
     952             :                                              &jb);
     953          60 :                 break;
     954             :             default:
     955           0 :                 elog(ERROR, "unexpected parent of nested structure");
     956             :         }
     957             :     }
     958             : }
     959             : 
     960             : /*
     961             :  * Process a single dimension of an array.
     962             :  * If it's the innermost dimension, output the values, otherwise call
     963             :  * ourselves recursively to process the next dimension.
     964             :  */
     965             : static void
     966          14 : array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *vals,
     967             :                    bool *nulls, int *valcount, JsonbTypeCategory tcategory,
     968             :                    Oid outfuncoid)
     969             : {
     970             :     int         i;
     971             : 
     972          14 :     Assert(dim < ndims);
     973             : 
     974          14 :     result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL);
     975             : 
     976          57 :     for (i = 1; i <= dims[dim]; i++)
     977             :     {
     978          43 :         if (dim + 1 == ndims)
     979             :         {
     980          43 :             datum_to_jsonb(vals[*valcount], nulls[*valcount], result, tcategory,
     981             :                            outfuncoid, false);
     982          43 :             (*valcount)++;
     983             :         }
     984             :         else
     985             :         {
     986           0 :             array_dim_to_jsonb(result, dim + 1, ndims, dims, vals, nulls,
     987             :                                valcount, tcategory, outfuncoid);
     988             :         }
     989             :     }
     990             : 
     991          14 :     result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
     992          14 : }
     993             : 
     994             : /*
     995             :  * Turn an array into JSON.
     996             :  */
     997             : static void
     998          14 : array_to_jsonb_internal(Datum array, JsonbInState *result)
     999             : {
    1000          14 :     ArrayType  *v = DatumGetArrayTypeP(array);
    1001          14 :     Oid         element_type = ARR_ELEMTYPE(v);
    1002             :     int        *dim;
    1003             :     int         ndim;
    1004             :     int         nitems;
    1005          14 :     int         count = 0;
    1006             :     Datum      *elements;
    1007             :     bool       *nulls;
    1008             :     int16       typlen;
    1009             :     bool        typbyval;
    1010             :     char        typalign;
    1011             :     JsonbTypeCategory tcategory;
    1012             :     Oid         outfuncoid;
    1013             : 
    1014          14 :     ndim = ARR_NDIM(v);
    1015          14 :     dim = ARR_DIMS(v);
    1016          14 :     nitems = ArrayGetNItems(ndim, dim);
    1017             : 
    1018          14 :     if (nitems <= 0)
    1019             :     {
    1020           0 :         result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL);
    1021           0 :         result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
    1022          14 :         return;
    1023             :     }
    1024             : 
    1025          14 :     get_typlenbyvalalign(element_type,
    1026             :                          &typlen, &typbyval, &typalign);
    1027             : 
    1028          14 :     jsonb_categorize_type(element_type,
    1029             :                           &tcategory, &outfuncoid);
    1030             : 
    1031          14 :     deconstruct_array(v, element_type, typlen, typbyval,
    1032             :                       typalign, &elements, &nulls,
    1033             :                       &nitems);
    1034             : 
    1035          14 :     array_dim_to_jsonb(result, 0, ndim, dim, elements, nulls, &count, tcategory,
    1036             :                        outfuncoid);
    1037             : 
    1038          14 :     pfree(elements);
    1039          14 :     pfree(nulls);
    1040             : }
    1041             : 
    1042             : /*
    1043             :  * Turn a composite / record into JSON.
    1044             :  */
    1045             : static void
    1046          18 : composite_to_jsonb(Datum composite, JsonbInState *result)
    1047             : {
    1048             :     HeapTupleHeader td;
    1049             :     Oid         tupType;
    1050             :     int32       tupTypmod;
    1051             :     TupleDesc   tupdesc;
    1052             :     HeapTupleData tmptup,
    1053             :                *tuple;
    1054             :     int         i;
    1055             : 
    1056          18 :     td = DatumGetHeapTupleHeader(composite);
    1057             : 
    1058             :     /* Extract rowtype info and find a tupdesc */
    1059          18 :     tupType = HeapTupleHeaderGetTypeId(td);
    1060          18 :     tupTypmod = HeapTupleHeaderGetTypMod(td);
    1061          18 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
    1062             : 
    1063             :     /* Build a temporary HeapTuple control structure */
    1064          18 :     tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
    1065          18 :     tmptup.t_data = td;
    1066          18 :     tuple = &tmptup;
    1067             : 
    1068          18 :     result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_OBJECT, NULL);
    1069             : 
    1070          58 :     for (i = 0; i < tupdesc->natts; i++)
    1071             :     {
    1072             :         Datum       val;
    1073             :         bool        isnull;
    1074             :         char       *attname;
    1075             :         JsonbTypeCategory tcategory;
    1076             :         Oid         outfuncoid;
    1077             :         JsonbValue  v;
    1078          40 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
    1079             : 
    1080          40 :         if (att->attisdropped)
    1081           0 :             continue;
    1082             : 
    1083          40 :         attname = NameStr(att->attname);
    1084             : 
    1085          40 :         v.type = jbvString;
    1086             :         /* don't need checkStringLen here - can't exceed maximum name length */
    1087          40 :         v.val.string.len = strlen(attname);
    1088          40 :         v.val.string.val = attname;
    1089             : 
    1090          40 :         result->res = pushJsonbValue(&result->parseState, WJB_KEY, &v);
    1091             : 
    1092          40 :         val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
    1093             : 
    1094          40 :         if (isnull)
    1095             :         {
    1096           1 :             tcategory = JSONBTYPE_NULL;
    1097           1 :             outfuncoid = InvalidOid;
    1098             :         }
    1099             :         else
    1100          39 :             jsonb_categorize_type(att->atttypid, &tcategory, &outfuncoid);
    1101             : 
    1102          40 :         datum_to_jsonb(val, isnull, result, tcategory, outfuncoid, false);
    1103             :     }
    1104             : 
    1105          18 :     result->res = pushJsonbValue(&result->parseState, WJB_END_OBJECT, NULL);
    1106          18 :     ReleaseTupleDesc(tupdesc);
    1107          18 : }
    1108             : 
    1109             : /*
    1110             :  * Append JSON text for "val" to "result".
    1111             :  *
    1112             :  * This is just a thin wrapper around datum_to_jsonb.  If the same type will be
    1113             :  * printed many times, avoid using this; better to do the jsonb_categorize_type
    1114             :  * lookups only once.
    1115             :  */
    1116             : 
    1117             : static void
    1118          51 : add_jsonb(Datum val, bool is_null, JsonbInState *result,
    1119             :           Oid val_type, bool key_scalar)
    1120             : {
    1121             :     JsonbTypeCategory tcategory;
    1122             :     Oid         outfuncoid;
    1123             : 
    1124          51 :     if (val_type == InvalidOid)
    1125           0 :         ereport(ERROR,
    1126             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1127             :                  errmsg("could not determine input data type")));
    1128             : 
    1129          51 :     if (is_null)
    1130             :     {
    1131           2 :         tcategory = JSONBTYPE_NULL;
    1132           2 :         outfuncoid = InvalidOid;
    1133             :     }
    1134             :     else
    1135          49 :         jsonb_categorize_type(val_type,
    1136             :                               &tcategory, &outfuncoid);
    1137             : 
    1138          51 :     datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
    1139          48 : }
    1140             : 
    1141             : /*
    1142             :  * SQL function to_jsonb(anyvalue)
    1143             :  */
    1144             : Datum
    1145          11 : to_jsonb(PG_FUNCTION_ARGS)
    1146             : {
    1147          11 :     Datum       val = PG_GETARG_DATUM(0);
    1148          11 :     Oid         val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
    1149             :     JsonbInState result;
    1150             :     JsonbTypeCategory tcategory;
    1151             :     Oid         outfuncoid;
    1152             : 
    1153          11 :     if (val_type == InvalidOid)
    1154           0 :         ereport(ERROR,
    1155             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1156             :                  errmsg("could not determine input data type")));
    1157             : 
    1158          11 :     jsonb_categorize_type(val_type,
    1159             :                           &tcategory, &outfuncoid);
    1160             : 
    1161          11 :     memset(&result, 0, sizeof(JsonbInState));
    1162             : 
    1163          11 :     datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
    1164             : 
    1165          11 :     PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
    1166             : }
    1167             : 
    1168             : /*
    1169             :  * SQL function jsonb_build_object(variadic "any")
    1170             :  */
    1171             : Datum
    1172          13 : jsonb_build_object(PG_FUNCTION_ARGS)
    1173             : {
    1174          13 :     int         nargs = PG_NARGS();
    1175             :     int         i;
    1176             :     Datum       arg;
    1177             :     Oid         val_type;
    1178             :     JsonbInState result;
    1179             : 
    1180          13 :     if (nargs % 2 != 0)
    1181           0 :         ereport(ERROR,
    1182             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1183             :                  errmsg("invalid number of arguments: object must be matched key value pairs")));
    1184             : 
    1185          13 :     memset(&result, 0, sizeof(JsonbInState));
    1186             : 
    1187          13 :     result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
    1188             : 
    1189          32 :     for (i = 0; i < nargs; i += 2)
    1190             :     {
    1191             :         /* process key */
    1192             : 
    1193          23 :         if (PG_ARGISNULL(i))
    1194           1 :             ereport(ERROR,
    1195             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1196             :                      errmsg("argument %d: key must not be null", i + 1)));
    1197          22 :         val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
    1198             : 
    1199             :         /*
    1200             :          * turn a constant (more or less literal) value that's of unknown type
    1201             :          * into text. Unknowns come in as a cstring pointer.
    1202             :          */
    1203          22 :         if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i))
    1204             :         {
    1205          18 :             val_type = TEXTOID;
    1206          18 :             arg = CStringGetTextDatum(PG_GETARG_POINTER(i));
    1207             :         }
    1208             :         else
    1209             :         {
    1210           4 :             arg = PG_GETARG_DATUM(i);
    1211             :         }
    1212          22 :         if (val_type == InvalidOid || val_type == UNKNOWNOID)
    1213           0 :             ereport(ERROR,
    1214             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1215             :                      errmsg("could not determine data type for argument %d", i + 1)));
    1216             : 
    1217          22 :         add_jsonb(arg, false, &result, val_type, true);
    1218             : 
    1219             :         /* process value */
    1220             : 
    1221          19 :         val_type = get_fn_expr_argtype(fcinfo->flinfo, i + 1);
    1222             :         /* see comments above */
    1223          19 :         if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i + 1))
    1224             :         {
    1225           1 :             val_type = TEXTOID;
    1226           2 :             if (PG_ARGISNULL(i + 1))
    1227           1 :                 arg = (Datum) 0;
    1228             :             else
    1229           0 :                 arg = CStringGetTextDatum(PG_GETARG_POINTER(i + 1));
    1230             :         }
    1231             :         else
    1232             :         {
    1233          18 :             arg = PG_GETARG_DATUM(i + 1);
    1234             :         }
    1235          19 :         if (val_type == InvalidOid || val_type == UNKNOWNOID)
    1236           0 :             ereport(ERROR,
    1237             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1238             :                      errmsg("could not determine data type for argument %d", i + 2)));
    1239          19 :         add_jsonb(arg, PG_ARGISNULL(i + 1), &result, val_type, false);
    1240             :     }
    1241             : 
    1242           9 :     result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
    1243             : 
    1244           9 :     PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
    1245             : }
    1246             : 
    1247             : /*
    1248             :  * degenerate case of jsonb_build_object where it gets 0 arguments.
    1249             :  */
    1250             : Datum
    1251           1 : jsonb_build_object_noargs(PG_FUNCTION_ARGS)
    1252             : {
    1253             :     JsonbInState result;
    1254             : 
    1255           1 :     memset(&result, 0, sizeof(JsonbInState));
    1256             : 
    1257           1 :     (void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
    1258           1 :     result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
    1259             : 
    1260           1 :     PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
    1261             : }
    1262             : 
    1263             : /*
    1264             :  * SQL function jsonb_build_array(variadic "any")
    1265             :  */
    1266             : Datum
    1267           1 : jsonb_build_array(PG_FUNCTION_ARGS)
    1268             : {
    1269           1 :     int         nargs = PG_NARGS();
    1270             :     int         i;
    1271             :     Datum       arg;
    1272             :     Oid         val_type;
    1273             :     JsonbInState result;
    1274             : 
    1275           1 :     memset(&result, 0, sizeof(JsonbInState));
    1276             : 
    1277           1 :     result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
    1278             : 
    1279          11 :     for (i = 0; i < nargs; i++)
    1280             :     {
    1281          10 :         val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
    1282             :         /* see comments in jsonb_build_object above */
    1283          10 :         if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i))
    1284             :         {
    1285           6 :             val_type = TEXTOID;
    1286          12 :             if (PG_ARGISNULL(i))
    1287           1 :                 arg = (Datum) 0;
    1288             :             else
    1289           5 :                 arg = CStringGetTextDatum(PG_GETARG_POINTER(i));
    1290             :         }
    1291             :         else
    1292             :         {
    1293           4 :             arg = PG_GETARG_DATUM(i);
    1294             :         }
    1295          10 :         if (val_type == InvalidOid || val_type == UNKNOWNOID)
    1296           0 :             ereport(ERROR,
    1297             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1298             :                      errmsg("could not determine data type for argument %d", i + 1)));
    1299          10 :         add_jsonb(arg, PG_ARGISNULL(i), &result, val_type, false);
    1300             :     }
    1301             : 
    1302           1 :     result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
    1303             : 
    1304           1 :     PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
    1305             : }
    1306             : 
    1307             : /*
    1308             :  * degenerate case of jsonb_build_array where it gets 0 arguments.
    1309             :  */
    1310             : Datum
    1311           1 : jsonb_build_array_noargs(PG_FUNCTION_ARGS)
    1312             : {
    1313             :     JsonbInState result;
    1314             : 
    1315           1 :     memset(&result, 0, sizeof(JsonbInState));
    1316             : 
    1317           1 :     (void) pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
    1318           1 :     result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
    1319             : 
    1320           1 :     PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
    1321             : }
    1322             : 
    1323             : 
    1324             : /*
    1325             :  * SQL function jsonb_object(text[])
    1326             :  *
    1327             :  * take a one or two dimensional array of text as name value pairs
    1328             :  * for a jsonb object.
    1329             :  *
    1330             :  */
    1331             : Datum
    1332           7 : jsonb_object(PG_FUNCTION_ARGS)
    1333             : {
    1334           7 :     ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
    1335           7 :     int         ndims = ARR_NDIM(in_array);
    1336             :     Datum      *in_datums;
    1337             :     bool       *in_nulls;
    1338             :     int         in_count,
    1339             :                 count,
    1340             :                 i;
    1341             :     JsonbInState result;
    1342             : 
    1343           7 :     memset(&result, 0, sizeof(JsonbInState));
    1344             : 
    1345           7 :     (void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
    1346             : 
    1347           7 :     switch (ndims)
    1348             :     {
    1349             :         case 0:
    1350           1 :             goto close_object;
    1351             :             break;
    1352             : 
    1353             :         case 1:
    1354           2 :             if ((ARR_DIMS(in_array)[0]) % 2)
    1355           1 :                 ereport(ERROR,
    1356             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1357             :                          errmsg("array must have even number of elements")));
    1358           1 :             break;
    1359             : 
    1360             :         case 2:
    1361           3 :             if ((ARR_DIMS(in_array)[1]) != 2)
    1362           2 :                 ereport(ERROR,
    1363             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1364             :                          errmsg("array must have two columns")));
    1365           1 :             break;
    1366             : 
    1367             :         default:
    1368           1 :             ereport(ERROR,
    1369             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1370             :                      errmsg("wrong number of array subscripts")));
    1371             :     }
    1372             : 
    1373           2 :     deconstruct_array(in_array,
    1374             :                       TEXTOID, -1, false, 'i',
    1375             :                       &in_datums, &in_nulls, &in_count);
    1376             : 
    1377           2 :     count = in_count / 2;
    1378             : 
    1379          10 :     for (i = 0; i < count; ++i)
    1380             :     {
    1381             :         JsonbValue  v;
    1382             :         char       *str;
    1383             :         int         len;
    1384             : 
    1385           8 :         if (in_nulls[i * 2])
    1386           0 :             ereport(ERROR,
    1387             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    1388             :                      errmsg("null value not allowed for object key")));
    1389             : 
    1390           8 :         str = TextDatumGetCString(in_datums[i * 2]);
    1391           8 :         len = strlen(str);
    1392             : 
    1393           8 :         v.type = jbvString;
    1394             : 
    1395           8 :         v.val.string.len = len;
    1396           8 :         v.val.string.val = str;
    1397             : 
    1398           8 :         (void) pushJsonbValue(&result.parseState, WJB_KEY, &v);
    1399             : 
    1400           8 :         if (in_nulls[i * 2 + 1])
    1401             :         {
    1402           2 :             v.type = jbvNull;
    1403             :         }
    1404             :         else
    1405             :         {
    1406           6 :             str = TextDatumGetCString(in_datums[i * 2 + 1]);
    1407           6 :             len = strlen(str);
    1408             : 
    1409           6 :             v.type = jbvString;
    1410             : 
    1411           6 :             v.val.string.len = len;
    1412           6 :             v.val.string.val = str;
    1413             :         }
    1414             : 
    1415           8 :         (void) pushJsonbValue(&result.parseState, WJB_VALUE, &v);
    1416             :     }
    1417             : 
    1418           2 :     pfree(in_datums);
    1419           2 :     pfree(in_nulls);
    1420             : 
    1421             : close_object:
    1422           3 :     result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
    1423             : 
    1424           3 :     PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
    1425             : }
    1426             : 
    1427             : /*
    1428             :  * SQL function jsonb_object(text[], text[])
    1429             :  *
    1430             :  * take separate name and value arrays of text to construct a jsonb object
    1431             :  * pairwise.
    1432             :  */
    1433             : Datum
    1434           7 : jsonb_object_two_arg(PG_FUNCTION_ARGS)
    1435             : {
    1436           7 :     ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(0);
    1437           7 :     ArrayType  *val_array = PG_GETARG_ARRAYTYPE_P(1);
    1438           7 :     int         nkdims = ARR_NDIM(key_array);
    1439           7 :     int         nvdims = ARR_NDIM(val_array);
    1440             :     Datum      *key_datums,
    1441             :                *val_datums;
    1442             :     bool       *key_nulls,
    1443             :                *val_nulls;
    1444             :     int         key_count,
    1445             :                 val_count,
    1446             :                 i;
    1447             :     JsonbInState result;
    1448             : 
    1449           7 :     memset(&result, 0, sizeof(JsonbInState));
    1450             : 
    1451           7 :     (void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
    1452             : 
    1453           7 :     if (nkdims > 1 || nkdims != nvdims)
    1454           1 :         ereport(ERROR,
    1455             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1456             :                  errmsg("wrong number of array subscripts")));
    1457             : 
    1458           6 :     if (nkdims == 0)
    1459           1 :         goto close_object;
    1460             : 
    1461           5 :     deconstruct_array(key_array,
    1462             :                       TEXTOID, -1, false, 'i',
    1463             :                       &key_datums, &key_nulls, &key_count);
    1464             : 
    1465           5 :     deconstruct_array(val_array,
    1466             :                       TEXTOID, -1, false, 'i',
    1467             :                       &val_datums, &val_nulls, &val_count);
    1468             : 
    1469           5 :     if (key_count != val_count)
    1470           2 :         ereport(ERROR,
    1471             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1472             :                  errmsg("mismatched array dimensions")));
    1473             : 
    1474          13 :     for (i = 0; i < key_count; ++i)
    1475             :     {
    1476             :         JsonbValue  v;
    1477             :         char       *str;
    1478             :         int         len;
    1479             : 
    1480          11 :         if (key_nulls[i])
    1481           1 :             ereport(ERROR,
    1482             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    1483             :                      errmsg("null value not allowed for object key")));
    1484             : 
    1485          10 :         str = TextDatumGetCString(key_datums[i]);
    1486          10 :         len = strlen(str);
    1487             : 
    1488          10 :         v.type = jbvString;
    1489             : 
    1490          10 :         v.val.string.len = len;
    1491          10 :         v.val.string.val = str;
    1492             : 
    1493          10 :         (void) pushJsonbValue(&result.parseState, WJB_KEY, &v);
    1494             : 
    1495          10 :         if (val_nulls[i])
    1496             :         {
    1497           0 :             v.type = jbvNull;
    1498             :         }
    1499             :         else
    1500             :         {
    1501          10 :             str = TextDatumGetCString(val_datums[i]);
    1502          10 :             len = strlen(str);
    1503             : 
    1504          10 :             v.type = jbvString;
    1505             : 
    1506          10 :             v.val.string.len = len;
    1507          10 :             v.val.string.val = str;
    1508             :         }
    1509             : 
    1510          10 :         (void) pushJsonbValue(&result.parseState, WJB_VALUE, &v);
    1511             :     }
    1512             : 
    1513           2 :     pfree(key_datums);
    1514           2 :     pfree(key_nulls);
    1515           2 :     pfree(val_datums);
    1516           2 :     pfree(val_nulls);
    1517             : 
    1518             : close_object:
    1519           3 :     result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
    1520             : 
    1521           3 :     PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
    1522             : }
    1523             : 
    1524             : 
    1525             : /*
    1526             :  * shallow clone of a parse state, suitable for use in aggregate
    1527             :  * final functions that will only append to the values rather than
    1528             :  * change them.
    1529             :  */
    1530             : static JsonbParseState *
    1531           6 : clone_parse_state(JsonbParseState *state)
    1532             : {
    1533             :     JsonbParseState *result,
    1534             :                *icursor,
    1535             :                *ocursor;
    1536             : 
    1537           6 :     if (state == NULL)
    1538           0 :         return NULL;
    1539             : 
    1540           6 :     result = palloc(sizeof(JsonbParseState));
    1541           6 :     icursor = state;
    1542           6 :     ocursor = result;
    1543             :     for (;;)
    1544             :     {
    1545           6 :         ocursor->contVal = icursor->contVal;
    1546           6 :         ocursor->size = icursor->size;
    1547           6 :         icursor = icursor->next;
    1548           6 :         if (icursor == NULL)
    1549           6 :             break;
    1550           0 :         ocursor->next = palloc(sizeof(JsonbParseState));
    1551           0 :         ocursor = ocursor->next;
    1552           0 :     }
    1553           6 :     ocursor->next = NULL;
    1554             : 
    1555           6 :     return result;
    1556             : }
    1557             : 
    1558             : 
    1559             : /*
    1560             :  * jsonb_agg aggregate function
    1561             :  */
    1562             : Datum
    1563          10 : jsonb_agg_transfn(PG_FUNCTION_ARGS)
    1564             : {
    1565             :     MemoryContext oldcontext,
    1566             :                 aggcontext;
    1567             :     JsonbAggState *state;
    1568             :     JsonbInState elem;
    1569             :     Datum       val;
    1570             :     JsonbInState *result;
    1571          10 :     bool        single_scalar = false;
    1572             :     JsonbIterator *it;
    1573             :     Jsonb      *jbelem;
    1574             :     JsonbValue  v;
    1575             :     JsonbIteratorToken type;
    1576             : 
    1577          10 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
    1578             :     {
    1579             :         /* cannot be called directly because of internal-type argument */
    1580           0 :         elog(ERROR, "jsonb_agg_transfn called in non-aggregate context");
    1581             :     }
    1582             : 
    1583             :     /* set up the accumulator on the first go round */
    1584             : 
    1585          10 :     if (PG_ARGISNULL(0))
    1586             :     {
    1587           3 :         Oid         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
    1588             : 
    1589           3 :         if (arg_type == InvalidOid)
    1590           0 :             ereport(ERROR,
    1591             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1592             :                      errmsg("could not determine input data type")));
    1593             : 
    1594           3 :         oldcontext = MemoryContextSwitchTo(aggcontext);
    1595           3 :         state = palloc(sizeof(JsonbAggState));
    1596           3 :         result = palloc0(sizeof(JsonbInState));
    1597           3 :         state->res = result;
    1598           3 :         result->res = pushJsonbValue(&result->parseState,
    1599             :                                      WJB_BEGIN_ARRAY, NULL);
    1600           3 :         MemoryContextSwitchTo(oldcontext);
    1601             : 
    1602           3 :         jsonb_categorize_type(arg_type, &state->val_category,
    1603             :                               &state->val_output_func);
    1604             :     }
    1605             :     else
    1606             :     {
    1607           7 :         state = (JsonbAggState *) PG_GETARG_POINTER(0);
    1608           7 :         result = state->res;
    1609             :     }
    1610             : 
    1611             :     /* turn the argument into jsonb in the normal function context */
    1612             : 
    1613          10 :     val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
    1614             : 
    1615          10 :     memset(&elem, 0, sizeof(JsonbInState));
    1616             : 
    1617          10 :     datum_to_jsonb(val, PG_ARGISNULL(1), &elem, state->val_category,
    1618             :                    state->val_output_func, false);
    1619             : 
    1620          10 :     jbelem = JsonbValueToJsonb(elem.res);
    1621             : 
    1622             :     /* switch to the aggregate context for accumulation operations */
    1623             : 
    1624          10 :     oldcontext = MemoryContextSwitchTo(aggcontext);
    1625             : 
    1626          10 :     it = JsonbIteratorInit(&jbelem->root);
    1627             : 
    1628         172 :     while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
    1629             :     {
    1630         152 :         switch (type)
    1631             :         {
    1632             :             case WJB_BEGIN_ARRAY:
    1633          12 :                 if (v.val.array.rawScalar)
    1634           0 :                     single_scalar = true;
    1635             :                 else
    1636          12 :                     result->res = pushJsonbValue(&result->parseState,
    1637             :                                                  type, NULL);
    1638          12 :                 break;
    1639             :             case WJB_END_ARRAY:
    1640          12 :                 if (!single_scalar)
    1641          12 :                     result->res = pushJsonbValue(&result->parseState,
    1642             :                                                  type, NULL);
    1643          12 :                 break;
    1644             :             case WJB_BEGIN_OBJECT:
    1645             :             case WJB_END_OBJECT:
    1646          36 :                 result->res = pushJsonbValue(&result->parseState,
    1647             :                                              type, NULL);
    1648          36 :                 break;
    1649             :             case WJB_ELEM:
    1650             :             case WJB_KEY:
    1651             :             case WJB_VALUE:
    1652          92 :                 if (v.type == jbvString)
    1653             :                 {
    1654             :                     /* copy string values in the aggregate context */
    1655          50 :                     char       *buf = palloc(v.val.string.len + 1);
    1656             : 
    1657          50 :                     snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
    1658          50 :                     v.val.string.val = buf;
    1659             :                 }
    1660          42 :                 else if (v.type == jbvNumeric)
    1661             :                 {
    1662             :                     /* same for numeric */
    1663          41 :                     v.val.numeric =
    1664          41 :                         DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
    1665             :                                                             NumericGetDatum(v.val.numeric)));
    1666             :                 }
    1667          92 :                 result->res = pushJsonbValue(&result->parseState,
    1668             :                                              type, &v);
    1669          92 :                 break;
    1670             :             default:
    1671           0 :                 elog(ERROR, "unknown jsonb iterator token type");
    1672             :         }
    1673             :     }
    1674             : 
    1675          10 :     MemoryContextSwitchTo(oldcontext);
    1676             : 
    1677          10 :     PG_RETURN_POINTER(state);
    1678             : }
    1679             : 
    1680             : Datum
    1681           3 : jsonb_agg_finalfn(PG_FUNCTION_ARGS)
    1682             : {
    1683             :     JsonbAggState *arg;
    1684             :     JsonbInState result;
    1685             :     Jsonb      *out;
    1686             : 
    1687             :     /* cannot be called directly because of internal-type argument */
    1688           3 :     Assert(AggCheckCallContext(fcinfo, NULL));
    1689             : 
    1690           3 :     if (PG_ARGISNULL(0))
    1691           0 :         PG_RETURN_NULL();       /* returns null iff no input values */
    1692             : 
    1693           3 :     arg = (JsonbAggState *) PG_GETARG_POINTER(0);
    1694             : 
    1695             :     /*
    1696             :      * We need to do a shallow clone of the argument in case the final
    1697             :      * function is called more than once, so we avoid changing the argument. A
    1698             :      * shallow clone is sufficient as we aren't going to change any of the
    1699             :      * values, just add the final array end marker.
    1700             :      */
    1701             : 
    1702           3 :     result.parseState = clone_parse_state(arg->res->parseState);
    1703             : 
    1704           3 :     result.res = pushJsonbValue(&result.parseState,
    1705             :                                 WJB_END_ARRAY, NULL);
    1706             : 
    1707           3 :     out = JsonbValueToJsonb(result.res);
    1708             : 
    1709           3 :     PG_RETURN_POINTER(out);
    1710             : }
    1711             : 
    1712             : /*
    1713             :  * jsonb_object_agg aggregate function
    1714             :  */
    1715             : Datum
    1716          12 : jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
    1717             : {
    1718             :     MemoryContext oldcontext,
    1719             :                 aggcontext;
    1720             :     JsonbInState elem;
    1721             :     JsonbAggState *state;
    1722             :     Datum       val;
    1723             :     JsonbInState *result;
    1724             :     bool        single_scalar;
    1725             :     JsonbIterator *it;
    1726             :     Jsonb      *jbkey,
    1727             :                *jbval;
    1728             :     JsonbValue  v;
    1729             :     JsonbIteratorToken type;
    1730             : 
    1731          12 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
    1732             :     {
    1733             :         /* cannot be called directly because of internal-type argument */
    1734           0 :         elog(ERROR, "jsonb_object_agg_transfn called in non-aggregate context");
    1735             :     }
    1736             : 
    1737             :     /* set up the accumulator on the first go round */
    1738             : 
    1739          12 :     if (PG_ARGISNULL(0))
    1740             :     {
    1741             :         Oid         arg_type;
    1742             : 
    1743           5 :         oldcontext = MemoryContextSwitchTo(aggcontext);
    1744           5 :         state = palloc(sizeof(JsonbAggState));
    1745           5 :         result = palloc0(sizeof(JsonbInState));
    1746           5 :         state->res = result;
    1747           5 :         result->res = pushJsonbValue(&result->parseState,
    1748             :                                      WJB_BEGIN_OBJECT, NULL);
    1749           5 :         MemoryContextSwitchTo(oldcontext);
    1750             : 
    1751           5 :         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
    1752             : 
    1753           5 :         if (arg_type == InvalidOid)
    1754           0 :             ereport(ERROR,
    1755             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1756             :                      errmsg("could not determine input data type")));
    1757             : 
    1758           5 :         jsonb_categorize_type(arg_type, &state->key_category,
    1759             :                               &state->key_output_func);
    1760             : 
    1761           5 :         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
    1762             : 
    1763           5 :         if (arg_type == InvalidOid)
    1764           0 :             ereport(ERROR,
    1765             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1766             :                      errmsg("could not determine input data type")));
    1767             : 
    1768           5 :         jsonb_categorize_type(arg_type, &state->val_category,
    1769             :                               &state->val_output_func);
    1770             :     }
    1771             :     else
    1772             :     {
    1773           7 :         state = (JsonbAggState *) PG_GETARG_POINTER(0);
    1774           7 :         result = state->res;
    1775             :     }
    1776             : 
    1777             :     /* turn the argument into jsonb in the normal function context */
    1778             : 
    1779          12 :     if (PG_ARGISNULL(1))
    1780           2 :         ereport(ERROR,
    1781             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1782             :                  errmsg("field name must not be null")));
    1783             : 
    1784          10 :     val = PG_GETARG_DATUM(1);
    1785             : 
    1786          10 :     memset(&elem, 0, sizeof(JsonbInState));
    1787             : 
    1788          10 :     datum_to_jsonb(val, false, &elem, state->key_category,
    1789             :                    state->key_output_func, true);
    1790             : 
    1791          10 :     jbkey = JsonbValueToJsonb(elem.res);
    1792             : 
    1793          10 :     val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2);
    1794             : 
    1795          10 :     memset(&elem, 0, sizeof(JsonbInState));
    1796             : 
    1797          10 :     datum_to_jsonb(val, PG_ARGISNULL(2), &elem, state->val_category,
    1798             :                    state->val_output_func, false);
    1799             : 
    1800          10 :     jbval = JsonbValueToJsonb(elem.res);
    1801             : 
    1802          10 :     it = JsonbIteratorInit(&jbkey->root);
    1803             : 
    1804             :     /* switch to the aggregate context for accumulation operations */
    1805             : 
    1806          10 :     oldcontext = MemoryContextSwitchTo(aggcontext);
    1807             : 
    1808             :     /*
    1809             :      * keys should be scalar, and we should have already checked for that
    1810             :      * above when calling datum_to_jsonb, so we only need to look for these
    1811             :      * things.
    1812             :      */
    1813             : 
    1814          10 :     while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
    1815             :     {
    1816          30 :         switch (type)
    1817             :         {
    1818             :             case WJB_BEGIN_ARRAY:
    1819          10 :                 if (!v.val.array.rawScalar)
    1820           0 :                     elog(ERROR, "unexpected structure for key");
    1821          10 :                 break;
    1822             :             case WJB_ELEM:
    1823          10 :                 if (v.type == jbvString)
    1824             :                 {
    1825             :                     /* copy string values in the aggregate context */
    1826          10 :                     char       *buf = palloc(v.val.string.len + 1);
    1827             : 
    1828          10 :                     snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
    1829          10 :                     v.val.string.val = buf;
    1830             :                 }
    1831             :                 else
    1832             :                 {
    1833           0 :                     ereport(ERROR,
    1834             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1835             :                              errmsg("object keys must be strings")));
    1836             :                 }
    1837          10 :                 result->res = pushJsonbValue(&result->parseState,
    1838             :                                              WJB_KEY, &v);
    1839          10 :                 break;
    1840             :             case WJB_END_ARRAY:
    1841          10 :                 break;
    1842             :             default:
    1843           0 :                 elog(ERROR, "unexpected structure for key");
    1844             :                 break;
    1845             :         }
    1846             :     }
    1847             : 
    1848          10 :     it = JsonbIteratorInit(&jbval->root);
    1849             : 
    1850          10 :     single_scalar = false;
    1851             : 
    1852             :     /*
    1853             :      * values can be anything, including structured and null, so we treat them
    1854             :      * as in json_agg_transfn, except that single scalars are always pushed as
    1855             :      * WJB_VALUE items.
    1856             :      */
    1857             : 
    1858          59 :     while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
    1859             :     {
    1860          39 :         switch (type)
    1861             :         {
    1862             :             case WJB_BEGIN_ARRAY:
    1863           7 :                 if (v.val.array.rawScalar)
    1864           7 :                     single_scalar = true;
    1865             :                 else
    1866           0 :                     result->res = pushJsonbValue(&result->parseState,
    1867             :                                                  type, NULL);
    1868           7 :                 break;
    1869             :             case WJB_END_ARRAY:
    1870           7 :                 if (!single_scalar)
    1871           0 :                     result->res = pushJsonbValue(&result->parseState,
    1872             :                                                  type, NULL);
    1873           7 :                 break;
    1874             :             case WJB_BEGIN_OBJECT:
    1875             :             case WJB_END_OBJECT:
    1876           6 :                 result->res = pushJsonbValue(&result->parseState,
    1877             :                                              type, NULL);
    1878           6 :                 break;
    1879             :             case WJB_ELEM:
    1880             :             case WJB_KEY:
    1881             :             case WJB_VALUE:
    1882          19 :                 if (v.type == jbvString)
    1883             :                 {
    1884             :                     /* copy string values in the aggregate context */
    1885          18 :                     char       *buf = palloc(v.val.string.len + 1);
    1886             : 
    1887          18 :                     snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
    1888          18 :                     v.val.string.val = buf;
    1889             :                 }
    1890           1 :                 else if (v.type == jbvNumeric)
    1891             :                 {
    1892             :                     /* same for numeric */
    1893           0 :                     v.val.numeric =
    1894           0 :                         DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
    1895             :                                                             NumericGetDatum(v.val.numeric)));
    1896             :                 }
    1897          19 :                 result->res = pushJsonbValue(&result->parseState,
    1898             :                                              single_scalar ? WJB_VALUE : type,
    1899             :                                              &v);
    1900          19 :                 break;
    1901             :             default:
    1902           0 :                 elog(ERROR, "unknown jsonb iterator token type");
    1903             :         }
    1904             :     }
    1905             : 
    1906          10 :     MemoryContextSwitchTo(oldcontext);
    1907             : 
    1908          10 :     PG_RETURN_POINTER(state);
    1909             : }
    1910             : 
    1911             : Datum
    1912           3 : jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
    1913             : {
    1914             :     JsonbAggState *arg;
    1915             :     JsonbInState result;
    1916             :     Jsonb      *out;
    1917             : 
    1918             :     /* cannot be called directly because of internal-type argument */
    1919           3 :     Assert(AggCheckCallContext(fcinfo, NULL));
    1920             : 
    1921           3 :     if (PG_ARGISNULL(0))
    1922           0 :         PG_RETURN_NULL();       /* returns null iff no input values */
    1923             : 
    1924           3 :     arg = (JsonbAggState *) PG_GETARG_POINTER(0);
    1925             : 
    1926             :     /*
    1927             :      * We need to do a shallow clone of the argument's res field in case the
    1928             :      * final function is called more than once, so we avoid changing the
    1929             :      * aggregate state value.  A shallow clone is sufficient as we aren't
    1930             :      * going to change any of the values, just add the final object end
    1931             :      * marker.
    1932             :      */
    1933             : 
    1934           3 :     result.parseState = clone_parse_state(arg->res->parseState);
    1935             : 
    1936           3 :     result.res = pushJsonbValue(&result.parseState,
    1937             :                                 WJB_END_OBJECT, NULL);
    1938             : 
    1939           3 :     out = JsonbValueToJsonb(result.res);
    1940             : 
    1941           3 :     PG_RETURN_POINTER(out);
    1942             : }

Generated by: LCOV version 1.11