Skip to content

Commit c80b992

Browse files
committed
Transform or iterate over json(b) string values
Dmitry Dolgov, reviewed and lightly edited by me.
1 parent 156d388 commit c80b992

File tree

2 files changed

+252
-0
lines changed

2 files changed

+252
-0
lines changed

src/backend/utils/adt/jsonfuncs.c

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,25 @@ typedef struct OkeysState
5252
int sent_count;
5353
} OkeysState;
5454

55+
/* state for iterate_json_string_values function */
56+
typedef struct IterateJsonStringValuesState
57+
{
58+
JsonLexContext *lex;
59+
JsonIterateStringValuesAction action; /* an action that will be applied
60+
to each json value */
61+
void *action_state; /* any necessary context for iteration */
62+
} IterateJsonStringValuesState;
63+
64+
/* state for transform_json_string_values function */
65+
typedef struct TransformJsonStringValuesState
66+
{
67+
JsonLexContext *lex;
68+
StringInfo strval; /* resulting json */
69+
JsonTransformStringValuesAction action; /* an action that will be applied
70+
to each json value */
71+
void *action_state; /* any necessary context for transformation */
72+
} TransformJsonStringValuesState;
73+
5574
/* state for json_get* functions */
5675
typedef struct GetState
5776
{
@@ -271,6 +290,18 @@ static void setPathArray(JsonbIterator **it, Datum *path_elems,
271290
int level, Jsonb *newval, uint32 nelems, int op_type);
272291
static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
273292

293+
/* function supporting iterate_json_string_values */
294+
static void iterate_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
295+
296+
/* functions supporting transform_json_string_values */
297+
static void transform_string_values_object_start(void *state);
298+
static void transform_string_values_object_end(void *state);
299+
static void transform_string_values_array_start(void *state);
300+
static void transform_string_values_array_end(void *state);
301+
static void transform_string_values_object_field_start(void *state, char *fname, bool isnull);
302+
static void transform_string_values_array_element_start(void *state, bool isnull);
303+
static void transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
304+
274305

275306
/*
276307
* SQL function json_object_keys
@@ -4130,3 +4161,208 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
41304161
}
41314162
}
41324163
}
4164+
4165+
/*
4166+
* Iterate over jsonb string values or elements, and pass them together with an
4167+
* iteration state to a specified JsonIterateStringValuesAction.
4168+
*/
4169+
void
4170+
iterate_jsonb_string_values(Jsonb *jb, void *state, JsonIterateStringValuesAction action)
4171+
{
4172+
JsonbIterator *it;
4173+
JsonbValue v;
4174+
JsonbIteratorToken type;
4175+
4176+
it = JsonbIteratorInit(&jb->root);
4177+
4178+
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
4179+
{
4180+
if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
4181+
{
4182+
action(state, v.val.string.val, v.val.string.len);
4183+
}
4184+
}
4185+
}
4186+
4187+
/*
4188+
* Iterate over json string values or elements, and pass them together with an
4189+
* iteration state to a specified JsonIterateStringValuesAction.
4190+
*/
4191+
void
4192+
iterate_json_string_values(text *json, void *action_state, JsonIterateStringValuesAction action)
4193+
{
4194+
JsonLexContext *lex = makeJsonLexContext(json, true);
4195+
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
4196+
IterateJsonStringValuesState *state = palloc0(sizeof(IterateJsonStringValuesState));
4197+
4198+
state->lex = lex;
4199+
state->action = action;
4200+
state->action_state = action_state;
4201+
4202+
sem->semstate = (void *) state;
4203+
sem->scalar = iterate_string_values_scalar;
4204+
4205+
pg_parse_json(lex, sem);
4206+
}
4207+
4208+
/*
4209+
* An auxiliary function for iterate_json_string_values to invoke a specified
4210+
* JsonIterateStringValuesAction.
4211+
*/
4212+
static void
4213+
iterate_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
4214+
{
4215+
IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
4216+
if (tokentype == JSON_TOKEN_STRING)
4217+
(*_state->action) (_state->action_state, token, strlen(token));
4218+
}
4219+
4220+
/*
4221+
* Iterate over a jsonb, and apply a specified JsonTransformStringValuesAction
4222+
* to every string value or element. Any necessary context for a
4223+
* JsonTransformStringValuesAction can be passed in the action_state variable.
4224+
* Function returns a copy of an original jsonb object with transformed values.
4225+
*/
4226+
Jsonb *
4227+
transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
4228+
JsonTransformStringValuesAction transform_action)
4229+
{
4230+
JsonbIterator *it;
4231+
JsonbValue v, *res = NULL;
4232+
JsonbIteratorToken type;
4233+
JsonbParseState *st = NULL;
4234+
text *out;
4235+
bool is_scalar = false;
4236+
4237+
it = JsonbIteratorInit(&jsonb->root);
4238+
is_scalar = it->isScalar;
4239+
4240+
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
4241+
{
4242+
if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
4243+
{
4244+
out = transform_action(action_state, v.val.string.val, v.val.string.len);
4245+
v.val.string.val = VARDATA_ANY(out);
4246+
v.val.string.len = VARSIZE_ANY_EXHDR(out);
4247+
res = pushJsonbValue(&st, type, type < WJB_BEGIN_ARRAY ? &v : NULL);
4248+
}
4249+
else
4250+
{
4251+
res = pushJsonbValue(&st, type, (type == WJB_KEY ||
4252+
type == WJB_VALUE ||
4253+
type == WJB_ELEM) ? &v : NULL);
4254+
}
4255+
}
4256+
4257+
if (res->type == jbvArray)
4258+
res->val.array.rawScalar = is_scalar;
4259+
4260+
return JsonbValueToJsonb(res);
4261+
}
4262+
4263+
/*
4264+
* Iterate over a json, and apply a specified JsonTransformStringValuesAction
4265+
* to every string value or element. Any necessary context for a
4266+
* JsonTransformStringValuesAction can be passed in the action_state variable.
4267+
* Function returns a StringInfo, which is a copy of an original json with
4268+
* transformed values.
4269+
*/
4270+
text *
4271+
transform_json_string_values(text *json, void *action_state,
4272+
JsonTransformStringValuesAction transform_action)
4273+
{
4274+
JsonLexContext *lex = makeJsonLexContext(json, true);
4275+
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
4276+
TransformJsonStringValuesState *state = palloc0(sizeof(TransformJsonStringValuesState));
4277+
4278+
state->lex = lex;
4279+
state->strval = makeStringInfo();
4280+
state->action = transform_action;
4281+
state->action_state = action_state;
4282+
4283+
sem->semstate = (void *) state;
4284+
sem->scalar = transform_string_values_scalar;
4285+
sem->object_start = transform_string_values_object_start;
4286+
sem->object_end = transform_string_values_object_end;
4287+
sem->array_start = transform_string_values_array_start;
4288+
sem->array_end = transform_string_values_array_end;
4289+
sem->scalar = transform_string_values_scalar;
4290+
sem->array_element_start = transform_string_values_array_element_start;
4291+
sem->object_field_start = transform_string_values_object_field_start;
4292+
4293+
pg_parse_json(lex, sem);
4294+
4295+
return cstring_to_text_with_len(state->strval->data, state->strval->len);
4296+
}
4297+
4298+
/*
4299+
* Set of auxiliary functions for transform_json_string_values to invoke a
4300+
* specified JsonTransformStringValuesAction for all values and left everything
4301+
* else untouched.
4302+
*/
4303+
static void
4304+
transform_string_values_object_start(void *state)
4305+
{
4306+
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4307+
appendStringInfoCharMacro(_state->strval, '{');
4308+
}
4309+
4310+
static void
4311+
transform_string_values_object_end(void *state)
4312+
{
4313+
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4314+
appendStringInfoCharMacro(_state->strval, '}');
4315+
}
4316+
4317+
static void
4318+
transform_string_values_array_start(void *state)
4319+
{
4320+
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4321+
appendStringInfoCharMacro(_state->strval, '[');
4322+
}
4323+
4324+
static void
4325+
transform_string_values_array_end(void *state)
4326+
{
4327+
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4328+
appendStringInfoCharMacro(_state->strval, ']');
4329+
}
4330+
4331+
static void
4332+
transform_string_values_object_field_start(void *state, char *fname, bool isnull)
4333+
{
4334+
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4335+
4336+
if (_state->strval->data[_state->strval->len - 1] != '{')
4337+
appendStringInfoCharMacro(_state->strval, ',');
4338+
4339+
/*
4340+
* Unfortunately we don't have the quoted and escaped string any more, so
4341+
* we have to re-escape it.
4342+
*/
4343+
escape_json(_state->strval, fname);
4344+
appendStringInfoCharMacro(_state->strval, ':');
4345+
}
4346+
4347+
static void
4348+
transform_string_values_array_element_start(void *state, bool isnull)
4349+
{
4350+
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4351+
4352+
if (_state->strval->data[_state->strval->len - 1] != '[')
4353+
appendStringInfoCharMacro(_state->strval, ',');
4354+
}
4355+
4356+
static void
4357+
transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
4358+
{
4359+
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4360+
4361+
if (tokentype == JSON_TOKEN_STRING)
4362+
{
4363+
text *out = (*_state->action) (_state->action_state, token, strlen(token));
4364+
escape_json(_state->strval, text_to_cstring(out));
4365+
}
4366+
else
4367+
appendStringInfoString(_state->strval, token);
4368+
}

src/include/utils/jsonapi.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef JSONAPI_H
1515
#define JSONAPI_H
1616

17+
#include "jsonb.h"
1718
#include "lib/stringinfo.h"
1819

1920
typedef enum
@@ -131,4 +132,19 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
131132
*/
132133
extern bool IsValidJsonNumber(const char *str, int len);
133134

135+
/* an action that will be applied to each value in iterate_json(b)_string_vaues functions */
136+
typedef void (*JsonIterateStringValuesAction) (void *state, char *elem_value, int elem_len);
137+
138+
/* an action that will be applied to each value in transform_json(b)_string_values functions */
139+
typedef text * (*JsonTransformStringValuesAction) (void *state, char *elem_value, int elem_len);
140+
141+
extern void iterate_jsonb_string_values(Jsonb *jb, void *state,
142+
JsonIterateStringValuesAction action);
143+
extern void iterate_json_string_values(text *json, void *action_state,
144+
JsonIterateStringValuesAction action);
145+
extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
146+
JsonTransformStringValuesAction transform_action);
147+
extern text *transform_json_string_values(text *json, void *action_state,
148+
JsonTransformStringValuesAction transform_action);
149+
134150
#endif /* JSONAPI_H */

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