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)
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