Skip to content

Commit 48c1aee

Browse files
author
Nikita Glukhov
committed
Preserve json formatting in json_agg(), json_object_agg()
1 parent 9f166b6 commit 48c1aee

File tree

1 file changed

+45
-168
lines changed

1 file changed

+45
-168
lines changed

src/backend/utils/adt/jsonb.c

Lines changed: 45 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,20 @@ static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
8888
static void jsonb_categorize_type(Oid typoid,
8989
JsonbTypeCategory *tcategory,
9090
Oid *outfuncoid);
91-
static void composite_to_jsonb(Datum composite, JsonbInState *result);
91+
static void composite_to_jsonb(Datum composite, JsonbInState *result,
92+
bool unpackJson);
9293
static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
9394
Datum *vals, bool *nulls, int *valcount,
94-
JsonbTypeCategory tcategory, Oid outfuncoid);
95-
static void array_to_jsonb_internal(Datum array, JsonbInState *result);
95+
JsonbTypeCategory tcategory, Oid outfuncoid,
96+
bool unpackJson);
97+
static void array_to_jsonb_internal(Datum array, JsonbInState *result,
98+
bool unpackJson);
9699
static void jsonb_categorize_type(Oid typoid,
97100
JsonbTypeCategory *tcategory,
98101
Oid *outfuncoid);
99102
static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
100103
JsonbTypeCategory tcategory, Oid outfuncoid,
101-
bool key_scalar);
104+
bool key_scalar, bool unpackJson);
102105
static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
103106
Oid val_type, bool key_scalar);
104107
#ifndef JSON_C
@@ -796,7 +799,7 @@ jsonb_categorize_type(Oid typoid,
796799
static void
797800
datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
798801
JsonbTypeCategory tcategory, Oid outfuncoid,
799-
bool key_scalar)
802+
bool key_scalar, bool unpackJson)
800803
{
801804
char *outputstr;
802805
bool numeric_error;
@@ -829,10 +832,10 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
829832
switch (tcategory)
830833
{
831834
case JSONBTYPE_ARRAY:
832-
array_to_jsonb_internal(val, result);
835+
array_to_jsonb_internal(val, result, unpackJson);
833836
return;
834837
case JSONBTYPE_COMPOSITE:
835-
composite_to_jsonb(val, result);
838+
composite_to_jsonb(val, result, unpackJson);
836839
return;
837840
case JSONBTYPE_BOOL:
838841
if (key_scalar)
@@ -954,6 +957,8 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
954957
pushScalarJsonbValue(&result->parseState,
955958
JsonToJsonValue(jsonb, &jb),
956959
false, false);
960+
else if (!unpackJson)
961+
result->res = JsonToJsonValue(jsonb, NULL);
957962
else
958963
{
959964
JsonbIteratorToken type;
@@ -996,7 +1001,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
9961001
static void
9971002
array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *vals,
9981003
bool *nulls, int *valcount, JsonbTypeCategory tcategory,
999-
Oid outfuncoid)
1004+
Oid outfuncoid, bool unpackJson)
10001005
{
10011006
int i;
10021007

@@ -1015,13 +1020,13 @@ array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *v
10151020
if (dim + 1 == ndims)
10161021
{
10171022
datum_to_jsonb(vals[*valcount], nulls[*valcount], result, tcategory,
1018-
outfuncoid, false);
1023+
outfuncoid, false, unpackJson);
10191024
(*valcount)++;
10201025
}
10211026
else
10221027
{
10231028
array_dim_to_jsonb(result, dim + 1, ndims, dims, vals, nulls,
1024-
valcount, tcategory, outfuncoid);
1029+
valcount, tcategory, outfuncoid, unpackJson);
10251030
}
10261031
}
10271032

@@ -1032,7 +1037,7 @@ array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *v
10321037
* Turn an array into JSON.
10331038
*/
10341039
static void
1035-
array_to_jsonb_internal(Datum array, JsonbInState *result)
1040+
array_to_jsonb_internal(Datum array, JsonbInState *result, bool unpackJson)
10361041
{
10371042
ArrayType *v = DatumGetArrayTypeP(array);
10381043
Oid element_type = ARR_ELEMTYPE(v);
@@ -1070,7 +1075,7 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
10701075
&nitems);
10711076

10721077
array_dim_to_jsonb(result, 0, ndim, dim, elements, nulls, &count, tcategory,
1073-
outfuncoid);
1078+
outfuncoid, unpackJson);
10741079

