Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Enumerations | Functions | Variables

json.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "access/transam.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/json.h"
#include "utils/jsonapi.h"
#include "utils/typcache.h"
#include "utils/syscache.h"
Include dependency graph for json.c:

Go to the source code of this file.

Defines

#define TYPCATEGORY_JSON   'j'
#define TYPCATEGORY_JSON_CAST   'c'
#define NON_NUMERIC_LETTER   "NnAaIiFfTtYy"
#define JSON_ALPHANUMERIC_CHAR(c)

Enumerations

enum  JsonParseContext {
  JSON_PARSE_VALUE, JSON_PARSE_STRING, JSON_PARSE_ARRAY_START, JSON_PARSE_ARRAY_NEXT,
  JSON_PARSE_OBJECT_START, JSON_PARSE_OBJECT_LABEL, JSON_PARSE_OBJECT_NEXT, JSON_PARSE_OBJECT_COMMA,
  JSON_PARSE_END
}

Functions

static void json_lex (JsonLexContext *lex)
static void json_lex_string (JsonLexContext *lex)
static void json_lex_number (JsonLexContext *lex, char *s)
static void parse_scalar (JsonLexContext *lex, JsonSemAction sem)
static void parse_object_field (JsonLexContext *lex, JsonSemAction sem)
static void parse_object (JsonLexContext *lex, JsonSemAction sem)
static void parse_array_element (JsonLexContext *lex, JsonSemAction sem)
static void parse_array (JsonLexContext *lex, JsonSemAction sem)
static void report_parse_error (JsonParseContext ctx, JsonLexContext *lex)
static void report_invalid_token (JsonLexContext *lex)
static int report_json_context (JsonLexContext *lex)
static char * extract_mb_char (char *s)
static void composite_to_json (Datum composite, StringInfo result, bool use_line_feeds)
static void array_dim_to_json (StringInfo result, int dim, int ndims, int *dims, Datum *vals, bool *nulls, int *valcount, TYPCATEGORY tcategory, Oid typoutputfunc, bool use_line_feeds)
static void array_to_json_internal (Datum array, StringInfo result, bool use_line_feeds)
static JsonTokenType lex_peek (JsonLexContext *lex)
static bool lex_accept (JsonLexContext *lex, JsonTokenType token, char **lexeme)
static void lex_expect (JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
Datum json_in (PG_FUNCTION_ARGS)
Datum json_out (PG_FUNCTION_ARGS)
Datum json_send (PG_FUNCTION_ARGS)
Datum json_recv (PG_FUNCTION_ARGS)
JsonLexContextmakeJsonLexContext (text *json, bool need_escapes)
void pg_parse_json (JsonLexContext *lex, JsonSemAction sem)
static void datum_to_json (Datum val, bool is_null, StringInfo result, TYPCATEGORY tcategory, Oid typoutputfunc)
Datum array_to_json (PG_FUNCTION_ARGS)
Datum array_to_json_pretty (PG_FUNCTION_ARGS)
Datum row_to_json (PG_FUNCTION_ARGS)
Datum row_to_json_pretty (PG_FUNCTION_ARGS)
Datum to_json (PG_FUNCTION_ARGS)
Datum json_agg_transfn (PG_FUNCTION_ARGS)
Datum json_agg_finalfn (PG_FUNCTION_ARGS)
void escape_json (StringInfo buf, const char *str)

Variables

static jsonSemAction nullSemAction
static JsonSemAction NullSemAction = &nullSemAction

Define Documentation

#define JSON_ALPHANUMERIC_CHAR (   c  ) 
Value:
(((c) >= 'a' && (c) <= 'z') || \
     ((c) >= 'A' && (c) <= 'Z') || \
     ((c) >= '0' && (c) <= '9') || \
     (c) == '_' || \
     IS_HIGHBIT_SET(c))

Definition at line 154 of file json.c.

Referenced by json_lex(), and json_lex_number().

#define NON_NUMERIC_LETTER   "NnAaIiFfTtYy"

Definition at line 152 of file json.c.

Referenced by datum_to_json().

#define TYPCATEGORY_JSON   'j'

Definition at line 148 of file json.c.

Referenced by datum_to_json().

#define TYPCATEGORY_JSON_CAST   'c'

Definition at line 150 of file json.c.

Referenced by datum_to_json().


Enumeration Type Documentation

Enumerator:
JSON_PARSE_VALUE 
JSON_PARSE_STRING 
JSON_PARSE_ARRAY_START 
JSON_PARSE_ARRAY_NEXT 
JSON_PARSE_OBJECT_START 
JSON_PARSE_OBJECT_LABEL 
JSON_PARSE_OBJECT_NEXT 
JSON_PARSE_OBJECT_COMMA 
JSON_PARSE_END 

Definition at line 38 of file json.c.

{
    JSON_PARSE_VALUE,           /* expecting a value */
    JSON_PARSE_STRING,          /* expecting a string (for a field name) */
    JSON_PARSE_ARRAY_START,     /* saw '[', expecting value or ']' */
    JSON_PARSE_ARRAY_NEXT,      /* saw array element, expecting ',' or ']' */
    JSON_PARSE_OBJECT_START,    /* saw '{', expecting label or '}' */
    JSON_PARSE_OBJECT_LABEL,    /* saw object label, expecting ':' */
    JSON_PARSE_OBJECT_NEXT,     /* saw object value, expecting ',' or '}' */
    JSON_PARSE_OBJECT_COMMA,    /* saw object ',', expecting next label */
    JSON_PARSE_END              /* saw the end of a document, expect nothing */
}   JsonParseContext;


Function Documentation

static void array_dim_to_json ( StringInfo  result,
int  dim,
int  ndims,
int *  dims,
Datum vals,
bool nulls,
int *  valcount,
TYPCATEGORY  tcategory,
Oid  typoutputfunc,
bool  use_line_feeds 
) [static]

Definition at line 1198 of file json.c.

References appendStringInfoChar(), appendStringInfoString(), Assert, datum_to_json(), and i.

Referenced by array_to_json_internal().

{
    int         i;
    const char *sep;

    Assert(dim < ndims);

    sep = use_line_feeds ? ",\n " : ",";

    appendStringInfoChar(result, '[');

    for (i = 1; i <= dims[dim]; i++)
    {
        if (i > 1)
            appendStringInfoString(result, sep);

        if (dim + 1 == ndims)
        {
            datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
                          typoutputfunc);
            (*valcount)++;
        }
        else
        {
            /*
             * Do we want line feeds on inner dimensions of arrays? For now
             * we'll say no.
             */
            array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
                              valcount, tcategory, typoutputfunc, false);
        }
    }

    appendStringInfoChar(result, ']');
}

Datum array_to_json ( PG_FUNCTION_ARGS   ) 
static void array_to_json_internal ( Datum  array,
StringInfo  result,
bool  use_line_feeds 
) [static]

Definition at line 1240 of file json.c.

References appendStringInfoString(), ARR_DIMS, ARR_ELEMTYPE, ARR_NDIM, array_dim_to_json(), ArrayGetNItems(), CASTSOURCETARGET, COERCION_METHOD_FUNCTION, DatumGetArrayTypeP, deconstruct_array(), FirstNormalObjectId, get_type_io_data(), GETSTRUCT, HeapTupleIsValid, InvalidOid, IOFunc_output, JSONOID, ObjectIdGetDatum, pfree(), RECORDOID, ReleaseSysCache(), SearchSysCache2, and TypeCategory().

Referenced by array_to_json(), array_to_json_pretty(), and datum_to_json().

{
    ArrayType  *v = DatumGetArrayTypeP(array);
    Oid         element_type = ARR_ELEMTYPE(v);
    int        *dim;
    int         ndim;
    int         nitems;
    int         count = 0;
    Datum      *elements;
    bool       *nulls;
    int16       typlen;
    bool        typbyval;
    char        typalign,
                typdelim;
    Oid         typioparam;
    Oid         typoutputfunc;
    TYPCATEGORY tcategory;
    Oid         castfunc = InvalidOid;

    ndim = ARR_NDIM(v);
    dim = ARR_DIMS(v);
    nitems = ArrayGetNItems(ndim, dim);

    if (nitems <= 0)
    {
        appendStringInfoString(result, "[]");
        return;
    }

    get_type_io_data(element_type, IOFunc_output,
                     &typlen, &typbyval, &typalign,
                     &typdelim, &typioparam, &typoutputfunc);

    if (element_type > FirstNormalObjectId)
    {
        HeapTuple   tuple;
        Form_pg_cast castForm;

        tuple = SearchSysCache2(CASTSOURCETARGET,
                                ObjectIdGetDatum(element_type),
                                ObjectIdGetDatum(JSONOID));
        if (HeapTupleIsValid(tuple))
        {
            castForm = (Form_pg_cast) GETSTRUCT(tuple);

            if (castForm->castmethod == COERCION_METHOD_FUNCTION)
                castfunc = typoutputfunc = castForm->castfunc;

            ReleaseSysCache(tuple);
        }
    }

    deconstruct_array(v, element_type, typlen, typbyval,
                      typalign, &elements, &nulls,
                      &nitems);

    if (castfunc != InvalidOid)
        tcategory = TYPCATEGORY_JSON_CAST;
    else if (element_type == RECORDOID)
        tcategory = TYPCATEGORY_COMPOSITE;
    else if (element_type == JSONOID)
        tcategory = TYPCATEGORY_JSON;
    else
        tcategory = TypeCategory(element_type);

    array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
                      typoutputfunc, use_line_feeds);

    pfree(elements);
    pfree(nulls);
}

Datum array_to_json_pretty ( PG_FUNCTION_ARGS   ) 

Definition at line 1441 of file json.c.

References array_to_json_internal(), cstring_to_text(), StringInfoData::data, makeStringInfo(), PG_GETARG_BOOL, PG_GETARG_DATUM, and PG_RETURN_TEXT_P.

{
    Datum       array = PG_GETARG_DATUM(0);
    bool        use_line_feeds = PG_GETARG_BOOL(1);
    StringInfo  result;

    result = makeStringInfo();

    array_to_json_internal(array, result, use_line_feeds);

    PG_RETURN_TEXT_P(cstring_to_text(result->data));
}

static void composite_to_json ( Datum  composite,
StringInfo  result,
bool  use_line_feeds 
) [static]

Definition at line 1316 of file json.c.

References appendStringInfoChar(), appendStringInfoString(), tupleDesc::attrs, CASTSOURCETARGET, COERCION_METHOD_FUNCTION, datum_to_json(), DatumGetHeapTupleHeader, DatumGetPointer, escape_json(), FirstNormalObjectId, GETSTRUCT, getTypeOutputInfo(), heap_getattr, HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, HeapTupleIsValid, i, InvalidOid, JSONOID, lookup_rowtype_tupdesc(), NameStr, tupleDesc::natts, ObjectIdGetDatum, pfree(), PG_DETOAST_DATUM, PointerGetDatum, RECORDARRAYOID, RECORDOID, ReleaseSysCache(), ReleaseTupleDesc, SearchSysCache2, HeapTupleData::t_data, HeapTupleData::t_len, TypeCategory(), and val.

Referenced by datum_to_json(), row_to_json(), and row_to_json_pretty().

{
    HeapTupleHeader td;
    Oid         tupType;
    int32       tupTypmod;
    TupleDesc   tupdesc;
    HeapTupleData tmptup,
               *tuple;
    int         i;
    bool        needsep = false;
    const char *sep;

    sep = use_line_feeds ? ",\n " : ",";

    td = DatumGetHeapTupleHeader(composite);

    /* Extract rowtype info and find a tupdesc */
    tupType = HeapTupleHeaderGetTypeId(td);
    tupTypmod = HeapTupleHeaderGetTypMod(td);
    tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);

    /* Build a temporary HeapTuple control structure */
    tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
    tmptup.t_data = td;
    tuple = &tmptup;

    appendStringInfoChar(result, '{');

    for (i = 0; i < tupdesc->natts; i++)
    {
        Datum       val,
                    origval;
        bool        isnull;
        char       *attname;
        TYPCATEGORY tcategory;
        Oid         typoutput;
        bool        typisvarlena;
        Oid         castfunc = InvalidOid;

        if (tupdesc->attrs[i]->attisdropped)
            continue;

        if (needsep)
            appendStringInfoString(result, sep);
        needsep = true;

        attname = NameStr(tupdesc->attrs[i]->attname);
        escape_json(result, attname);
        appendStringInfoChar(result, ':');

        origval = heap_getattr(tuple, i + 1, tupdesc, &isnull);

        getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
                          &typoutput, &typisvarlena);

        if (tupdesc->attrs[i]->atttypid > FirstNormalObjectId)
        {
            HeapTuple   cast_tuple;
            Form_pg_cast castForm;

            cast_tuple = SearchSysCache2(CASTSOURCETARGET,
                               ObjectIdGetDatum(tupdesc->attrs[i]->atttypid),
                                         ObjectIdGetDatum(JSONOID));
            if (HeapTupleIsValid(cast_tuple))
            {
                castForm = (Form_pg_cast) GETSTRUCT(cast_tuple);

                if (castForm->castmethod == COERCION_METHOD_FUNCTION)
                    castfunc = typoutput = castForm->castfunc;

                ReleaseSysCache(cast_tuple);
            }
        }

        if (castfunc != InvalidOid)
            tcategory = TYPCATEGORY_JSON_CAST;
        else if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID)
            tcategory = TYPCATEGORY_ARRAY;
        else if (tupdesc->attrs[i]->atttypid == RECORDOID)
            tcategory = TYPCATEGORY_COMPOSITE;
        else if (tupdesc->attrs[i]->atttypid == JSONOID)
            tcategory = TYPCATEGORY_JSON;
        else
            tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);

        /*
         * If we have a toasted datum, forcibly detoast it here to avoid
         * memory leakage inside the type's output routine.
         */
        if (typisvarlena && !isnull)
            val = PointerGetDatum(PG_DETOAST_DATUM(origval));
        else
            val = origval;

        datum_to_json(val, isnull, result, tcategory, typoutput);

        /* Clean up detoasted copy, if any */
        if (val != origval)
            pfree(DatumGetPointer(val));
    }

    appendStringInfoChar(result, '}');
    ReleaseTupleDesc(tupdesc);
}

static void datum_to_json ( Datum  val,
bool  is_null,
StringInfo  result,
TYPCATEGORY  tcategory,
Oid  typoutputfunc 
) [static]

