Skip to content

Commit 197f98a

Browse files
committed
Convert hstore_in to report errors softly.
The error reporting here was not only old and crufty, but untested. I took the opportunity to bring the messages into some sort of compliance with our message style guidelines. Discussion: https://postgr.es/m/6B6A5C77-60AD-4A71-9F3A-B2C026A281A6@dunslane.net
1 parent a5434c5 commit 197f98a

File tree

3 files changed

+133
-28
lines changed

3 files changed

+133
-28
lines changed

contrib/hstore/expected/hstore.out

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,40 @@ select ' '::hstore;
243243

244244
(1 row)
245245

246+
-- invalid input
247+
select ' =>null'::hstore;
248+
ERROR: syntax error in hstore, near "=" at position 2
249+
LINE 1: select ' =>null'::hstore;
250+
^
251+
select 'aa=>"'::hstore;
252+
ERROR: syntax error in hstore: unexpected end of string
253+
LINE 1: select 'aa=>"'::hstore;
254+
^
255+
-- also try it with non-error-throwing API
256+
select pg_input_is_valid('a=>b', 'hstore');
257+
pg_input_is_valid
258+
-------------------
259+
t
260+
(1 row)
261+
262+
select pg_input_is_valid('a=b', 'hstore');
263+
pg_input_is_valid
264+
-------------------
265+
f
266+
(1 row)
267+
268+
select pg_input_error_message('a=b', 'hstore');
269+
pg_input_error_message
270+
------------------------------------------------
271+
syntax error in hstore, near "b" at position 2
272+
(1 row)
273+
274+
select pg_input_error_message(' =>b', 'hstore');
275+
pg_input_error_message
276+
------------------------------------------------
277+
syntax error in hstore, near "=" at position 1
278+
(1 row)
279+
246280
-- -> operator
247281
select 'aa=>b, c=>d , b=>16'::hstore->'c';
248282
?column?

contrib/hstore/hstore_io.c

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "hstore.h"
1313
#include "lib/stringinfo.h"
1414
#include "libpq/pqformat.h"
15+
#include "nodes/miscnodes.h"
1516
#include "utils/builtins.h"
1617
#include "utils/json.h"
1718
#include "utils/jsonb.h"
@@ -32,12 +33,17 @@ typedef struct
3233
char *cur;
3334
char *word;
3435
int wordlen;
36+
Node *escontext;
3537

3638
Pairs *pairs;
3739
int pcur;
3840
int plen;
3941
} HSParser;
4042

43+
static bool hstoreCheckKeyLength(size_t len, HSParser *state);
44+
static bool hstoreCheckValLength(size_t len, HSParser *state);
45+
46+
4147
#define RESIZEPRSBUF \
4248
do { \
4349
if ( state->cur - state->word + 1 >= state->wordlen ) \
@@ -49,6 +55,32 @@ do { \
4955
} \
5056
} while (0)
5157

58+
#define PRSSYNTAXERROR return prssyntaxerror(state)
59+
60+
static bool
61+
prssyntaxerror(HSParser *state)
62+
{
63+
errsave(state->escontext,
64+
(errcode(ERRCODE_SYNTAX_ERROR),
65+
errmsg("syntax error in hstore, near \"%.*s\" at position %d",
66+
pg_mblen(state->ptr), state->ptr,
67+
(int) (state->ptr - state->begin))));
68+
/* In soft error situation, return false as convenience for caller */
69+
return false;
70+
}
71+
72+
#define PRSEOF return prseof(state)
73+
74+
static bool
75+
prseof(HSParser *state)
76+
{
77+
errsave(state->escontext,
78+
(errcode(ERRCODE_SYNTAX_ERROR),
79+
errmsg("syntax error in hstore: unexpected end of string")));
80+
/* In soft error situation, return false as convenience for caller */
81+
return false;
82+
}
83+
5284

5385
#define GV_WAITVAL 0
5486
#define GV_INVAL 1
@@ -80,9 +112,7 @@ get_val(HSParser *state, bool ignoreeq, bool *escaped)
80112
}
81113
else if (*(state->ptr) == '=' && !ignoreeq)
82114
{
83-
elog(ERROR, "Syntax error near \"%.*s\" at position %d",
84-
pg_mblen(state->ptr), state->ptr,
85-
(int32) (state->ptr - state->begin));
115+
PRSSYNTAXERROR;
86116
}
87117
else if (*(state->ptr) == '\\')
88118
{
@@ -139,7 +169,7 @@ get_val(HSParser *state, bool ignoreeq, bool *escaped)
139169
}
140170
else if (*(state->ptr) == '\0')
141171
{
142-
elog(ERROR, "Unexpected end of string");
172+
PRSEOF;
143173
}
144174
else
145175
{
@@ -151,7 +181,7 @@ get_val(HSParser *state, bool ignoreeq, bool *escaped)
151181
else if (st == GV_WAITESCIN)
152182
{
153183
if (*(state->ptr) == '\0')
154-
elog(ERROR, "Unexpected end of string");
184+
PRSEOF;
155185
RESIZEPRSBUF;
156186
*(state->cur) = *(state->ptr);
157187
state->cur++;
@@ -160,14 +190,14 @@ get_val(HSParser *state, bool ignoreeq, bool *escaped)
160190
else if (st == GV_WAITESCESCIN)
161191
{
162192
if (*(state->ptr) == '\0')
163-
elog(ERROR, "Unexpected end of string");
193+
PRSEOF;
164194
RESIZEPRSBUF;
165195
*(state->cur) = *(state->ptr);
166196
state->cur++;
167197
st = GV_INESCVAL;
168198
}
169199
else
170-
elog(ERROR, "Unknown state %d at position line %d in file '%s'", st, __LINE__, __FILE__);
200+
elog(ERROR, "unrecognized get_val state: %d", st);
171201

172202
state->ptr++;
173203
}
@@ -180,7 +210,7 @@ get_val(HSParser *state, bool ignoreeq, bool *escaped)
180210
#define WDEL 4
181211

182212

183-
static void
213+
static bool
184214
parse_hstore(HSParser *state)
185215
{
186216
int st = WKEY;
@@ -197,14 +227,20 @@ parse_hstore(HSParser *state)
197227
if (st == WKEY)
198228
{
199229
if (!get_val(state, false, &escaped))
200-
return;
230+
{
231+
if (SOFT_ERROR_OCCURRED(state->escontext))
232+
return false;
233+
return true; /* EOF, all okay */
234+
}
201235
if (state->pcur >= state->plen)
202236
{
203237
state->plen *= 2;
204238
state->pairs = (Pairs *) repalloc(state->pairs, sizeof(Pairs) * state->plen);
205239
}
240+
if (!hstoreCheckKeyLength(state->cur - state->word, state))
241+
return false;
206242
state->pairs[state->pcur].key = state->word;
207-
state->pairs[state->pcur].keylen = hstoreCheckKeyLen(state->cur - state->word);
243+
state->pairs[state->pcur].keylen = state->cur - state->word;
208244
state->pairs[state->pcur].val = NULL;
209245
state->word = NULL;
210246
st = WEQ;
@@ -217,13 +253,11 @@ parse_hstore(HSParser *state)
217253
}
218254
else if (*(state->ptr) == '\0')
219255
{
220-
elog(ERROR, "Unexpected end of string");
256+
PRSEOF;
221257
}
222258
else if (!isspace((unsigned char) *(state->ptr)))
223259
{
224-
elog(ERROR, "Syntax error near \"%.*s\" at position %d",
225-
pg_mblen(state->ptr), state->ptr,
226-
(int32) (state->ptr - state->begin));
260+
PRSSYNTAXERROR;
227261
}
228262
}
229263
else if (st == WGT)
@@ -234,27 +268,31 @@ parse_hstore(HSParser *state)
234268
}
235269
else if (*(state->ptr) == '\0')
236270
{
237-
elog(ERROR, "Unexpected end of string");
271+
PRSEOF;
238272
}
239273
else
240274
{
241-
elog(ERROR, "Syntax error near \"%.*s\" at position %d",
242-
pg_mblen(state->ptr), state->ptr,
243-
(int32) (state->ptr - state->begin));
275+
PRSSYNTAXERROR;
244276
}
245277
}
246278
else if (st == WVAL)
247279
{
248280
if (!get_val(state, true, &escaped))
249-
elog(ERROR, "Unexpected end of string");
281+
{
282+
if (SOFT_ERROR_OCCURRED(state->escontext))
283+
return false;
284+
PRSEOF;
285+
}
286+
if (!hstoreCheckValLength(state->cur - state->word, state))
287+
return false;
250288
state->pairs[state->pcur].val = state->word;
251-
state->pairs[state->pcur].vallen = hstoreCheckValLen(state->cur - state->word);
289+
state->pairs[state->pcur].vallen = state->cur - state->word;
252290
state->pairs[state->pcur].isnull = false;
253291
state->pairs[state->pcur].needfree = true;
254292
if (state->cur - state->word == 4 && !escaped)
255293
{
256294
state->word[4] = '\0';
257-
if (0 == pg_strcasecmp(state->word, "null"))
295+
if (pg_strcasecmp(state->word, "null") == 0)
258296
state->pairs[state->pcur].isnull = true;
259297
}
260298
state->word = NULL;
@@ -269,17 +307,15 @@ parse_hstore(HSParser *state)
269307
}
270308
else if (*(state->ptr) == '\0')
271309
{
272-
return;
310+
return true;
273311
}
274312
else if (!isspace((unsigned char) *(state->ptr)))
275313
{
276-
elog(ERROR, "Syntax error near \"%.*s\" at position %d",
277-
pg_mblen(state->ptr), state->ptr,
278-
(int32) (state->ptr - state->begin));
314+
PRSSYNTAXERROR;
279315
}
280316
}
281317
else
282-
elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
318+
elog(ERROR, "unrecognized parse_hstore state: %d", st);
283319

284320
state->ptr++;
285321
}
@@ -373,6 +409,16 @@ hstoreCheckKeyLen(size_t len)
373409
return len;
374410
}
375411

412+
static bool
413+
hstoreCheckKeyLength(size_t len, HSParser *state)
414+
{
415+
if (len > HSTORE_MAX_KEY_LEN)
416+
ereturn(state->escontext, false,
417+
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
418+
errmsg("string too long for hstore key")));
419+
return true;
420+
}
421+
376422
size_t
377423
hstoreCheckValLen(size_t len)
378424
{
@@ -383,6 +429,16 @@ hstoreCheckValLen(size_t len)
383429
return len;
384430
}
385431

432+
static bool
433+
hstoreCheckValLength(size_t len, HSParser *state)
434+
{
435+
if (len > HSTORE_MAX_VALUE_LEN)
436+
ereturn(state->escontext, false,
437+
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
438+
errmsg("string too long for hstore value")));
439+
return true;
440+
}
441+
386442

387443
HStore *
388444
hstorePairs(Pairs *pairs, int32 pcount, int32 buflen)
@@ -418,13 +474,17 @@ PG_FUNCTION_INFO_V1(hstore_in);
418474
Datum
419475
hstore_in(PG_FUNCTION_ARGS)
420476
{
477+
char *str = PG_GETARG_CSTRING(0);
478+
Node *escontext = fcinfo->context;
421479
HSParser state;
422480
int32 buflen;
423481
HStore *out;
424482

425-
state.begin = PG_GETARG_CSTRING(0);
483+
state.begin = str;
484+
state.escontext = escontext;
426485

427-
parse_hstore(&state);
486+
if (!parse_hstore(&state))
487+
PG_RETURN_NULL();
428488

429489
state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen);
430490

contrib/hstore/sql/hstore.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ select e'\\"a=>q"w'::hstore;
5353
select ''::hstore;
5454
select ' '::hstore;
5555

56+
-- invalid input
57+
select ' =>null'::hstore;
58+
select 'aa=>"'::hstore;
59+
60+
-- also try it with non-error-throwing API
61+
select pg_input_is_valid('a=>b', 'hstore');
62+
select pg_input_is_valid('a=b', 'hstore');
63+
select pg_input_error_message('a=b', 'hstore');
64+
select pg_input_error_message(' =>b', 'hstore');
65+
66+
5667
-- -> operator
5768

5869
select 'aa=>b, c=>d , b=>16'::hstore->'c';

0 commit comments

Comments
 (0)