From ec8529348008e58826442e43809772c19d02a067 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Thu, 26 Apr 2018 15:20:43 -0400 Subject: [PATCH] Prevent infinity and NaN in jsonb/plperl transform jsonb uses numeric internally, and numeric can store NaN, but that is not allowed by jsonb on input, so we shouldn't store it. Also prevent infinity to get a consistent error message. (numeric input would reject infinity anyway.) --- .../jsonb_plperl/expected/jsonb_plperl.out | 24 +++++++++++++++++-- .../jsonb_plperl/expected/jsonb_plperlu.out | 24 +++++++++++++++++-- contrib/jsonb_plperl/jsonb_plperl.c | 16 +++++++++++-- contrib/jsonb_plperl/sql/jsonb_plperl.sql | 23 +++++++++++++++++- contrib/jsonb_plperl/sql/jsonb_plperlu.sql | 22 +++++++++++++++++ 5 files changed, 102 insertions(+), 7 deletions(-) diff --git a/contrib/jsonb_plperl/expected/jsonb_plperl.out b/contrib/jsonb_plperl/expected/jsonb_plperl.out index 99a2e8e135..d6c3becf63 100644 --- a/contrib/jsonb_plperl/expected/jsonb_plperl.out +++ b/contrib/jsonb_plperl/expected/jsonb_plperl.out @@ -39,6 +39,26 @@ SELECT testSVToJsonb(); 1 (1 row) +CREATE FUNCTION testInf() RETURNS jsonb +LANGUAGE plperl +TRANSFORM FOR TYPE jsonb +AS $$ +$val = 0 + 'Inf'; +return $val; +$$; +SELECT testInf(); +ERROR: cannot convert infinity to jsonb +CONTEXT: PL/Perl function "testinf" +CREATE FUNCTION testNaN() RETURNS jsonb +LANGUAGE plperl +TRANSFORM FOR TYPE jsonb +AS $$ +$val = 0 + 'NaN'; +return $val; +$$; +SELECT testNaN(); +ERROR: cannot convert NaN to jsonb +CONTEXT: PL/Perl function "testnan" -- this revealed a bug in the original implementation CREATE FUNCTION testRegexpResultToJsonb() RETURNS jsonb LANGUAGE plperl @@ -71,7 +91,7 @@ SELECT roundtrip('1'); (1 row) SELECT roundtrip('1E+131071'); -ERROR: cannot convert infinite value to jsonb +ERROR: cannot convert infinity to jsonb CONTEXT: PL/Perl function "roundtrip" SELECT roundtrip('-1'); roundtrip @@ -207,4 +227,4 @@ SELECT roundtrip('{"1": {"2": [3, 4, 5]}, "2": 3}'); \set VERBOSITY terse \\ -- suppress cascade details DROP EXTENSION plperl CASCADE; -NOTICE: drop cascades to 6 other objects +NOTICE: drop cascades to 8 other objects diff --git a/contrib/jsonb_plperl/expected/jsonb_plperlu.out b/contrib/jsonb_plperl/expected/jsonb_plperlu.out index 8053cf6aa8..65ed21f3b2 100644 --- a/contrib/jsonb_plperl/expected/jsonb_plperlu.out +++ b/contrib/jsonb_plperl/expected/jsonb_plperlu.out @@ -39,6 +39,26 @@ SELECT testSVToJsonb(); 1 (1 row) +CREATE FUNCTION testInf() RETURNS jsonb +LANGUAGE plperlu +TRANSFORM FOR TYPE jsonb +AS $$ +$val = 0 + 'Inf'; +return $val; +$$; +SELECT testInf(); +ERROR: cannot convert infinity to jsonb +CONTEXT: PL/Perl function "testinf" +CREATE FUNCTION testNaN() RETURNS jsonb +LANGUAGE plperlu +TRANSFORM FOR TYPE jsonb +AS $$ +$val = 0 + 'NaN'; +return $val; +$$; +SELECT testNaN(); +ERROR: cannot convert NaN to jsonb +CONTEXT: PL/Perl function "testnan" -- this revealed a bug in the original implementation CREATE FUNCTION testRegexpResultToJsonb() RETURNS jsonb LANGUAGE plperlu @@ -71,7 +91,7 @@ SELECT roundtrip('1'); (1 row) SELECT roundtrip('1E+131071'); -ERROR: cannot convert infinite value to jsonb +ERROR: cannot convert infinity to jsonb CONTEXT: PL/Perl function "roundtrip" SELECT roundtrip('-1'); roundtrip @@ -207,4 +227,4 @@ SELECT roundtrip('{"1": {"2": [3, 4, 5]}, "2": 3}'); \set VERBOSITY terse \\ -- suppress cascade details DROP EXTENSION plperlu CASCADE; -NOTICE: drop cascades to 6 other objects +NOTICE: drop cascades to 8 other objects diff --git a/contrib/jsonb_plperl/jsonb_plperl.c b/contrib/jsonb_plperl/jsonb_plperl.c index 837bae2ab5..3f9802c696 100644 --- a/contrib/jsonb_plperl/jsonb_plperl.c +++ b/contrib/jsonb_plperl/jsonb_plperl.c @@ -209,10 +209,22 @@ SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem) { 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 NaN, so we have to prevent it here + * explicitly. We don't really have to check for isinf() + * here, as numeric doesn't allow it and it would be caught + * later, but it makes for a nicer error message. + */ if (isinf(nval)) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - (errmsg("cannot convert infinite value to jsonb")))); + (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; out.val.numeric = diff --git a/contrib/jsonb_plperl/sql/jsonb_plperl.sql b/contrib/jsonb_plperl/sql/jsonb_plperl.sql index 8b0a8764af..b543579eab 100644 --- a/contrib/jsonb_plperl/sql/jsonb_plperl.sql +++ b/contrib/jsonb_plperl/sql/jsonb_plperl.sql @@ -34,6 +34,28 @@ CREATE FUNCTION testSVToJsonb() RETURNS jsonb SELECT testSVToJsonb(); +CREATE FUNCTION testInf() RETURNS jsonb +LANGUAGE plperl +TRANSFORM FOR TYPE jsonb +AS $$ +$val = 0 + 'Inf'; +return $val; +$$; + +SELECT testInf(); + + +CREATE FUNCTION testNaN() RETURNS jsonb +LANGUAGE plperl +TRANSFORM FOR TYPE jsonb +AS $$ +$val = 0 + 'NaN'; +return $val; +$$; + +SELECT testNaN(); + + -- this revealed a bug in the original implementation CREATE FUNCTION testRegexpResultToJsonb() RETURNS jsonb LANGUAGE plperl @@ -83,6 +105,5 @@ CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb SELECT roundtrip('{"1": {"2": [3, 4, 5]}, "2": 3}'); - \set VERBOSITY terse \\ -- suppress cascade details DROP EXTENSION plperl CASCADE; diff --git a/contrib/jsonb_plperl/sql/jsonb_plperlu.sql b/contrib/jsonb_plperl/sql/jsonb_plperlu.sql index 9287f7672f..9c0c083c99 100644 --- a/contrib/jsonb_plperl/sql/jsonb_plperlu.sql +++ b/contrib/jsonb_plperl/sql/jsonb_plperlu.sql @@ -34,6 +34,28 @@ CREATE FUNCTION testSVToJsonb() RETURNS jsonb SELECT testSVToJsonb(); +CREATE FUNCTION testInf() RETURNS jsonb +LANGUAGE plperlu +TRANSFORM FOR TYPE jsonb +AS $$ +$val = 0 + 'Inf'; +return $val; +$$; + +SELECT testInf(); + + +CREATE FUNCTION testNaN() RETURNS jsonb +LANGUAGE plperlu +TRANSFORM FOR TYPE jsonb +AS $$ +$val = 0 + 'NaN'; +return $val; +$$; + +SELECT testNaN(); + + -- this revealed a bug in the original implementation CREATE FUNCTION testRegexpResultToJsonb() RETURNS jsonb LANGUAGE plperlu base-commit: dd4cc9d7069c04d0ee8ae040511274c6e71b60f7 -- 2.17.0