From 72897ab85bfd890ff5fe13ca7a9e5d2f0168f8fa Mon Sep 17 00:00:00 2001 From: jian he 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