1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +03:00

New json functions.

json_build_array() and json_build_object allow for the construction of
arbitrarily complex json trees. json_object() turns a one or two
dimensional array, or two separate arrays, into a json_object of
name/value pairs, similarly to the hstore() function.
json_object_agg() aggregates its two arguments into a single json object
as name value pairs.

Catalog version bumped.

Andrew Dunstan, reviewed by Marko Tiikkaja.
This commit is contained in:
Andrew Dunstan
2014-01-28 17:48:21 -05:00
parent 9132b189bf
commit 105639900b
10 changed files with 1221 additions and 71 deletions

View File

@ -75,6 +75,10 @@ static void elements_scalar(void *state, char *token, JsonTokenType tokentype);
/* turn a json object into a hash table */
static HTAB *get_json_object_as_hash(text *json, char *funcname, bool use_json_as_text);
/* common worker for populate_record and to_record */
static inline Datum populate_record_worker(PG_FUNCTION_ARGS,
bool have_record_arg);
/* semantic action functions for get_json_object_as_hash */
static void hash_object_field_start(void *state, char *fname, bool isnull);
static void hash_object_field_end(void *state, char *fname, bool isnull);
@ -90,6 +94,10 @@ static void populate_recordset_object_end(void *state);
static void populate_recordset_array_start(void *state);
static void populate_recordset_array_element_start(void *state, bool isnull);
/* worker function for populate_recordset and to_recordset */
static inline Datum populate_recordset_worker(PG_FUNCTION_ARGS,
bool have_record_arg);
/* search type classification for json_get* functions */
typedef enum
{
@ -1216,11 +1224,22 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
Datum
json_populate_record(PG_FUNCTION_ARGS)
{
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
return populate_record_worker(fcinfo, true);
}
Datum
json_to_record(PG_FUNCTION_ARGS)
{
return populate_record_worker(fcinfo, false);
}
static inline Datum
populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
{
text *json;
bool use_json_as_text;
HTAB *json_hash;
HeapTupleHeader rec;
HeapTupleHeader rec = NULL;
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
@ -1234,54 +1253,75 @@ json_populate_record(PG_FUNCTION_ARGS)
char fname[NAMEDATALEN];
JsonHashEntry *hashentry;
use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
if (!type_is_rowtype(argtype))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("first argument of json_populate_record must be a row type")));
if (PG_ARGISNULL(0))
if (have_record_arg)
{
if (PG_ARGISNULL(1))
PG_RETURN_NULL();
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
rec = NULL;
use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
/*
* have no tuple to look at, so the only source of type info is the
* argtype. The lookup_rowtype_tupdesc call below will error out if we
* don't have a known composite type oid here.
*/
tupType = argtype;
tupTypmod = -1;
if (!type_is_rowtype(argtype))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("first argument of json_populate_record must be a row type")));
if (PG_ARGISNULL(0))
{
if (PG_ARGISNULL(1))
PG_RETURN_NULL();
/*
* have no tuple to look at, so the only source of type info is
* the argtype. The lookup_rowtype_tupdesc call below will error
* out if we don't have a known composite type oid here.
*/
tupType = argtype;
tupTypmod = -1;
}
else
{
rec = PG_GETARG_HEAPTUPLEHEADER(0);
if (PG_ARGISNULL(1))
PG_RETURN_POINTER(rec);
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
}
json = PG_GETARG_TEXT_P(1);
}
else
{
rec = PG_GETARG_HEAPTUPLEHEADER(0);
/* json_to_record case */
if (PG_ARGISNULL(1))
PG_RETURN_POINTER(rec);
use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
json = PG_GETARG_TEXT_P(0);
get_call_result_type(fcinfo, NULL, &tupdesc);
}
json = PG_GETARG_TEXT_P(1);
json_hash = get_json_object_as_hash(json, "json_populate_record",
use_json_as_text);
json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
/*
* if the input json is empty, we can only skip the rest if we were passed
* in a non-null record, since otherwise there may be issues with domain
* nulls.
*/
if (hash_get_num_entries(json_hash) == 0 && rec)
PG_RETURN_POINTER(rec);
if (have_record_arg)
{
/*
* if the input json is empty, we can only skip the rest if we were
* passed in a non-null record, since otherwise there may be issues
* with domain nulls.
*/
if (hash_get_num_entries(json_hash) == 0 && rec)
PG_RETURN_POINTER(rec);
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
}
ncolumns = tupdesc->natts;
if (rec)
@ -1310,8 +1350,8 @@ json_populate_record(PG_FUNCTION_ARGS)
my_extra->record_typmod = 0;
}
if (my_extra->record_type != tupType ||
my_extra->record_typmod != tupTypmod)
if (have_record_arg && (my_extra->record_type != tupType ||
my_extra->record_typmod != tupTypmod))
{
MemSet(my_extra, 0,
sizeof(RecordIOData) - sizeof(ColumnIOData)
@ -1561,7 +1601,22 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
Datum
json_populate_recordset(PG_FUNCTION_ARGS)
{
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
return populate_recordset_worker(fcinfo, true);
}
Datum
json_to_recordset(PG_FUNCTION_ARGS)
{
return populate_recordset_worker(fcinfo, false);
}
/*
* common worker for json_populate_recordset() and json_to_recordset()
*/
static inline Datum
populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
{
Oid argtype;
text *json;
bool use_json_as_text;
ReturnSetInfo *rsi;
@ -1576,12 +1631,23 @@ json_populate_recordset(PG_FUNCTION_ARGS)
JsonSemAction *sem;
PopulateRecordsetState *state;
use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
if (have_record_arg)
{
argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (!type_is_rowtype(argtype))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("first argument of json_populate_recordset must be a row type")));
use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
if (!type_is_rowtype(argtype))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("first argument of json_populate_recordset must be a row type")));
}
else
{
argtype = InvalidOid;
use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
}
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
@ -1618,15 +1684,27 @@ json_populate_recordset(PG_FUNCTION_ARGS)
MemoryContextSwitchTo(old_cxt);
/* if the json is null send back an empty set */
if (PG_ARGISNULL(1))
PG_RETURN_NULL();
if (have_record_arg)
{
if (PG_ARGISNULL(1))
PG_RETURN_NULL();
json = PG_GETARG_TEXT_P(1);
json = PG_GETARG_TEXT_P(1);
if (PG_ARGISNULL(0))
rec = NULL;
if (PG_ARGISNULL(0))
rec = NULL;
else
rec = PG_GETARG_HEAPTUPLEHEADER(0);
}
else
rec = PG_GETARG_HEAPTUPLEHEADER(0);
{
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
json = PG_GETARG_TEXT_P(0);
rec = NULL;
}
tupType = tupdesc->tdtypeid;
tupTypmod = tupdesc->tdtypmod;