Skip to content

Commit aa223a0

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 95a2cca commit aa223a0

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
@@ -68,7 +68,8 @@ typedef enum /* type categories for datum_to_json */
6868

6969
static inline void json_lex(JsonLexContext *lex);
7070
static inline void json_lex_string(JsonLexContext *lex);
71-
static inline void json_lex_number(JsonLexContext *lex, char *s, bool *num_err);
71+
static inline void json_lex_number(JsonLexContext *lex, char *s,
72+
bool *num_err, int *total_len);
7273
static inline void parse_scalar(JsonLexContext *lex, JsonSemAction *sem);
7374
static void parse_object_field(JsonLexContext *lex, JsonSemAction *sem);
7475
static void parse_object(JsonLexContext *lex, JsonSemAction *sem);
@@ -174,13 +175,20 @@ lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
174175
(c) == '_' || \
175176
IS_HIGHBIT_SET(c))
176177

177-
/* utility function to check if a string is a valid JSON number */
178-
extern bool
178+
/*
179+
* Utility function to check if a string is a valid JSON number.
180+
*
181+
* str is of length len, and need not be null-terminated.
182+
*/
183+
bool
179184
IsValidJsonNumber(const char *str, int len)
180185
{
181186
bool numeric_error;
187+
int total_len;
182188
JsonLexContext dummy_lex;
183189

190+
if (len <= 0)
191+
return false;
184192

185193
/*
186194
* json_lex_number expects a leading '-' to have been eaten already.
@@ -199,9 +207,9 @@ IsValidJsonNumber(const char *str, int len)
199207
dummy_lex.input_length = len;
200208
}
201209

202-
json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error);
210+
json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error, &total_len);
203211

204-
return !numeric_error;
212+
return (!numeric_error) && (total_len == dummy_lex.input_length);
205213
}
206214

207215
/*
@@ -622,7 +630,7 @@ json_lex(JsonLexContext *lex)
622630
break;
623631
case '-':
624632
/* Negative number. */
625-
json_lex_number(lex, s + 1, NULL);
633+
json_lex_number(lex, s + 1, NULL, NULL);
626634
lex->token_type = JSON_TOKEN_NUMBER;
627635
break;
628636
case '0':
@@ -636,7 +644,7 @@ json_lex(JsonLexContext *lex)
636644
case '8':
637645
case '9':
638646
/* Positive number. */
639-
json_lex_number(lex, s, NULL);
647+
json_lex_number(lex, s, NULL, NULL);
640648
lex->token_type = JSON_TOKEN_NUMBER;
641649
break;
642650
default:
@@ -936,7 +944,7 @@ json_lex_string(JsonLexContext *lex)
936944
lex->token_terminator = s + 1;
937945
}
938946

939-
/*-------------------------------------------------------------------------
947+
/*
940948
* The next token in the input stream is known to be a number; lex it.
941949
*
942950
* In JSON, a number consists of four parts:
@@ -957,29 +965,30 @@ json_lex_string(JsonLexContext *lex)
957965
* followed by at least one digit.)
958966
*
959967
* The 's' argument to this function points to the ostensible beginning
960-
* of part 2 - i.e. the character after any optional minus sign, and the
968+
* of part 2 - i.e. the character after any optional minus sign, or the
961969
* first character of the string if there is none.
962970
*
963-
*-------------------------------------------------------------------------
971+
* If num_err is not NULL, we return an error flag to *num_err rather than
972+
* raising an error for a badly-formed number. Also, if total_len is not NULL
973+
* the distance from lex->input to the token end+1 is returned to *total_len.
964974
*/
965975
static inline void
966-
json_lex_number(JsonLexContext *lex, char *s, bool *num_err)
976+
json_lex_number(JsonLexContext *lex, char *s,
977+
bool *num_err, int *total_len)
967978
{
968979
bool error = false;
969-
char *p;
970-
int len;
980+
int len = s - lex->input;
971981

972-
len = s - lex->input;
973982
/* Part (1): leading sign indicator. */
974983
/* Caller already did this for us; so do nothing. */
975984

976985
/* Part (2): parse main digit string. */
977-
if (*s == '0')
986+
if (len < lex->input_length && *s == '0')
978987
{
979988
s++;
980989
len++;
981990
}
982-
else if (*s >= '1' && *s <= '9')
991+
else if (len < lex->input_length && *s >= '1' && *s <= '9')
983992
{
984993
do
985994
{
@@ -1034,18 +1043,23 @@ json_lex_number(JsonLexContext *lex, char *s, bool *num_err)
10341043
* here should be considered part of the token for error-reporting
10351044
* purposes.
10361045
*/
1037-
for (p = s; len < lex->input_length && JSON_ALPHANUMERIC_CHAR(*p); p++, len++)
1046+
for (; len < lex->input_length && JSON_ALPHANUMERIC_CHAR(*s); s++, len++)
10381047
error = true;
10391048

1049+
if (total_len != NULL)
1050+
*total_len = len;
1051+
10401052
if (num_err != NULL)
10411053
{
1042-
/* let the caller handle the error */
1054+
/* let the caller handle any error */
10431055
*num_err = error;
10441056
}
10451057
else
10461058
{
1059+
/* return token endpoint */
10471060
lex->prev_token_terminator = lex->token_terminator;
1048-
lex->token_terminator = p;
1061+
lex->token_terminator = s;
1062+
/* handle error if any */
10491063
if (error)
10501064
report_invalid_token(lex);
10511065
}

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