Skip to content

Commit 55c8807

Browse files
committed
Fix hstore_to_json_loose's detection of valid JSON number values.
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.
1 parent 5c9a4a8 commit 55c8807

File tree

3 files changed

+42
-48
lines changed

3 files changed

+42
-48
lines changed

contrib/hstore/hstore_io.c

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "libpq/pqformat.h"
1313
#include "utils/builtins.h"
1414
#include "utils/json.h"
15+
#include "utils/jsonapi.h"
1516
#include "utils/lsyscache.h"
1617
#include "utils/memutils.h"
1718
#include "utils/typcache.h"
@@ -1253,7 +1254,6 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
12531254
int count = HS_COUNT(in);
12541255
char *base = STRPTR(in);
12551256
HEntry *entries = ARRPTR(in);
1256-
bool is_number;
12571257
StringInfoData tmp,
12581258
dst;
12591259

@@ -1280,48 +1280,9 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
12801280
appendStringInfoString(&dst, "false");
12811281
else
12821282
{
1283-
is_number = false;
12841283
resetStringInfo(&tmp);
12851284
appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
1286-
1287-
/*
1288-
* don't treat something with a leading zero followed by another
1289-
* digit as numeric - could be a zip code or similar
1290-
*/
1291-
if (tmp.len > 0 &&
1292-
!(tmp.data[0] == '0' &&
1293-
isdigit((unsigned char) tmp.data[1])) &&
1294-
strspn(tmp.data, "+-0123456789Ee.") == tmp.len)
1295-
{
1296-
/*
1297-
* might be a number. See if we can input it as a numeric
1298-
* value. Ignore any actual parsed value.
1299-
*/
1300-
char *endptr = "junk";
1301-
long lval;
1302-
1303-
lval = strtol(tmp.data, &endptr, 10);
1304-
(void) lval;
1305-
if (*endptr == '\0')
1306-
{
1307-
/*
1308-
* strol man page says this means the whole string is
1309-
* valid
1310-
*/
1311-
is_number = true;
1312-
}
1313-
else
1314-
{
1315-
/* not an int - try a double */
1316-
double dval;
1317-
1318-
dval = strtod(tmp.data, &endptr);
1319-
(void) dval;
1320-
if (*endptr == '\0')
1321-
is_number = true;
1322-
}
1323-
}
1324-
if (is_number)
1285+
if (IsValidJsonNumber(tmp.data, tmp.len))
13251286
appendBinaryStringInfo(&dst, tmp.data, tmp.len);
13261287
else
13271288
escape_json(&dst, tmp.data);

src/backend/utils/adt/json.c

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,36 @@ lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
164164
(c) == '_' || \
165165
IS_HIGHBIT_SET(c))
166166

167+
/* utility function to check if a string is a valid JSON number */
168+
extern bool
169+
IsValidJsonNumber(const char * str, int len)
170+
{
171+
bool numeric_error;
172+
JsonLexContext dummy_lex;
173+
174+
175+
/*
176+
* json_lex_number expects a leading '-' to have been eaten already.
177+
*
178+
* having to cast away the constness of str is ugly, but there's not much
179+
* easy alternative.
180+
*/
181+
if (*str == '-')
182+
{
183+
dummy_lex.input = (char *) str + 1;
184+
dummy_lex.input_length = len - 1;
185+
}
186+
else
187+
{
188+
dummy_lex.input = (char *) str;
189+
dummy_lex.input_length = len;
190+
}
191+
192+
json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error);
193+
194+
return ! numeric_error;
195+
}
196+
167197
/*
168198
* Input.
169199
*/
@@ -1306,8 +1336,6 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
13061336
{
13071337
char *outputstr;
13081338
text *jsontext;
1309-
bool numeric_error;
1310-
JsonLexContext dummy_lex;
13111339

13121340
if (is_null)
13131341
{
@@ -1332,12 +1360,10 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
13321360
case JSONTYPE_NUMERIC:
13331361
outputstr = OidOutputFunctionCall(outfuncoid, val);
13341362
/*
1335-
* Don't call escape_json here if it's a valid JSON number.
1363+
* Don't call escape_json for a non-key if it's a valid JSON
1364+
* number.
13361365
*/
1337-
dummy_lex.input = *outputstr == '-' ? outputstr + 1 : outputstr;
1338-
dummy_lex.input_length = strlen(dummy_lex.input);
1339-
json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error);
1340-
if (! numeric_error)
1366+
if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr)))
13411367
appendStringInfoString(result, outputstr);
13421368
else
13431369
escape_json(result, outputstr);

src/include/utils/jsonapi.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,11 @@ extern void pg_parse_json(JsonLexContext *lex, JsonSemAction *sem);
107107
*/
108108
extern JsonLexContext *makeJsonLexContext(text *json, bool need_escapes);
109109

110+
/*
111+
* Utility function to check if a string is a valid JSON number.
112+
*
113+
* str agrument does not need to be nul-terminated.
114+
*/
115+
extern bool IsValidJsonNumber(const char * str, int len);
116+
110117
#endif /* JSONAPI_H */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy