Skip to content

Commit 1f8928c

Browse files
author
Nikita Glukhov
committed
Preserve json formatting in json_agg(), json_object_agg()
1 parent cc253f9 commit 1f8928c

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy