|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
Fix platform and Perl-version dependencies in new jsonb_plperl code.
Testing SvTYPE() directly is more fraught with problems than one might
think, because depending on context Perl might be storing a scalar value
in one of several forms, eg both numeric and string values. This resulted
in Perl-version-dependent buildfarm test failures. Instead use the SvTYPE
test only to distinguish non-scalar cases (AV, HV, NULL). Disambiguate
scalars by testing SvIOK, SvNOK, then SvPOK. This creates a preference
order for how we will resolve cases where the value is available in more
than one form, which seems fine to me.
Furthermore, because we're now dealing directly with a "double" value
in the SvNOK case, we can get rid of an inadequate and unportable
string-comparison test for infinities, and use isinf() instead.
(We do need some additional #include and "-lm" infrastructure to use
that in a contrib module, per prior experiences.)
In passing, prevent the regression test results from depending on DROP
CASCADE order; I've not seen that malfunction, but it's trouble waiting
to happen.
Discussion: https://postgr.es/m/E1f3MMJ-0006bf-B0@gemulon.postgresql.org
8 years ago
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
#include "fmgr.h"
|
|
|
|
|
#include "plperl.h"
|
|
|
|
|
#include "utils/fmgrprotos.h"
|
|
|
|
|
#include "utils/jsonb.h"
|
|
|
|
|
|
|
|
|
|
PG_MODULE_MAGIC_EXT(
|
|
|
|
|
.name = "jsonb_plperl",
|
|
|
|
|
.version = PG_VERSION
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
static SV *Jsonb_to_SV(JsonbContainer *jsonb);
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
static void SV_to_JsonbValue(SV *obj, JsonbInState *ps, bool is_elem);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static SV *
|
|
|
|
|
JsonbValue_to_SV(JsonbValue *jbv)
|
|
|
|
|
{
|
|
|
|
|
dTHX;
|
|
|
|
|
|
|
|
|
|
switch (jbv->type)
|
|
|
|
|
{
|
|
|
|
|
case jbvBinary:
|
Fix excessive enreferencing in jsonb-to-plperl transform.
We want, say, 2 to be transformed as 2, not \\2 which is what the
original coding produced. Perl's standard seems to be to add an RV
wrapper only for hash and array SVs, so do it like that.
This was missed originally because the test cases only checked what came
out of a round trip back to SQL, and the strip-all-dereferences loop at
the top of SV_to_JsonbValue hides the extra refs from view. As a better
test, print the Perl value with Data::Dumper, like the hstore_plperlu
tests do. While we can't do that in the plperl test, only plperlu,
that should be good enough because this code is the same for both PLs.
But also add a simplistic test for extra REFs, which we can do in both.
That strip-all-dereferences behavior is now a bit dubious; it's unlike
what happens for other Perl-to-SQL conversions. However, the best
thing to do seems to be to leave it alone and make the other conversions
act similarly. That will be done separately.
Dagfinn Ilmari Mannsåker, adjusted a bit by me
Discussion: https://postgr.es/m/d8jlgbq66t9.fsf@dalvik.ping.uio.no
8 years ago
|
|
|
return Jsonb_to_SV(jbv->val.binary.data);
|
|
|
|
|
|
|
|
|
|
case jbvNumeric:
|
|
|
|
|
{
|
|
|
|
|
char *str = DatumGetCString(DirectFunctionCall1(numeric_out,
|
|
|
|
|
NumericGetDatum(jbv->val.numeric)));
|
|
|
|
|
SV *result = newSVnv(SvNV(cstr2sv(str)));
|
|
|
|
|
|
|
|
|
|
pfree(str);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case jbvString:
|
|
|
|
|
{
|
|
|
|
|
char *str = pnstrdup(jbv->val.string.val,
|
|
|
|
|
jbv->val.string.len);
|
|
|
|
|
SV *result = cstr2sv(str);
|
|
|
|
|
|
|
|
|
|
pfree(str);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case jbvBool:
|
|
|
|
|
return newSVnv(SvNV(jbv->val.boolean ? &PL_sv_yes : &PL_sv_no));
|
|
|
|
|
|
|
|
|
|
case jbvNull:
|
|
|
|
|
return newSV(0);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
elog(ERROR, "unexpected jsonb value type: %d", jbv->type);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SV *
|
|
|
|
|
Jsonb_to_SV(JsonbContainer *jsonb)
|
|
|
|
|
{
|
|
|
|
|
dTHX;
|
|
|
|
|
JsonbValue v;
|
|
|
|
|
JsonbIterator *it;
|
|
|
|
|
JsonbIteratorToken r;
|
|
|
|
|
|
|
|
|
|
it = JsonbIteratorInit(jsonb);
|
|
|
|
|
r = 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);
|
|
|
|
|
|
Fix excessive enreferencing in jsonb-to-plperl transform.
We want, say, 2 to be transformed as 2, not \\2 which is what the
original coding produced. Perl's standard seems to be to add an RV
wrapper only for hash and array SVs, so do it like that.
This was missed originally because the test cases only checked what came
out of a round trip back to SQL, and the strip-all-dereferences loop at
the top of SV_to_JsonbValue hides the extra refs from view. As a better
test, print the Perl value with Data::Dumper, like the hstore_plperlu
tests do. While we can't do that in the plperl test, only plperlu,
that should be good enough because this code is the same for both PLs.
But also add a simplistic test for extra REFs, which we can do in both.
That strip-all-dereferences behavior is now a bit dubious; it's unlike
what happens for other Perl-to-SQL conversions. However, the best
thing to do seems to be to leave it alone and make the other conversions
act similarly. That will be done separately.
Dagfinn Ilmari Mannsåker, adjusted a bit by me
Discussion: https://postgr.es/m/d8jlgbq66t9.fsf@dalvik.ping.uio.no
8 years ago
|
|
|
return JsonbValue_to_SV(&v);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AV *av = newAV();
|
|
|
|
|
|
|
|
|
|
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
|
|
|
|
|
{
|
|
|
|
|
if (r == WJB_ELEM)
|
|
|
|
|
av_push(av, JsonbValue_to_SV(&v));
|
|
|
|
|
}
|
|
|
|
|
|
Fix excessive enreferencing in jsonb-to-plperl transform.
We want, say, 2 to be transformed as 2, not \\2 which is what the
original coding produced. Perl's standard seems to be to add an RV
wrapper only for hash and array SVs, so do it like that.
This was missed originally because the test cases only checked what came
out of a round trip back to SQL, and the strip-all-dereferences loop at
the top of SV_to_JsonbValue hides the extra refs from view. As a better
test, print the Perl value with Data::Dumper, like the hstore_plperlu
tests do. While we can't do that in the plperl test, only plperlu,
that should be good enough because this code is the same for both PLs.
But also add a simplistic test for extra REFs, which we can do in both.
That strip-all-dereferences behavior is now a bit dubious; it's unlike
what happens for other Perl-to-SQL conversions. However, the best
thing to do seems to be to leave it alone and make the other conversions
act similarly. That will be done separately.
Dagfinn Ilmari Mannsåker, adjusted a bit by me
Discussion: https://postgr.es/m/d8jlgbq66t9.fsf@dalvik.ping.uio.no
8 years ago
|
|
|
return newRV((SV *) av);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case WJB_BEGIN_OBJECT:
|
|
|
|
|
{
|
|
|
|
|
HV *hv = newHV();
|
|
|
|
|
|
|
|
|
|
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
|
|
|
|
|
{
|
|
|
|
|
if (r == WJB_KEY)
|
|
|
|
|
{
|
|
|
|
|
/* json key in v, json value in val */
|
|
|
|
|
JsonbValue val;
|
|
|
|
|
|
|
|
|
|
if (JsonbIteratorNext(&it, &val, true) == WJB_VALUE)
|
|
|
|
|
{
|
|
|
|
|
SV *value = JsonbValue_to_SV(&val);
|
|
|
|
|
|
|
|
|
|
(void) hv_store(hv,
|
|
|
|
|
v.val.string.val, v.val.string.len,
|
|
|
|
|
value, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Fix excessive enreferencing in jsonb-to-plperl transform.
We want, say, 2 to be transformed as 2, not \\2 which is what the
original coding produced. Perl's standard seems to be to add an RV
wrapper only for hash and array SVs, so do it like that.
This was missed originally because the test cases only checked what came
out of a round trip back to SQL, and the strip-all-dereferences loop at
the top of SV_to_JsonbValue hides the extra refs from view. As a better
test, print the Perl value with Data::Dumper, like the hstore_plperlu
tests do. While we can't do that in the plperl test, only plperlu,
that should be good enough because this code is the same for both PLs.
But also add a simplistic test for extra REFs, which we can do in both.
That strip-all-dereferences behavior is now a bit dubious; it's unlike
what happens for other Perl-to-SQL conversions. However, the best
thing to do seems to be to leave it alone and make the other conversions
act similarly. That will be done separately.
Dagfinn Ilmari Mannsåker, adjusted a bit by me
Discussion: https://postgr.es/m/d8jlgbq66t9.fsf@dalvik.ping.uio.no
8 years ago
|
|
|
return newRV((SV *) hv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
elog(ERROR, "unexpected jsonb token: %d", r);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
static void
|
|
|
|
|
AV_to_JsonbValue(AV *in, JsonbInState *jsonb_state)
|
|
|
|
|
{
|
|
|
|
|
dTHX;
|
|
|
|
|
SSize_t pcount = av_len(in) + 1;
|
|
|
|
|
SSize_t i;
|
|
|
|
|
|
|
|
|
|
pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < pcount; i++)
|
|
|
|
|
{
|
|
|
|
|
SV **value = av_fetch(in, i, FALSE);
|
|
|
|
|
|
|
|
|
|
if (value)
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
SV_to_JsonbValue(*value, jsonb_state, true);
|
|
|
|
|
}
|
|
|
|
|
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
static void
|
|
|
|
|
HV_to_JsonbValue(HV *obj, JsonbInState *jsonb_state)
|
|
|
|
|
{
|
|
|
|
|
dTHX;
|
|
|
|
|
JsonbValue key;
|
|
|
|
|
SV *val;
|
|
|
|
|
char *kstr;
|
|
|
|
|
I32 klen;
|
|
|
|
|
|
|
|
|
|
key.type = jbvString;
|
|
|
|
|
|
|
|
|
|
pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
|
|
|
|
|
|
|
|
|
|
(void) hv_iterinit(obj);
|
|
|
|
|
|
|
|
|
|
while ((val = hv_iternextsv(obj, &kstr, &klen)))
|
|
|
|
|
{
|
|
|
|
|
key.val.string.val = pnstrdup(kstr, klen);
|
|
|
|
|
key.val.string.len = klen;
|
|
|
|
|
pushJsonbValue(jsonb_state, WJB_KEY, &key);
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
SV_to_JsonbValue(val, jsonb_state, false);
|
|
|
|
|
}
|
|
|
|
|
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
static void
|
|
|
|
|
SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem)
|
|
|
|
|
{
|
|
|
|
|
dTHX;
|
|
|
|
|
JsonbValue out; /* result */
|
|
|
|
|
|
|
|
|
|
/* Dereference references recursively. */
|
|
|
|
|
while (SvROK(in))
|
|
|
|
|
in = SvRV(in);
|
|
|
|
|
|
|
|
|
|
switch (SvTYPE(in))
|
|
|
|
|
{
|
|
|
|
|
case SVt_PVAV:
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
AV_to_JsonbValue((AV *) in, jsonb_state);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case SVt_PVHV:
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
HV_to_JsonbValue((HV *) in, jsonb_state);
|
|
|
|
|
return;
|
|
|
|
|
|
Fix platform and Perl-version dependencies in new jsonb_plperl code.
Testing SvTYPE() directly is more fraught with problems than one might
think, because depending on context Perl might be storing a scalar value
in one of several forms, eg both numeric and string values. This resulted
in Perl-version-dependent buildfarm test failures. Instead use the SvTYPE
test only to distinguish non-scalar cases (AV, HV, NULL). Disambiguate
scalars by testing SvIOK, SvNOK, then SvPOK. This creates a preference
order for how we will resolve cases where the value is available in more
than one form, which seems fine to me.
Furthermore, because we're now dealing directly with a "double" value
in the SvNOK case, we can get rid of an inadequate and unportable
string-comparison test for infinities, and use isinf() instead.
(We do need some additional #include and "-lm" infrastructure to use
that in a contrib module, per prior experiences.)
In passing, prevent the regression test results from depending on DROP
CASCADE order; I've not seen that malfunction, but it's trouble waiting
to happen.
Discussion: https://postgr.es/m/E1f3MMJ-0006bf-B0@gemulon.postgresql.org
8 years ago
|
|
|
default:
|
|
|
|
|
if (!SvOK(in))
|
|
|
|
|
{
|
|
|
|
|
out.type = jbvNull;
|
|
|
|
|
}
|
|
|
|
|
else if (SvUOK(in))
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If UV is >=64 bits, we have no better way to make this
|
|
|
|
|
* happen than converting to text and back. Given the low
|
|
|
|
|
* usage of UV in Perl code, it's not clear it's worth working
|
|
|
|
|
* hard to provide alternate code paths.
|
|
|
|
|
*/
|
|
|
|
|
const char *strval = SvPV_nolen(in);
|
|
|
|
|
|
|
|
|
|
out.type = jbvNumeric;
|
|
|
|
|
out.val.numeric =
|
|
|
|
|
DatumGetNumeric(DirectFunctionCall3(numeric_in,
|
|
|
|
|
CStringGetDatum(strval),
|
|
|
|
|
ObjectIdGetDatum(InvalidOid),
|
|
|
|
|
Int32GetDatum(-1)));
|
|
|
|
|
}
|
|
|
|
|
else if (SvIOK(in))
|
|
|
|
|
{
|
Fix platform and Perl-version dependencies in new jsonb_plperl code.
Testing SvTYPE() directly is more fraught with problems than one might
think, because depending on context Perl might be storing a scalar value
in one of several forms, eg both numeric and string values. This resulted
in Perl-version-dependent buildfarm test failures. Instead use the SvTYPE
test only to distinguish non-scalar cases (AV, HV, NULL). Disambiguate
scalars by testing SvIOK, SvNOK, then SvPOK. This creates a preference
order for how we will resolve cases where the value is available in more
than one form, which seems fine to me.
Furthermore, because we're now dealing directly with a "double" value
in the SvNOK case, we can get rid of an inadequate and unportable
string-comparison test for infinities, and use isinf() instead.
(We do need some additional #include and "-lm" infrastructure to use
that in a contrib module, per prior experiences.)
In passing, prevent the regression test results from depending on DROP
CASCADE order; I've not seen that malfunction, but it's trouble waiting
to happen.
Discussion: https://postgr.es/m/E1f3MMJ-0006bf-B0@gemulon.postgresql.org
8 years ago
|
|
|
IV ival = SvIV(in);
|
|
|
|
|
|
Fix platform and Perl-version dependencies in new jsonb_plperl code.
Testing SvTYPE() directly is more fraught with problems than one might
think, because depending on context Perl might be storing a scalar value
in one of several forms, eg both numeric and string values. This resulted
in Perl-version-dependent buildfarm test failures. Instead use the SvTYPE
test only to distinguish non-scalar cases (AV, HV, NULL). Disambiguate
scalars by testing SvIOK, SvNOK, then SvPOK. This creates a preference
order for how we will resolve cases where the value is available in more
than one form, which seems fine to me.
Furthermore, because we're now dealing directly with a "double" value
in the SvNOK case, we can get rid of an inadequate and unportable
string-comparison test for infinities, and use isinf() instead.
(We do need some additional #include and "-lm" infrastructure to use
that in a contrib module, per prior experiences.)
In passing, prevent the regression test results from depending on DROP
CASCADE order; I've not seen that malfunction, but it's trouble waiting
to happen.
Discussion: https://postgr.es/m/E1f3MMJ-0006bf-B0@gemulon.postgresql.org
8 years ago
|
|
|
out.type = jbvNumeric;
|
|
|
|
|
out.val.numeric = int64_to_numeric(ival);
|
Fix platform and Perl-version dependencies in new jsonb_plperl code.
Testing SvTYPE() directly is more fraught with problems than one might
think, because depending on context Perl might be storing a scalar value
in one of several forms, eg both numeric and string values. This resulted
in Perl-version-dependent buildfarm test failures. Instead use the SvTYPE
test only to distinguish non-scalar cases (AV, HV, NULL). Disambiguate
scalars by testing SvIOK, SvNOK, then SvPOK. This creates a preference
order for how we will resolve cases where the value is available in more
than one form, which seems fine to me.
Furthermore, because we're now dealing directly with a "double" value
in the SvNOK case, we can get rid of an inadequate and unportable
string-comparison test for infinities, and use isinf() instead.
(We do need some additional #include and "-lm" infrastructure to use
that in a contrib module, per prior experiences.)
In passing, prevent the regression test results from depending on DROP
CASCADE order; I've not seen that malfunction, but it's trouble waiting
to happen.
Discussion: https://postgr.es/m/E1f3MMJ-0006bf-B0@gemulon.postgresql.org
8 years ago
|
|
|
}
|
|
|
|
|
else if (SvNOK(in))
|
|
|
|
|
{
|
|
|
|
|
double nval = SvNV(in);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* jsonb doesn't allow infinity or NaN (per JSON
|
|
|
|
|
* specification), but the numeric type that is used for the
|
|
|
|
|
* storage accepts those, so we have to reject them here
|
|
|
|
|
* explicitly.
|
|
|
|
|
*/
|
Fix platform and Perl-version dependencies in new jsonb_plperl code.
Testing SvTYPE() directly is more fraught with problems than one might
think, because depending on context Perl might be storing a scalar value
in one of several forms, eg both numeric and string values. This resulted
in Perl-version-dependent buildfarm test failures. Instead use the SvTYPE
test only to distinguish non-scalar cases (AV, HV, NULL). Disambiguate
scalars by testing SvIOK, SvNOK, then SvPOK. This creates a preference
order for how we will resolve cases where the value is available in more
than one form, which seems fine to me.
Furthermore, because we're now dealing directly with a "double" value
in the SvNOK case, we can get rid of an inadequate and unportable
string-comparison test for infinities, and use isinf() instead.
(We do need some additional #include and "-lm" infrastructure to use
that in a contrib module, per prior experiences.)
In passing, prevent the regression test results from depending on DROP
CASCADE order; I've not seen that malfunction, but it's trouble waiting
to happen.
Discussion: https://postgr.es/m/E1f3MMJ-0006bf-B0@gemulon.postgresql.org
8 years ago
|
|
|
if (isinf(nval))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("cannot convert infinity to jsonb")));
|
|
|
|
|
if (isnan(nval))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("cannot convert NaN to jsonb")));
|
|
|
|
|
|
|
|
|
|
out.type = jbvNumeric;
|
Fix platform and Perl-version dependencies in new jsonb_plperl code.
Testing SvTYPE() directly is more fraught with problems than one might
think, because depending on context Perl might be storing a scalar value
in one of several forms, eg both numeric and string values. This resulted
in Perl-version-dependent buildfarm test failures. Instead use the SvTYPE
test only to distinguish non-scalar cases (AV, HV, NULL). Disambiguate
scalars by testing SvIOK, SvNOK, then SvPOK. This creates a preference
order for how we will resolve cases where the value is available in more
than one form, which seems fine to me.
Furthermore, because we're now dealing directly with a "double" value
in the SvNOK case, we can get rid of an inadequate and unportable
string-comparison test for infinities, and use isinf() instead.
(We do need some additional #include and "-lm" infrastructure to use
that in a contrib module, per prior experiences.)
In passing, prevent the regression test results from depending on DROP
CASCADE order; I've not seen that malfunction, but it's trouble waiting
to happen.
Discussion: https://postgr.es/m/E1f3MMJ-0006bf-B0@gemulon.postgresql.org
8 years ago
|
|
|
out.val.numeric =
|
|
|
|
|
DatumGetNumeric(DirectFunctionCall1(float8_numeric,
|
|
|
|
|
Float8GetDatum(nval)));
|
|
|
|
|
}
|
|
|
|
|
else if (SvPOK(in))
|
|
|
|
|
{
|
|
|
|
|
out.type = jbvString;
|
|
|
|
|
out.val.string.val = sv2cstr(in);
|
|
|
|
|
out.val.string.len = strlen(out.val.string.val);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* XXX It might be nice if we could include the Perl type in
|
|
|
|
|
* the error message.
|
|
|
|
|
*/
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
|
errmsg("cannot transform this Perl type to jsonb")));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
if (jsonb_state->parseState)
|
|
|
|
|
{
|
|
|
|
|
/* We're in an array or object, so push value as element or field. */
|
|
|
|
|
pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* We are at top level, so it's a raw scalar. If we just shove the
|
|
|
|
|
* scalar value into jsonb_state->result, JsonbValueToJsonb will take
|
|
|
|
|
* care of wrapping it into a dummy array.
|
|
|
|
|
*/
|
|
|
|
|
jsonb_state->result = palloc_object(JsonbValue);
|
|
|
|
|
memcpy(jsonb_state->result, &out, sizeof(JsonbValue));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(jsonb_to_plperl);
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
jsonb_to_plperl(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
dTHX;
|
|
|
|
|
Jsonb *in = PG_GETARG_JSONB_P(0);
|
|
|
|
|
SV *sv = Jsonb_to_SV(&in->root);
|
|
|
|
|
|
Fix excessive enreferencing in jsonb-to-plperl transform.
We want, say, 2 to be transformed as 2, not \\2 which is what the
original coding produced. Perl's standard seems to be to add an RV
wrapper only for hash and array SVs, so do it like that.
This was missed originally because the test cases only checked what came
out of a round trip back to SQL, and the strip-all-dereferences loop at
the top of SV_to_JsonbValue hides the extra refs from view. As a better
test, print the Perl value with Data::Dumper, like the hstore_plperlu
tests do. While we can't do that in the plperl test, only plperlu,
that should be good enough because this code is the same for both PLs.
But also add a simplistic test for extra REFs, which we can do in both.
That strip-all-dereferences behavior is now a bit dubious; it's unlike
what happens for other Perl-to-SQL conversions. However, the best
thing to do seems to be to leave it alone and make the other conversions
act similarly. That will be done separately.
Dagfinn Ilmari Mannsåker, adjusted a bit by me
Discussion: https://postgr.es/m/d8jlgbq66t9.fsf@dalvik.ping.uio.no
8 years ago
|
|
|
return PointerGetDatum(sv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(plperl_to_jsonb);
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
plperl_to_jsonb(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
dTHX;
|
|
|
|
|
SV *in = (SV *) PG_GETARG_POINTER(0);
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
JsonbInState jsonb_state = {0};
|
|
|
|
|
|
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(),
pass a pointer to a JsonbInState, which will contain the
parseState stack pointer as well as other useful fields.
Also, instead of returning a JsonbValue pointer that is often
meaningless/ignored, return the top-level JsonbValue pointer
in the "result" field of the JsonbInState.
This involves a lot of (mostly mechanical) edits, but I think
the results are notationally cleaner and easier to understand.
Certainly the business with sometimes capturing the result of
pushJsonbValue() and sometimes not was bug-prone and incapable of
mechanical verification. In the new arrangement, JsonbInState.result
remains null until we've completed a valid sequence of pushes, so
that an incorrect sequence will result in a null-pointer dereference,
not mistaken use of a partial result.
However, this isn't simply an exercise in prettier notation.
The real reason for doing it is to provide a mechanism whereby
pushJsonbValue() can be told to construct the JsonbValue tree
in a context that is not CurrentMemoryContext. That happens
when a non-null "outcontext" is specified in the JsonbInState.
No callers exercise that option in this patch, but the next
patch in the series will make use of it.
I tried to improve the comments in this area too.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
2 weeks ago
|
|
|
SV_to_JsonbValue(in, &jsonb_state, true);
|
|
|
|
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(jsonb_state.result));
|
|
|
|
|
}
|