From 16375595a67f1816cb753a3644198ba9b27cc823 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 14 Jun 2019 15:24:54 +0300
Subject: [PATCH] Implement like_regex flag 'q' in jsonpath

---
 src/backend/utils/adt/jsonpath.c             |  2 ++
 src/backend/utils/adt/jsonpath_exec.c        |  6 +++++
 src/backend/utils/adt/jsonpath_gram.y        |  8 +++++++
 src/include/utils/jsonpath.h                 |  1 +
 src/test/regress/expected/jsonb_jsonpath.out | 36 ++++++++++++++++++++++++++++
 src/test/regress/expected/jsonpath.out       | 18 ++++++++++++++
 src/test/regress/sql/jsonb_jsonpath.sql      |  6 +++++
 src/test/regress/sql/jsonpath.sql            |  3 +++
 8 files changed, 80 insertions(+)

diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index d5da155..87ae60e 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -563,6 +563,8 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
 					appendStringInfoChar(buf, 'm');
 				if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
 					appendStringInfoChar(buf, 'x');
+				if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
+					appendStringInfoChar(buf, 'q');
 
 				appendStringInfoChar(buf, '"');
 			}
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 873d64b..bef9112 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1664,6 +1664,12 @@ executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
 			cxt->cflags &= ~REG_NEWLINE;
 		if (flags & JSP_REGEX_WSPACE)
 			cxt->cflags |= REG_EXPANDED;
+		if ((flags & JSP_REGEX_QUOTE) &&
+			!(flags & (JSP_REGEX_MLINE | JSP_REGEX_SLINE | JSP_REGEX_WSPACE)))
+		{
+			cxt->cflags &= ~REG_ADVANCED;
+			cxt->cflags |= REG_QUOTE;
+		}
 	}
 
 	if (RE_compile_and_execute(cxt->regex, str->val.string.val,
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 22c2089..a0a930c 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -510,6 +510,14 @@ makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
 				v->value.like_regex.flags |= JSP_REGEX_WSPACE;
 				cflags |= REG_EXPANDED;
 				break;
+			case 'q':
+				v->value.like_regex.flags |= JSP_REGEX_QUOTE;
+				if (!(v->value.like_regex.flags & (JSP_REGEX_MLINE | JSP_REGEX_SLINE | JSP_REGEX_WSPACE)))
+				{
+					cflags &= ~REG_ADVANCED;
+					cflags |= REG_QUOTE;
+				}
+				break;
 			default:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 3e9d60c..40ad5fd 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -91,6 +91,7 @@ typedef enum JsonPathItemType
 #define JSP_REGEX_SLINE		0x02	/* s flag, single-line mode */
 #define JSP_REGEX_MLINE		0x04	/* m flag, multi-line mode */
 #define JSP_REGEX_WSPACE	0x08	/* x flag, expanded syntax */
+#define JSP_REGEX_QUOTE		0x10	/* q flag, no special characters */
 
 /*
  * Support functions to parse/construct binary value.
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index b486fb6..31a871a 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1622,6 +1622,42 @@ select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "
  "abdacb"
 (2 rows)
 
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "a\\b" flag "q")');
+ jsonb_path_query 
+------------------
+ "a\\b"
+ "^a\\b$"
+(2 rows)
+
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "a\\b" flag "")');
+ jsonb_path_query 
+------------------
+ "a\b"
+(1 row)
+
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "^a\\b$" flag "q")');
+ jsonb_path_query 
+------------------
+ "^a\\b$"
+(1 row)
+
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "^a\\B$" flag "q")');
+ jsonb_path_query 
+------------------
+(0 rows)
+
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "^a\\B$" flag "iq")');
+ jsonb_path_query 
+------------------
+ "^a\\b$"
+(1 row)
+
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "^a\\b$" flag "")');
+ jsonb_path_query 
+------------------
+ "a\b"
+(1 row)
+
 -- jsonpath operators
 SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*]');
  jsonb_path_query 
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index 0f9cd17..ecdd453 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -453,6 +453,24 @@ select '$ ? (@ like_regex "pattern" flag "xsms")'::jsonpath;
  $?(@ like_regex "pattern" flag "sx")
 (1 row)
 
+select '$ ? (@ like_regex "pattern" flag "q")'::jsonpath;
+              jsonpath               
+-------------------------------------
+ $?(@ like_regex "pattern" flag "q")
+(1 row)
+
+select '$ ? (@ like_regex "pattern" flag "iq")'::jsonpath;
+               jsonpath               
+--------------------------------------
+ $?(@ like_regex "pattern" flag "iq")
+(1 row)
+
+select '$ ? (@ like_regex "pattern" flag "smixq")'::jsonpath;
+                jsonpath                
+----------------------------------------
+ $?(@ like_regex "pattern" flag "imxq")
+(1 row)
+
 select '$ ? (@ like_regex "pattern" flag "a")'::jsonpath;
 ERROR:  invalid input syntax for type jsonpath
 LINE 1: select '$ ? (@ like_regex "pattern" flag "a")'::jsonpath;
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index 464ff94..733fbd4 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -339,6 +339,12 @@ select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "
 select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^a  b.*  c " flag "ix")');
 select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^ab.*c" flag "m")');
 select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^ab.*c" flag "s")');
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "a\\b" flag "q")');
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "a\\b" flag "")');
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "^a\\b$" flag "q")');
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "^a\\B$" flag "q")');
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "^a\\B$" flag "iq")');
+select jsonb_path_query('[null, 1, "a\b", "a\\b", "^a\\b$"]', 'lax $[*] ? (@ like_regex "^a\\b$" flag "")');
 
 -- jsonpath operators
 
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 9171ddb..29ea77a 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -83,6 +83,9 @@ select '$ ? (@ like_regex "pattern" flag "i")'::jsonpath;
 select '$ ? (@ like_regex "pattern" flag "is")'::jsonpath;
 select '$ ? (@ like_regex "pattern" flag "isim")'::jsonpath;
 select '$ ? (@ like_regex "pattern" flag "xsms")'::jsonpath;
+select '$ ? (@ like_regex "pattern" flag "q")'::jsonpath;
+select '$ ? (@ like_regex "pattern" flag "iq")'::jsonpath;
+select '$ ? (@ like_regex "pattern" flag "smixq")'::jsonpath;
 select '$ ? (@ like_regex "pattern" flag "a")'::jsonpath;
 
 select '$ < 1'::jsonpath;
-- 
2.7.4