10751080
pfree(elements);
10761081
pfree(nulls);
@@ -1080,7 +1085,7 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
10801085
* Turn a composite / record into JSON.
10811086
*/
10821087
static void
1083-
composite_to_jsonb(Datum composite, JsonbInState *result)
1088+
composite_to_jsonb(Datum composite, JsonbInState *result, bool unpackJson)
10841089
{
10851090
HeapTupleHeader td;
10861091
Oid tupType;
@@ -1143,7 +1148,8 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
11431148
else
11441149
jsonb_categorize_type(att->atttypid, &tcategory, &outfuncoid);
11451150

1146-
datum_to_jsonb(val, isnull, result, tcategory, outfuncoid, false);
1151+
datum_to_jsonb(val, isnull, result, tcategory, outfuncoid, false,
1152+
unpackJson);
11471153
}
11481154

11491155
result->res = pushJsonbValue(&result->parseState, WJB_END_OBJECT, NULL);
@@ -1179,7 +1185,8 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result,
11791185
jsonb_categorize_type(val_type,
11801186
&tcategory, &outfuncoid);
11811187

1182-
datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
1188+
datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar,
1189+
true);
11831190
}
11841191

11851192
/*
@@ -1204,7 +1211,7 @@ to_jsonb(PG_FUNCTION_ARGS)
12041211

12051212
memset(&result, 0, sizeof(JsonbInState));
12061213

1207-
datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
1214+
datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false, true);
12081215

12091216
PG_RETURN_JSONB_P(JsonbValueToJsonb(result.res));
12101217
}
@@ -1567,11 +1574,7 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
15671574
JsonbInState elem;
15681575
Datum val;
15691576
JsonbInState *result;
1570-
bool single_scalar = false;
1571-
JsonbIterator *it;
1572-
Jsonb *jbelem;
15731577
JsonbValue v;
1574-
JsonbIteratorToken type;
15751578

15761579
if (!AggCheckCallContext(fcinfo, &aggcontext))
15771580
{
@@ -1631,62 +1634,14 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
16311634
memset(&elem, 0, sizeof(JsonbInState));
16321635

16331636
datum_to_jsonb(val, PG_ARGISNULL(1), &elem, state->val_category,
1634-
state->val_output_func, false);
1635-
1636-
jbelem = JsonbValueToJsonb(elem.res);
1637+
state->val_output_func, false, false);
16371638

16381639
/* switch to the aggregate context for accumulation operations */
16391640

16401641
oldcontext = MemoryContextSwitchTo(aggcontext);
16411642

1642-
it = JsonbIteratorInit(&jbelem->root);
1643-
1644-
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
1645-
{
1646-
switch (type)
1647-
{
1648-
case WJB_BEGIN_ARRAY:
1649-
if (v.val.array.rawScalar)
1650-
single_scalar = true;
1651-
else
1652-
result->res = pushJsonbValue(&result->parseState,
1653-
type, NULL);
1654-
break;
1655-
case WJB_END_ARRAY:
1656-
if (!single_scalar)
1657-
result->res = pushJsonbValue(&result->parseState,
1658-
type, NULL);
1659-
break;
1660-
case WJB_BEGIN_OBJECT:
1661-
case WJB_END_OBJECT:
1662-
result->res = pushJsonbValue(&result->parseState,
1663-
type, NULL);
1664-
break;
1665-
case WJB_ELEM:
1666-
case WJB_KEY:
1667-
case WJB_VALUE:
1668-
if (v.type == jbvString)
1669-
{
1670-
/* copy string values in the aggregate context */
1671-
char *buf = palloc(v.val.string.len + 1);
1672-
1673-
snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
1674-
v.val.string.val = buf;
1675-
}
1676-
else if (v.type == jbvNumeric)
1677-
{
1678-
/* same for numeric */
1679-
v.val.numeric =
1680-
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
1681-
NumericGetDatum(v.val.numeric)));
1682-
}
1683-
result->res = pushJsonbValue(&result->parseState,
1684-
type, &v);
1685-
break;
1686-
default:
1687-
elog(ERROR, "unknown jsonb iterator token type");
1688-
}
1689-
}
1643+
result->res = pushJsonbValueExt(&result->parseState, WJB_ELEM,
1644+
JsonValueCopy(&v, elem.res), false);
16901645

16911646
MemoryContextSwitchTo(oldcontext);
16921647

@@ -1737,12 +1692,10 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
17371692
JsonbAggState *state;
17381693
Datum val;
17391694
JsonbInState *result;
1740-
bool single_scalar;
1741-
JsonbIterator *it;
1742-
Jsonb *jbkey,
1743-
*jbval;
1744-
JsonbValue v;
1745-
JsonbIteratorToken type;
1695+
const JsonbValue *jbkey,
1696+
*jbval;
1697+
JsonbValue jbkeybuf,
1698+
v;
17461699

17471700
if (!AggCheckCallContext(fcinfo, &aggcontext))
17481701
{
@@ -1810,122 +1763,46 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
18101763
memset(&elem, 0, sizeof(JsonbInState));
18111764

18121765
datum_to_jsonb(val, false, &elem, state->key_category,
1813-
state->key_output_func, true);
1766+
state->key_output_func, true, false);
18141767

