Skip to content

Commit e37fe1d

Browse files
committed
Convert jsonpath's input function to report errors softly
Reviewed by Tom Lane Discussion: https://postgr.es/m/a8dc5700-c341-3ba8-0507-cc09881e6200@dunslane.net
1 parent 780ec9f commit e37fe1d

File tree

8 files changed

+322
-125
lines changed

8 files changed

+322
-125
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,19 @@
6666
#include "funcapi.h"
6767
#include "lib/stringinfo.h"
6868
#include "libpq/pqformat.h"
69+
#include "nodes/miscnodes.h"
6970
#include "miscadmin.h"
7071
#include "utils/builtins.h"
7172
#include "utils/json.h"
7273
#include "utils/jsonpath.h"
7374

7475

75-
static Datum jsonPathFromCstring(char *in, int len);
76+
static Datum jsonPathFromCstring(char *in, int len, struct Node *escontext);
7677
static char *jsonPathToCstring(StringInfo out, JsonPath *in,
7778
int estimated_len);
78-
static int flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
79+
static bool flattenJsonPathParseItem(StringInfo buf, int *result,
80+
struct Node *escontext,
81+
JsonPathParseItem *item,
7982
int nestingLevel, bool insideArraySubscript);
8083
static void alignStringInfoInt(StringInfo buf);
8184
static int32 reserveSpaceForItemPointer(StringInfo buf);
@@ -95,7 +98,7 @@ jsonpath_in(PG_FUNCTION_ARGS)
9598
char *in = PG_GETARG_CSTRING(0);
9699
int len = strlen(in);
97100

98-
return jsonPathFromCstring(in, len);
101+
return jsonPathFromCstring(in, len, fcinfo->context);
99102
}
100103

101104
/*
@@ -119,7 +122,7 @@ jsonpath_recv(PG_FUNCTION_ARGS)
119122
else
120123
elog(ERROR, "unsupported jsonpath version number: %d", version);
121124

122-
return jsonPathFromCstring(str, nbytes);
125+
return jsonPathFromCstring(str, nbytes, NULL);
123126
}
124127

125128
/*
@@ -165,24 +168,29 @@ jsonpath_send(PG_FUNCTION_ARGS)
165168
* representation of jsonpath.
166169
*/
167170
static Datum
168-
jsonPathFromCstring(char *in, int len)
171+
jsonPathFromCstring(char *in, int len, struct Node *escontext)
169172
{
170-
JsonPathParseResult *jsonpath = parsejsonpath(in, len);
173+
JsonPathParseResult *jsonpath = parsejsonpath(in, len, escontext);
171174
JsonPath *res;
172175
StringInfoData buf;
173176

174-
initStringInfo(&buf);
175-
enlargeStringInfo(&buf, 4 * len /* estimation */ );
176-
177-
appendStringInfoSpaces(&buf, JSONPATH_HDRSZ);
177+
if (SOFT_ERROR_OCCURRED(escontext))
178+
return (Datum) 0;
178179

179180
if (!jsonpath)
180-
ereport(ERROR,
181+
ereturn(escontext, (Datum) 0,
181182
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
182183
errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
183184
in)));
184185

185-
flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
186+
initStringInfo(&buf);
187+
enlargeStringInfo(&buf, 4 * len /* estimation */ );
188+
189+
appendStringInfoSpaces(&buf, JSONPATH_HDRSZ);
190+
191+
if (!flattenJsonPathParseItem(&buf, NULL, escontext,
192+
jsonpath->expr, 0, false))
193+
return (Datum) 0;
186194

187195
res = (JsonPath *) buf.data;
188196
SET_VARSIZE(res, buf.len);
@@ -225,9 +233,10 @@ jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
225233
* Recursive function converting given jsonpath parse item and all its
226234
* children into a binary representation.
227235
*/
228-
static int
229-
flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
230-
int nestingLevel, bool insideArraySubscript)
236+
static bool
237+
flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
238+
JsonPathParseItem *item, int nestingLevel,
239+
bool insideArraySubscript)
231240
{
232241
/* position from beginning of jsonpath data */
233242
int32 pos = buf->len - JSONPATH_HDRSZ;
@@ -295,16 +304,22 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
295304
int32 left = reserveSpaceForItemPointer(buf);
296305
int32 right = reserveSpaceForItemPointer(buf);
297306

298-
chld = !item->value.args.left ? pos :
299-
flattenJsonPathParseItem(buf, item->value.args.left,
300-
nestingLevel + argNestingLevel,
301-
insideArraySubscript);
307+
if (!item->value.args.left)
308+
chld = pos;
309+
else if (! flattenJsonPathParseItem(buf, &chld, escontext,
310+
item->value.args.left,
311+
nestingLevel + argNestingLevel,
312+
insideArraySubscript))
313+
return false;
302314
*(int32 *) (buf->data + left) = chld - pos;
303315

304-
chld = !item->value.args.right ? pos :
305-
flattenJsonPathParseItem(buf, item->value.args.right,
306-
nestingLevel + argNestingLevel,
307-
insideArraySubscript);
316+
if (!item->value.args.right)
317+
chld = pos;
318+
else if (! flattenJsonPathParseItem(buf, &chld, escontext,
319+
item->value.args.right,
320+
nestingLevel + argNestingLevel,
321+
insideArraySubscript))
322+
return false;
308323
*(int32 *) (buf->data + right) = chld - pos;
309324
}
310325
break;
@@ -323,9 +338,11 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
323338
item->value.like_regex.patternlen);
324339
appendStringInfoChar(buf, '\0');
325340

