v4-0001-make-simplified-accessor-works-with-domain-ove.no-cfbot

application/octet-stream

Filename: v4-0001-make-simplified-accessor-works-with-domain-ove.no-cfbot
Type: application/octet-stream
Part: 0
Message: Re: SQL:2023 JSON simplified accessor support
From 72897ab85bfd890ff5fe13ca7a9e5d2f0168f8fa Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 4 Oct 2024 13:24:06 +0800
Subject: [PATCH v4 1/1] make simplified accessor works with domain over
 JSON/JSONB

---
 src/backend/parser/parse_func.c     | 49 +++++++++++++++++++++++------
 src/include/parser/parse_type.h     |  1 -
 src/test/regress/expected/jsonb.out | 43 +++++++++++++++++++++++++
 src/test/regress/sql/jsonb.sql      | 14 +++++++++
 4 files changed, 97 insertions(+), 10 deletions(-)

diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index a13c001dd4..9b990d58a1 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -51,13 +51,35 @@ static Oid	FuncNameAsType(List *funcname);
 static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
 									Node *first_arg, int location);
 static Node *ParseJsonSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
-													Node *first_arg, int location);
+													Node *first_arg, int location, bool is_json);
 static Oid	LookupFuncNameInternal(ObjectType objtype, List *funcname,
 								   int nargs, const Oid *argtypes,
 								   bool include_out_arguments, bool missing_ok,
 								   FuncLookupError *lookupError);
 
+static void is_json_or_jsonb(Oid typeid, bool *is_json, bool *is_jsonb);
 
+
+static void
+is_json_or_jsonb(Oid typeid, bool *is_json, bool *is_jsonb)
+{
+	Oid 	base_type = InvalidOid;
+
+	if(typeid == JSONOID || typeid == JSONBOID)
+	{
+		if(typeid == JSONOID)
+			*is_json = true;
+		if (typeid == JSONBOID)
+			*is_jsonb = true;
+		return;
+	}
+
+	base_type = getBaseType(typeid);
+	if(base_type == JSONOID)
+		*is_json = true;
+	if (base_type == JSONBOID)
+		*is_jsonb = true;
+}
 /*
  *	Parse a function call
  *
@@ -104,6 +126,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 	bool		func_variadic = (fn ? fn->func_variadic : false);
 	CoercionForm funcformat = (fn ? fn->funcformat : COERCE_EXPLICIT_CALL);
 	bool		could_be_projection;
+	bool		is_json = false;
+	bool		is_jsonb = false;
 	Oid			rettype;
 	Oid			funcid;
 	ListCell   *l;
@@ -217,6 +241,13 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		Assert(first_arg != NULL);
 	}
 
+	if (nargs == 1 && !proc_call &&
+		agg_order == NIL && agg_filter == NULL &&
+		!agg_star && !agg_distinct && over == NULL &&
+		!func_variadic && argnames == NIL &&
+		list_length(funcname) == 1)
+		is_json_or_jsonb(actual_arg_types[0], &is_json, &is_jsonb);
+
 	/*
 	 * Decide whether it's legitimate to consider the construct to be a column
 	 * projection.  For that, there has to be a single argument of complex
@@ -231,18 +262,18 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 						   list_length(funcname) == 1 &&
 						   (actual_arg_types[0] == RECORDOID ||
 							ISCOMPLEX(actual_arg_types[0]) ||
-							ISJSON(actual_arg_types[0])));
+							is_json || is_jsonb ));
 
 	/*
 	 * If it's column syntax, check for column projection case first.
 	 */
 	if (could_be_projection && is_column)
 	{
-		if (ISJSON(actual_arg_types[0]))
+		if (is_json || is_jsonb)
 			retval = ParseJsonSimplifiedAccessorObjectField(pstate,
 															strVal(linitial(funcname)),
 															first_arg,
-															location);
+															location, is_json);
 		else
 			retval = ParseComplexProjection(pstate,
 											strVal(linitial(funcname)),
@@ -252,8 +283,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 			return retval;
 
 		/*
-		 * If ParseComplexProjection doesn't recognize it as a projection,
-		 * just press on.
+		 * If ParseJsonSimplifiedAccessorObjectField, ParseComplexProjection
+		 * doesn't recognize it as a projection, just press on.
 		 */
 	}
 