1815-
jbkey = JsonbValueToJsonb(elem.res);
1768+
jbkey = elem.res;
18161769

18171770
val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2);
18181771

18191772
memset(&elem, 0, sizeof(JsonbInState));
18201773

18211774
datum_to_jsonb(val, PG_ARGISNULL(2), &elem, state->val_category,
1822-
state->val_output_func, false);
1823-
1824-
jbval = JsonbValueToJsonb(elem.res);
1825-
1826-
it = JsonbIteratorInit(&jbkey->root);
1775+
state->val_output_func, false, false);
18271776

1828-
/* switch to the aggregate context for accumulation operations */
1829-
1830-
oldcontext = MemoryContextSwitchTo(aggcontext);
1777+
jbval = elem.res;
18311778

18321779
/*
18331780
* keys should be scalar, and we should have already checked for that
18341781
* above when calling datum_to_jsonb, so we only need to look for these
18351782
* things.
18361783
*/
18371784

1838-
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
1839-
{
1840-
switch (type)
1841-
{
1842-
case WJB_BEGIN_ARRAY:
1843-
if (!v.val.array.rawScalar)
1844-
elog(ERROR, "unexpected structure for key");
1845-
break;
1846-
case WJB_ELEM:
1847-
if (v.type == jbvString)
1848-
{
1849-
/* copy string values in the aggregate context */
1850-
char *buf = palloc(v.val.string.len + 1);
1785+
jbkey = JsonValueUnwrap(jbkey, &jbkeybuf);
18511786

1852-
snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
1853-
v.val.string.val = buf;
1854-
}
1855-
else
1856-
{
1857-
ereport(ERROR,
1858-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1859-
errmsg("object keys must be strings")));
1860-
}
1861-
result->res = pushJsonbValue(&result->parseState,
1862-
WJB_KEY, &v);
1863-
break;
1864-
case WJB_END_ARRAY:
1865-
break;
1866-
default:
1867-
elog(ERROR, "unexpected structure for key");
1868-
break;
1869-
}
1870-
}
1787+
if (jbkey->type != jbvString)
1788+
ereport(ERROR,
1789+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1790+
errmsg("object keys must be strings")));
18711791

1872-
it = JsonbIteratorInit(&jbval->root);
1792+
/* switch to the aggregate context for accumulation operations */
18731793

1874-
single_scalar = false;
1794+
oldcontext = MemoryContextSwitchTo(aggcontext);
18751795

1796+
result->res = pushJsonbValue(&result->parseState, WJB_KEY,
1797+
JsonValueCopy(&v, jbkey));
18761798
/*
18771799
* values can be anything, including structured and null, so we treat them
18781800
* as in json_agg_transfn, except that single scalars are always pushed as
18791801
* WJB_VALUE items.
18801802
*/
18811803

1882-
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
1883-
{
1884-
switch (type)
1885-
{
1886-
case WJB_BEGIN_ARRAY:
1887-
if (v.val.array.rawScalar)
1888-
single_scalar = true;
1889-
else
1890-
result->res = pushJsonbValue(&result->parseState,
1891-
type, NULL);
1892-
break;
1893-
case WJB_END_ARRAY:
1894-
if (!single_scalar)
1895-
result->res = pushJsonbValue(&result->parseState,
1896-
type, NULL);
1897-
break;
1898-
case WJB_BEGIN_OBJECT:
1899-
case WJB_END_OBJECT:
1900-
result->res = pushJsonbValue(&result->parseState,
1901-
type, NULL);
1902-
break;
1903-
case WJB_ELEM:
1904-
case WJB_KEY:
1905-
case WJB_VALUE:
1906-
if (v.type == jbvString)
1907-
{
1908-
/* copy string values in the aggregate context */
1909-
char *buf = palloc(v.val.string.len + 1);
1910-
1911-
snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
1912-
v.val.string.val = buf;
1913-
}
1914-
else if (v.type == jbvNumeric)
1915-
{
1916-
/* same for numeric */
1917-
v.val.numeric =
1918-
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
1919-
NumericGetDatum(v.val.numeric)));
1920-
}
1921-
result->res = pushJsonbValue(&result->parseState,
1922-
single_scalar ? WJB_VALUE : type,
1923-
&v);
1924-
break;
1925-
default:
1926-
elog(ERROR, "unknown jsonb iterator token type");
1927-
}
1928-
}
1804+
result->res = pushJsonbValueExt(&result->parseState, WJB_VALUE,
1805+
JsonValueCopy(&v, jbval), false);
19291806

19301807
MemoryContextSwitchTo(oldcontext);
19311808

0 commit comments

Comments
 (0)