326-
chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
327-
nestingLevel,
328-
insideArraySubscript);
341+
if (! flattenJsonPathParseItem(buf, &chld, escontext,
342+
item->value.like_regex.expr,
343+
nestingLevel,
344+
insideArraySubscript))
345+
return false;
329346
*(int32 *) (buf->data + offs) = chld - pos;
330347
}
331348
break;
@@ -341,10 +358,13 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
341358
{
342359
int32 arg = reserveSpaceForItemPointer(buf);
343360

344-
chld = !item->value.arg ? pos :
345-
flattenJsonPathParseItem(buf, item->value.arg,
346-
nestingLevel + argNestingLevel,
347-
insideArraySubscript);
361+
if (!item->value.arg)
362+
chld = pos;
363+
else if (! flattenJsonPathParseItem(buf, &chld, escontext,
364+
item->value.arg,
365+
nestingLevel + argNestingLevel,
366+
insideArraySubscript))
367+
return false;
348368
*(int32 *) (buf->data + arg) = chld - pos;
349369
}
350370
break;
@@ -357,13 +377,13 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
357377
break;
358378
case jpiCurrent:
359379
if (nestingLevel <= 0)
360-
ereport(ERROR,
380+
ereturn(escontext, false,
361381
(errcode(ERRCODE_SYNTAX_ERROR),
362382
errmsg("@ is not allowed in root expressions")));
363383
break;
364384
case jpiLast:
365385
if (!insideArraySubscript)
366-
ereport(ERROR,
386+
ereturn(escontext, false,
367387
(errcode(ERRCODE_SYNTAX_ERROR),
368388
errmsg("LAST is allowed only in array subscripts")));
369389
break;
@@ -383,15 +403,22 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
383403
{
384404
int32 *ppos;
385405
int32 topos;
386-
int32 frompos =
387-
flattenJsonPathParseItem(buf,
388-
item->value.array.elems[i].from,
389-
nestingLevel, true) - pos;
406+
int32 frompos;
407+
408+
if (! flattenJsonPathParseItem(buf, &frompos, escontext,
409+
item->value.array.elems[i].from,
410+
nestingLevel, true))
411+
return false;
412+
frompos -= pos;
390413

391414
if (item->value.array.elems[i].to)
392-
topos = flattenJsonPathParseItem(buf,
393-
item->value.array.elems[i].to,
394-
nestingLevel, true) - pos;
415+
{
416+
if (! flattenJsonPathParseItem(buf, &topos, escontext,
417+
item->value.array.elems[i].to,
418+
nestingLevel, true))
419+
return false;
420+
topos -= pos;
421+
}
395422
else
396423
topos = 0;
397424

@@ -424,12 +451,17 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
424451

425452
if (item->next)
426453
{
427-
chld = flattenJsonPathParseItem(buf, item->next, nestingLevel,
428-
insideArraySubscript) - pos;
454+
if (! flattenJsonPathParseItem(buf, &chld, escontext,
455+
item->next, nestingLevel,
456+
insideArraySubscript))
457+
return false;
458+
chld -= pos;
429459
*(int32 *) (buf->data + next) = chld;
430460
}
431461

432-
return pos;
462+
if (result)
463+
*result = pos;
464+
return true;
433465
}
434466

435467
/*

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,8 @@ executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
17211721
cxt->regex =
17221722
cstring_to_text_with_len(jsp->content.like_regex.pattern,
17231723
jsp->content.like_regex.patternlen);
1724-
cxt->cflags = jspConvertRegexFlags(jsp->content.like_regex.flags);
1724+
(void) jspConvertRegexFlags(jsp->content.like_regex.flags,
1725+
&(cxt->cflags), NULL);
17251726
}
17261727

17271728
if (RE_compile_and_execute(cxt->regex, str->val.string.val,

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ static JsonPathParseItem *makeItemUnary(JsonPathItemType type,
3838
static JsonPathParseItem *makeItemList(List *list);
3939
static JsonPathParseItem *makeIndexArray(List *list);
4040
static JsonPathParseItem *makeAny(int first, int last);
41-
static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
42-
JsonPathString *pattern,
43-
JsonPathString *flags);
41+
static bool makeItemLikeRegex(JsonPathParseItem *expr,
42+
JsonPathString *pattern,
43+
JsonPathString *flags,
44+
JsonPathParseItem ** result,
45+
struct Node *escontext);
4446

4547
/*
4648
* Bison doesn't allocate anything that needs to live across parser calls,
@@ -57,6 +59,9 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
5759
%expect 0
5860
%name-prefix="jsonpath_yy"
5961
%parse-param {JsonPathParseResult **result}
62+
%parse-param {struct Node *escontext}
63+
%lex-param {JsonPathParseResult **result}
64+
%lex-param {struct Node *escontext}
6065

6166
%union
6267
{
@@ -163,9 +168,20 @@ predicate:
163168
{ $$ = makeItemUnary(jpiIsUnknown, $2); }
164169
| expr STARTS_P WITH_P starts_with_initial
165170
{ $$ = makeItemBinary(jpiStartsWith, $1, $4); }
166-
| expr LIKE_REGEX_P STRING_P { $$ = makeItemLikeRegex($1, &$3, NULL); }
171+
| expr LIKE_REGEX_P STRING_P
172+
{
173+
JsonPathParseItem *jppitem;
174+
if (! makeItemLikeRegex($1, &$3, NULL, &jppitem, escontext))
175+
YYABORT;
176+
$$ = jppitem;
177+
}
167178
| expr LIKE_REGEX_P STRING_P FLAG_P STRING_P
168-
{ $$ = makeItemLikeRegex($1, &$3, &$5); }
179+
{
180+
JsonPathParseItem *jppitem;
181+
if (! makeItemLikeRegex($1, &$3, &$5, &jppitem, escontext))
182+
YYABORT;
183+
$$ = jppitem;
184+
}
169185
;
170186

171187
starts_with_initial:
@@ -472,9 +488,10 @@ makeAny(int first, int last)
472488
return v;
473489
}
474490

475-
static JsonPathParseItem *
491+
static bool
476492
makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
477-
JsonPathString *flags)
493+
JsonPathString *flags, JsonPathParseItem ** result,
494+
struct Node *escontext)
478495
{
479496
JsonPathParseItem *v = makeItemType(jpiLikeRegex);
480497
int i;
@@ -506,7 +523,7 @@ makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
506523
v->value.like_regex.flags |= JSP_REGEX_QUOTE;
507524
break;
508525
default:
509-
ereport(ERROR,
526+
ereturn(escontext, false,
510527
(errcode(ERRCODE_SYNTAX_ERROR),
511528
errmsg("invalid input syntax for type %s", "jsonpath"),
512529
errdetail("Unrecognized flag character \"%.*s\" in LIKE_REGEX predicate.",
@@ -515,22 +532,48 @@ makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
515532
}
516533
}
517534

518-
/* Convert flags to what RE_compile_and_cache needs */
519-
cflags = jspConvertRegexFlags(v->value.like_regex.flags);
535+
/* Convert flags to what pg_regcomp needs */
536+
if ( !jspConvertRegexFlags(v->value.like_regex.flags, &cflags, escontext))
537+
return false;
520538

521539
/* check regex validity */
522-
(void) RE_compile_and_cache(cstring_to_text_with_len(pattern->val,
523-
pattern->len),
524-
cflags, DEFAULT_COLLATION_OID);
540+
{
541+
regex_t re_tmp;
542+
pg_wchar *wpattern;
543+
int wpattern_len;
544+
int re_result;
545+
546+
wpattern = (pg_wchar *) palloc((pattern->len + 1) * sizeof(pg_wchar));
547+
wpattern_len = pg_mb2wchar_with_len(pattern->val,
548+
wpattern,
549+
pattern->len);
550+
551+
if ((re_result = pg_regcomp(&re_tmp, wpattern, wpattern_len, cflags,
552+
DEFAULT_COLLATION_OID)) != REG_OKAY)
553+
{
554+
char errMsg[100];
555+
556+
/* See regexp.c for explanation */
557+
CHECK_FOR_INTERRUPTS();
558+
pg_regerror(re_result, &re_tmp, errMsg, sizeof(errMsg));
559+
ereturn(escontext, false,
560+
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
561+
errmsg("invalid regular expression: %s", errMsg)));
562+
}
525563

526-
return v;
564+
pg_regfree(&re_tmp);
565+
}
566+
567+
*result = v;
568+
569+
return true;
527570
}
528571

