Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 55e4b88

Browse files
author
Nikita Glukhov
committed
Add jsonpath 'pg' modifier for enabling extensions
1 parent 079ac29 commit 55e4b88

File tree

9 files changed

+116
-20
lines changed

9 files changed

+116
-20
lines changed

doc/src/sgml/func.sgml

+29
Original file line numberDiff line numberDiff line change
@@ -13663,6 +13663,35 @@ table2-mapping
1366313663

1366413664
</sect3>
1366513665

13666+
<sect3 id="pg-extensions">
13667+
<title>Extensions</title>
13668+
<para>
13669+
<productname>PostgreSQL</productname> has some extensions to the SQL/JSON
13670+
Path standard. These syntax extensions that can enabled by the specifying
13671+
additional <literal>pg</literal> modifier before the
13672+
<literal>strict</literal>/<literal>lax</literal> flags.
13673+
<xref linkend="functions-jsonpath-extensions"/> shows these
13674+
extensions with examples.
13675+
</para>
13676+
13677+
<table id="functions-jsonpath-extensions">
13678+
<title><type>jsonpath</type> Syntax Extensions</title>
13679+
<tgroup cols="5">
13680+
<thead>
13681+
<row>
13682+
<entry>Name</entry>
13683+
<entry>Description</entry>
13684+
<entry>Example JSON</entry>
13685+
<entry>Example JSON path</entry>
13686+
<entry>Result</entry>
13687+
</row>
13688+
</thead>
13689+
<tbody>
13690+
</tbody>
13691+
</tgroup>
13692+
</table>
13693+
</sect3>
13694+
1366613695
<sect3 id="jsonpath-regular-expressions">
1366713696
<title>Regular Expressions</title>
1366813697

src/backend/utils/adt/jsonpath.c

+42-12
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,20 @@
7272
#include "utils/jsonpath.h"
7373

7474

75+
/* Context for jsonpath encoding. */
76+
typedef struct JsonPathEncodingContext
77+
{
78+
StringInfo buf; /* output buffer */
79+
bool ext; /* PG extensions are enabled? */
80+
} JsonPathEncodingContext;
81+
7582
static Datum jsonPathFromCstring(char *in, int len);
7683
static char *jsonPathToCstring(StringInfo out, JsonPath *in,
7784
int estimated_len);
78-
static int flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
79-
int nestingLevel, bool insideArraySubscript);
85+
static int flattenJsonPathParseItem(JsonPathEncodingContext *cxt,
86+
JsonPathParseItem *item,
87+
int nestingLevel,
88+
bool insideArraySubscript);
8089
static void alignStringInfoInt(StringInfo buf);
8190
static int32 reserveSpaceForItemPointer(StringInfo buf);
8291
static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
@@ -167,6 +176,7 @@ jsonpath_send(PG_FUNCTION_ARGS)
167176
static Datum
168177
jsonPathFromCstring(char *in, int len)
169178
{
179+
JsonPathEncodingContext cxt;
170180
JsonPathParseResult *jsonpath = parsejsonpath(in, len);
171181
JsonPath *res;
172182
StringInfoData buf;
@@ -182,13 +192,18 @@ jsonPathFromCstring(char *in, int len)
182192
errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
183193
in)));
184194

185-
flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
195+
cxt.buf = &buf;
196+
cxt.ext = jsonpath->ext;
197+
198+
flattenJsonPathParseItem(&cxt, jsonpath->expr, 0, false);
186199

187200
res = (JsonPath *) buf.data;
188201
SET_VARSIZE(res, buf.len);
189202
res->header = JSONPATH_VERSION;
190203
if (jsonpath->lax)
191204
res->header |= JSONPATH_LAX;
205+
if (jsonpath->ext)
206+
res->header |= JSONPATH_EXT;
192207

193208
PG_RETURN_JSONPATH_P(res);
194209
}
@@ -212,6 +227,8 @@ jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
212227
}
213228
enlargeStringInfo(out, estimated_len);
214229

230+
if (in->header & JSONPATH_EXT)
231+
appendBinaryStringInfo(out, "pg ", 3);
215232
if (!(in->header & JSONPATH_LAX))
216233
appendBinaryStringInfo(out, "strict ", 7);
217234

@@ -221,14 +238,27 @@ jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
221238
return out->data;
222239
}
223240

