mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Fix IsValidJsonNumber() to notice trailing non-alphanumeric garbage.
Commit e09996ff8d 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.
			
			
This commit is contained in:
		@@ -1466,10 +1466,10 @@ select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=
 | 
				
			|||||||
 {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
 | 
					 {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
 | 
				
			||||||
(1 row)
 | 
					(1 row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
					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"');
 | 
				
			||||||
                                            hstore_to_json_loose                                             
 | 
					                                            hstore_to_json_loose                                             
 | 
				
			||||||
------------------------------------------------------------------------------------------
 | 
					-------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}
 | 
					 {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "h": "2016-01-01", "a key": 1}
 | 
				
			||||||
(1 row)
 | 
					(1 row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
					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=
 | 
				
			|||||||
 {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
 | 
					 {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
 | 
				
			||||||
(1 row)
 | 
					(1 row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
					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"');
 | 
				
			||||||
                                          hstore_to_jsonb_loose                                           
 | 
					                                          hstore_to_jsonb_loose                                           
 | 
				
			||||||
---------------------------------------------------------------------------------------
 | 
					----------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
 | 
					 {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "h": "2016-01-01", "a key": 1}
 | 
				
			||||||
(1 row)
 | 
					(1 row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
create table test_json_agg (f1 text, f2 hstore);
 | 
					create table test_json_agg (f1 text, f2 hstore);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -334,11 +334,11 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
 | 
				
			|||||||
-- json and jsonb
 | 
					-- json and jsonb
 | 
				
			||||||
select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
					select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
				
			||||||
select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
 | 
					select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
 | 
				
			||||||
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
					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"');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
					select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
				
			||||||
select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb);
 | 
					select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb);
 | 
				
			||||||
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 | 
					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"');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
create table test_json_agg (f1 text, f2 hstore);
 | 
					create table test_json_agg (f1 text, f2 hstore);
 | 
				
			||||||
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'),
 | 
					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'),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,7 +76,8 @@ typedef struct JsonAggState
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static inline void json_lex(JsonLexContext *lex);
 | 
					static inline void json_lex(JsonLexContext *lex);
 | 
				
			||||||
static inline void json_lex_string(JsonLexContext *lex);
 | 
					static inline void json_lex_string(JsonLexContext *lex);
 | 
				
			||||||
static inline void json_lex_number(JsonLexContext *lex, char *s, bool *num_err);
 | 
					static inline void json_lex_number(JsonLexContext *lex, char *s,
 | 
				
			||||||
 | 
									bool *num_err, int *total_len);
 | 
				
			||||||
static inline void parse_scalar(JsonLexContext *lex, JsonSemAction *sem);
 | 
					static inline void parse_scalar(JsonLexContext *lex, JsonSemAction *sem);
 | 
				
			||||||
static void parse_object_field(JsonLexContext *lex, JsonSemAction *sem);
 | 
					static void parse_object_field(JsonLexContext *lex, JsonSemAction *sem);
 | 
				
			||||||
static void parse_object(JsonLexContext *lex, JsonSemAction *sem);
 | 
					static void parse_object(JsonLexContext *lex, JsonSemAction *sem);
 | 
				
			||||||
@@ -182,13 +183,20 @@ lex_expect(JsonParseContext ctx, JsonLexContext *lex, JsonTokenType token)
 | 
				
			|||||||
	 (c) == '_' || \
 | 
						 (c) == '_' || \
 | 
				
			||||||
	 IS_HIGHBIT_SET(c))
 | 
						 IS_HIGHBIT_SET(c))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* utility function to check if a string is a valid JSON number */
 | 
					/*
 | 
				
			||||||
extern bool
 | 
					 * Utility function to check if a string is a valid JSON number.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * str is of length len, and need not be null-terminated.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool
 | 
				
			||||||
IsValidJsonNumber(const char *str, int len)
 | 
					IsValidJsonNumber(const char *str, int len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bool		numeric_error;
 | 
						bool		numeric_error;
 | 
				
			||||||
 | 
						int			total_len;
 | 
				
			||||||
	JsonLexContext dummy_lex;
 | 
						JsonLexContext dummy_lex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (len <= 0)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * json_lex_number expects a leading  '-' to have been eaten already.
 | 
						 * json_lex_number expects a leading  '-' to have been eaten already.
 | 
				
			||||||
@@ -207,9 +215,9 @@ IsValidJsonNumber(const char *str, int len)
 | 
				
			|||||||
		dummy_lex.input_length = len;
 | 
							dummy_lex.input_length = len;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error);
 | 
						json_lex_number(&dummy_lex, dummy_lex.input, &numeric_error, &total_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return !numeric_error;
 | 
						return (!numeric_error) && (total_len == dummy_lex.input_length);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@@ -669,7 +677,7 @@ json_lex(JsonLexContext *lex)
 | 
				
			|||||||
				break;
 | 
									break;
 | 
				
			||||||
			case '-':
 | 
								case '-':
 | 
				
			||||||
				/* Negative number. */
 | 
									/* Negative number. */
 | 
				
			||||||
				json_lex_number(lex, s + 1, NULL);
 | 
									json_lex_number(lex, s + 1, NULL, NULL);
 | 
				
			||||||
				lex->token_type = JSON_TOKEN_NUMBER;
 | 
									lex->token_type = JSON_TOKEN_NUMBER;
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case '0':
 | 
								case '0':
 | 
				
			||||||
@@ -683,7 +691,7 @@ json_lex(JsonLexContext *lex)
 | 
				
			|||||||
			case '8':
 | 
								case '8':
 | 
				
			||||||
			case '9':
 | 
								case '9':
 | 
				
			||||||
				/* Positive number. */
 | 
									/* Positive number. */
 | 
				
			||||||
				json_lex_number(lex, s, NULL);
 | 
									json_lex_number(lex, s, NULL, NULL);
 | 
				
			||||||
				lex->token_type = JSON_TOKEN_NUMBER;
 | 
									lex->token_type = JSON_TOKEN_NUMBER;
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
@@ -983,7 +991,7 @@ json_lex_string(JsonLexContext *lex)
 | 
				
			|||||||
	lex->token_terminator = s + 1;
 | 
						lex->token_terminator = s + 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*-------------------------------------------------------------------------
 | 
					/*
 | 
				
			||||||
 * The next token in the input stream is known to be a number; lex it.
 | 
					 * The next token in the input stream is known to be a number; lex it.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * In JSON, a number consists of four parts:
 | 
					 * In JSON, a number consists of four parts:
 | 
				
			||||||
@@ -1004,29 +1012,30 @@ json_lex_string(JsonLexContext *lex)
 | 
				
			|||||||
 *	   followed by at least one digit.)
 | 
					 *	   followed by at least one digit.)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * The 's' argument to this function points to the ostensible beginning
 | 
					 * The 's' argument to this function points to the ostensible beginning
 | 
				
			||||||
 * of part 2 - i.e. the character after any optional minus sign, and the
 | 
					 * of part 2 - i.e. the character after any optional minus sign, or the
 | 
				
			||||||
 * first character of the string if there is none.
 | 
					 * first character of the string if there is none.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *-------------------------------------------------------------------------
 | 
					 * If num_err is not NULL, we return an error flag to *num_err rather than
 | 
				
			||||||
 | 
					 * raising an error for a badly-formed number.  Also, if total_len is not NULL
 | 
				
			||||||
 | 
					 * the distance from lex->input to the token end+1 is returned to *total_len.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static inline void
 | 
					static inline void
 | 
				
			||||||
json_lex_number(JsonLexContext *lex, char *s, bool *num_err)
 | 
					json_lex_number(JsonLexContext *lex, char *s,
 | 
				
			||||||
 | 
									bool *num_err, int *total_len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bool		error = false;
 | 
						bool		error = false;
 | 
				
			||||||
	char	   *p;
 | 
						int			len = s - lex->input;
 | 
				
			||||||
	int			len;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	len = s - lex->input;
 | 
					 | 
				
			||||||
	/* Part (1): leading sign indicator. */
 | 
						/* Part (1): leading sign indicator. */
 | 
				
			||||||
	/* Caller already did this for us; so do nothing. */
 | 
						/* Caller already did this for us; so do nothing. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Part (2): parse main digit string. */
 | 
						/* Part (2): parse main digit string. */
 | 
				
			||||||
	if (*s == '0')
 | 
						if (len < lex->input_length && *s == '0')
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		s++;
 | 
							s++;
 | 
				
			||||||
		len++;
 | 
							len++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (*s >= '1' && *s <= '9')
 | 
						else if (len < lex->input_length && *s >= '1' && *s <= '9')
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		do
 | 
							do
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -1081,18 +1090,23 @@ json_lex_number(JsonLexContext *lex, char *s, bool *num_err)
 | 
				
			|||||||
	 * here should be considered part of the token for error-reporting
 | 
						 * here should be considered part of the token for error-reporting
 | 
				
			||||||
	 * purposes.
 | 
						 * purposes.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	for (p = s; len < lex->input_length && JSON_ALPHANUMERIC_CHAR(*p); p++, len++)
 | 
						for (; len < lex->input_length && JSON_ALPHANUMERIC_CHAR(*s); s++, len++)
 | 
				
			||||||
		error = true;
 | 
							error = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (total_len != NULL)
 | 
				
			||||||
 | 
							*total_len = len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (num_err != NULL)
 | 
						if (num_err != NULL)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		/* let the caller handle the error */
 | 
							/* let the caller handle any error */
 | 
				
			||||||
		*num_err = error;
 | 
							*num_err = error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							/* return token endpoint */
 | 
				
			||||||
		lex->prev_token_terminator = lex->token_terminator;
 | 
							lex->prev_token_terminator = lex->token_terminator;
 | 
				
			||||||
		lex->token_terminator = p;
 | 
							lex->token_terminator = s;
 | 
				
			||||||
 | 
							/* handle error if any */
 | 
				
			||||||
		if (error)
 | 
							if (error)
 | 
				
			||||||
			report_invalid_token(lex);
 | 
								report_invalid_token(lex);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user