Add array_to_json and row_to_json functions.
Also move the escape_json function from explain.c to json.c where it seems to belong. Andrew Dunstan, Reviewd by Abhijit Menon-Sen.
This commit is contained in:
parent
69e9768e7b
commit
39909d1d39
@ -9617,6 +9617,65 @@ table2-mapping
|
|||||||
</sect2>
|
</sect2>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="functions-json">
|
||||||
|
<title>JSON functions</title>
|
||||||
|
|
||||||
|
<indexterm zone="datatype-json">
|
||||||
|
<primary>JSON</primary>
|
||||||
|
<secondary>Functions and operators</secondary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This section descripbes the functions that are available for creating
|
||||||
|
JSON (see <xref linkend="datatype-json">) data.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table id="functions-json-table">
|
||||||
|
<title>JSON Support Functions</title>
|
||||||
|
<tgroup cols="4">
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Function</entry>
|
||||||
|
<entry>Description</entry>
|
||||||
|
<entry>Example</entry>
|
||||||
|
<entry>Example Result</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<indexterm>
|
||||||
|
<primary>array_to_json</primary>
|
||||||
|
</indexterm>
|
||||||
|
<literal>array_to_json(anyarray [, pretty_bool])</literal>
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Returns the array as JSON. A Postgres multi-dimensional array
|
||||||
|
becomes a JSON array of arrays. Line feeds will be added between
|
||||||
|
dimension 1 elements if pretty_bool is true.
|
||||||
|
</entry>
|
||||||
|
<entry><literal>array_to_json('{{1,5},{99,100}}'::int[])</literal></entry>
|
||||||
|
<entry><literal>[[1,5],[99,100]]</literal></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<indexterm>
|
||||||
|
<primary>row_to_json</primary>
|
||||||
|
</indexterm>
|
||||||
|
<literal>row_to_json(record [, pretty_bool])</literal>
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
Returns the row as JSON. Line feeds will be added between level
|
||||||
|
1 elements if pretty_bool is true.
|
||||||
|
</entry>
|
||||||
|
<entry><literal>row_to_json(row(1,'foo'))</literal></entry>
|
||||||
|
<entry><literal>{"f1":1,"f2":"foo"}</literal></entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="functions-sequence">
|
<sect1 id="functions-sequence">
|
||||||
<title>Sequence Manipulation Functions</title>
|
<title>Sequence Manipulation Functions</title>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "rewrite/rewriteHandler.h"
|
#include "rewrite/rewriteHandler.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/json.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
#include "utils/snapmgr.h"
|
#include "utils/snapmgr.h"
|
||||||
@ -99,7 +100,6 @@ static void ExplainDummyGroup(const char *objtype, const char *labelname,
|
|||||||
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
|
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
|
||||||
static void ExplainJSONLineEnding(ExplainState *es);
|
static void ExplainJSONLineEnding(ExplainState *es);
|
||||||
static void ExplainYAMLLineStarting(ExplainState *es);
|
static void ExplainYAMLLineStarting(ExplainState *es);
|
||||||
static void escape_json(StringInfo buf, const char *str);
|
|
||||||
static void escape_yaml(StringInfo buf, const char *str);
|
static void escape_yaml(StringInfo buf, const char *str);
|
||||||
|
|
||||||
|
|
||||||
@ -2318,51 +2318,6 @@ ExplainYAMLLineStarting(ExplainState *es)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Produce a JSON string literal, properly escaping characters in the text.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
escape_json(StringInfo buf, const char *str)
|
|
||||||
{
|
|
||||||
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, '\"');
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* YAML is a superset of JSON; unfortuantely, the YAML quoting rules are
|
* YAML is a superset of JSON; unfortuantely, the YAML quoting rules are
|
||||||
* ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
|
* ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
|
||||||
|
@ -13,11 +13,17 @@
|
|||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
|
#include "executor/spi.h"
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
|
#include "parser/parse_coerce.h"
|
||||||
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/json.h"
|
#include "utils/json.h"
|
||||||
|
#include "utils/typcache.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@ -72,8 +78,11 @@ static void json_lex_number(JsonLexContext *lex, char *s);
|
|||||||
static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex);
|
static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex);
|
||||||
static void report_invalid_token(JsonLexContext *lex);
|
static void report_invalid_token(JsonLexContext *lex);
|
||||||
static char *extract_mb_char(char *s);
|
static char *extract_mb_char(char *s);
|
||||||
|
static void composite_to_json(Datum composite, StringInfo result, bool use_line_feeds);
|
||||||
extern Datum json_in(PG_FUNCTION_ARGS);
|
static void array_dim_to_json(StringInfo result, int dim, int ndims,int * dims,
|
||||||
|
Datum *vals, int * valcount, TYPCATEGORY tcategory,
|
||||||
|
Oid typoutputfunc, bool use_line_feeds);
|
||||||
|
static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Input.
|
* Input.
|
||||||
@ -663,3 +672,344 @@ extract_mb_char(char *s)
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn a scalar Datum into JSON. Hand off a non-scalar datum to
|
||||||
|
* composite_to_json or array_to_json_internal as appropriate.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
datum_to_json(Datum val, StringInfo result, TYPCATEGORY tcategory,
|
||||||
|
Oid typoutputfunc)
|
||||||
|
{
|
||||||
|
|
||||||
|
char *outputstr;
|
||||||
|
|
||||||
|
if (val == (Datum) 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. Numeric output should
|
||||||
|
* be a valid JSON number and JSON numbers shouldn't
|
||||||
|
* be quoted.
|
||||||
|
*/
|
||||||
|
appendStringInfoString(result, outputstr);
|
||||||
|
pfree(outputstr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
outputstr = OidOutputFunctionCall(typoutputfunc, val);
|
||||||
|
escape_json(result, outputstr);
|
||||||
|
pfree(outputstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process a single dimension of an array.
|
||||||
|
* If it's the innermost dimension, output the values, otherwise call
|
||||||
|
* ourselves recursively to process the next dimension.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
array_dim_to_json(StringInfo result, int dim, int ndims,int * dims, Datum *vals,
|
||||||
|
int * valcount, TYPCATEGORY tcategory, Oid typoutputfunc,
|
||||||
|
bool use_line_feeds)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
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],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, valcount,
|
||||||
|
tcategory,typoutputfunc,false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStringInfoChar(result, ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn an array into JSON.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
deconstruct_array(v, element_type, typlen, typbyval,
|
||||||
|
typalign, &elements, &nulls,
|
||||||
|
&nitems);
|
||||||
|
|
||||||
|
/* can't have an array of arrays, so this is the only special case here */
|
||||||
|
if (element_type == RECORDOID)
|
||||||
|
tcategory = TYPCATEGORY_COMPOSITE;
|
||||||
|
else
|
||||||
|
tcategory = TypeCategory(element_type);
|
||||||
|
|
||||||
|
array_dim_to_json(result, 0, ndim, dim, elements, &count, tcategory,
|
||||||
|
typoutputfunc, use_line_feeds);
|
||||||
|
|
||||||
|
pfree(elements);
|
||||||
|
pfree(nulls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn a composite / record into JSON.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
|
||||||
|
{
|
||||||
|
HeapTupleHeader td;
|
||||||
|
Oid tupType;
|
||||||
|
int32 tupTypmod;
|
||||||
|
TupleDesc tupdesc;
|
||||||
|
HeapTupleData tmptup, *tuple;
|
||||||
|
int i;
|
||||||
|
bool needsep = false;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID)
|
||||||
|
tcategory = TYPCATEGORY_ARRAY;
|
||||||
|
else if (tupdesc->attrs[i]->atttypid == RECORDOID)
|
||||||
|
tcategory = TYPCATEGORY_COMPOSITE;
|
||||||
|
else
|
||||||
|
tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
|
||||||
|
|
||||||
|
getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
|
||||||
|
&typoutput, &typisvarlena);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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, result, tcategory, typoutput);
|
||||||
|
|
||||||
|
/* Clean up detoasted copy, if any */
|
||||||
|
if (val != origval)
|
||||||
|
pfree(DatumGetPointer(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStringInfoChar(result,'}');
|
||||||
|
ReleaseTupleDesc(tupdesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL function array_to_json(row)
|
||||||
|
*/
|
||||||
|
extern Datum
|
||||||
|
array_to_json(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum array = PG_GETARG_DATUM(0);
|
||||||
|
StringInfo result;
|
||||||
|
|
||||||
|
result = makeStringInfo();
|
||||||
|
|
||||||
|
array_to_json_internal(array, result, false);
|
||||||
|
|
||||||
|
PG_RETURN_TEXT_P(cstring_to_text(result->data));
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL function array_to_json(row, prettybool)
|
||||||
|
*/
|
||||||
|
extern Datum
|
||||||
|
array_to_json_pretty(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL function row_to_json(row)
|
||||||
|
*/
|
||||||
|
extern Datum
|
||||||
|
row_to_json(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum array = PG_GETARG_DATUM(0);
|
||||||
|
StringInfo result;
|
||||||
|
|
||||||
|
result = makeStringInfo();
|
||||||
|
|
||||||
|
composite_to_json(array, result, false);
|
||||||
|
|
||||||
|
PG_RETURN_TEXT_P(cstring_to_text(result->data));
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL function row_to_json(row, prettybool)
|
||||||
|
*/
|
||||||
|
extern Datum
|
||||||
|
row_to_json_pretty(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Produce a JSON string literal, properly escaping characters in the text.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
escape_json(StringInfo buf, const char *str)
|
||||||
|
{
|
||||||
|
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, '\"');
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -4031,6 +4031,14 @@ DATA(insert OID = 323 ( json_recv PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 11
|
|||||||
DESCR("I/O");
|
DESCR("I/O");
|
||||||
DATA(insert OID = 324 ( json_send PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 17 "114" _null_ _null_ _null_ _null_ json_send _null_ _null_ _null_ ));
|
DATA(insert OID = 324 ( json_send PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 17 "114" _null_ _null_ _null_ _null_ json_send _null_ _null_ _null_ ));
|
||||||
DESCR("I/O");
|
DESCR("I/O");
|
||||||
|
DATA(insert OID = 3153 ( array_to_json PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 114 "2277" _null_ _null_ _null_ _null_ array_to_json _null_ _null_ _null_ ));
|
||||||
|
DESCR("map array to json");
|
||||||
|
DATA(insert OID = 3154 ( array_to_json PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 114 "2277 16" _null_ _null_ _null_ _null_ array_to_json_pretty _null_ _null_ _null_ ));
|
||||||
|
DESCR("map array to json with optional pretty printing");
|
||||||
|
DATA(insert OID = 3155 ( row_to_json PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 114 "2249" _null_ _null_ _null_ _null_ row_to_json _null_ _null_ _null_ ));
|
||||||
|
DESCR("map row to json");
|
||||||
|
DATA(insert OID = 3156 ( row_to_json PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 114 "2249 16" _null_ _null_ _null_ _null_ row_to_json_pretty _null_ _null_ _null_ ));
|
||||||
|
DESCR("map row to json with optional pretty printing");
|
||||||
|
|
||||||
/* uuid */
|
/* uuid */
|
||||||
DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2950 "2275" _null_ _null_ _null_ _null_ uuid_in _null_ _null_ _null_ ));
|
DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2950 "2275" _null_ _null_ _null_ _null_ uuid_in _null_ _null_ _null_ ));
|
||||||
|
@ -20,5 +20,10 @@ extern Datum json_in(PG_FUNCTION_ARGS);
|
|||||||
extern Datum json_out(PG_FUNCTION_ARGS);
|
extern Datum json_out(PG_FUNCTION_ARGS);
|
||||||
extern Datum json_recv(PG_FUNCTION_ARGS);
|
extern Datum json_recv(PG_FUNCTION_ARGS);
|
||||||
extern Datum json_send(PG_FUNCTION_ARGS);
|
extern Datum json_send(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum array_to_json(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum array_to_json_pretty(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum row_to_json(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum row_to_json_pretty(PG_FUNCTION_ARGS);
|
||||||
|
extern void escape_json(StringInfo buf, const char *str);
|
||||||
|
|
||||||
#endif /* XML_H */
|
#endif /* XML_H */
|
||||||
|
@ -256,3 +256,114 @@ ERROR: invalid input syntax for type json: " "
|
|||||||
LINE 1: SELECT ' '::json;
|
LINE 1: SELECT ' '::json;
|
||||||
^
|
^
|
||||||
DETAIL: The input string ended unexpectedly.
|
DETAIL: The input string ended unexpectedly.
|
||||||
|
--constructors
|
||||||
|
-- array_to_json
|
||||||
|
SELECT array_to_json(array(select 1 as a));
|
||||||
|
array_to_json
|
||||||
|
---------------
|
||||||
|
[1]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_to_json(array_agg(q),false) from (select x as b, x * 2 as c from generate_series(1,3) x) q;
|
||||||
|
array_to_json
|
||||||
|
---------------------------------------------------
|
||||||
|
[{"f1":1,"f2":2},{"f1":2,"f2":4},{"f1":3,"f2":6}]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_to_json(array_agg(q),true) from (select x as b, x * 2 as c from generate_series(1,3) x) q;
|
||||||
|
array_to_json
|
||||||
|
-------------------
|
||||||
|
[{"f1":1,"f2":2},+
|
||||||
|
{"f1":2,"f2":4},+
|
||||||
|
{"f1":3,"f2":6}]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_to_json(array_agg(q),false)
|
||||||
|
FROM ( SELECT $$a$$ || x AS b, y AS c,
|
||||||
|
ARRAY[ROW(x.*,ARRAY[1,2,3]),
|
||||||
|
ROW(y.*,ARRAY[4,5,6])] AS z
|
||||||
|
FROM generate_series(1,2) x,
|
||||||
|
generate_series(4,5) y) q;
|
||||||
|
array_to_json
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
[{"b":"a1","c":4,"z":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]},{"b":"a1","c":5,"z":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]},{"b":"a2","c":4,"z":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]},{"b":"a2","c":5,"z":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_to_json(array_agg(x),false) from generate_series(5,10) x;
|
||||||
|
array_to_json
|
||||||
|
----------------
|
||||||
|
[5,6,7,8,9,10]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_to_json('{{1,5},{99,100}}'::int[]);
|
||||||
|
array_to_json
|
||||||
|
------------------
|
||||||
|
[[1,5],[99,100]]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- row_to_json
|
||||||
|
SELECT row_to_json(row(1,'foo'));
|
||||||
|
row_to_json
|
||||||
|
---------------------
|
||||||
|
{"f1":1,"f2":"foo"}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT row_to_json(q)
|
||||||
|
FROM (SELECT $$a$$ || x AS b,
|
||||||
|
y AS c,
|
||||||
|
ARRAY[ROW(x.*,ARRAY[1,2,3]),
|
||||||
|
ROW(y.*,ARRAY[4,5,6])] AS z
|
||||||
|
FROM generate_series(1,2) x,
|
||||||
|
generate_series(4,5) y) q;
|
||||||
|
row_to_json
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
{"b":"a1","c":4,"z":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
|
||||||
|
{"b":"a1","c":5,"z":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
|
||||||
|
{"b":"a2","c":4,"z":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
|
||||||
|
{"b":"a2","c":5,"z":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT row_to_json(q,true)
|
||||||
|
FROM (SELECT $$a$$ || x AS b,
|
||||||
|
y AS c,
|
||||||
|
ARRAY[ROW(x.*,ARRAY[1,2,3]),
|
||||||
|
ROW(y.*,ARRAY[4,5,6])] AS z
|
||||||
|
FROM generate_series(1,2) x,
|
||||||
|
generate_series(4,5) y) q;
|
||||||
|
row_to_json
|
||||||
|
-----------------------------------------------------
|
||||||
|
{"b":"a1", +
|
||||||
|
"c":4, +
|
||||||
|
"z":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
|
||||||
|
{"b":"a1", +
|
||||||
|
"c":5, +
|
||||||
|
"z":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
|
||||||
|
{"b":"a2", +
|
||||||
|
"c":4, +
|
||||||
|
"z":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
|
||||||
|
{"b":"a2", +
|
||||||
|
"c":5, +
|
||||||
|
"z":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
CREATE TEMP TABLE rows AS
|
||||||
|
SELECT x, 'txt' || x as y
|
||||||
|
FROM generate_series(1,3) AS x;
|
||||||
|
SELECT row_to_json(q,true)
|
||||||
|
FROM rows q;
|
||||||
|
row_to_json
|
||||||
|
--------------
|
||||||
|
{"x":1, +
|
||||||
|
"y":"txt1"}
|
||||||
|
{"x":2, +
|
||||||
|
"y":"txt2"}
|
||||||
|
{"x":3, +
|
||||||
|
"y":"txt3"}
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT row_to_json(row((select array_agg(x) as d from generate_series(5,10) x)),false);
|
||||||
|
row_to_json
|
||||||
|
-----------------------
|
||||||
|
{"f1":[5,6,7,8,9,10]}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -54,3 +54,46 @@ SELECT 'truf'::json; -- ERROR, not a keyword
|
|||||||
SELECT 'trues'::json; -- ERROR, not a keyword
|
SELECT 'trues'::json; -- ERROR, not a keyword
|
||||||
SELECT ''::json; -- ERROR, no value
|
SELECT ''::json; -- ERROR, no value
|
||||||
SELECT ' '::json; -- ERROR, no value
|
SELECT ' '::json; -- ERROR, no value
|
||||||
|
|
||||||
|
--constructors
|
||||||
|
-- array_to_json
|
||||||
|
|
||||||
|
SELECT array_to_json(array(select 1 as a));
|
||||||
|
SELECT array_to_json(array_agg(q),false) from (select x as b, x * 2 as c from generate_series(1,3) x) q;
|
||||||
|
SELECT array_to_json(array_agg(q),true) from (select x as b, x * 2 as c from generate_series(1,3) x) q;
|
||||||
|
SELECT array_to_json(array_agg(q),false)
|
||||||
|
FROM ( SELECT $$a$$ || x AS b, y AS c,
|
||||||
|
ARRAY[ROW(x.*,ARRAY[1,2,3]),
|
||||||
|
ROW(y.*,ARRAY[4,5,6])] AS z
|
||||||
|
FROM generate_series(1,2) x,
|
||||||
|
generate_series(4,5) y) q;
|
||||||
|
SELECT array_to_json(array_agg(x),false) from generate_series(5,10) x;
|
||||||
|
SELECT array_to_json('{{1,5},{99,100}}'::int[]);
|
||||||
|
|
||||||
|
-- row_to_json
|
||||||
|
SELECT row_to_json(row(1,'foo'));
|
||||||
|
|
||||||
|
SELECT row_to_json(q)
|
||||||
|
FROM (SELECT $$a$$ || x AS b,
|
||||||
|
y AS c,
|
||||||
|
ARRAY[ROW(x.*,ARRAY[1,2,3]),
|
||||||
|
ROW(y.*,ARRAY[4,5,6])] AS z
|
||||||
|
FROM generate_series(1,2) x,
|
||||||
|
generate_series(4,5) y) q;
|
||||||
|
|
||||||
|
SELECT row_to_json(q,true)
|
||||||
|
FROM (SELECT $$a$$ || x AS b,
|
||||||
|
y AS c,
|
||||||
|
ARRAY[ROW(x.*,ARRAY[1,2,3]),
|
||||||
|
ROW(y.*,ARRAY[4,5,6])] AS z
|
||||||
|
FROM generate_series(1,2) x,
|
||||||
|
generate_series(4,5) y) q;
|
||||||
|
|
||||||
|
CREATE TEMP TABLE rows AS
|
||||||
|
SELECT x, 'txt' || x as y
|
||||||
|
FROM generate_series(1,3) AS x;
|
||||||
|
|
||||||
|
SELECT row_to_json(q,true)
|
||||||
|
FROM rows q;
|
||||||
|
|
||||||
|
SELECT row_to_json(row((select array_agg(x) as d from generate_series(5,10) x)),false);
|
||||||
|
Loading…
Reference in New Issue
Block a user