241+
static void
242+
checkJsonPathExtensionsEnabled(JsonPathEncodingContext *cxt,
243+
JsonPathItemType type)
244+
{
245+
if (!cxt->ext)
246+
ereport(ERROR,
247+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
248+
errmsg("%s contains extended operators that were not enabled", "jsonpath"),
249+
errhint("use \"%s\" modifier at the start of %s string to enable extensions",
250+
"pg", "jsonpath")));
251+
}
252+
224253
/*
225254
* Recursive function converting given jsonpath parse item and all its
226255
* children into a binary representation.
227256
*/
228257
static int
229-
flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
258+
flattenJsonPathParseItem(JsonPathEncodingContext *cxt, JsonPathParseItem *item,
230259
int nestingLevel, bool insideArraySubscript)
231260
{
261+
StringInfo buf = cxt->buf;
232262
/* position from beginning of jsonpath data */
233263
int32 pos = buf->len - JSONPATH_HDRSZ;
234264
int32 chld;
@@ -296,13 +326,13 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
296326
int32 right = reserveSpaceForItemPointer(buf);
297327

298328
chld = !item->value.args.left ? pos :
299-
flattenJsonPathParseItem(buf, item->value.args.left,
329+
flattenJsonPathParseItem(cxt, item->value.args.left,
300330
nestingLevel + argNestingLevel,
301331
insideArraySubscript);
302332
*(int32 *) (buf->data + left) = chld - pos;
303333

304334
chld = !item->value.args.right ? pos :
305-
flattenJsonPathParseItem(buf, item->value.args.right,
335+
flattenJsonPathParseItem(cxt, item->value.args.right,
306336
nestingLevel + argNestingLevel,
307337
insideArraySubscript);
308338
*(int32 *) (buf->data + right) = chld - pos;
@@ -323,7 +353,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
323353
item->value.like_regex.patternlen);
324354
appendStringInfoChar(buf, '\0');
325355

326-
chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
356+
chld = flattenJsonPathParseItem(cxt, item->value.like_regex.expr,
327357
nestingLevel,
328358
insideArraySubscript);
329359
*(int32 *) (buf->data + offs) = chld - pos;
@@ -342,7 +372,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
342372
int32 arg = reserveSpaceForItemPointer(buf);
343373

344374
chld = !item->value.arg ? pos :
345-
flattenJsonPathParseItem(buf, item->value.arg,
375+
flattenJsonPathParseItem(cxt, item->value.arg,
346376
nestingLevel + argNestingLevel,
347377
insideArraySubscript);
348378
*(int32 *) (buf->data + arg) = chld - pos;
@@ -384,12 +414,12 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
384414
int32 *ppos;
385415
int32 topos;
386416
int32 frompos =
387-
flattenJsonPathParseItem(buf,
417+
flattenJsonPathParseItem(cxt,
388418
item->value.array.elems[i].from,
389419
nestingLevel, true) - pos;
390420

391421
if (item->value.array.elems[i].to)
392-
topos = flattenJsonPathParseItem(buf,
422+
topos = flattenJsonPathParseItem(cxt,
393423
item->value.array.elems[i].to,
394424
nestingLevel, true) - pos;
395425
else
@@ -424,7 +454,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
424454

425455
if (item->next)
426456
{
427-
chld = flattenJsonPathParseItem(buf, item->next, nestingLevel,
457+
chld = flattenJsonPathParseItem(cxt, item->next, nestingLevel,
428458
insideArraySubscript) - pos;
429459
*(int32 *) (buf->data + next) = chld;
430460
}
@@ -832,7 +862,7 @@ operationPriority(JsonPathItemType op)
832862
void
833863
jspInit(JsonPathItem *v, JsonPath *js)
834864
{
835-
Assert((js->header & ~JSONPATH_LAX) == JSONPATH_VERSION);
865+
Assert((js->header & JSONPATH_VERSION_MASK) == JSONPATH_VERSION);
836866
jspInitByBuffer(v, js->data, 0);
837867
}
838868

src/backend/utils/adt/jsonpath_exec.c

+4
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ typedef struct JsonPathExecContext
101101
int innermostArraySize; /* for LAST array index evaluation */
102102
bool laxMode; /* true for "lax" mode, false for "strict"
103103
* mode */
104+
bool useExtensions; /* use PostgreSQL-specific extensions?
105+
* (enabled by 'pg' modifier in jsonpath) */
104106
bool ignoreStructuralErrors; /* with "true" structural errors such
105107
* as absence of required json item or
106108
* unexpected json item type are
@@ -157,6 +159,7 @@ typedef struct JsonValueListIterator
157159
#define jspAutoWrap(cxt) ((cxt)->laxMode)
158160
#define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
159161
#define jspThrowErrors(cxt) ((cxt)->throwErrors)
162+
#define jspUseExtensions(cxt) ((cxt)->useExtensions)
160163

161164
/* Convenience macro: return or throw error depending on context */
162165
#define RETURN_ERROR(throw_error) \
@@ -559,6 +562,7 @@ executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
559562

560563
cxt.vars = vars;
561564
cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
565+
cxt.useExtensions = (path->header & JSONPATH_EXT) != 0;
562566
cxt.ignoreStructuralErrors = cxt.laxMode;
563567
cxt.root = &jbv;
564568
cxt.current = &jbv;

src/backend/utils/adt/jsonpath_gram.y

+12-5
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
8888
int integer;
8989
}
9090

91-
%token <str> TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P EXISTS_P
91+
%token <str> TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P EXISTS_P PG_P
9292
%token <str> IDENT_P STRING_P NUMERIC_P INT_P VARIABLE_P
9393
%token <str> OR_P AND_P NOT_P
9494
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
@@ -109,7 +109,7 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
109109

110110
%type <optype> comp_op method
111111

112-
%type <boolean> mode
112+
%type <boolean> mode pg_opt
113113

114114
%type <str> key_name
115115

@@ -127,10 +127,11 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
127127
%%
128128

129129
result:
130-
mode expr_or_predicate {
130+
pg_opt mode expr_or_predicate {
131131
*result = palloc(sizeof(JsonPathParseResult));
132-
(*result)->expr = $2;
133-
(*result)->lax = $1;
132+
(*result)->expr = $3;
133+
(*result)->lax = $2;
134+
(*result)->ext = $1;
134135
}
135136
| /* EMPTY */ { *result = NULL; }
136137
;
@@ -140,6 +141,11 @@ expr_or_predicate:
140141
| predicate { $$ = $1; }
141142
;
142143

144+
pg_opt:
145+
PG_P { $$ = true; }
146+
| /* EMPTY */ { $$ = false; }
147+
;
148+
143149
mode:
144150
STRICT_P { $$ = false; }
145151
| LAX_P { $$ = true; }
@@ -292,6 +298,7 @@ key_name:
292298
| WITH_P
293299
| LIKE_REGEX_P
294300
| FLAG_P
301+
| PG_P
295302
;
296303

297304
method:

src/backend/utils/adt/jsonpath_scan.l

+1
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ typedef struct JsonPathKeyword
305305
*/
306306
static const JsonPathKeyword keywords[] = {
307307
{ 2, false, IS_P, "is"},
308+
{ 2, false, PG_P, "pg"},
308309
{ 2, false, TO_P, "to"},
309310
{ 3, false, ABS_P, "abs"},
310311
{ 3, false, LAX_P, "lax"},

src/include/utils/jsonpath.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ typedef struct
2525
char data[FLEXIBLE_ARRAY_MEMBER];
2626
} JsonPath;
2727

28-
#define JSONPATH_VERSION (0x01)
29-
#define JSONPATH_LAX (0x80000000)
28+
#define JSONPATH_VERSION 0x01
29+
#define JSONPATH_LAX 0x80000000 /* lax/strict mode */
30+
#define JSONPATH_EXT 0x40000000 /* PG extensions */
31+
#define JSONPATH_VERSION_MASK (~(JSONPATH_LAX | JSONPATH_EXT))
3032
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
3133

3234
#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
@@ -241,7 +243,8 @@ struct JsonPathParseItem
241243
typedef struct JsonPathParseResult
242244
{
243245
JsonPathParseItem *expr;
244-
bool lax;
246+
bool lax; /* lax/strict mode */
247+
bool ext; /* PostgreSQL extensions */
245248
} JsonPathParseResult;
246249

247250
extern JsonPathParseResult *parsejsonpath(const char *str, int len);

src/test/regress/expected/jsonpath.out

+18
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ select 'lax $'::jsonpath;
2121
$
2222
(1 row)
2323

24+
select 'pg $'::jsonpath;
25+
jsonpath
26+
----------
27+
pg $
28+
(1 row)
29+
30+
select 'pg strict $'::jsonpath;
31+
jsonpath
32+
-------------
33+
pg strict $
34+
(1 row)
35+
36+
select 'pg lax $'::jsonpath;
37+
jsonpath
38+
----------
39+
pg $
40+
(1 row)
41+
2442
select '$.a'::jsonpath;
2543
jsonpath
2644
----------

src/test/regress/sql/jsonpath.sql

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ select ''::jsonpath;
44
select '$'::jsonpath;
55
select 'strict $'::jsonpath;
66
select 'lax $'::jsonpath;
7+
select 'pg $'::jsonpath;
8+
select 'pg strict $'::jsonpath;
9+
select 'pg lax $'::jsonpath;
710
select '$.a'::jsonpath;
811
select '$.a.v'::jsonpath;
912
select '$.a.*'::jsonpath;

src/tools/pgindent/typedefs.list

+1
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,7 @@ JsonLikeRegexContext
11361136
JsonParseContext
11371137
JsonPath
11381138
JsonPathBool
1139+
JsonPathEncodingContext
11391140
JsonPathExecContext
11401141
JsonPathExecResult
11411142
JsonPathGinAddPathItemFunc

0 commit comments

Comments
 (0)