diff --git a/src/common/backend/utils/adt/jsonfuncs.cpp b/src/common/backend/utils/adt/jsonfuncs.cpp index 12dec56529da321c1388025cf25d120a964254fa..66165a4ef936e9d5af52c0893d5d61ecc45aa3bc 100644 --- a/src/common/backend/utils/adt/jsonfuncs.cpp +++ b/src/common/backend/utils/adt/jsonfuncs.cpp @@ -145,11 +145,10 @@ typedef enum { ERROR_ON_ERROR } OnErrorType; -typedef enum { - JSON_PATH_NO_RELAX = 0, - JSON_PATH_ALLOW_RELAX, - JSON_PATH_RELAXED -} JsonPathRelaxState; +#define JSON_PATH_NO_RELAX 0x0 +#define JSON_PATH_RELAX_OBJECT 0x1 +#define JSON_PATH_ALLOW_RELAX 0x3 +#define JSON_PATH_RELAXED 0x4 /* state for json_object_keys */ typedef struct OkeysState { @@ -267,7 +266,7 @@ struct OvalsState { struct JsonPathContext { text* topJson; - JsonPathRelaxState relax; + int relax; }; struct JsonExistsPathContext { @@ -3547,11 +3546,11 @@ static void JPWalkArrayStep(JsonPathItem* path, text* json, void (*pwalker)(text*, void*), void* context) { JsonPathArrayStep* as = (JsonPathArrayStep*)path; - JsonPathRelaxState relax = ((JsonPathContext*)context)->relax; + int relax = ((JsonPathContext*)context)->relax; char* jsonType = text_to_cstring(DatumGetTextP(DirectFunctionCall1(json_typeof, PointerGetDatum(json)))); text* result = NULL; if (strcmp(jsonType, "array") != 0) { - if (relax != JSON_PATH_ALLOW_RELAX) { + if (!(relax & JSON_PATH_ALLOW_RELAX) || (relax & JSON_PATH_RELAXED)) { return; } else { /* implicitly wrap a non-array object in an array */ @@ -3567,18 +3566,17 @@ static void JPWalkArrayStep(JsonPathItem* path, text* json, return; } } - ((JsonPathContext*)context)->relax = JSON_PATH_RELAXED; + ((JsonPathContext*)context)->relax |= JSON_PATH_RELAXED; JsonPathWalker(path->next, json, pwalker, context); return; } } - if (relax == JSON_PATH_RELAXED) { - ((JsonPathContext*)context)->relax = JSON_PATH_ALLOW_RELAX; + if (relax & JSON_PATH_RELAXED) { + ((JsonPathContext*)context)->relax &= JSON_PATH_ALLOW_RELAX; } if (as->indexes != NIL) { ListCell* idxCell = NULL; - int index; foreach (idxCell, as->indexes) { int index = lfirst_int(idxCell); @@ -3598,16 +3596,15 @@ static void JPWalkObjectStep(JsonPathItem* path, text* json, void (*pwalker)(text*, void*), void* context) { JsonPathObjectStep* os = (JsonPathObjectStep*)path; - JsonPathRelaxState relax = ((JsonPathContext*)context)->relax; + int relax = ((JsonPathContext*)context)->relax; char* jsonType = text_to_cstring(DatumGetTextP(DirectFunctionCall1(json_typeof, PointerGetDatum(json)))); text* result = NULL; if (strcmp(jsonType, "object") != 0) { - if (relax != JSON_PATH_ALLOW_RELAX || strcmp(jsonType, "array") != 0) { + if (!(relax & JSON_PATH_RELAX_OBJECT) || strcmp(jsonType, "array") != 0) { return; } else { /* implicitly unwrap the array */ int length = DatumGetInt32(DirectFunctionCall1(json_array_length, PointerGetDatum(json))); - ((JsonPathContext*)context)->relax = JSON_PATH_RELAXED; for (int i = 0; i < length; i++) { result = get_worker(json, NULL, i, NULL, NULL, -1, false); JsonPathWalker(path, result, pwalker, context); @@ -3643,7 +3640,7 @@ static void JsonPathWalker(JsonPathItem* path, text* json, void (*pwalker)(text* return; } else if (json == NULL || !IsJsonText(json) || JsonElemntType(json)) { /* allow redundant tailing [0] in syntax relaxation */ - JsonPathRelaxState relax = ((JsonPathContext*)context)->relax; + int relax = ((JsonPathContext*)context)->relax; if (relax == JSON_PATH_NO_RELAX || path->type != JPI_ARRAY) { return; } @@ -3886,7 +3883,6 @@ Datum json_textcontains(PG_FUNCTION_ARGS) JsonTextContainsContext context; context.cxt.topJson = json; - context.cxt.relax = JSON_PATH_NO_RELAX; context.result = false; if (!IsJsonText(json)) @@ -3895,6 +3891,7 @@ Datum json_textcontains(PG_FUNCTION_ARGS) char* target = pstrdup(raw); tok = strtok(target, ","); while (!(context.result) && tok != NULL) { + context.cxt.relax = JSON_PATH_RELAX_OBJECT; context.target = tok; JsonPathWalker(path, json, (void (*)(text*, void*))JsonTextContainsWalker, (void*)(&context)); tok = strtok(NULL, ","); @@ -3923,7 +3920,6 @@ Datum json_textcontains_text(PG_FUNCTION_ARGS) JsonTextContainsContext context; context.cxt.topJson = json; - context.cxt.relax = JSON_PATH_NO_RELAX; context.result = false; if (!IsJsonText(json)) @@ -3932,6 +3928,7 @@ Datum json_textcontains_text(PG_FUNCTION_ARGS) char* target = pstrdup(raw); tok = strtok(target, ","); while (!(context.result) && tok != NULL) { + context.cxt.relax = JSON_PATH_RELAX_OBJECT; context.target = tok; JsonPathWalker(path, json, (void (*)(text*, void*))JsonTextContainsWalker, (void*)(&context)); tok = strtok(NULL, ","); diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out index 6bdd69a5d4ab229e63325aeeacdda13f237608bc..2fafd6d48dec83d2020b89b4f16c4cc74e2a6fc7 100644 --- a/src/test/regress/expected/jsonpath.out +++ b/src/test/regress/expected/jsonpath.out @@ -570,6 +570,34 @@ select json_textcontains('{"family" : {"id":12, "ages":[25,23], "address" : {"st t (1 row) +select json_textcontains('{ + "name":"web", + "num":3, + "sites": [ + { "name":"Google", "info":[ "Android", "Google Search", "Googlee" ] }, + { "name":"Runoob", "info":[ "book", "tool", "wechat" ] }, + { "name":"Taobao", "info":[ "taobao", "shop" ] } + ] +}', '$.sites.info.name','shop'); + json_textcontains +------------------- + f +(1 row) + +select json_textcontains('{ + "name":"web", + "num":3, + "sites": [ + { "name":"Google", "info":[[ {"name":"Android"}, {"name":"Google Search"}, {"name":"Googlee"} ]] }, + { "name":"Runoob", "info":[[ {"name":"book"}, {"name":"tool"}, "wechat" ]] }, + { "name":"Taobao", "info":[[ {"name":"taobao"}, "shop" ]] } + ] +}', '$.sites.info.name','Androidd,Search,Googlee'); + json_textcontains +------------------- + t +(1 row) + create or replace procedure p_JsonTextcontains_Case0011(col1 text,col2 text,col3 text) as val1 bool; diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql index a0078b1e9d9aa6c828050f155a60b8d976fe682b..a520eb9a9779afc4b3deb7e1fba6e0f6320a2c2a 100644 --- a/src/test/regress/sql/jsonpath.sql +++ b/src/test/regress/sql/jsonpath.sql @@ -166,6 +166,24 @@ select json_textcontains('{ "zebra" : { "name" : "Marty", "handler" : "Bob" }}','$.zebra.name','Marty'); select json_textcontains('{"family" : {"id":12, "ages":[25,23], "address" : {"street" : "300 Oak Street", "apt" : 10}}}', '$.family.address.street','300'); +select json_textcontains('{ + "name":"web", + "num":3, + "sites": [ + { "name":"Google", "info":[ "Android", "Google Search", "Googlee" ] }, + { "name":"Runoob", "info":[ "book", "tool", "wechat" ] }, + { "name":"Taobao", "info":[ "taobao", "shop" ] } + ] +}', '$.sites.info.name','shop'); +select json_textcontains('{ + "name":"web", + "num":3, + "sites": [ + { "name":"Google", "info":[[ {"name":"Android"}, {"name":"Google Search"}, {"name":"Googlee"} ]] }, + { "name":"Runoob", "info":[[ {"name":"book"}, {"name":"tool"}, "wechat" ]] }, + { "name":"Taobao", "info":[[ {"name":"taobao"}, "shop" ]] } + ] +}', '$.sites.info.name','Androidd,Search,Googlee'); create or replace procedure p_JsonTextcontains_Case0011(col1 text,col2 text,col3 text) as val1 bool;