529572
/*
530573
* Convert from XQuery regex flags to those recognized by our regex library.
531574
*/
532-
int
533-
jspConvertRegexFlags(uint32 xflags)
575+
bool
576+
jspConvertRegexFlags(uint32 xflags, int *result, struct Node *escontext)
534577
{
535578
/* By default, XQuery is very nearly the same as Spencer's AREs */
536579
int cflags = REG_ADVANCED;
@@ -561,18 +604,12 @@ jspConvertRegexFlags(uint32 xflags)
561604
* XQuery-style ignore-whitespace mode.
562605
*/
563606
if (xflags & JSP_REGEX_WSPACE)
564-
ereport(ERROR,
607+
ereturn(escontext, false,
565608
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
566609
errmsg("XQuery \"x\" flag (expanded regular expressions) is not implemented")));
567610
}
568611

569-
/*
570-
* We'll never need sub-match details at execution. While
571-
* RE_compile_and_execute would set this flag anyway, force it on here to
572-
* ensure that the regex cache entries created by makeItemLikeRegex are
573-
* useful.
574-
*/
575-
cflags |= REG_NOSUB;
612+
*result = cflags;
576613

577-
return cflags;
614+
return true;
578615
}

src/backend/utils/adt/jsonpath_internal.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,14 @@ typedef struct JsonPathString
2525
#include "utils/jsonpath.h"
2626
#include "jsonpath_gram.h"
2727

28-
extern int jsonpath_yylex(YYSTYPE *yylval_param);
29-
extern int jsonpath_yyparse(JsonPathParseResult **result);
30-
extern void jsonpath_yyerror(JsonPathParseResult **result, const char *message);
28+
#define YY_DECL extern int jsonpath_yylex(YYSTYPE *yylval_param, \
29+
JsonPathParseResult **result, \
30+
struct Node *escontext)
31+
YY_DECL;
32+
extern int jsonpath_yyparse(JsonPathParseResult **result,
33+
struct Node *escontext);
34+
extern void jsonpath_yyerror(JsonPathParseResult **result,
35+
struct Node *escontext,
36+
const char *message);
3137

3238
#endif /* JSONPATH_INTERNAL_H */

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