public inbox for [email protected]
help / color / mirror / Atom feedFrom: Nikita Glukhov <[email protected]>
Subject: [PATCH v1 1/8] Add jsonpath 'pg' modifier for enabling extensions
Date: Thu, 27 Jun 2019 19:58:35 +0300
---
doc/src/sgml/func.sgml | 29 ++++++++++++++++++
src/backend/utils/adt/jsonpath.c | 54 ++++++++++++++++++++++++++--------
src/backend/utils/adt/jsonpath_exec.c | 4 +++
src/backend/utils/adt/jsonpath_gram.y | 17 +++++++----
src/backend/utils/adt/jsonpath_scan.l | 1 +
src/include/utils/jsonpath.h | 9 ++++--
src/test/regress/expected/jsonpath.out | 18 ++++++++++++
src/test/regress/sql/jsonpath.sql | 3 ++
src/tools/pgindent/typedefs.list | 1 +
9 files changed, 116 insertions(+), 20 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 28035f1..d344b95 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -12601,6 +12601,35 @@ table2-mapping
</sect3>
+ <sect3 id="pg-extensions">
+ <title>Extensions</title>
+ <para>
+ <productname>PostgreSQL</productname> has some extensions to the SQL/JSON
+ Path standard. These syntax extensions that can enabled by the specifying
+ additional <literal>pg</literal> modifier before the
+ <literal>strict</literal>/<literal>lax</literal> flags.
+ <xref linkend="functions-jsonpath-extensions"/> shows these
+ extensions with examples.
+ </para>
+
+ <table id="functions-jsonpath-extensions">
+ <title><type>jsonpath</type> Syntax Extensions</title>
+ <tgroup cols="5">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Description</entry>
+ <entry>Example JSON</entry>
+ <entry>Example JSON path</entry>
+ <entry>Result</entry>
+ </row>
+ </thead>
+ <tbody>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect3>
+
<sect3 id="jsonpath-regular-expressions">
<title>Regular Expressions</title>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 3c0dc38..17c09a7 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -72,11 +72,20 @@
#include "utils/jsonpath.h"
+/* Context for jsonpath encoding. */
+typedef struct JsonPathEncodingContext
+{
+ StringInfo buf; /* output buffer */
+ bool ext; /* PG extensions are enabled? */
+} JsonPathEncodingContext;
+
static Datum jsonPathFromCstring(char *in, int len);
static char *jsonPathToCstring(StringInfo out, JsonPath *in,
int estimated_len);
-static int flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
- int nestingLevel, bool insideArraySubscript);
+static int flattenJsonPathParseItem(JsonPathEncodingContext *cxt,
+ JsonPathParseItem *item,
+ int nestingLevel,
+ bool insideArraySubscript);
static void alignStringInfoInt(StringInfo buf);
static int32 reserveSpaceForItemPointer(StringInfo buf);
static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
@@ -167,6 +176,7 @@ jsonpath_send(PG_FUNCTION_ARGS)
static Datum
jsonPathFromCstring(char *in, int len)
{
+ JsonPathEncodingContext cxt;
JsonPathParseResult *jsonpath = parsejsonpath(in, len);
JsonPath *res;
StringInfoData buf;
@@ -182,13 +192,18 @@ jsonPathFromCstring(char *in, int len)
errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
in)));
- flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
+ cxt.buf = &buf;
+ cxt.ext = jsonpath->ext;
+
+ flattenJsonPathParseItem(&cxt, jsonpath->expr, 0, false);
res = (JsonPath *) buf.data;
SET_VARSIZE(res, buf.len);
res->header = JSONPATH_VERSION;
if (jsonpath->lax)
res->header |= JSONPATH_LAX;
+ if (jsonpath->ext)
+ res->header |= JSONPATH_EXT;
PG_RETURN_JSONPATH_P(res);
}
@@ -212,6 +227,8 @@ jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
}
enlargeStringInfo(out, estimated_len);
+ if (in->header & JSONPATH_EXT)
+ appendBinaryStringInfo(out, "pg ", 3);
if (!(in->header & JSONPATH_LAX))
appendBinaryStringInfo(out, "strict ", 7);
@@ -221,14 +238,27 @@ jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
return out->data;
}
+static void
+checkJsonPathExtensionsEnabled(JsonPathEncodingContext *cxt,
+ JsonPathItemType type)
+{
+ if (!cxt->ext)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("%s contains extended operators that were not enabled", "jsonpath"),
+ errhint("use \"%s\" modifier at the start of %s string to enable extensions",
+ "pg", "jsonpath")));
+}
+
/*
* Recursive function converting given jsonpath parse item and all its
* children into a binary representation.
*/
static int
-flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
+flattenJsonPathParseItem(JsonPathEncodingContext *cxt, JsonPathParseItem *item,
int nestingLevel, bool insideArraySubscript)
{
+ StringInfo buf = cxt->buf;
/* position from beginning of jsonpath data */
int32 pos = buf->len - JSONPATH_HDRSZ;
int32 chld;
@@ -296,13 +326,13 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
int32 right = reserveSpaceForItemPointer(buf);
chld = !item->value.args.left ? pos :
- flattenJsonPathParseItem(buf, item->value.args.left,
+ flattenJsonPathParseItem(cxt, item->value.args.left,
nestingLevel + argNestingLevel,
insideArraySubscript);
*(int32 *) (buf->data + left) = chld - pos;
chld = !item->value.args.right ? pos :
- flattenJsonPathParseItem(buf, item->value.args.right,
+ flattenJsonPathParseItem(cxt, item->value.args.right,
nestingLevel + argNestingLevel,
insideArraySubscript);
*(int32 *) (buf->data + right) = chld - pos;
@@ -323,7 +353,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
item->value.like_regex.patternlen);
appendStringInfoChar(buf, '\0');
- chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
+ chld = flattenJsonPathParseItem(cxt, item->value.like_regex.expr,
nestingLevel,
insideArraySubscript);
*(int32 *) (buf->data + offs) = chld - pos;
@@ -342,7 +372,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
int32 arg = reserveSpaceForItemPointer(buf);
chld = !item->value.arg ? pos :
- flattenJsonPathParseItem(buf, item->value.arg,
+ flattenJsonPathParseItem(cxt, item->value.arg,
nestingLevel + argNestingLevel,
insideArraySubscript);
*(int32 *) (buf->data + arg) = chld - pos;
@@ -384,12 +414,12 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
int32 *ppos;
int32 topos;
int32 frompos =
- flattenJsonPathParseItem(buf,
+ flattenJsonPathParseItem(cxt,
item->value.array.elems[i].from,
nestingLevel, true) - pos;
if (item->value.array.elems[i].to)
- topos = flattenJsonPathParseItem(buf,
+ topos = flattenJsonPathParseItem(cxt,
item->value.array.elems[i].to,
nestingLevel, true) - pos;
else
@@ -424,7 +454,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
if (item->next)
{
- chld = flattenJsonPathParseItem(buf, item->next, nestingLevel,
+ chld = flattenJsonPathParseItem(cxt, item->next, nestingLevel,
insideArraySubscript) - pos;
*(int32 *) (buf->data + next) = chld;
}
@@ -832,7 +862,7 @@ operationPriority(JsonPathItemType op)
void
jspInit(JsonPathItem *v, JsonPath *js)
{
- Assert((js->header & ~JSONPATH_LAX) == JSONPATH_VERSION);
+ Assert((js->header & JSONPATH_VERSION_MASK) == JSONPATH_VERSION);
jspInitByBuffer(v, js->data, 0);
}
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index b6fdd47..9f13f4b 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -101,6 +101,8 @@ typedef struct JsonPathExecContext
int innermostArraySize; /* for LAST array index evaluation */
bool laxMode; /* true for "lax" mode, false for "strict"
* mode */
+ bool useExtensions; /* use PostgreSQL-specific extensions?
+ * (enabled by 'pg' modifier in jsonpath) */
bool ignoreStructuralErrors; /* with "true" structural errors such
* as absence of required json item or
* unexpected json item type are
@@ -157,6 +159,7 @@ typedef struct JsonValueListIterator
#define jspAutoWrap(cxt) ((cxt)->laxMode)
#define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
#define jspThrowErrors(cxt) ((cxt)->throwErrors)
+#define jspUseExtensions(cxt) ((cxt)->useExtensions)
/* Convenience macro: return or throw error depending on context */
#define RETURN_ERROR(throw_error) \
@@ -559,6 +562,7 @@ executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
cxt.vars = vars;
cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
+ cxt.useExtensions = (path->header & JSONPATH_EXT) != 0;
cxt.ignoreStructuralErrors = cxt.laxMode;
cxt.root = &jbv;
cxt.current = &jbv;
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index f87db8c..426dbb1 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -88,7 +88,7 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
int integer;
}
-%token <str> TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P EXISTS_P
+%token <str> TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P EXISTS_P PG_P
%token <str> IDENT_P STRING_P NUMERIC_P INT_P VARIABLE_P
%token <str> OR_P AND_P NOT_P
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
@@ -109,7 +109,7 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
%type <optype> comp_op method
-%type <boolean> mode
+%type <boolean> mode pg_opt
%type <str> key_name
@@ -127,10 +127,11 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
%%
result:
- mode expr_or_predicate {
+ pg_opt mode expr_or_predicate {
*result = palloc(sizeof(JsonPathParseResult));
- (*result)->expr = $2;
- (*result)->lax = $1;
+ (*result)->expr = $3;
+ (*result)->lax = $2;
+ (*result)->ext = $1;
}
| /* EMPTY */ { *result = NULL; }
;
@@ -140,6 +141,11 @@ expr_or_predicate:
| predicate { $$ = $1; }
;
+pg_opt:
+ PG_P { $$ = true; }
+ | /* EMPTY */ { $$ = false; }
+ ;
+
mode:
STRICT_P { $$ = false; }
| LAX_P { $$ = true; }
@@ -292,6 +298,7 @@ key_name:
| WITH_P
| LIKE_REGEX_P
| FLAG_P
+ | PG_P
;
method:
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 70681b7..c8bce27 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -305,6 +305,7 @@ typedef struct JsonPathKeyword
*/
static const JsonPathKeyword keywords[] = {
{ 2, false, IS_P, "is"},
+ { 2, false, PG_P, "pg"},
{ 2, false, TO_P, "to"},
{ 3, false, ABS_P, "abs"},
{ 3, false, LAX_P, "lax"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 4ef0880..d56175f 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -25,8 +25,10 @@ typedef struct
char data[FLEXIBLE_ARRAY_MEMBER];
} JsonPath;
-#define JSONPATH_VERSION (0x01)
-#define JSONPATH_LAX (0x80000000)
+#define JSONPATH_VERSION 0x01
+#define JSONPATH_LAX 0x80000000 /* lax/strict mode */
+#define JSONPATH_EXT 0x40000000 /* PG extensions */
+#define JSONPATH_VERSION_MASK (~(JSONPATH_LAX | JSONPATH_EXT))
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
@@ -241,7 +243,8 @@ struct JsonPathParseItem
typedef struct JsonPathParseResult
{
JsonPathParseItem *expr;
- bool lax;
+ bool lax; /* lax/strict mode */
+ bool ext; /* PostgreSQL extensions */
} JsonPathParseResult;
extern JsonPathParseResult *parsejsonpath(const char *str, int len);
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index e399fa9..52b36a8 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -21,6 +21,24 @@ select 'lax $'::jsonpath;
$
(1 row)
+select 'pg $'::jsonpath;
+ jsonpath
+----------
+ pg $
+(1 row)
+
+select 'pg strict $'::jsonpath;
+ jsonpath
+-------------
+ pg strict $
+(1 row)
+
+select 'pg lax $'::jsonpath;
+ jsonpath
+----------
+ pg $
+(1 row)
+
select '$.a'::jsonpath;
jsonpath
----------
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 17ab775..315e42f 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -4,6 +4,9 @@ select ''::jsonpath;
select '$'::jsonpath;
select 'strict $'::jsonpath;
select 'lax $'::jsonpath;
+select 'pg $'::jsonpath;
+select 'pg strict $'::jsonpath;
+select 'pg lax $'::jsonpath;
select '$.a'::jsonpath;
select '$.a.v'::jsonpath;
select '$.a.*'::jsonpath;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e216de9..d4148e1 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1130,6 +1130,7 @@ JsonLikeRegexContext
JsonParseContext
JsonPath
JsonPathBool
+JsonPathEncodingContext
JsonPathExecContext
JsonPathExecResult
JsonPathGinAddPathItemFunc
--
2.7.4
--------------574DCE16CAD28E71E2083DCD
Content-Type: text/x-patch;
name="v1-0002-Add-raw-jbvArray-and-jbvObject-support-to-jsonpat.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename*0="v1-0002-Add-raw-jbvArray-and-jbvObject-support-to-jsonpat.pa";
filename*1="tch"
view thread (17+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH v1 1/8] Add jsonpath 'pg' modifier for enabling extensions
In-Reply-To: <no-message-id-1880894@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox