]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix hstore_to_json_loose's detection of valid JSON number values.
authorAndrew Dunstan <andrew@dunslane.net>
Mon, 1 Dec 2014 16:28:45 +0000 (11:28 -0500)
committerAndrew Dunstan <andrew@dunslane.net>
Mon, 1 Dec 2014 16:44:48 +0000 (11:44 -0500)
We expose a function IsValidJsonNumber that internally calls the lexer
for json numbers. That allows us to use the same test everywhere,
instead of inventing a broken test for hstore conversions. The new
function is also used in datum_to_json, replacing the code that is now
moved to the new function.

Backpatch to 9.3 where hstore_to_json_loose was introduced.

contrib/hstore/hstore_io.c
src/backend/utils/adt/json.c
src/include/utils/jsonapi.h

index 4e6a54b775e0215f60614d9acbe707edc0d5865e..932061939167752a0bddb46898050b713ff95052 100644 (file)
@@ -12,6 +12,7 @@
 #include "libpq/pqformat.h"
 #include "utils/builtins.h"
 #include "utils/json.h"
+#include "utils/jsonapi.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/typcache.h"
@@ -1253,7 +1254,6 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
        int                     count = HS_COUNT(in);
        char       *base = STRPTR(in);
        HEntry     *entries = ARRPTR(in);
-       bool            is_number;
        StringInfoData tmp,
                                dst;
 
@@ -1280,48 +1280,9 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
                        appendStringInfoString(&dst, "false");
                else
                {
-                       is_number = false;
                        resetStringInfo(&tmp);
                        appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-
-                       /*
-                        * don't treat something with a leading zero followed by another
-                        * digit as numeric - could be a zip code or similar
-                        */
-                       if (tmp.len > 0 &&
-                               !(tmp.data[0] == '0' &&
-                                 isdigit((unsigned char) tmp.data[1])) &&
-                               strspn(tmp.data, "+-0123456789Ee.") == tmp.len)
-                       {
-                               /*
-                                * might be a number. See if we can input it as a numeric
-                                * value. Ignore any actual parsed value.
-                                */
-                               char       *endptr = "junk";
-                               long            lval;
-
-                               lval = strtol(tmp.data, &endptr, 10);
-                               (void) lval;
-                               if (*endptr == '\0')
-                               {
-                                       /*
-                                        * strol man page says this means the whole string is
-                                        * valid
-                                        */
-                                       is_number = true;
-                               }
-                               else
-                               {
-                                       /* not an int - try a double */
-                                       double          dval;
-
-                                       dval = strtod(tmp.data, &endptr);
-                                       (void) dval;
-                                       if (*endptr == '\0')
-                                               is_number = true;
-                               }
-                       }
-                       if (is_number)
+                       if (IsValidJsonNumber(tmp.data, tmp.len))
                                appendBinaryStringInfo(&dst, tmp.data, tmp.len);
                        else
                                escape_json(&dst, tmp.data);
index 53a011d50b48e4b77915d4d160ac37779080b4e3..0873bb3ce15c11a3dd08bb11ab2877ff403b8758 100644 (file)
@@ -164,6 +164,36 @@ lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
         (c) == '_' || \
         IS_HIGHBIT_SET(c))
 
+/* utility function to check if a string is a valid JSON number */
+extern bool
+IsValidJsonNumber(const char * str, int len)
+{
+       bool            numeric_error;
+       JsonLexContext dummy_lex;
+
+
+       /*
+        * json_lex_number expects a leading  '-' to have been eaten already.
+        *
+        * having to cast away the constness of str is ugly, but there's not much
+        * easy alternative.
+        */
+       if (*str == '-')
+       {
+               dummy_lex.input = (char *) str + 1;
+               dummy_lex.input_length = len - 1;
+       }
+       else
+       {
+               dummy_lex.input = (char *) str;
+               dummy_lex.input_length = len;
+       }
+
+       json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error);
+
+       return ! numeric_error;
+}
+
 /*
  * Input.
  */
@@ -1306,8 +1336,6 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 {
        char       *outputstr;
        text       *jsontext;
-       bool       numeric_error;
-       JsonLexContext dummy_lex;
 
        if (is_null)
        {
@@ -1332,12 +1360,10 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
                case JSONTYPE_NUMERIC:
                        outputstr = OidOutputFunctionCall(outfuncoid, val);
                        /*
-                        * Don't call escape_json here if it's a valid JSON number.
+                        * Don't call escape_json for a non-key if it's a valid JSON
+                        * number.
                         */
-                       dummy_lex.input = *outputstr == '-' ? outputstr + 1 : outputstr;
-                       dummy_lex.input_length = strlen(dummy_lex.input);
-                       json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error);
-                       if (! numeric_error)
+                       if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr)))
                                appendStringInfoString(result, outputstr);
                        else
                                escape_json(result, outputstr);
index e25a0d93d89aa81125b776c50580b27bac39c0a4..dcfe71c29dd093241144f6f7601f72bee73a6420 100644 (file)
@@ -107,4 +107,11 @@ extern void pg_parse_json(JsonLexContext *lex, JsonSemAction *sem);
  */
 extern JsonLexContext *makeJsonLexContext(text *json, bool need_escapes);
 
+/*
+ * Utility function to check if a string is a valid JSON number.
+ *
+ * str agrument does not need to be nul-terminated.
+ */
+extern bool IsValidJsonNumber(const char * str, int len);
+
 #endif   /* JSONAPI_H */