diff --git a/contrib/jsonb_plperl/jsonb_plperl.c b/contrib/jsonb_plperl/jsonb_plperl.c index 52b2d35..80be238 100644 --- a/contrib/jsonb_plperl/jsonb_plperl.c +++ b/contrib/jsonb_plperl/jsonb_plperl.c @@ -18,132 +18,134 @@ PG_MODULE_MAGIC; -static SV *SV_FromJsonb(JsonbContainer *jsonb); - -static JsonbValue *HV_ToJsonbValue(HV *obj, JsonbParseState *jsonb_state); - -static JsonbValue *SV_ToJsonbValue(SV *obj, JsonbParseState *jsonb_state); +static SV *Jsonb_ToSV(JsonbContainer *jsonb); +static JsonbValue *SV_ToJsonbValue(SV *obj, JsonbParseState **ps, bool is_elem); /* - * SV_FromJsonbValue + * JsonbValue_ToSV * * Transform JsonbValue into SV */ static SV * -SV_FromJsonbValue(JsonbValue *jsonbValue) +JsonbValue_ToSV(JsonbValue *jbv) { dTHX; - SV *result; - char *str; - switch (jsonbValue->type) + switch (jbv->type) { case jbvBinary: - result = (SV *) newRV((SV *) SV_FromJsonb(jsonbValue->val.binary.data)); - break; + return newRV(Jsonb_ToSV(jbv->val.binary.data)); + case jbvNumeric: + { + /* + * Transform incoming value into string and generate SV from + * string + */ + char *str = DatumGetCString( + DirectFunctionCall1(numeric_out, + NumericGetDatum(jbv->val.numeric))); + SV *result = newSVnv(SvNV(cstr2sv(str))); + + pfree(str); + + return result; + } - /* - * Transform incoming value into string and generate SV from - * string - */ - str = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(jsonbValue->val.numeric))); - result = newSVnv(SvNV(cstr2sv(pstrdup(str)))); - break; case jbvString: - result = cstr2sv(pnstrdup(jsonbValue->val.string.val, jsonbValue->val.string.len)); - break; + { + char *str = pnstrdup(jbv->val.string.val, + jbv->val.string.len); + SV *result = cstr2sv(str); + + pfree(str); + + return result; + } + case jbvBool: - result = newSVnv(SvNV(jsonbValue->val.boolean ? &PL_sv_yes : &PL_sv_no)); - break; - case jbvArray: - result = SV_FromJsonbValue(jsonbValue->val.array.elems); - break; - case jbvObject: - result = SV_FromJsonbValue(&(jsonbValue->val.object.pairs->value)); - break; + return newSVnv(SvNV(jbv->val.boolean ? &PL_sv_yes : &PL_sv_no)); + case jbvNull: - result = newSV(0); - break; + return newSV(0); + default: - pg_unreachable(); - break; + elog(ERROR, "unexpected jsonb value type: %d", jbv->type); + return NULL; } - return result; } /* - * SV_FromJsonb + * Jsonb_ToSV * * Transform JsonbContainer into SV */ -static SV * -SV_FromJsonb(JsonbContainer *jsonb) +static SV * +Jsonb_ToSV(JsonbContainer *jsonb) { dTHX; - SV *result; - SV *value; - JsonbIterator *it; JsonbValue v; + JsonbIterator *it; + JsonbIteratorToken r; it = JsonbIteratorInit(jsonb); + r = JsonbIteratorNext(&it, &v, true); - switch (JsonbIteratorNext(&it, &v, true)) + switch (r) { case WJB_BEGIN_ARRAY: + if (v.val.array.rawScalar) + { + JsonbValue tmp; + + if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM || + (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY || + (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE) + elog(ERROR, "unexpected jsonb token: %d", r); + + return newRV(JsonbValue_ToSV(&v)); + } + else { - AV *av; - bool raw_scalar; - - /* array in v */ - av = newAV(); - raw_scalar = (v.val.array.rawScalar); - value = newSV(0); - while (JsonbIteratorNext(&it, &v, true) == WJB_ELEM) + AV *av = newAV(); + + while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { - value = SV_FromJsonbValue(&v); - av_push(av, value); + if (r == WJB_ELEM) + av_push(av, JsonbValue_ToSV(&v)); } - if (raw_scalar) - result = newRV(value); - else - result = (SV *) av; - break; + + return (SV *) av; } + case WJB_BEGIN_OBJECT: { - HV *object; - const char *key; - int keyLength; + HV *hv = newHV(); - /* hash in v */ - object = newHV(); - while (JsonbIteratorNext(&it, &v, true) == WJB_KEY) + while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { - /* json key in v */ - keyLength = v.val.string.len; - key = pnstrdup(v.val.string.val, keyLength); - JsonbIteratorNext(&it, &v, true); - value = SV_FromJsonbValue(&v); - (void) hv_store(object, key, keyLength, value, 0); + if (r == WJB_KEY) + { + /* json key in v, json value in val */ + JsonbValue val; + + if (JsonbIteratorNext(&it, &val, true) == WJB_VALUE) + { + SV *value = JsonbValue_ToSV(&val); + + (void) hv_store(hv, v.val.string.val, + v.val.string.len, value, 0); + } + } } - result = (SV *) object; - break; + + return (SV *) hv; } - case WJB_ELEM: - case WJB_VALUE: - case WJB_KEY: - /* simple objects */ - result = SV_FromJsonbValue(&v); - break; - case WJB_DONE: - case WJB_END_OBJECT: - case WJB_END_ARRAY: + default: - pg_unreachable(); - break; + elog(ERROR, "unexpected jsonb token: %d", r); + return NULL; } - return result; } /* @@ -157,9 +159,7 @@ jsonb_to_plperl(PG_FUNCTION_ARGS) { dTHX; Jsonb *in = PG_GETARG_JSONB_P(0); - SV *sv; - - sv = SV_FromJsonb(&in->root); + SV *sv = Jsonb_ToSV(&in->root); return PointerGetDatum(newRV(sv)); } @@ -171,34 +171,51 @@ jsonb_to_plperl(PG_FUNCTION_ARGS) * jsonb_state defines conversion state */ static JsonbValue * -AV_ToJsonbValue(AV *in, JsonbParseState *jsonb_state) +AV_ToJsonbValue(AV *in, JsonbParseState **jsonb_state) { dTHX; - - JsonbValue *jbvElem; - JsonbValue *out = NULL; - ssize_t pcount; + ssize_t pcount = av_len(in) + 1; ssize_t i; - pcount = av_len(in) + 1; - pushJsonbValue(&jsonb_state, WJB_BEGIN_ARRAY, NULL); + pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL); for (i = 0; i < pcount; i++) { - SV **value; + SV **value = av_fetch(in, i, false); + + if (value) + (void) SV_ToJsonbValue(*value, jsonb_state, true); + } + + return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL); +} + +/* + * HV_ToJsonbValue + * + * Transform HV into Jsonb + */ +static JsonbValue * +HV_ToJsonbValue(HV *obj, JsonbParseState **jsonb_state) +{ + dTHX; + JsonbValue key; + SV *val; + + key.type = jbvString; + + pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL); - value = av_fetch(in, i, false); - jbvElem = SV_ToJsonbValue(*value, jsonb_state); + (void) hv_iterinit(obj); - /* - * If "value" was a complex structure, it was already pushed to jsonb - * and there is no need to push it again - */ - if (IsAJsonbScalar(jbvElem)) - pushJsonbValue(&jsonb_state, WJB_ELEM, jbvElem); + while ((val = hv_iternextsv(obj, &key.val.string.val, &key.val.string.len))) + { + key.val.string.val = pnstrdup(key.val.string.val,key.val.string.len); + pushJsonbValue(jsonb_state, WJB_KEY, &key); + (void) SV_ToJsonbValue(val, jsonb_state, false); } - out = pushJsonbValue(&jsonb_state, WJB_END_ARRAY, NULL); - return out; + + return pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL); } /* @@ -207,129 +224,92 @@ AV_ToJsonbValue(AV *in, JsonbParseState *jsonb_state) * Transform SV into Jsonb */ static JsonbValue * -SV_ToJsonbValue(SV *in, JsonbParseState *jsonb_state) +SV_ToJsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem) { dTHX; svtype type; /* type of incoming object */ - JsonbValue *out; /* result */ + JsonbValue out; /* result */ + + /* Dereference references recursively. */ + while (SvROK(in)) + in = SvRV(in); type = SvTYPE(in); + switch (type) { case SVt_PVAV: - out = AV_ToJsonbValue((AV *) in, jsonb_state); - break; + return AV_ToJsonbValue((AV *) in, jsonb_state); + case SVt_PVHV: - out = HV_ToJsonbValue((HV *) in, jsonb_state); - break; + return HV_ToJsonbValue((HV *) in, jsonb_state); + case SVt_NV: case SVt_IV: { - if (SvROK(in)) - /* if "in" is a pointer */ - out = SV_ToJsonbValue((SV *) SvRV(in), jsonb_state); - else - { - /* if "in" is a numeric */ - char *str; - int i; - - out = palloc(sizeof(JsonbValue)); - str = sv2cstr(in); - - /* - * We need to lowercase the string because infinity - * representation varies from version to version - */ - for (i = 0; str[i]; i++) - str[i] = tolower(str[i]); - - if (strcmp(str, "inf") == 0) - /* in case when variable in is "inf" */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - (errmsg("could not transform to type \"%s\"", "jsonb"), - errdetail("The type you are trying to transform can't be transformed to jsonb")))); - else - { - Datum tmp; - - tmp = DirectFunctionCall3(numeric_in, CStringGetDatum(str), 0, -1); - out->val.numeric = DatumGetNumeric(tmp); - out->type = jbvNumeric; - } - } - break; + /* if "in" is a numeric */ + char *str = sv2cstr(in); + int i; + + /* + * We need to lowercase the string because infinity + * representation varies from version to version + */ + for (i = 0; str[i]; i++) + str[i] = tolower(str[i]); + + if (strcmp(str, "inf") == 0) + /* in case when variable in is "inf" */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + (errmsg("could not transform to type \"%s\"", "jsonb"), + errdetail("The type you are trying to transform can't be transformed to jsonb")))); + + out.type = jbvNumeric; + out.val.numeric = DatumGetNumeric( + DirectFunctionCall3(numeric_in, + CStringGetDatum(str), 0, -1)); } + break; + case SVt_NULL: - out = palloc(sizeof(JsonbValue)); - out->type = jbvNull; + out.type = jbvNull; break; - case SVt_PV: - - /* - * String - */ - out = palloc(sizeof(JsonbValue)); - out->val.string.val = sv2cstr(in); - out->val.string.len = strlen(out->val.string.val); - out->type = jbvString; + + case SVt_PV: /* string */ + out.type = jbvString; + out.val.string.val = sv2cstr(in); + out.val.string.len = strlen(out.val.string.val); break; + default: ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("could not transform to type \"%s\"", "jsonb"), errdetail("The type you are trying to transform can't be transformed to jsonb")))); - break; + return NULL; } - return out; -} -/* - * HV_ToJsonbValue - * - * Transform Jsonb into SV - */ -static JsonbValue * -HV_ToJsonbValue(HV *obj, JsonbParseState *jsonb_state) -{ - dTHX; - JsonbValue *out; - HE *he; - - pushJsonbValue(&jsonb_state, WJB_BEGIN_OBJECT, NULL); - while ((he = hv_iternext(obj)) != NULL) - { - JsonbValue *key; - JsonbValue *val; - - key = SV_ToJsonbValue(HeSVKEY_force(he), jsonb_state); - pushJsonbValue(&jsonb_state, WJB_KEY, key); - val = SV_ToJsonbValue(HeVAL(he), jsonb_state); - if ((val == NULL) || (IsAJsonbScalar(val))) - pushJsonbValue(&jsonb_state, WJB_VALUE, val); - } - out = pushJsonbValue(&jsonb_state, WJB_END_OBJECT, NULL); - return out; + /* Push result into 'jsonb_state' unless it is a raw scalar. */ + return *jsonb_state + ? pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out) + : memcpy(palloc(sizeof(JsonbValue)), &out, sizeof(JsonbValue)); } /* * plperl_to_jsonb(SV *in) * - * Transform Jsonb into SV + * Transform SV into Jsonb */ PG_FUNCTION_INFO_V1(plperl_to_jsonb); Datum plperl_to_jsonb(PG_FUNCTION_ARGS) { dTHX; - JsonbValue *out = NULL; - Jsonb *result; JsonbParseState *jsonb_state = NULL; - SV *in; + SV *in = (SV *) PG_GETARG_POINTER(0); + JsonbValue *out = SV_ToJsonbValue(in, &jsonb_state, true); + Jsonb *result = JsonbValueToJsonb(out); - in = (SV *) PG_GETARG_POINTER(0); - out = SV_ToJsonbValue(in, jsonb_state); - result = JsonbValueToJsonb(out); - PG_RETURN_POINTER(result); + PG_RETURN_JSONB_P(result); }