Skip to content

Commit 7bca449

Browse files
author
Nikita Glukhov
committed
Preserve json formatting in json_agg(), json_object_agg()
1 parent e7877e0 commit 7bca449

File tree

1 file changed

+44
-168
lines changed

1 file changed

+44
-168
lines changed

src/backend/utils/adt/jsonb.c

Lines changed: 44 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,19 @@ static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
8787
static void jsonb_categorize_type(Oid typoid,
8888
JsonbTypeCategory *tcategory,
8989
Oid *outfuncoid);
90-
static void composite_to_jsonb(Datum composite, JsonbInState *result);
90+
static void composite_to_jsonb(Datum composite, JsonbInState *result,
91+
bool unpackJson);
9192
static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
9293
Datum *vals, bool *nulls, int *valcount,
93-
JsonbTypeCategory tcategory, Oid outfuncoid);
94-
static void array_to_jsonb_internal(Datum array, JsonbInState *result);
94+
JsonbTypeCategory tcategory, Oid outfuncoid, bool unpackJson);
95+
static void array_to_jsonb_internal(Datum array, JsonbInState *result,
96+
bool unpackJson);
9597
static void jsonb_categorize_type(Oid typoid,
9698
JsonbTypeCategory *tcategory,
9799
Oid *outfuncoid);
98100
static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
99101
JsonbTypeCategory tcategory, Oid outfuncoid,
100-
bool key_scalar);
102+
bool key_scalar, bool unpackJson);
101103
static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
102104
Oid val_type, bool key_scalar);
103105
#ifndef JSON_C
@@ -743,7 +745,7 @@ jsonb_categorize_type(Oid typoid,
743745
static void
744746
datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
745747
JsonbTypeCategory tcategory, Oid outfuncoid,
746-
bool key_scalar)
748+
bool key_scalar, bool unpackJson)
747749
{
748750
char *outputstr;
749751
bool numeric_error;
@@ -776,10 +778,10 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
776778
switch (tcategory)
777779
{
778780
case JSONBTYPE_ARRAY:
779-
array_to_jsonb_internal(val, result);
781+
array_to_jsonb_internal(val, result, unpackJson);
780782
return;
781783
case JSONBTYPE_COMPOSITE:
782-
composite_to_jsonb(val, result);
784+
composite_to_jsonb(val, result, unpackJson);
783785
return;
784786
case JSONBTYPE_BOOL:
785787
if (key_scalar)
@@ -945,6 +947,8 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
945947
pushScalarJsonbValue(&result->parseState,
946948
JsonToJsonValue(jsonb, &jb),
947949
false, false);
950+
else if (!unpackJson)
951+
result->res = JsonToJsonValue(jsonb, NULL);
948952
else
949953
{
950954
JsonbIteratorToken type;
@@ -987,7 +991,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
987991
static void
988992
array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *vals,
989993
bool *nulls, int *valcount, JsonbTypeCategory tcategory,
990-
Oid outfuncoid)
994+
Oid outfuncoid, bool unpackJson)
991995
{
992996
int i;
993997

@@ -1006,13 +1010,13 @@ array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *v
10061010
if (dim + 1 == ndims)
10071011
{
10081012
datum_to_jsonb(vals[*valcount], nulls[*valcount], result, tcategory,
1009-
outfuncoid, false);
1013+
outfuncoid, false, unpackJson);
10101014
(*valcount)++;
10111015
}
10121016
else
10131017
{
10141018
array_dim_to_jsonb(result, dim + 1, ndims, dims, vals, nulls,
1015-
valcount, tcategory, outfuncoid);
1019+
valcount, tcategory, outfuncoid, unpackJson);
10161020
}
10171021
}
10181022

@@ -1023,7 +1027,7 @@ array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *v
10231027
* Turn an array into JSON.
10241028
*/
10251029
static void
1026-
array_to_jsonb_internal(Datum array, JsonbInState *result)
1030+
array_to_jsonb_internal(Datum array, JsonbInState *result, bool unpackJson)
10271031
{
10281032
ArrayType *v = DatumGetArrayTypeP(array);
10291033
Oid element_type = ARR_ELEMTYPE(v);
@@ -1061,7 +1065,7 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
10611065
&nitems);
10621066

10631067
array_dim_to_jsonb(result, 0, ndim, dim, elements, nulls, &count, tcategory,
1064-
outfuncoid);
1068+
outfuncoid, unpackJson);
10651069

10661070
pfree(elements);
10671071
pfree(nulls);
@@ -1071,7 +1075,7 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
10711075
* Turn a composite / record into JSON.
10721076
*/
10731077
static void
1074-
composite_to_jsonb(Datum composite, JsonbInState *result)
1078+
composite_to_jsonb(Datum composite, JsonbInState *result, bool unpackJson)
10751079
{
10761080
HeapTupleHeader td;
10771081
Oid tupType;
@@ -1134,7 +1138,8 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
11341138
jsonb_categorize_type(tupdesc->attrs[i]->atttypid,
11351139
&tcategory, &outfuncoid);
11361140

1137-
datum_to_jsonb(val, isnull, result, tcategory, outfuncoid, false);
1141+
datum_to_jsonb(val, isnull, result, tcategory, outfuncoid, false,
1142+
unpackJson);
11381143
}
11391144

11401145
result->res = pushJsonbValue(&result->parseState, WJB_END_OBJECT, NULL);
@@ -1170,7 +1175,8 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result,
11701175
jsonb_categorize_type(val_type,
11711176
&tcategory, &outfuncoid);
11721177

1173-
datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
1178+
datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar,
1179+
true);
11741180
}
11751181

11761182
/*
@@ -1195,7 +1201,7 @@ to_jsonb(PG_FUNCTION_ARGS)
11951201

11961202
memset(&result, 0, sizeof(JsonbInState));
11971203

1198-
datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
1204+
datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false, true);
11991205

12001206
PG_RETURN_JSONB(JsonbValueToJsonb(result.res));
12011207
}
@@ -1597,11 +1603,7 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
15971603
JsonbInState elem;
15981604
Datum val;
15991605
JsonbInState *result;
1600-
bool single_scalar = false;
1601-
JsonbIterator *it;
1602-
Jsonb *jbelem;
16031606
JsonbValue v;
1604-
JsonbIteratorToken type;
16051607

16061608
if (!AggCheckCallContext(fcinfo, &aggcontext))
16071609
{
@@ -1661,62 +1663,14 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
16611663
memset(&elem, 0, sizeof(JsonbInState));
16621664

16631665
datum_to_jsonb(val, PG_ARGISNULL(1), &elem, state->val_category,
1664-
state->val_output_func, false);
1665-
1666-
jbelem = JsonbValueToJsonb(elem.res);
1666+
state->val_output_func, false, false);
16671667

16681668
/* switch to the aggregate context for accumulation operations */
16691669

16701670
oldcontext = MemoryContextSwitchTo(aggcontext);
16711671

1672-
it = JsonbIteratorInit(&jbelem->root);
1673-
1674-
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
1675-
{
1676-
switch (type)
1677-
{
1678-
case WJB_BEGIN_ARRAY:
1679-
if (v.val.array.rawScalar)
1680-
single_scalar = true;
1681-
else
1682-
result->res = pushJsonbValue(&result->parseState,
1683-
type, NULL);
1684-
break;
1685-
case WJB_END_ARRAY:
1686-
if (!single_scalar)
1687-
result->res = pushJsonbValue(&result->parseState,
1688-
type, NULL);
1689-
break;
1690-
case WJB_BEGIN_OBJECT:
1691-
case WJB_END_OBJECT:
1692-
result->res = pushJsonbValue(&result->parseState,
1693-
type, NULL);
1694-
break;
1695-
case WJB_ELEM:
1696-
case WJB_KEY:
1697-
case WJB_VALUE:
1698-
if (v.type == jbvString)
1699-
{
1700-
/* copy string values in the aggregate context */
1701-
char *buf = palloc(v.val.string.len + 1);
1702-
1703-
snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
1704-
v.val.string.val = buf;
1705-
}
1706-
else if (v.type == jbvNumeric)
1707-
{
1708-
/* same for numeric */
1709-
v.val.numeric =
1710-
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
1711-
NumericGetDatum(v.val.numeric)));
1712-
}
1713-
result->res = pushJsonbValue(&result->parseState,
1714-
type, &v);
1715-
break;
1716-
default:
1717-
elog(ERROR, "unknown jsonb iterator token type");
1718-
}
1719-
}
1672+
result->res = pushJsonbValueExt(&result->parseState, WJB_ELEM,
1673+
JsonValueCopy(&v, elem.res), false);
17201674

17211675
MemoryContextSwitchTo(oldcontext);
17221676

@@ -1767,12 +1721,10 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
17671721
JsonbAggState *state;
17681722
Datum val;
17691723
JsonbInState *result;
1770-
bool single_scalar;
1771-
JsonbIterator *it;
1772-
Jsonb *jbkey,
1773-
*jbval;
1774-
JsonbValue v;
1775-
JsonbIteratorToken type;
1724+
const JsonbValue *jbkey,
1725+
*jbval;
1726+
JsonbValue jbkeybuf,
1727+
v;
17761728

17771729
if (!AggCheckCallContext(fcinfo, &aggcontext))
17781730
{
@@ -1839,122 +1791,46 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
18391791
memset(&elem, 0, sizeof(JsonbInState));
18401792

18411793
datum_to_jsonb(val, false, &elem, state->key_category,
1842-
state->key_output_func, true);
1794+
state->key_output_func, true, false);
18431795

1844-
jbkey = JsonbValueToJsonb(elem.res);
1796+
jbkey = elem.res;
18451797

18461798
val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2);
18471799

18481800
memset(&elem, 0, sizeof(JsonbInState));
18491801

18501802
datum_to_jsonb(val, PG_ARGISNULL(2), &elem, state->val_category,
1851-
state->val_output_func, false);
1852-
1853-
jbval = JsonbValueToJsonb(elem.res);
1854-
1855-
it = JsonbIteratorInit(&jbkey->root);
1803+
state->val_output_func, false, false);
18561804

1857-
/* switch to the aggregate context for accumulation operations */
1858-
1859-
oldcontext = MemoryContextSwitchTo(aggcontext);
1805+
jbval = elem.res;
18601806

18611807
/*
18621808
* keys should be scalar, and we should have already checked for that
18631809
* above when calling datum_to_jsonb, so we only need to look for these
18641810
* things.
18651811
*/
18661812

1867-
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
1868-
{
1869-
switch (type)
1870-
{
1871-
case WJB_BEGIN_ARRAY:
1872-
if (!v.val.array.rawScalar)
1873-
elog(ERROR, "unexpected structure for key");
1874-
break;
1875-
case WJB_ELEM:
1876-
if (v.type == jbvString)
1877-
{
1878-
/* copy string values in the aggregate context */
1879-
char *buf = palloc(v.val.string.len + 1);
1813+
jbkey = JsonValueUnwrap(jbkey, &jbkeybuf);
18801814

1881-
snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
1882-
v.val.string.val = buf;
1883-
}
1884-
else
1885-
{
1886-
ereport(ERROR,
1887-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1888-
errmsg("object keys must be strings")));
1889-
}
1890-
result->res = pushJsonbValue(&result->parseState,
1891-
WJB_KEY, &v);
1892-
break;
1893-
case WJB_END_ARRAY:
1894-
break;
1895-
default:
1896-
elog(ERROR, "unexpected structure for key");
1897-
break;
1898-
}
1899-
}
1815+
if (jbkey->type != jbvString)
1816+
ereport(ERROR,
1817+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1818+
errmsg("object keys must be strings")));
19001819

1901-
it = JsonbIteratorInit(&jbval->root);
1820+
/* switch to the aggregate context for accumulation operations */
19021821

1903-
single_scalar = false;
1822+
oldcontext = MemoryContextSwitchTo(aggcontext);
19041823

1824+
result->res = pushJsonbValue(&result->parseState, WJB_KEY,
1825+
JsonValueCopy(&v, jbkey));
19051826
/*
19061827
* values can be anything, including structured and null, so we treat them
19071828
* as in json_agg_transfn, except that single scalars are always pushed as
19081829
* WJB_VALUE items.
19091830
*/
19101831

1911-
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
1912-
{
1913-
switch (type)
1914-
{
1915-
case WJB_BEGIN_ARRAY:
1916-
if (v.val.array.rawScalar)
1917-
single_scalar = true;
1918-
else
1919-
result->res = pushJsonbValue(&result->parseState,
1920-
type, NULL);
1921-
break;
1922-
case WJB_END_ARRAY:
1923-
if (!single_scalar)
1924-
result->res = pushJsonbValue(&result->parseState,
1925-
type, NULL);
1926-
break;
1927-
case WJB_BEGIN_OBJECT:
1928-
case WJB_END_OBJECT:
1929-
result->res = pushJsonbValue(&result->parseState,
1930-
type, NULL);
1931-
break;
1932-
case WJB_ELEM:
1933-
case WJB_KEY:
1934-
case WJB_VALUE:
1935-
if (v.type == jbvString)
1936-
{
1937-
/* copy string values in the aggregate context */
1938-
char *buf = palloc(v.val.string.len + 1);
1939-
1940-
snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
1941-
v.val.string.val = buf;
1942-
}
1943-
else if (v.type == jbvNumeric)
1944-
{
1945-
/* same for numeric */
1946-
v.val.numeric =
1947-
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
1948-
NumericGetDatum(v.val.numeric)));
1949-
}
1950-
result->res = pushJsonbValue(&result->parseState,
1951-
single_scalar ? WJB_VALUE : type,
1952-
&v);
1953-
break;
1954-
default:
1955-
elog(ERROR, "unknown jsonb iterator token type");
1956-
}
1957-
}
1832+
result->res = pushJsonbValueExt(&result->parseState, WJB_VALUE,
1833+
JsonValueCopy(&v, jbval), false);
19581834

19591835
MemoryContextSwitchTo(oldcontext);
19601836

0 commit comments

Comments
 (0)