Skip to content

Commit e6ecc93

Browse files
committed
Fix IsValidJsonNumber() to notice trailing non-alphanumeric garbage.
Commit e09996f was one brick shy of a load: it didn't insist that the detected JSON number be the whole of the supplied string. This allowed inputs such as "2016-01-01" to be misdetected as valid JSON numbers. Per bug #13906 from Dmitry Ryabov. In passing, be more wary of zero-length input (I'm not sure this can happen given current callers, but better safe than sorry), and do some minor cosmetic cleanup.
1 parent 7d17e68 commit e6ecc93

File tree

3 files changed

+43
-29
lines changed

3 files changed

+43
-29
lines changed

contrib/hstore/expected/hstore.out

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,10 +1466,10 @@ select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=
14661466
{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
14671467
(1 row)
14681468

1469-
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
1470-
hstore_to_json_loose
1471-
------------------------------------------------------------------------------------------
1472-
{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}
1469+
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4, h=> "2016-01-01"');
1470+
hstore_to_json_loose
1471+
-------------------------------------------------------------------------------------------------------------
1472+
{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "h": "2016-01-01", "a key": 1}
14731473
(1 row)
14741474

14751475
select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
@@ -1484,10 +1484,10 @@ select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=
14841484
{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
14851485
(1 row)
14861486

1487-
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
1488-
hstore_to_jsonb_loose
1489-
---------------------------------------------------------------------------------------
1490-
{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
1487+
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4, h=> "2016-01-01"');
1488+
hstore_to_jsonb_loose
1489+
----------------------------------------------------------------------------------------------------------
1490+
{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "h": "2016-01-01", "a key": 1}
14911491
(1 row)
14921492

14931493
create table test_json_agg (f1 text, f2 hstore);

contrib/hstore/sql/hstore.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,11 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
334334
-- json and jsonb
335335
select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
336336
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
337-
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
337+
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4, h=> "2016-01-01"');
338338

339339
select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
340340
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb);
341-
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
341+
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4, h=> "2016-01-01"');
342342

343343
create table test_json_agg (f1 text, f2 hstore);
344344
insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),

src/backend/utils/adt/json.c

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ typedef struct JsonAggState
7676

7777
static inline void json_lex(JsonLexContext *lex);
7878
static inline void json_lex_string(JsonLexContext *lex);
79-
static inline void json_lex_number(JsonLexContext *lex, char *s, bool *num_err);
79+
static inline void json_lex_number(JsonLexContext *lex, char *s,
80+
bool *num_err, int *total_len);
8081
static inline void parse_scalar(JsonLexContext *lex, JsonSemAction *sem);
8182
static void parse_object_field(JsonLexContext *lex, JsonSemAction *sem);
8283
static void parse_object(JsonLexContext *lex, JsonSemAction *sem);
@@ -182,13 +183,20 @@ lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
182183
(c) == '_' || \
183184
IS_HIGHBIT_SET(c))
184185

185-
/* utility function to check if a string is a valid JSON number */
186-
extern bool
186+
/*
187+
* Utility function to check if a string is a valid JSON number.
188+
*
189+
* str is of length len, and need not be null-terminated.
190+
*/
191+
bool
187192
IsValidJsonNumber(const char *str, int len)
188193
{
189194
bool numeric_error;
195+
int total_len;
190196
JsonLexContext dummy_lex;
191197

198+
if (len <= 0)
199+
return false;
192200

193201
/*
194202
* json_lex_number expects a leading '-' to have been eaten already.
@@ -207,9 +215,9 @@ IsValidJsonNumber(const char *str, int len)
207215
dummy_lex.input_length = len;
208216
}
209217

210-
json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error);
218+
json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error, &total_len);
211219

212-
return !numeric_error;
220+
return (!numeric_error) && (total_len == dummy_lex.input_length);
213221
}
214222

215223
/*
@@ -669,7 +677,7 @@ json_lex(JsonLexContext *lex)
669677
break;
670678
case '-':
671679
/* Negative number. */
672-
json_lex_number(lex, s + 1, NULL);
680+
json_lex_number(lex, s + 1, NULL, NULL);
673681
lex->token_type = JSON_TOKEN_NUMBER;
674682
break;
675683
case '0':
@@ -683,7 +691,7 @@ json_lex(JsonLexContext *lex)
683691
case '8':
684692
case '9':
685693
/* Positive number. */
686-
json_lex_number(lex, s, NULL);
694+
json_lex_number(lex, s, NULL, NULL);
687695
lex->token_type = JSON_TOKEN_NUMBER;
688696
break;
689697
default:
@@ -983,7 +991,7 @@ json_lex_string(JsonLexContext *lex)
983991
lex->token_terminator = s + 1;
984992
}
985993

986-
/*-------------------------------------------------------------------------
994+
/*
987995
* The next token in the input stream is known to be a number; lex it.
988996
*
989997
* In JSON, a number consists of four parts:
@@ -1004,29 +1012,30 @@ json_lex_string(JsonLexContext *lex)
10041012
* followed by at least one digit.)
10051013
*
10061014
* The 's' argument to this function points to the ostensible beginning
1007-
* of part 2 - i.e. the character after any optional minus sign, and the
1015+
* of part 2 - i.e. the character after any optional minus sign, or the
10081016
* first character of the string if there is none.
10091017
*
1010-
*-------------------------------------------------------------------------
1018+
* If num_err is not NULL, we return an error flag to *num_err rather than
1019+
* raising an error for a badly-formed number. Also, if total_len is not NULL
1020+
* the distance from lex->input to the token end+1 is returned to *total_len.
10111021
*/
10121022
static inline void
1013-
json_lex_number(JsonLexContext *lex, char *s, bool *num_err)
1023+
json_lex_number(JsonLexContext *lex, char *s,
1024+
bool *num_err, int *total_len)
10141025
{
10151026
bool error = false;
1016-
char *p;
1017-
int len;
1027+
int len = s - lex->input;
10181028

1019-
len = s - lex->input;
10201029
/* Part (1): leading sign indicator. */
10211030
/* Caller already did this for us; so do nothing. */
10221031

10231032
/* Part (2): parse main digit string. */
1024-
if (*s == '0')
1033+
if (len < lex->input_length && *s == '0')
10251034
{
10261035
s++;
10271036
len++;
10281037
}
1029-
else if (*s >= '1' && *s <= '9')
1038+
else if (len < lex->input_length && *s >= '1' && *s <= '9')
10301039
{
10311040
do
10321041
{
@@ -1081,18 +1090,23 @@ json_lex_number(JsonLexContext *lex, char *s, bool *num_err)
10811090
* here should be considered part of the token for error-reporting
10821091
* purposes.
10831092
*/
1084-
for (p = s; len < lex->input_length && JSON_ALPHANUMERIC_CHAR(*p); p++, len++)
1093+
for (; len < lex->input_length && JSON_ALPHANUMERIC_CHAR(*s); s++, len++)
10851094
error = true;
10861095

1096+
if (total_len != NULL)
1097+
*total_len = len;
1098+
10871099
if (num_err != NULL)
10881100
{
1089-
/* let the caller handle the error */
1101+
/* let the caller handle any error */
10901102
*num_err = error;
10911103
}
10921104
else
10931105
{
1106+
/* return token endpoint */
10941107
lex->prev_token_terminator = lex->token_terminator;
1095-
lex->token_terminator = p;
1108+
lex->token_terminator = s;
1109+
/* handle error if any */
10961110
if (error)
10971111
report_invalid_token(lex);
10981112
}

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