Definition at line 1130 of file json.c.

References appendStringInfoString(), array_to_json_internal(), composite_to_json(), DatumGetBool, DatumGetTextP, escape_json(), NON_NUMERIC_LETTER, NULL, OidFunctionCall1, OidOutputFunctionCall(), pfree(), text_to_cstring(), TYPCATEGORY_ARRAY, TYPCATEGORY_BOOLEAN, TYPCATEGORY_COMPOSITE, TYPCATEGORY_JSON, TYPCATEGORY_JSON_CAST, and TYPCATEGORY_NUMERIC.

Referenced by array_dim_to_json(), composite_to_json(), json_agg_transfn(), and to_json().

{
    char       *outputstr;
    text       *jsontext;

    if (is_null)
    {
        appendStringInfoString(result, "null");
        return;
    }

    switch (tcategory)
    {
        case TYPCATEGORY_ARRAY:
            array_to_json_internal(val, result, false);
            break;
        case TYPCATEGORY_COMPOSITE:
            composite_to_json(val, result, false);
            break;
        case TYPCATEGORY_BOOLEAN:
            if (DatumGetBool(val))
                appendStringInfoString(result, "true");
            else
                appendStringInfoString(result, "false");
            break;
        case TYPCATEGORY_NUMERIC:
            outputstr = OidOutputFunctionCall(typoutputfunc, val);

            /*
             * Don't call escape_json here if it's a valid JSON number.
             * Numeric output should usually be a valid JSON number and JSON
             * numbers shouldn't be quoted. Quote cases like "Nan" and
             * "Infinity", however.
             */
            if (strpbrk(outputstr, NON_NUMERIC_LETTER) == NULL)
                appendStringInfoString(result, outputstr);
            else
                escape_json(result, outputstr);
            pfree(outputstr);
            break;
        case TYPCATEGORY_JSON:
            /* JSON will already be escaped */
            outputstr = OidOutputFunctionCall(typoutputfunc, val);
            appendStringInfoString(result, outputstr);
            pfree(outputstr);
            break;
        case TYPCATEGORY_JSON_CAST:
            jsontext = DatumGetTextP(OidFunctionCall1(typoutputfunc, val));
            outputstr = text_to_cstring(jsontext);
            appendStringInfoString(result, outputstr);
            pfree(outputstr);
            pfree(jsontext);
            break;
        default:
            outputstr = OidOutputFunctionCall(typoutputfunc, val);
            escape_json(result, outputstr);
            pfree(outputstr);
            break;
    }
}

void escape_json ( StringInfo  buf,
const char *  str 
)

Definition at line 1707 of file json.c.

References appendStringInfo(), appendStringInfoCharMacro, and appendStringInfoString().

Referenced by composite_to_json(), datum_to_json(), escape_yaml(), ExplainDummyGroup(), ExplainOpenGroup(), ExplainProperty(), ExplainPropertyList(), hstore_to_json(), and hstore_to_json_loose().

{
    const char *p;

    appendStringInfoCharMacro(buf, '\"');
    for (p = str; *p; p++)
    {
        switch (*p)
        {
            case '\b':
                appendStringInfoString(buf, "\\b");
                break;
            case '\f':
                appendStringInfoString(buf, "\\f");
                break;
            case '\n':
                appendStringInfoString(buf, "\\n");
                break;
            case '\r':
                appendStringInfoString(buf, "\\r");
                break;
            case '\t':
                appendStringInfoString(buf, "\\t");
                break;
            case '"':
                appendStringInfoString(buf, "\\\"");
                break;
            case '\\':
                appendStringInfoString(buf, "\\\\");
                break;
            default:
                if ((unsigned char) *p < ' ')
                    appendStringInfo(buf, "\\u%04x", (int) *p);
                else
                    appendStringInfoCharMacro(buf, *p);
                break;
        }
    }
    appendStringInfoCharMacro(buf, '\"');
}

static char * extract_mb_char ( char *  s  )  [static]

Definition at line 1110 of file json.c.

References palloc(), and pg_mblen().

Referenced by json_lex_string().

{
    char       *res;
    int         len;

    len = pg_mblen(s);
    res = palloc(len + 1);
    memcpy(res, s, len);
    res[len] = '\0';

    return res;
}

Datum json_agg_finalfn ( PG_FUNCTION_ARGS   ) 

Definition at line 1686 of file json.c.

References AggCheckCallContext(), appendStringInfoChar(), Assert, cstring_to_text(), StringInfoData::data, NULL, PG_ARGISNULL, PG_GETARG_POINTER, PG_RETURN_NULL, and PG_RETURN_TEXT_P.

{
    StringInfo  state;

    /* cannot be called directly because of internal-type argument */
    Assert(AggCheckCallContext(fcinfo, NULL));

    state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);

    if (state == NULL)
        PG_RETURN_NULL();

    appendStringInfoChar(state, ']');

    PG_RETURN_TEXT_P(cstring_to_text(state->data));
}

Datum json_agg_transfn ( PG_FUNCTION_ARGS   ) 

Definition at line 1566 of file json.c.

References AggCheckCallContext(), appendStringInfoChar(), appendStringInfoString(), CASTSOURCETARGET, COERCION_METHOD_FUNCTION, datum_to_json(), DatumGetPointer, elog, ereport, errcode(), errmsg(), ERROR, FirstNormalObjectId, get_fn_expr_argtype(), GETSTRUCT, getTypeOutputInfo(), HeapTupleIsValid, InvalidOid, JSONOID, makeStringInfo(), MemoryContextSwitchTo(), ObjectIdGetDatum, pfree(), PG_ARGISNULL, PG_DETOAST_DATUM, PG_GETARG_DATUM, PG_GETARG_POINTER, PG_RETURN_POINTER, PointerGetDatum, RECORDARRAYOID, RECORDOID, ReleaseSysCache(), SearchSysCache2, TYPCATEGORY_ARRAY, TYPCATEGORY_COMPOSITE, TypeCategory(), and val.

{
    Oid         val_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
    MemoryContext aggcontext,
                oldcontext;
    StringInfo  state;
    Datum       orig_val,
                val;
    TYPCATEGORY tcategory;
    Oid         typoutput;
    bool        typisvarlena;
    Oid         castfunc = InvalidOid;

    if (val_type == InvalidOid)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("could not determine input data type")));

    if (!AggCheckCallContext(fcinfo, &aggcontext))
    {
        /* cannot be called directly because of internal-type argument */
        elog(ERROR, "json_agg_transfn called in non-aggregate context");
    }

    if (PG_ARGISNULL(0))
    {
        /*
         * Make this StringInfo in a context where it will persist for the
         * duration off the aggregate call. It's only needed for this initial
         * piece, as the StringInfo routines make sure they use the right
         * context to enlarge the object if necessary.
         */
        oldcontext = MemoryContextSwitchTo(aggcontext);
        state = makeStringInfo();
        MemoryContextSwitchTo(oldcontext);

        appendStringInfoChar(state, '[');
    }
    else
    {
        state = (StringInfo) PG_GETARG_POINTER(0);
        appendStringInfoString(state, ", ");
    }

    /* fast path for NULLs */
    if (PG_ARGISNULL(1))
    {
        orig_val = (Datum) 0;
        datum_to_json(orig_val, true, state, 0, InvalidOid);
        PG_RETURN_POINTER(state);
    }


    orig_val = PG_GETARG_DATUM(1);

    getTypeOutputInfo(val_type, &typoutput, &typisvarlena);

    if (val_type > FirstNormalObjectId)
    {
        HeapTuple   tuple;
        Form_pg_cast castForm;

        tuple = SearchSysCache2(CASTSOURCETARGET,
                                ObjectIdGetDatum(val_type),
                                ObjectIdGetDatum(JSONOID));
        if (HeapTupleIsValid(tuple))
        {
            castForm = (Form_pg_cast) GETSTRUCT(tuple);

            if (castForm->castmethod == COERCION_METHOD_FUNCTION)
                castfunc = typoutput = castForm->castfunc;

            ReleaseSysCache(tuple);
        }
    }

    if (castfunc != InvalidOid)
        tcategory = TYPCATEGORY_JSON_CAST;
    else if (val_type == RECORDARRAYOID)
        tcategory = TYPCATEGORY_ARRAY;
    else if (val_type == RECORDOID)
        tcategory = TYPCATEGORY_COMPOSITE;
    else if (val_type == JSONOID)
        tcategory = TYPCATEGORY_JSON;
    else
        tcategory = TypeCategory(val_type);

    /*
     * If we have a toasted datum, forcibly detoast it here to avoid memory
     * leakage inside the type's output routine.
     */
    if (typisvarlena)
        val = PointerGetDatum(PG_DETOAST_DATUM(orig_val));
    else
        val = orig_val;

    if (!PG_ARGISNULL(0) &&
      (tcategory == TYPCATEGORY_ARRAY || tcategory == TYPCATEGORY_COMPOSITE))
    {
        appendStringInfoString(state, "\n ");
    }

    datum_to_json(val, false, state, tcategory, typoutput);

    /* Clean up detoasted copy, if any */
    if (val != orig_val)
        pfree(DatumGetPointer(val));

    /*
     * The transition type for array_agg() is declared to be "internal", which
     * is a pass-by-value type the same size as a pointer.  So we can safely
     * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
     */
    PG_RETURN_POINTER(state);
}

Datum json_in ( PG_FUNCTION_ARGS   ) 

Definition at line 165 of file json.c.

References cstring_to_text(), makeJsonLexContext(), PG_GETARG_CSTRING, pg_parse_json(), and PG_RETURN_TEXT_P.

{
    char       *json = PG_GETARG_CSTRING(0);
    text       *result = cstring_to_text(json);
    JsonLexContext *lex;

    /* validate it */
    lex = makeJsonLexContext(result, false);
    pg_parse_json(lex, NullSemAction);

    /* Internal representation is the same as text, for now */
    PG_RETURN_TEXT_P(result);
}

static void json_lex ( JsonLexContext lex  )  [inline, static]

Definition at line 504 of file json.c.

References JsonLexContext::input, JsonLexContext::input_length, JSON_ALPHANUMERIC_CHAR, json_lex_number(), json_lex_string(), JsonLexContext::line_number, memcmp(), JsonLexContext::prev_token_terminator, report_invalid_token(), JsonLexContext::token_start, JsonLexContext::token_terminator, and JsonLexContext::token_type.

Referenced by lex_accept(), and pg_parse_json().

{
    char       *s;
    int         len;

    /* Skip leading whitespace. */
    s = lex->token_terminator;
    len = s - lex->input;
    while (len < lex->input_length &&
           (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r'))
    {
        if (*s == '\n')
            ++lex->line_number;
        ++s;
        ++len;
    }
    lex->token_start = s;

    /* Determine token type. */
    if (len >= lex->input_length)
    {
        lex->token_start = NULL;
        lex->prev_token_terminator = lex->token_terminator;
        lex->token_terminator = s;
        lex->token_type = JSON_TOKEN_END;
    }
    else
        switch (*s)
        {
                /* Single-character token, some kind of punctuation mark. */
            case '{':
                lex->prev_token_terminator = lex->token_terminator;
                lex->token_terminator = s + 1;
                lex->token_type = JSON_TOKEN_OBJECT_START;
                break;
            case '}':
                lex->prev_token_terminator = lex->token_terminator;
                lex->token_terminator = s + 1;
                lex->token_type = JSON_TOKEN_OBJECT_END;
                break;
            case '[':
                lex->prev_token_terminator = lex->token_terminator;
                lex->token_terminator = s + 1;
                lex->token_type = JSON_TOKEN_ARRAY_START;
                break;
            case ']':
                lex->prev_token_terminator = lex->token_terminator;
                lex->token_terminator = s + 1;
                lex->token_type = JSON_TOKEN_ARRAY_END;
                break;
            case ',':
                lex->prev_token_terminator = lex->token_terminator;
                lex->token_terminator = s + 1;
                lex->token_type = JSON_TOKEN_COMMA;
                break;
            case ':':
                lex->prev_token_terminator = lex->token_terminator;
                lex->token_terminator = s + 1;
                lex->token_type = JSON_TOKEN_COLON;
                break;
            case '"':
                /* string */
                json_lex_string(lex);
                lex->token_type = JSON_TOKEN_STRING;
                break;
            case '-':
                /* Negative number. */
                json_lex_number(lex, s + 1);
                lex->token_type = JSON_TOKEN_NUMBER;
                break;
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                /* Positive number. */
                json_lex_number(lex, s);
                lex->token_type = JSON_TOKEN_NUMBER;
                break;
            default:
                {
                    char       *p;

                    /*
                     * We're not dealing with a string, number, legal
                     * punctuation mark, or end of string.  The only legal
                     * tokens we might find here are true, false, and null,
                     * but for error reporting purposes we scan until we see a
                     * non-alphanumeric character.  That way, we can report
                     * the whole word as an unexpected token, rather than just
                     * some unintuitive prefix thereof.
                     */
                    for (p = s; JSON_ALPHANUMERIC_CHAR(*p) && p - s < lex->input_length - len; p++)
                         /* skip */ ;

                    /*
                     * We got some sort of unexpected punctuation or an
                     * otherwise unexpected character, so just complain about
                     * that one character.
                     */
                    if (p == s)
                    {
                        lex->prev_token_terminator = lex->token_terminator;
                        lex->token_terminator = s + 1;
                        report_invalid_token(lex);
                    }

                    /*
                     * We've got a real alphanumeric token here.  If it
                     * happens to be true, false, or null, all is well.  If
                     * not, error out.
                     */
                    lex->prev_token_terminator = lex->token_terminator;
                    lex->token_terminator = p;
                    if (p - s == 4)
                    {
                        if (memcmp(s, "true", 4) == 0)
                            lex->token_type = JSON_TOKEN_TRUE;
                        else if (memcmp(s, "null", 4) == 0)
                            lex->token_type = JSON_TOKEN_NULL;
                        else
                            report_invalid_token(lex);
                    }
                    else if (p - s == 5 && memcmp(s, "false", 5) == 0)
                        lex->token_type = JSON_TOKEN_FALSE;
                    else
                        report_invalid_token(lex);

                }
        }                       /* end of switch */
}

static void json_lex_number ( JsonLexContext lex,
char *  s 
) [inline, static]

Definition at line 824 of file json.c.

References error(), JsonLexContext::input, JsonLexContext::input_length, JSON_ALPHANUMERIC_CHAR, JsonLexContext::prev_token_terminator, report_invalid_token(), and JsonLexContext::token_terminator.

Referenced by json_lex().

{
    bool        error = false;
    char       *p;
    int         len;

    len = s - lex->input;
    /* Part (1): leading sign indicator. */
    /* Caller already did this for us; so do nothing. */

    /* Part (2): parse main digit string. */
    if (*s == '0')
    {
        s++;
        len++;
    }
    else if (*s >= '1' && *s <= '9')
    {
        do
        {
            s++;
            len++;
        } while (*s >= '0' && *s <= '9' && len < lex->input_length);
    }
    else
        error = true;

    /* Part (3): parse optional decimal portion. */
    if (len < lex->input_length && *s == '.')
    {
        s++;
        len++;
        if (len == lex->input_length || *s < '0' || *s > '9')
            error = true;
        else
        {
            do
            {
                s++;
                len++;
            } while (*s >= '0' && *s <= '9' && len < lex->input_length);
        }
    }

    /* Part (4): parse optional exponent. */
    if (len < lex->input_length && (*s == 'e' || *s == 'E'))
    {
        s++;
        len++;
        if (len < lex->input_length && (*s == '+' || *s == '-'))
        {
            s++;
            len++;
        }
        if (len == lex->input_length || *s < '0' || *s > '9')
            error = true;
        else
        {
            do
            {
                s++;
                len++;
            } while (len < lex->input_length && *s >= '0' && *s <= '9');
        }
    }

    /*
     * Check for trailing garbage.  As in json_lex(), any alphanumeric stuff
     * here should be considered part of the token for error-reporting
     * purposes.
     */
    for (p = s; JSON_ALPHANUMERIC_CHAR(*p) && len < lex->input_length; p++, len++)
        error = true;
    lex->prev_token_terminator = lex->token_terminator;
    lex->token_terminator = p;
    if (error)
        report_invalid_token(lex);
}

static void json_lex_string ( JsonLexContext lex  )  [inline, static]

Definition at line 645 of file json.c.

References appendStringInfoChar(), appendStringInfoString(), ereport, errcode(), errdetail(), errmsg(), ERROR, extract_mb_char(), i, JsonLexContext::input, JsonLexContext::input_length, NULL, pfree(), pg_any_to_server(), pg_mblen(), PG_UTF8, pg_utf_mblen(), JsonLexContext::prev_token_terminator, report_invalid_token(), report_json_context(), resetStringInfo(), JsonLexContext::strval, JsonLexContext::token_start, JsonLexContext::token_terminator, and unicode_to_utf8().

Referenced by json_lex().

{
    char       *s;
    int         len;

    if (lex->strval != NULL)
        resetStringInfo(lex->strval);

    len = lex->token_start - lex->input;
    len++;
    for (s = lex->token_start + 1; *s != '"'; s++, len++)
    {
        /* Premature end of the string. */
        if (len >= lex->input_length)
        {
            lex->token_terminator = s;
            report_invalid_token(lex);
        }
        else if ((unsigned char) *s < 32)
        {
            /* Per RFC4627, these characters MUST be escaped. */
            /* Since *s isn't printable, exclude it from the context string */
            lex->token_terminator = s;
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("invalid input syntax for type json"),
                     errdetail("Character with value 0x%02x must be escaped.",
                               (unsigned char) *s),
                     report_json_context(lex)));
        }
        else if (*s == '\\')
        {
            /* OK, we have an escape character. */
            s++;
            len++;
            if (len >= lex->input_length)
            {
                lex->token_terminator = s;
                report_invalid_token(lex);
            }
            else if (*s == 'u')
            {
                int         i;
                int         ch = 0;

                for (i = 1; i <= 4; i++)
                {
                    s++;
                    len++;
                    if (len >= lex->input_length)
                    {
                        lex->token_terminator = s;
                        report_invalid_token(lex);
                    }
                    else if (*s >= '0' && *s <= '9')
                        ch = (ch * 16) + (*s - '0');
                    else if (*s >= 'a' && *s <= 'f')
                        ch = (ch * 16) + (*s - 'a') + 10;
                    else if (*s >= 'A' && *s <= 'F')
                        ch = (ch * 16) + (*s - 'A') + 10;
                    else
                    {
                        lex->token_terminator = s + pg_mblen(s);
                        ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("invalid input syntax for type json"),
                                 errdetail("\"\\u\" must be followed by four hexadecimal digits."),
                                 report_json_context(lex)));
                    }
                }
                if (lex->strval != NULL)
                {
                    char        utf8str[5];
                    int         utf8len;
                    char       *converted;

                    unicode_to_utf8(ch, (unsigned char *) utf8str);
                    utf8len = pg_utf_mblen((unsigned char *) utf8str);
                    utf8str[utf8len] = '\0';
                    converted = pg_any_to_server(utf8str, utf8len, PG_UTF8);
                    appendStringInfoString(lex->strval, converted);
                    if (converted != utf8str)
                        pfree(converted);

                }
            }
            else if (lex->strval != NULL)
            {
                switch (*s)
                {
                    case '"':
                    case '\\':
                    case '/':
                        appendStringInfoChar(lex->strval, *s);
                        break;
                    case 'b':
                        appendStringInfoChar(lex->strval, '\b');
                        break;
                    case 'f':
                        appendStringInfoChar(lex->strval, '\f');
                        break;
                    case 'n':
                        appendStringInfoChar(lex->strval, '\n');
                        break;
                    case 'r':
                        appendStringInfoChar(lex->strval, '\r');
                        break;
                    case 't':
                        appendStringInfoChar(lex->strval, '\t');
                        break;
                    default:
                        /* Not a valid string escape, so error out. */
                        lex->token_terminator = s + pg_mblen(s);
                        ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("invalid input syntax for type json"),
                            errdetail("Escape sequence \"\\%s\" is invalid.",
                                      extract_mb_char(s)),
                                 report_json_context(lex)));
                }
            }
            else if (strchr("\"\\/bfnrt", *s) == NULL)
            {
                /*
                 * Simpler processing if we're not bothered about de-escaping
                 *
                 * It's very tempting to remove the strchr() call here and
                 * replace it with a switch statement, but testing so far has
                 * shown it's not a performance win.
                 */
                lex->token_terminator = s + pg_mblen(s);
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                         errdetail("Escape sequence \"\\%s\" is invalid.",
                                   extract_mb_char(s)),
                         report_json_context(lex)));
            }

        }
        else if (lex->strval != NULL)
        {
            appendStringInfoChar(lex->strval, *s);
        }

    }

    /* Hooray, we found the end of the string! */
    lex->prev_token_terminator = lex->token_terminator;
    lex->token_terminator = s + 1;
}

Datum json_out ( PG_FUNCTION_ARGS   ) 

Definition at line 183 of file json.c.

References PG_GETARG_DATUM, PG_RETURN_CSTRING, and TextDatumGetCString.

{
    /* we needn't detoast because text_to_cstring will handle that */
    Datum       txt = PG_GETARG_DATUM(0);

    PG_RETURN_CSTRING(TextDatumGetCString(txt));
}

Datum json_recv ( PG_FUNCTION_ARGS   ) 

Definition at line 209 of file json.c.

References buf, StringInfoData::cursor, StringInfoData::len, makeJsonLexContext(), palloc(), PG_GETARG_POINTER, pg_parse_json(), PG_RETURN_TEXT_P, pq_getmsgtext(), SET_VARSIZE, VARDATA, and VARHDRSZ.

{
    StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    text       *result;
    char       *str;
    int         nbytes;
    JsonLexContext *lex;

    str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);

    result = palloc(nbytes + VARHDRSZ);
    SET_VARSIZE(result, nbytes + VARHDRSZ);
    memcpy(VARDATA(result), str, nbytes);

    /* Validate it. */
    lex = makeJsonLexContext(result, false);
    pg_parse_json(lex, NullSemAction);

    PG_RETURN_TEXT_P(result);
}

Datum json_send ( PG_FUNCTION_ARGS   ) 
static bool lex_accept ( JsonLexContext lex,
JsonTokenType  token,
char **  lexeme 
) [inline, static]

Definition at line 103 of file json.c.

References StringInfoData::data, json_lex(), JSON_TOKEN_STRING, NULL, palloc(), pstrdup(), JsonLexContext::strval, JsonLexContext::token_start, JsonLexContext::token_terminator, and JsonLexContext::token_type.

Referenced by lex_expect(), parse_array(), parse_object(), parse_object_field(), and parse_scalar().

{
    if (lex->token_type == token)
    {
        if (lexeme != NULL)
        {
            if (lex->token_type == JSON_TOKEN_STRING)
            {
                if (lex->strval != NULL)
                    *lexeme = pstrdup(lex->strval->data);
            }
            else
            {
                int         len = (lex->token_terminator - lex->token_start);
                char       *tokstr = palloc(len + 1);

                memcpy(tokstr, lex->token_start, len);
                tokstr[len] = '\0';
                *lexeme = tokstr;
            }
        }
        json_lex(lex);
        return true;
    }
    return false;
}

static void lex_expect ( JsonParseContext  ctx,
JsonLexContext lex,
JsonTokenType  token 
) [inline, static]

Definition at line 137 of file json.c.

References lex_accept(), NULL, and report_parse_error().

Referenced by parse_array(), parse_object(), parse_object_field(), and pg_parse_json().

{
    if (!lex_accept(lex, token, NULL))
        report_parse_error(ctx, lex);;
}

static JsonTokenType lex_peek ( JsonLexContext lex  )  [inline, static]
JsonLexContext* makeJsonLexContext ( text json,
bool  need_escapes 
)
static void parse_array ( JsonLexContext lex,
JsonSemAction  sem 
) [static]

Definition at line 462 of file json.c.

References jsonSemAction::array_end, jsonSemAction::array_start, JSON_PARSE_ARRAY_NEXT, JSON_PARSE_ARRAY_START, JSON_TOKEN_ARRAY_END, JSON_TOKEN_ARRAY_START, JSON_TOKEN_COMMA, lex_accept(), lex_expect(), JsonLexContext::lex_level, lex_peek(), NULL, parse_array_element(), and jsonSemAction::semstate.

Referenced by parse_array_element(), parse_object_field(), and pg_parse_json().

{
    /*
     * an array is a possibly empty sequence of array elements, separated by
     * commas and surrounded by square brackets.
     */
    json_struct_action astart = sem->array_start;
    json_struct_action aend = sem->array_end;

    if (astart != NULL)
        (*astart) (sem->semstate);

    /*
     * Data inside an array at at a higher nesting level than the array
     * itself. Note that we increment this after we call the semantic routine
     * for the array start and restore it before we call the routine for the
     * array end.
     */
    lex->lex_level++;

    lex_expect(JSON_PARSE_ARRAY_START, lex, JSON_TOKEN_ARRAY_START);
    if (lex_peek(lex) != JSON_TOKEN_ARRAY_END)
    {

        parse_array_element(lex, sem);

        while (lex_accept(lex, JSON_TOKEN_COMMA, NULL))
            parse_array_element(lex, sem);
    }

    lex_expect(JSON_PARSE_ARRAY_NEXT, lex, JSON_TOKEN_ARRAY_END);

    lex->lex_level--;

    if (aend != NULL)
        (*aend) (sem->semstate);
}

static void parse_array_element ( JsonLexContext lex,
JsonSemAction  sem 
) [static]

Definition at line 431 of file json.c.

References jsonSemAction::array_element_end, jsonSemAction::array_element_start, JSON_TOKEN_ARRAY_START, JSON_TOKEN_OBJECT_START, lex_peek(), NULL, parse_array(), parse_object(), parse_scalar(), and jsonSemAction::semstate.

Referenced by parse_array().

{
    json_aelem_action astart = sem->array_element_start;
    json_aelem_action aend = sem->array_element_end;
    JsonTokenType tok = lex_peek(lex);

    bool        isnull;

    isnull = tok == JSON_TOKEN_NULL;

    if (astart != NULL)
        (*astart) (sem->semstate, isnull);

    /* an array element is any object, array or scalar */
    switch (tok)
    {
        case JSON_TOKEN_OBJECT_START:
            parse_object(lex, sem);
            break;
        case JSON_TOKEN_ARRAY_START:
            parse_array(lex, sem);
            break;
        default:
            parse_scalar(lex, sem);
    }

    if (aend != NULL)
        (*aend) (sem->semstate, isnull);
}

static void parse_object ( JsonLexContext lex,
JsonSemAction  sem 
) [static]

Definition at line 383 of file json.c.

References JSON_PARSE_OBJECT_NEXT, JSON_PARSE_OBJECT_START, JSON_TOKEN_COMMA, JSON_TOKEN_OBJECT_END, JSON_TOKEN_OBJECT_START, JSON_TOKEN_STRING, lex_accept(), lex_expect(), JsonLexContext::lex_level, lex_peek(), NULL, jsonSemAction::object_end, jsonSemAction::object_start, parse_object_field(), report_parse_error(), and jsonSemAction::semstate.

Referenced by parse_array_element(), parse_object_field(), and pg_parse_json().

{
    /*
     * an object is a possibly empty sequence of object fields, separated by
     * commas and surrounde by curly braces.
     */
    json_struct_action ostart = sem->object_start;
    json_struct_action oend = sem->object_end;
    JsonTokenType tok;

    if (ostart != NULL)
        (*ostart) (sem->semstate);

    /*
     * Data inside an object at at a higher nesting level than the object
     * itself. Note that we increment this after we call the semantic routine
     * for the object start and restore it before we call the routine for the
     * object end.
     */
    lex->lex_level++;

    /* we know this will succeeed, just clearing the token */
    lex_expect(JSON_PARSE_OBJECT_START, lex, JSON_TOKEN_OBJECT_START);

    tok = lex_peek(lex);
    switch (tok)
    {
        case JSON_TOKEN_STRING:
            parse_object_field(lex, sem);
            while (lex_accept(lex, JSON_TOKEN_COMMA, NULL))
                parse_object_field(lex, sem);
            break;
        case JSON_TOKEN_OBJECT_END:
            break;
        default:
            /* case of an invalid initial token inside the object */
            report_parse_error(JSON_PARSE_OBJECT_START, lex);
    }

    lex_expect(JSON_PARSE_OBJECT_NEXT, lex, JSON_TOKEN_OBJECT_END);

    lex->lex_level--;

    if (oend != NULL)
        (*oend) (sem->semstate);
}

static void parse_object_field ( JsonLexContext lex,
JsonSemAction  sem 
) [static]

Definition at line 335 of file json.c.

References JSON_PARSE_OBJECT_LABEL, JSON_PARSE_STRING, JSON_TOKEN_ARRAY_START, JSON_TOKEN_COLON, JSON_TOKEN_OBJECT_START, JSON_TOKEN_STRING, lex_accept(), lex_expect(), lex_peek(), NULL, jsonSemAction::object_field_end, jsonSemAction::object_field_start, parse_array(), parse_object(), parse_scalar(), pfree(), report_parse_error(), and jsonSemAction::semstate.

Referenced by parse_object().

{
    /*
     * an object field is "fieldname" : value where value can be a scalar,
     * object or array
     */

    char       *fname = NULL;   /* keep compiler quiet */
    json_ofield_action ostart = sem->object_field_start;
    json_ofield_action oend = sem->object_field_end;
    bool        isnull;
    char      **fnameaddr = NULL;
    JsonTokenType tok;

    if (ostart != NULL || oend != NULL)
        fnameaddr = &fname;

    if (!lex_accept(lex, JSON_TOKEN_STRING, fnameaddr))
        report_parse_error(JSON_PARSE_STRING, lex);

    lex_expect(JSON_PARSE_OBJECT_LABEL, lex, JSON_TOKEN_COLON);

    tok = lex_peek(lex);
    isnull = tok == JSON_TOKEN_NULL;

    if (ostart != NULL)
        (*ostart) (sem->semstate, fname, isnull);

    switch (tok)
    {
        case JSON_TOKEN_OBJECT_START:
            parse_object(lex, sem);
            break;
        case JSON_TOKEN_ARRAY_START:
            parse_array(lex, sem);
            break;
        default:
            parse_scalar(lex, sem);
    }

    if (oend != NULL)
        (*oend) (sem->semstate, fname, isnull);

    if (fname != NULL)
        pfree(fname);
}

static void parse_scalar ( JsonLexContext lex,
JsonSemAction  sem 
) [inline, static]

Definition at line 299 of file json.c.

References JSON_PARSE_VALUE, JSON_TOKEN_FALSE, JSON_TOKEN_NULL, JSON_TOKEN_NUMBER, JSON_TOKEN_STRING, JSON_TOKEN_TRUE, lex_accept(), lex_peek(), NULL, report_parse_error(), jsonSemAction::scalar, jsonSemAction::semstate, and val.

Referenced by parse_array_element(), parse_object_field(), and pg_parse_json().

{
    char       *val = NULL;
    json_scalar_action sfunc = sem->scalar;
    char      **valaddr;
    JsonTokenType tok = lex_peek(lex);

    valaddr = sfunc == NULL ? NULL : &val;

    /* a scalar must be a string, a number, true, false, or null */
    switch (tok)
    {
        case JSON_TOKEN_TRUE:
            lex_accept(lex, JSON_TOKEN_TRUE, valaddr);
            break;
        case JSON_TOKEN_FALSE:
            lex_accept(lex, JSON_TOKEN_FALSE, valaddr);
            break;
        case JSON_TOKEN_NULL:
            lex_accept(lex, JSON_TOKEN_NULL, valaddr);
            break;
        case JSON_TOKEN_NUMBER:
            lex_accept(lex, JSON_TOKEN_NUMBER, valaddr);
            break;
        case JSON_TOKEN_STRING:
            lex_accept(lex, JSON_TOKEN_STRING, valaddr);
            break;
        default:
            report_parse_error(JSON_PARSE_VALUE, lex);
    }

    if (sfunc != NULL)
        (*sfunc) (sem->semstate, val, tok);
}

void pg_parse_json ( JsonLexContext lex,
JsonSemAction  sem 
)

Definition at line 263 of file json.c.

References json_lex(), JSON_PARSE_END, JSON_TOKEN_ARRAY_START, JSON_TOKEN_END, JSON_TOKEN_OBJECT_START, lex_expect(), lex_peek(), parse_array(), parse_object(), and parse_scalar().

Referenced by each_worker(), get_json_object_as_hash(), get_worker(), json_array_elements(), json_array_length(), json_in(), json_object_keys(), json_populate_recordset(), and json_recv().

{
    JsonTokenType tok;

    /* get the initial token */
    json_lex(lex);

    tok = lex_peek(lex);

    /* parse by recursive descent */
    switch (tok)
    {
        case JSON_TOKEN_OBJECT_START:
            parse_object(lex, sem);
            break;
        case JSON_TOKEN_ARRAY_START:
            parse_array(lex, sem);
            break;
        default:
            parse_scalar(lex, sem);     /* json can be a bare scalar */
    }

    lex_expect(JSON_PARSE_END, lex, JSON_TOKEN_END);

}

static void report_invalid_token ( JsonLexContext lex  )  [static]

Definition at line 1016 of file json.c.

References ereport, errcode(), errdetail(), errmsg(), ERROR, palloc(), report_json_context(), JsonLexContext::token_start, and JsonLexContext::token_terminator.

Referenced by json_lex(), json_lex_number(), and json_lex_string().

{
    char       *token;
    int         toklen;

    /* Separate out the offending token. */
    toklen = lex->token_terminator - lex->token_start;
    token = palloc(toklen + 1);
    memcpy(token, lex->token_start, toklen);
    token[toklen] = '\0';

    ereport(ERROR,
            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
             errmsg("invalid input syntax for type json"),
             errdetail("Token \"%s\" is invalid.", token),
             report_json_context(lex)));
}

static int report_json_context ( JsonLexContext lex  )  [static]

Definition at line 1045 of file json.c.

References errcontext, JsonLexContext::input, JsonLexContext::input_length, IS_HIGHBIT_SET, JSON_TOKEN_END, palloc(), pg_mblen(), JsonLexContext::token_terminator, and JsonLexContext::token_type.

Referenced by json_lex_string(), report_invalid_token(), and report_parse_error().

{
    const char *context_start;
    const char *context_end;
    const char *line_start;
    int         line_number;
    char       *ctxt;
    int         ctxtlen;
    const char *prefix;
    const char *suffix;

    /* Choose boundaries for the part of the input we will display */
    context_start = lex->input;
    context_end = lex->token_terminator;
    line_start = context_start;
    line_number = 1;
    for (;;)
    {
        /* Always advance over newlines (context_end test is just paranoia) */
        if (*context_start == '\n' && context_start < context_end)
        {
            context_start++;
            line_start = context_start;
            line_number++;
            continue;
        }
        /* Otherwise, done as soon as we are close enough to context_end */
        if (context_end - context_start < 50)
            break;
        /* Advance to next multibyte character */
        if (IS_HIGHBIT_SET(*context_start))
            context_start += pg_mblen(context_start);
        else
            context_start++;
    }

    /*
     * We add "..." to indicate that the excerpt doesn't start at the
     * beginning of the line ... but if we're within 3 characters of the
     * beginning of the line, we might as well just show the whole line.
     */
    if (context_start - line_start <= 3)
        context_start = line_start;

    /* Get a null-terminated copy of the data to present */
    ctxtlen = context_end - context_start;
    ctxt = palloc(ctxtlen + 1);
    memcpy(ctxt, context_start, ctxtlen);
    ctxt[ctxtlen] = '\0';

    /*
     * Show the context, prefixing "..." if not starting at start of line, and
     * suffixing "..." if not ending at end of line.
     */
    prefix = (context_start > line_start) ? "..." : "";
    suffix = (lex->token_type != JSON_TOKEN_END && context_end - lex->input < lex->input_length && *context_end != '\n' && *context_end != '\r') ? "..." : "";

    return errcontext("JSON data, line %d: %s%s%s",
                      line_number, prefix, ctxt, suffix);
}

static void report_parse_error ( JsonParseContext  ctx,
JsonLexContext lex 
) [static]

Definition at line 909 of file json.c.

References elog, ereport, errcode(), errdetail(), errmsg(), ERROR, JSON_PARSE_ARRAY_NEXT, JSON_PARSE_ARRAY_START, JSON_PARSE_END, JSON_PARSE_OBJECT_COMMA, JSON_PARSE_OBJECT_LABEL, JSON_PARSE_OBJECT_NEXT, JSON_PARSE_OBJECT_START, JSON_PARSE_STRING, JSON_PARSE_VALUE, JSON_TOKEN_END, NULL, palloc(), report_json_context(), JsonLexContext::token_start, JsonLexContext::token_terminator, and JsonLexContext::token_type.

Referenced by lex_expect(), parse_object(), parse_object_field(), and parse_scalar().

{
    char       *token;
    int         toklen;

    /* Handle case where the input ended prematurely. */
    if (lex->token_start == NULL || lex->token_type == JSON_TOKEN_END)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid input syntax for type json"),
                 errdetail("The input string ended unexpectedly."),
                 report_json_context(lex)));

    /* Separate out the current token. */
    toklen = lex->token_terminator - lex->token_start;
    token = palloc(toklen + 1);
    memcpy(token, lex->token_start, toklen);
    token[toklen] = '\0';

    /* Complain, with the appropriate detail message. */
    if (ctx == JSON_PARSE_END)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid input syntax for type json"),
                 errdetail("Expected end of input, but found \"%s\".",
                           token),
                 report_json_context(lex)));
    else
    {
        switch (ctx)
        {
            case JSON_PARSE_VALUE:
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                         errdetail("Expected JSON value, but found \"%s\".",
                                   token),
                         report_json_context(lex)));
                break;
            case JSON_PARSE_STRING:
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                         errdetail("Expected string, but found \"%s\".",
                                   token),
                         report_json_context(lex)));
                break;
            case JSON_PARSE_ARRAY_START:
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                         errdetail("Expected array element or \"]\", but found \"%s\".",
                                   token),
                         report_json_context(lex)));
                break;
            case JSON_PARSE_ARRAY_NEXT:
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                      errdetail("Expected \",\" or \"]\", but found \"%s\".",
                                token),
                         report_json_context(lex)));
                break;
            case JSON_PARSE_OBJECT_START:
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                     errdetail("Expected string or \"}\", but found \"%s\".",
                               token),
                         report_json_context(lex)));
                break;
            case JSON_PARSE_OBJECT_LABEL:
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                         errdetail("Expected \":\", but found \"%s\".",
                                   token),
                         report_json_context(lex)));
                break;
            case JSON_PARSE_OBJECT_NEXT:
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                      errdetail("Expected \",\" or \"}\", but found \"%s\".",
                                token),
                         report_json_context(lex)));
                break;
            case JSON_PARSE_OBJECT_COMMA:
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type json"),
                         errdetail("Expected string, but found \"%s\".",
                                   token),
                         report_json_context(lex)));
                break;
            default:
                elog(ERROR, "unexpected json parse state: %d", ctx);
        }
    }
}

Datum row_to_json ( PG_FUNCTION_ARGS   ) 
Datum row_to_json_pretty ( PG_FUNCTION_ARGS   ) 

Definition at line 1474 of file json.c.

References composite_to_json(), cstring_to_text(), StringInfoData::data, makeStringInfo(), PG_GETARG_BOOL, PG_GETARG_DATUM, and PG_RETURN_TEXT_P.

{
    Datum       array = PG_GETARG_DATUM(0);
    bool        use_line_feeds = PG_GETARG_BOOL(1);
    StringInfo  result;

    result = makeStringInfo();

    composite_to_json(array, result, use_line_feeds);

    PG_RETURN_TEXT_P(cstring_to_text(result->data));
}

Datum to_json ( PG_FUNCTION_ARGS   ) 

Definition at line 1491 of file json.c.

References CASTSOURCETARGET, COERCION_METHOD_FUNCTION, cstring_to_text(), StringInfoData::data, datum_to_json(), DatumGetPointer, ereport, errcode(), errmsg(), ERROR, FirstNormalObjectId, get_fn_expr_argtype(), GETSTRUCT, getTypeOutputInfo(), HeapTupleIsValid, InvalidOid, JSONOID, makeStringInfo(), ObjectIdGetDatum, pfree(), PG_ARGISNULL, PG_DETOAST_DATUM, PG_GETARG_DATUM, PG_RETURN_TEXT_P, PointerGetDatum, RECORDARRAYOID, RECORDOID, ReleaseSysCache(), SearchSysCache2, TypeCategory(), and val.

{
    Oid         val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
    StringInfo  result;
    Datum       orig_val,
                val;
    TYPCATEGORY tcategory;
    Oid         typoutput;
    bool        typisvarlena;
    Oid         castfunc = InvalidOid;

    if (val_type == InvalidOid)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("could not determine input data type")));


    result = makeStringInfo();

    orig_val = PG_ARGISNULL(0) ? (Datum) 0 : PG_GETARG_DATUM(0);

    getTypeOutputInfo(val_type, &typoutput, &typisvarlena);

    if (val_type > FirstNormalObjectId)
    {
        HeapTuple   tuple;
        Form_pg_cast castForm;

        tuple = SearchSysCache2(CASTSOURCETARGET,
                                ObjectIdGetDatum(val_type),
                                ObjectIdGetDatum(JSONOID));
        if (HeapTupleIsValid(tuple))
        {
            castForm = (Form_pg_cast) GETSTRUCT(tuple);

            if (castForm->castmethod == COERCION_METHOD_FUNCTION)
                castfunc = typoutput = castForm->castfunc;

            ReleaseSysCache(tuple);
        }
    }

    if (castfunc != InvalidOid)
        tcategory = TYPCATEGORY_JSON_CAST;
    else if (val_type == RECORDARRAYOID)
        tcategory = TYPCATEGORY_ARRAY;
    else if (val_type == RECORDOID)
        tcategory = TYPCATEGORY_COMPOSITE;
    else if (val_type == JSONOID)
        tcategory = TYPCATEGORY_JSON;
    else
        tcategory = TypeCategory(val_type);

    /*
     * If we have a toasted datum, forcibly detoast it here to avoid memory
     * leakage inside the type's output routine.
     */
    if (typisvarlena && orig_val != (Datum) 0)
        val = PointerGetDatum(PG_DETOAST_DATUM(orig_val));
    else
        val = orig_val;

    datum_to_json(val, false, result, tcategory, typoutput);

    /* Clean up detoasted copy, if any */
    if (val != orig_val)
        pfree(DatumGetPointer(val));

    PG_RETURN_TEXT_P(cstring_to_text(result->data));
}


Variable Documentation

Initial value:
{
    NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL
}

Definition at line 73 of file json.c.

Definition at line 78 of file json.c.