@@ -1958,7 +1989,7 @@ ParseJsonSimplifiedAccessorArrayElement(ParseState *pstate, A_Indices *subscript
  */
 Node *
 ParseJsonSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
-									   Node *first_arg, int location)
+									   Node *first_arg, int location, bool is_json)
 {
 	OpExpr	   *result;
 	Node	   *rexpr;
@@ -1973,14 +2004,14 @@ ParseJsonSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
 							   false);
 
 	result = makeNode(OpExpr);
-	if (exprType(first_arg) == JSONOID)
+	if (is_json)
 	{
 		result->opno = OID_JSON_OBJECT_FIELD_OP;
 		result->opresulttype = JSONOID;
 	}
 	else
 	{
-		Assert(exprType(first_arg) == JSONBOID);
+		Assert(getBaseType(exprType(first_arg)) == JSONBOID);
 		result->opno = OID_JSONB_OBJECT_FIELD_OP;
 		result->opresulttype = JSONBOID;
 	}
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index 9c8b3bfb2f..b62e7a6ce9 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -57,6 +57,5 @@ extern bool parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
 
 /* true if typeid is composite, or domain over composite, but not RECORD */
 #define ISCOMPLEX(typeid) (typeOrDomainTypeRelid(typeid) != InvalidOid)
-#define ISJSON(typeid) (typeid == JSONOID || typeid == JSONBOID)
 
 #endif							/* PARSE_TYPE_H */
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index 954efe67b6..3dd89f28fe 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -5801,3 +5801,46 @@ select (test_jsonb_dot.test_jsonb).d[0].x[1] from test_jsonb_dot;
  
 (3 rows)
 
+create domain json_d as jsonb;
+create type test as (a int, b json_d);
+create table t1(a test);
+insert into t1 select $$(1,"{""a"": 3, ""key1"": {""c"": ""42""}, ""key2"": [11, 12]}") $$;
+insert into t1 select $$ (1,"{""a"": 3, ""key1"": {""c"": ""42""}, ""key2"": [11, 12, {""x"": [31, 42]}]}") $$;
+select (t1.a).b.key1.c from t1;
+  c   
+------
+ "42"
+ "42"
+(2 rows)
+
+select (t1.a).b.key2 from t1;
+           key2            
+---------------------------
+ [11, 12]
+ [11, 12, {"x": [31, 42]}]
+(2 rows)
+
+select (t1.a).b.key2[0] from t1;
+ key2 
+------
+ 11
+ 11
+(2 rows)
+
+select (t1.a).b.key2[0::text] from t1;
+ key2 
+------
+ 11
+ 11
+(2 rows)
+
+select (t1.a).b.key2[2].x[1] from t1;
+ x  
+----
+ 
+ 42
+(2 rows)
+
+drop table t1 cascade;
+drop type test cascade;
+drop domain json_d cascade;
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index f095dc2bbe..19fb3dd230 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -1581,3 +1581,17 @@ select (test_jsonb_dot.test_jsonb).d[0:] from test_jsonb_dot;
 select (test_jsonb_dot.test_jsonb).d[0::int] from test_jsonb_dot;
 select (test_jsonb_dot.test_jsonb).d[0::float] from test_jsonb_dot;
 select (test_jsonb_dot.test_jsonb).d[0].x[1] from test_jsonb_dot;
+
+create domain json_d as jsonb;
+create type test as (a int, b json_d);
+create table t1(a test);
+insert into t1 select $$(1,"{""a"": 3, ""key1"": {""c"": ""42""}, ""key2"": [11, 12]}") $$;
+insert into t1 select $$ (1,"{""a"": 3, ""key1"": {""c"": ""42""}, ""key2"": [11, 12, {""x"": [31, 42]}]}") $$;
+select (t1.a).b.key1.c from t1;
+select (t1.a).b.key2 from t1;
+select (t1.a).b.key2[0] from t1;
+select (t1.a).b.key2[0::text] from t1;
+select (t1.a).b.key2[2].x[1] from t1;
+drop table t1 cascade;
+drop type test cascade;
+drop domain json_d cascade;
\ No newline at end of file
-- 
2.34.1