mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
Transform or iterate over json(b) string values
Dmitry Dolgov, reviewed and lightly edited by me.
This commit is contained in:
parent
156d3882f8
commit
c80b9920fc
@ -52,6 +52,25 @@ typedef struct OkeysState
|
|||||||
int sent_count;
|
int sent_count;
|
||||||
} OkeysState;
|
} OkeysState;
|
||||||
|
|
||||||
|
/* state for iterate_json_string_values function */
|
||||||
|
typedef struct IterateJsonStringValuesState
|
||||||
|
{
|
||||||
|
JsonLexContext *lex;
|
||||||
|
JsonIterateStringValuesAction action; /* an action that will be applied
|
||||||
|
to each json value */
|
||||||
|
void *action_state; /* any necessary context for iteration */
|
||||||
|
} IterateJsonStringValuesState;
|
||||||
|
|
||||||
|
/* state for transform_json_string_values function */
|
||||||
|
typedef struct TransformJsonStringValuesState
|
||||||
|
{
|
||||||
|
JsonLexContext *lex;
|
||||||
|
StringInfo strval; /* resulting json */
|
||||||
|
JsonTransformStringValuesAction action; /* an action that will be applied
|
||||||
|
to each json value */
|
||||||
|
void *action_state; /* any necessary context for transformation */
|
||||||
|
} TransformJsonStringValuesState;
|
||||||
|
|
||||||
/* state for json_get* functions */
|
/* state for json_get* functions */
|
||||||
typedef struct GetState
|
typedef struct GetState
|
||||||
{
|
{
|
||||||
@ -271,6 +290,18 @@ static void setPathArray(JsonbIterator **it, Datum *path_elems,
|
|||||||
int level, Jsonb *newval, uint32 nelems, int op_type);
|
int level, Jsonb *newval, uint32 nelems, int op_type);
|
||||||
static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
|
static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
|
||||||
|
|
||||||
|
/* function supporting iterate_json_string_values */
|
||||||
|
static void iterate_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
|
||||||
|
|
||||||
|
/* functions supporting transform_json_string_values */
|
||||||
|
static void transform_string_values_object_start(void *state);
|
||||||
|
static void transform_string_values_object_end(void *state);
|
||||||
|
static void transform_string_values_array_start(void *state);
|
||||||
|
static void transform_string_values_array_end(void *state);
|
||||||
|
static void transform_string_values_object_field_start(void *state, char *fname, bool isnull);
|
||||||
|
static void transform_string_values_array_element_start(void *state, bool isnull);
|
||||||
|
static void transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SQL function json_object_keys
|
* SQL function json_object_keys
|
||||||
@ -4130,3 +4161,208 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over jsonb string values or elements, and pass them together with an
|
||||||
|
* iteration state to a specified JsonIterateStringValuesAction.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
iterate_jsonb_string_values(Jsonb *jb, void *state, JsonIterateStringValuesAction action)
|
||||||
|
{
|
||||||
|
JsonbIterator *it;
|
||||||
|
JsonbValue v;
|
||||||
|
JsonbIteratorToken type;
|
||||||
|
|
||||||
|
it = JsonbIteratorInit(&jb->root);
|
||||||
|
|
||||||
|
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
|
||||||
|
{
|
||||||
|
if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
|
||||||
|
{
|
||||||
|
action(state, v.val.string.val, v.val.string.len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over json string values or elements, and pass them together with an
|
||||||
|
* iteration state to a specified JsonIterateStringValuesAction.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
iterate_json_string_values(text *json, void *action_state, JsonIterateStringValuesAction action)
|
||||||
|
{
|
||||||
|
JsonLexContext *lex = makeJsonLexContext(json, true);
|
||||||
|
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
|
||||||
|
IterateJsonStringValuesState *state = palloc0(sizeof(IterateJsonStringValuesState));
|
||||||
|
|
||||||
|
state->lex = lex;
|
||||||
|
state->action = action;
|
||||||
|
state->action_state = action_state;
|
||||||
|
|
||||||
|
sem->semstate = (void *) state;
|
||||||
|
sem->scalar = iterate_string_values_scalar;
|
||||||
|
|
||||||
|
pg_parse_json(lex, sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An auxiliary function for iterate_json_string_values to invoke a specified
|
||||||
|
* JsonIterateStringValuesAction.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
iterate_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
|
||||||
|
{
|
||||||
|
IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
|
||||||
|
if (tokentype == JSON_TOKEN_STRING)
|
||||||
|
(*_state->action) (_state->action_state, token, strlen(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over a jsonb, and apply a specified JsonTransformStringValuesAction
|
||||||
|
* to every string value or element. Any necessary context for a
|
||||||
|
* JsonTransformStringValuesAction can be passed in the action_state variable.
|
||||||
|
* Function returns a copy of an original jsonb object with transformed values.
|
||||||
|
*/
|
||||||
|
Jsonb *
|
||||||
|
transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
|
||||||
|
JsonTransformStringValuesAction transform_action)
|
||||||
|
{
|
||||||
|
JsonbIterator *it;
|
||||||
|
JsonbValue v, *res = NULL;
|
||||||
|
JsonbIteratorToken type;
|
||||||
|
JsonbParseState *st = NULL;
|
||||||
|
text *out;
|
||||||
|
bool is_scalar = false;
|
||||||
|
|
||||||
|
it = JsonbIteratorInit(&jsonb->root);
|
||||||
|
is_scalar = it->isScalar;
|
||||||
|
|
||||||
|
while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
|
||||||
|
{
|
||||||
|
if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
|
||||||
|
{
|
||||||
|
out = transform_action(action_state, v.val.string.val, v.val.string.len);
|
||||||
|
v.val.string.val = VARDATA_ANY(out);
|
||||||
|
v.val.string.len = VARSIZE_ANY_EXHDR(out);
|
||||||
|
res = pushJsonbValue(&st, type, type < WJB_BEGIN_ARRAY ? &v : NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = pushJsonbValue(&st, type, (type == WJB_KEY ||
|
||||||
|
type == WJB_VALUE ||
|
||||||
|
type == WJB_ELEM) ? &v : NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res->type == jbvArray)
|
||||||
|
res->val.array.rawScalar = is_scalar;
|
||||||
|
|
||||||
|
return JsonbValueToJsonb(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over a json, and apply a specified JsonTransformStringValuesAction
|
||||||
|
* to every string value or element. Any necessary context for a
|
||||||
|
* JsonTransformStringValuesAction can be passed in the action_state variable.
|
||||||
|
* Function returns a StringInfo, which is a copy of an original json with
|
||||||
|
* transformed values.
|
||||||
|
*/
|
||||||
|
text *
|
||||||
|
transform_json_string_values(text *json, void *action_state,
|
||||||
|
JsonTransformStringValuesAction transform_action)
|
||||||
|
{
|
||||||
|
JsonLexContext *lex = makeJsonLexContext(json, true);
|
||||||
|
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
|
||||||
|
TransformJsonStringValuesState *state = palloc0(sizeof(TransformJsonStringValuesState));
|
||||||
|
|
||||||
|
state->lex = lex;
|
||||||
|
state->strval = makeStringInfo();
|
||||||
|
state->action = transform_action;
|
||||||
|
state->action_state = action_state;
|
||||||
|
|
||||||
|
sem->semstate = (void *) state;
|
||||||
|
sem->scalar = transform_string_values_scalar;
|
||||||
|
sem->object_start = transform_string_values_object_start;
|
||||||
|
sem->object_end = transform_string_values_object_end;
|
||||||
|
sem->array_start = transform_string_values_array_start;
|
||||||
|
sem->array_end = transform_string_values_array_end;
|
||||||
|
sem->scalar = transform_string_values_scalar;
|
||||||
|
sem->array_element_start = transform_string_values_array_element_start;
|
||||||
|
sem->object_field_start = transform_string_values_object_field_start;
|
||||||
|
|
||||||
|
pg_parse_json(lex, sem);
|
||||||
|
|
||||||
|
return cstring_to_text_with_len(state->strval->data, state->strval->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set of auxiliary functions for transform_json_string_values to invoke a
|
||||||
|
* specified JsonTransformStringValuesAction for all values and left everything
|
||||||
|
* else untouched.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
transform_string_values_object_start(void *state)
|
||||||
|
{
|
||||||
|
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
|
||||||
|
appendStringInfoCharMacro(_state->strval, '{');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
transform_string_values_object_end(void *state)
|
||||||
|
{
|
||||||
|
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
|
||||||
|
appendStringInfoCharMacro(_state->strval, '}');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
transform_string_values_array_start(void *state)
|
||||||
|
{
|
||||||
|
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
|
||||||
|
appendStringInfoCharMacro(_state->strval, '[');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
transform_string_values_array_end(void *state)
|
||||||
|
{
|
||||||
|
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
|
||||||
|
appendStringInfoCharMacro(_state->strval, ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
transform_string_values_object_field_start(void *state, char *fname, bool isnull)
|
||||||
|
{
|
||||||
|
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
|
||||||
|
|
||||||
|
if (_state->strval->data[_state->strval->len - 1] != '{')
|
||||||
|
appendStringInfoCharMacro(_state->strval, ',');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unfortunately we don't have the quoted and escaped string any more, so
|
||||||
|
* we have to re-escape it.
|
||||||
|
*/
|
||||||
|
escape_json(_state->strval, fname);
|
||||||
|
appendStringInfoCharMacro(_state->strval, ':');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
transform_string_values_array_element_start(void *state, bool isnull)
|
||||||
|
{
|
||||||
|
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
|
||||||
|
|
||||||
|
if (_state->strval->data[_state->strval->len - 1] != '[')
|
||||||
|
appendStringInfoCharMacro(_state->strval, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
|
||||||
|
{
|
||||||
|
TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
|
||||||
|
|
||||||
|
if (tokentype == JSON_TOKEN_STRING)
|
||||||
|
{
|
||||||
|
text *out = (*_state->action) (_state->action_state, token, strlen(token));
|
||||||
|
escape_json(_state->strval, text_to_cstring(out));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
appendStringInfoString(_state->strval, token);
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#ifndef JSONAPI_H
|
#ifndef JSONAPI_H
|
||||||
#define JSONAPI_H
|
#define JSONAPI_H
|
||||||
|
|
||||||
|
#include "jsonb.h"
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
@ -131,4 +132,19 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
|
|||||||
*/
|
*/
|
||||||
extern bool IsValidJsonNumber(const char *str, int len);
|
extern bool IsValidJsonNumber(const char *str, int len);
|
||||||
|
|
||||||
|
/* an action that will be applied to each value in iterate_json(b)_string_vaues functions */
|
||||||
|
typedef void (*JsonIterateStringValuesAction) (void *state, char *elem_value, int elem_len);
|
||||||
|
|
||||||
|
/* an action that will be applied to each value in transform_json(b)_string_values functions */
|
||||||
|
typedef text * (*JsonTransformStringValuesAction) (void *state, char *elem_value, int elem_len);
|
||||||
|
|
||||||
|
extern void iterate_jsonb_string_values(Jsonb *jb, void *state,
|
||||||
|
JsonIterateStringValuesAction action);
|
||||||
|
extern void iterate_json_string_values(text *json, void *action_state,
|
||||||
|
JsonIterateStringValuesAction action);
|
||||||
|
extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
|
||||||
|
JsonTransformStringValuesAction transform_action);
|
||||||
|
extern text *transform_json_string_values(text *json, void *action_state,
|
||||||
|
JsonTransformStringValuesAction transform_action);
|
||||||
|
|
||||||
#endif /* JSONAPI_H */
|
#endif /* JSONAPI_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user