From 3913fcbb6c5a54e79be8a488e8a68335b8b312fb Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Thu, 10 Nov 2022 09:48:49 +0200 Subject: .......PS. [ZBXNEXT-8040] added object based json parsing and jsonpath optimizations Merge in ZBX/zabbix from feature/ZBXNEXT-8040-6.0 to release/6.0 * commit 'a776758776e65ec288b9dffa688e296980fcff33': .......PS. [ZBXNEXT-8040] fixed warnings ...G...... [ZBXNEXT-8040] fixed windows agent builds .......PS. [ZBXNEXT-8040] added object based json parsing and jsonpath indexing for simple expressions (cherry picked from commit 3385fecdb61d2899a9dd50b05c5f6d76180538e1) --- .gitignore | 3 +- ChangeLog.d/feature/ZBXNEXT-8040 | 1 + build/mingw/Makefile | 14 + build/win32/project/Makefile_agent | 3 + build/win32/project/Makefile_get | 2 + build/win32/project/Makefile_sender | 2 + build/win32/project/Makefile_sender_dll | 2 + include/zbxjson.h | 51 +- src/go/pkg/zbxlib/globals_windows.go | 2 + src/libs/zbxjson/Makefile.am | 4 +- src/libs/zbxjson/json.c | 14 +- src/libs/zbxjson/json.h | 2 + src/libs/zbxjson/json_parser.c | 127 +- src/libs/zbxjson/json_parser.h | 8 +- src/libs/zbxjson/jsonobj.c | 365 ++++++ src/libs/zbxjson/jsonobj.h | 56 + src/libs/zbxjson/jsonpath.c | 1219 +++++++++++++------- src/libs/zbxjson/jsonpath.h | 17 +- src/zabbix_agent/Makefile.am | 2 +- src/zabbix_server/Makefile.am | 4 +- src/zabbix_server/preprocessor/item_preproc.c | 73 +- src/zabbix_server/preprocessor/preproc_cache.c | 5 + tests/libs/zbxcommon/Makefile.am | 2 +- tests/libs/zbxjson/Makefile.am | 20 +- tests/libs/zbxjson/zbx_jsonobj_query.c | 114 ++ tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml | 77 ++ tests/libs/zbxjson/zbx_jsonobj_query.yaml | 800 +++++++++++++ tests/libs/zbxjson/zbx_jsonpath_query.c | 100 -- tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml | 77 -- tests/libs/zbxjson/zbx_jsonpath_query.yaml | 762 ------------ tests/libs/zbxsysinfo/Makefile.am | 8 +- tests/libs/zbxsysinfo/common/Makefile.am | 2 +- tests/libs/zbxsysinfo/linux/Makefile.am | 2 +- tests/zabbix_server/poller/Makefile.am | 61 + .../preprocessor/zbx_item_preproc.yaml | 6 +- 35 files changed, 2564 insertions(+), 1443 deletions(-) create mode 100644 ChangeLog.d/feature/ZBXNEXT-8040 create mode 100644 src/libs/zbxjson/jsonobj.c create mode 100644 src/libs/zbxjson/jsonobj.h create mode 100644 tests/libs/zbxjson/zbx_jsonobj_query.c create mode 100644 tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml create mode 100644 tests/libs/zbxjson/zbx_jsonobj_query.yaml delete mode 100644 tests/libs/zbxjson/zbx_jsonpath_query.c delete mode 100644 tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml delete mode 100644 tests/libs/zbxjson/zbx_jsonpath_query.yaml create mode 100644 tests/zabbix_server/poller/Makefile.am diff --git a/.gitignore b/.gitignore index 22da35ca50b..ac51c004d6c 100644 --- a/.gitignore +++ b/.gitignore @@ -191,8 +191,8 @@ tests/libs/zbxhistory/zbx_history_get_values tests/libs/zbxjson/zbx_json_decodevalue tests/libs/zbxjson/zbx_json_decodevalue_dyn tests/libs/zbxjson/zbx_json_open_path +tests/libs/zbxjson/zbx_jsonobj_query tests/libs/zbxjson/zbx_jsonpath_compile -tests/libs/zbxjson/zbx_jsonpath_query tests/libs/zbxprometheus/prometheus_filter_init tests/libs/zbxprometheus/prometheus_parse_row tests/libs/zbxprometheus/zbx_prometheus_pattern @@ -226,6 +226,7 @@ tests/libs/zbxcommon/zbx_tm_add tests/libs/zbxcommon/zbx_tm_sub tests/libs/zbxcommon/zbx_tm_round_up tests/libs/zbxcommon/zbx_tm_round_down +tests/libs/zbxtime/zbx_iso8601_utc tests/libs/zbxtrends/zbx_baseline_get_data tests/libs/zbxtrends/zbx_trends_parse_range tests/libs/zbxsysinfo/process_http diff --git a/ChangeLog.d/feature/ZBXNEXT-8040 b/ChangeLog.d/feature/ZBXNEXT-8040 new file mode 100644 index 00000000000..15238ae2c93 --- /dev/null +++ b/ChangeLog.d/feature/ZBXNEXT-8040 @@ -0,0 +1 @@ +.......PS. [ZBXNEXT-8040] added object based json parsing and jsonpath optimizations (wiper) diff --git a/build/mingw/Makefile b/build/mingw/Makefile index eb2c64e5f91..3fa89b23d1c 100644 --- a/build/mingw/Makefile +++ b/build/mingw/Makefile @@ -24,6 +24,7 @@ OBJS = \ $(OUTPUTDIR)\md5.o \ $(OUTPUTDIR)\sysinfo.o \ $(OUTPUTDIR)\vector.o \ + $(OUTPUTDIR)\hashset.o \ $(OUTPUTDIR)\zbxregexp.o \ $(OUTPUTDIR)\persistent_state.o \ $(OUTPUTDIR)\logfiles.o \ @@ -32,6 +33,7 @@ OBJS = \ $(OUTPUTDIR)\json.o \ $(OUTPUTDIR)\json_parser.o \ $(OUTPUTDIR)\jsonpath.o \ + $(OUTPUTDIR)\jsonobj.o \ $(OUTPUTDIR)\sha256crypt.o \ $(OUTPUTDIR)\variant.o \ $(OUTPUTDIR)\sysinfo_system.o \ @@ -142,6 +144,12 @@ $(OUTPUTDIR)\jsonpath.o: $(TOPDIR)\src\libs\zbxjson\jsonpath.c $(OUTPUTDIR)\sha256crypt.o: $(TOPDIR)\src\libs\zbxhash\sha256crypt.c $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ +$(OUTPUTDIR)\jsonobj.o: $(TOPDIR)\src\libs\zbxjson\jsonobj.c + $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ + +$(OUTPUTDIR)\sha256crypt.o: $(TOPDIR)\src\libs\zbxcrypto\sha256crypt.c + $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ + $(OUTPUTDIR)\variant.o: $(TOPDIR)\src\libs\zbxvariant\variant.c $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ @@ -169,6 +177,12 @@ $(OUTPUTDIR)\sysinfo_alias.o: $(TOPDIR)\src\libs\zbxsysinfo\alias\alias.c $(OUTPUTDIR)\vector.o: $(TOPDIR)\src\libs\zbxalgo\vector.c $(CC) $(CFLAGS) -DUNICODE -c $^ -o $@ +$(OUTPUTDIR)\hashset.o: $(TOPDIR)\src\libs\zbxalgo\hashset.c + $(CC) $(CFLAGS) -DUNICODE -c $^ -o $@ + +$(OUTPUTDIR)\version.o: $(TOPDIR)\src\libs\zbxversion\version.c + $(CC) $(CFLAGS) -DUNICODE -c $^ -o $@ + $(OUTPUTDIR)\algodefs.o: $(TOPDIR)\src\libs\zbxalgo\algodefs.c $(CC) $(CFLAGS) -DUNICODE -c $^ -o $@ diff --git a/build/win32/project/Makefile_agent b/build/win32/project/Makefile_agent index 773fbdb88c4..4d0bae5f60d 100644 --- a/build/win32/project/Makefile_agent +++ b/build/win32/project/Makefile_agent @@ -37,6 +37,8 @@ ADD_RFLAGS = /d "ZABBIX_AGENT" OBJS = \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxcommon\alias.o \ + ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxcommon\comms.o \ ..\..\..\src\libs\zbxcommon\iprange.o \ ..\..\..\src\libs\zbxcommon\misc.o \ @@ -56,6 +58,7 @@ OBJS = \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ ..\..\..\src\libs\zbxjson\jsonpath.o \ + ..\..\..\src\libs\zbxjson\jsonobj.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxmutexs\mutexs.o \ ..\..\..\src\libs\zbxsymbols\symbols.o \ diff --git a/build/win32/project/Makefile_get b/build/win32/project/Makefile_get index 287f13bb546..789e910c022 100644 --- a/build/win32/project/Makefile_get +++ b/build/win32/project/Makefile_get @@ -29,6 +29,7 @@ ADD_RFLAGS = /d "ZABBIX_GET" OBJS = \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxcommon\comms.o \ ..\..\..\src\libs\zbxcommon\iprange.o \ ..\..\..\src\libs\zbxcommon\misc.o \ @@ -46,6 +47,7 @@ OBJS = \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ ..\..\..\src\libs\zbxjson\jsonpath.o \ + ..\..\..\src\libs\zbxjson\jsonobj.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxmutexs\mutexs.o \ ..\..\..\src\libs\zbxsymbols\symbols.o \ diff --git a/build/win32/project/Makefile_sender b/build/win32/project/Makefile_sender index ffe2300f9f5..12aab7e5f4b 100644 --- a/build/win32/project/Makefile_sender +++ b/build/win32/project/Makefile_sender @@ -45,6 +45,7 @@ OBJS = \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ ..\..\..\src\libs\zbxjson\jsonpath.o \ + ..\..\..\src\libs\zbxjson\jsonobj.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxmutexs\mutexs.o \ ..\..\..\src\libs\zbxsymbols\symbols.o \ @@ -53,6 +54,7 @@ OBJS = \ ..\..\..\src\libs\zbxwin32\fatal.o \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxregexp\zbxregexp.o \ ..\..\..\src\libs\zbxxml\xml.o \ ..\..\..\src\zabbix_sender\zabbix_sender.o diff --git a/build/win32/project/Makefile_sender_dll b/build/win32/project/Makefile_sender_dll index f1d4269069e..af3eca17ca8 100644 --- a/build/win32/project/Makefile_sender_dll +++ b/build/win32/project/Makefile_sender_dll @@ -50,6 +50,7 @@ OBJS = \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ ..\..\..\src\libs\zbxjson\jsonpath.o \ + ..\..\..\src\libs\zbxjson\jsonobj.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxmutexs\mutexs.o \ ..\..\..\src\libs\zbxsymbols\symbols.o \ @@ -58,6 +59,7 @@ OBJS = \ ..\..\..\src\libs\zbxwin32\fatal.o \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxregexp\zbxregexp.o \ ..\..\..\src\libs\zbxxml\xml.o \ ..\..\..\src\zabbix_sender\win32\zabbix_sender.o diff --git a/include/zbxjson.h b/include/zbxjson.h index cea6646a0c5..3879063e893 100644 --- a/include/zbxjson.h +++ b/include/zbxjson.h @@ -21,6 +21,7 @@ #define ZABBIX_ZJSON_H #include "zbxtypes.h" +#include "zbxalgo.h" #define ZBX_PROTO_TAG_CLOCK "clock" #define ZBX_PROTO_TAG_NS "ns" @@ -245,7 +246,8 @@ typedef enum ZBX_JSON_TYPE_OBJECT, ZBX_JSON_TYPE_NULL, ZBX_JSON_TYPE_TRUE, - ZBX_JSON_TYPE_FALSE + ZBX_JSON_TYPE_FALSE, + ZBX_JSON_TYPE_NUMBER } zbx_json_type_t; @@ -323,15 +325,52 @@ typedef struct int segments_num; int segments_alloc; - /* set to 1 when jsonpath points at single location */ - unsigned char definite; - - unsigned char first_match; + unsigned char definite; /* set to 1 when jsonpath points at single location */ + unsigned char first_match; /* set to 1 if first match must be returned */ } zbx_jsonpath_t; -void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath); +typedef struct zbx_jsonobj zbx_jsonobj_t; + +ZBX_PTR_VECTOR_DECL(jsonobj_ptr, zbx_jsonobj_t *) + +typedef union +{ + char *string; + double number; + zbx_hashset_t object; + zbx_vector_jsonobj_ptr_t array; +} +zbx_jsonobj_data_t; + +typedef struct +{ + char *path; /* the path that was indexed - for example @.a.b.c */ + zbx_hashset_t objects; +} +zbx_jsonobj_index_t; + +struct zbx_jsonobj +{ + zbx_json_type_t type; + zbx_jsonobj_data_t data; + zbx_jsonobj_index_t *index; +}; + +typedef struct +{ + char *name; + zbx_jsonobj_t value; +} +zbx_jsonobj_el_t; + int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath); int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output); +void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath); + +int zbx_jsonobj_open(const char *data, zbx_jsonobj_t *obj); +void zbx_jsonobj_clear(zbx_jsonobj_t *obj); +int zbx_jsonobj_query(zbx_jsonobj_t *obj, const char *path, char **output); +int zbx_jsonobj_to_string(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj); #endif /* ZABBIX_ZJSON_H */ diff --git a/src/go/pkg/zbxlib/globals_windows.go b/src/go/pkg/zbxlib/globals_windows.go index a176056e829..dfc8105df69 100644 --- a/src/go/pkg/zbxlib/globals_windows.go +++ b/src/go/pkg/zbxlib/globals_windows.go @@ -39,6 +39,7 @@ package zbxlib #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/md5.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sysinfo.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/vector.o +#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/hashset.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/zbxregexp.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/algodefs.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/persistent_state.o @@ -46,6 +47,7 @@ package zbxlib #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/json.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/json_parser.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/jsonpath.o +#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/jsonobj.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sha256crypt.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/variant.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sysinfo_system.o diff --git a/src/libs/zbxjson/Makefile.am b/src/libs/zbxjson/Makefile.am index ad27f80c98e..f7981730a30 100644 --- a/src/libs/zbxjson/Makefile.am +++ b/src/libs/zbxjson/Makefile.am @@ -8,4 +8,6 @@ libzbxjson_a_SOURCES = \ json_parser.c \ json_parser.h \ jsonpath.c \ - jsonpath.h + jsonpath.h \ + jsonobj.c \ + jsonobj.h diff --git a/src/libs/zbxjson/json.c b/src/libs/zbxjson/json.c index 4960eca4466..412dcc098cf 100644 --- a/src/libs/zbxjson/json.c +++ b/src/libs/zbxjson/json.c @@ -840,7 +840,7 @@ static unsigned int zbx_json_decode_character(const char **p, unsigned char *byt * string copying failed. * * * ******************************************************************************/ -static const char *zbx_json_copy_string(const char *p, char *out, size_t size) +const char *json_copy_string(const char *p, char *out, size_t size) { char *start = out; @@ -919,7 +919,7 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j /* only primitive values are decoded */ return NULL; default: - if (0 == (len = json_parse_value(p, NULL))) + if (0 == (len = json_parse_value(p, NULL, NULL))) return NULL; } @@ -929,7 +929,7 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j switch (type_local) { case ZBX_JSON_TYPE_STRING: - return zbx_json_copy_string(p, string, size); + return json_copy_string(p, string, size); case ZBX_JSON_TYPE_NULL: if (0 == size) return NULL; @@ -953,7 +953,7 @@ const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *strin /* only primitive values are decoded */ return NULL; default: - if (0 == (len = json_parse_value(p, NULL))) + if (0 == (len = json_parse_value(p, NULL, NULL))) return NULL; } @@ -969,7 +969,7 @@ const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *strin switch (type_local) { case ZBX_JSON_TYPE_STRING: - return zbx_json_copy_string(p, *string, *string_alloc); + return json_copy_string(p, *string, *string_alloc); case ZBX_JSON_TYPE_NULL: **string = '\0'; return p + len; @@ -986,7 +986,7 @@ const char *zbx_json_pair_next(const struct zbx_json_parse *jp, const char *p, c if (ZBX_JSON_TYPE_STRING != __zbx_json_type(p)) return NULL; - if (NULL == (p = zbx_json_copy_string(p, name, len))) + if (NULL == (p = json_copy_string(p, name, len))) return NULL; SKIP_WHITESPACE(p); @@ -1214,7 +1214,7 @@ int json_open_path(const struct zbx_json_parse *jp, const zbx_jsonpath_t *jsonpa object.start = p; if (NULL == (object.end = __zbx_json_rbracket(p))) - object.end = p + json_parse_value(p, NULL) - 1; + object.end = p + json_parse_value(p, NULL, NULL) - 1; } *out = object; diff --git a/src/libs/zbxjson/json.h b/src/libs/zbxjson/json.h index a9b7a4f5f29..2a53f0ec687 100644 --- a/src/libs/zbxjson/json.h +++ b/src/libs/zbxjson/json.h @@ -35,4 +35,6 @@ void zbx_set_json_strerror(const char *fmt, ...) __zbx_attr_format_printf(1, 2); int json_open_path(const struct zbx_json_parse *jp, const zbx_jsonpath_t *jsonpath, struct zbx_json_parse *out); +const char *json_copy_string(const char *p, char *out, size_t size); + #endif diff --git a/src/libs/zbxjson/json_parser.c b/src/libs/zbxjson/json_parser.c index 20ce1098437..7f64f4164f2 100644 --- a/src/libs/zbxjson/json_parser.c +++ b/src/libs/zbxjson/json_parser.c @@ -21,27 +21,31 @@ #include "common.h" #include "json.h" - -static zbx_int64_t json_parse_object(const char *start, char **error); +#include "jsonobj.h" /****************************************************************************** * * * Purpose: Prepares JSON parsing error message * * * - * Parameters: message - [IN] the error message * - * json_buffer - [IN] the failing data fragment * - * error - [OUT] the parsing error message (can be NULL) * + * Parameters: message - [IN] the error message * + * ptr - [IN] the failing data fragment * + * error - [OUT] the parsing error message (can be NULL) * * * * Return value: 0 - the json_error() function always returns 0 value * * so it can be used to return from failed parses * * * ******************************************************************************/ -static zbx_int64_t json_error(const char *message, const char *json_buffer, char **error) +zbx_int64_t json_error(const char *message, const char *ptr, char **error) { if (NULL != error) { - if (NULL != json_buffer) - *error = zbx_dsprintf(*error, "%s at: '%s'", message, json_buffer); + if (NULL != ptr) + { + if (128 < strlen(ptr)) + *error = zbx_dsprintf(*error, "%s at: '%128s...'", message, ptr); + else + *error = zbx_dsprintf(*error, "%s at: '%s'", message, ptr); + } else *error = zbx_strdup(*error, message); } @@ -54,6 +58,7 @@ static zbx_int64_t json_error(const char *message, const char *json_buffer, char * Purpose: Parses JSON string value or object name * * * * Parameters: start - [IN] the JSON data without leading whitespace * + * str - [OUT] the parsed unquoted string (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -61,7 +66,7 @@ static zbx_int64_t json_error(const char *message, const char *json_buffer, char * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_string(const char *start, char **error) +static zbx_int64_t json_parse_string(const char *start, char **str, char **error) { const char *ptr = start; @@ -119,6 +124,12 @@ static zbx_int64_t json_parse_string(const char *start, char **error) ptr++; } + if (NULL != str) + { + *str = (char *)zbx_malloc(NULL, (size_t)(ptr - start)); + json_copy_string(start, *str, (size_t)(ptr - start)); + } + return ptr - start + 1; } @@ -127,6 +138,7 @@ static zbx_int64_t json_parse_string(const char *start, char **error) * Purpose: Parses JSON array value * * * * Parameters: start - [IN] the JSON data without leading whitespace * + * obj - [IN/OUT] the JSON object (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -134,11 +146,14 @@ static zbx_int64_t json_parse_string(const char *start, char **error) * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_array(const char *start, char **error) +zbx_int64_t json_parse_array(const char *start, zbx_jsonobj_t *obj, char **error) { const char *ptr = start; zbx_int64_t len; + if (NULL != obj) + jsonobj_init(obj, ZBX_JSON_TYPE_ARRAY); + ptr++; SKIP_WHITESPACE(ptr); @@ -146,9 +161,29 @@ static zbx_int64_t json_parse_array(const char *start, char **error) { while (1) { + zbx_jsonobj_t *value; + + if (NULL != obj) + { + value = zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + jsonobj_init(value, ZBX_JSON_TYPE_UNKNOWN); + } + else + value = NULL; + /* json_parse_value strips leading whitespace, so we don't have to do it here */ - if (0 == (len = json_parse_value(ptr, error))) + if (0 == (len = json_parse_value(ptr, value, error))) + { + if (NULL != obj) + { + zbx_jsonobj_clear(value); + zbx_free(value); + } return 0; + } + + if (NULL != obj) + zbx_vector_jsonobj_ptr_append(&obj->data.array, value); ptr += len; SKIP_WHITESPACE(ptr); @@ -171,15 +206,16 @@ static zbx_int64_t json_parse_array(const char *start, char **error) * * * Purpose: Parses JSON number value * * * - * Parameters: start - [IN] the JSON data without leading whitespace * - * error - [OUT] the parsing error message (can be NULL) * + * Parameters: start - [IN] the JSON data without leading whitespace * + * number - [OUT] the parsed number (can be NULL) * + * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * * error parameter (if not NULL) contains allocated error * * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_number(const char *start, char **error) +static zbx_int64_t json_parse_number(const char *start, double *number, char **error) { const char *ptr = start; char first_digit; @@ -235,6 +271,9 @@ static zbx_int64_t json_parse_number(const char *start, char **error) } } + if (NULL != number) + *number = atof(start); + return ptr - start; } @@ -281,10 +320,12 @@ static zbx_int64_t json_parse_literal(const char *start, const char *text, char * message. * * * ******************************************************************************/ -zbx_int64_t json_parse_value(const char *start, char **error) +zbx_int64_t json_parse_value(const char *start, zbx_jsonobj_t *obj, char **error) { const char *ptr = start; zbx_int64_t len; + char *str = NULL; + double number; SKIP_WHITESPACE(ptr); @@ -293,28 +334,40 @@ zbx_int64_t json_parse_value(const char *start, char **error) case '\0': return json_error("unexpected end of object value", NULL, error); case '"': - if (0 == (len = json_parse_string(ptr, error))) + if (0 == (len = json_parse_string(ptr, (NULL != obj ? &str : NULL), error))) return 0; + + if (NULL != obj) + jsonobj_set_string(obj, str); break; case '{': - if (0 == (len = json_parse_object(ptr, error))) + if (0 == (len = json_parse_object(ptr, obj, error))) return 0; break; case '[': - if (0 == (len = json_parse_array(ptr, error))) + if (0 == (len = json_parse_array(ptr, obj, error))) return 0; break; case 't': if (0 == (len = json_parse_literal(ptr, "true", error))) return 0; + + if (NULL != obj) + jsonobj_set_true(obj); break; case 'f': if (0 == (len = json_parse_literal(ptr, "false", error))) return 0; + + if (NULL != obj) + jsonobj_set_false(obj); break; case 'n': if (0 == (len = json_parse_literal(ptr, "null", error))) return 0; + + if (NULL != obj) + jsonobj_set_null(obj); break; case '0': case '1': @@ -327,8 +380,12 @@ zbx_int64_t json_parse_value(const char *start, char **error) case '8': case '9': case '-': - if (0 == (len = json_parse_number(ptr, error))) + if (0 == (len = json_parse_number(ptr, (NULL != obj ? &number : NULL), error))) return 0; + + if (NULL != obj) + jsonobj_set_number(obj, number); + break; default: return json_error("invalid JSON object value starting character", ptr, error); @@ -342,6 +399,7 @@ zbx_int64_t json_parse_value(const char *start, char **error) * Purpose: Parses JSON object * * * * Parameters: start - [IN] the JSON data * + * obj - [IN/OUT] the JSON object (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -349,10 +407,13 @@ zbx_int64_t json_parse_value(const char *start, char **error) * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_object(const char *start, char **error) +zbx_int64_t json_parse_object(const char *start, zbx_jsonobj_t *obj, char **error) { - const char *ptr = start; - zbx_int64_t len; + const char *ptr = start; + zbx_int64_t len; + + if (NULL != obj) + jsonobj_init(obj, ZBX_JSON_TYPE_OBJECT); /* parse object name */ SKIP_WHITESPACE(ptr); @@ -364,11 +425,15 @@ static zbx_int64_t json_parse_object(const char *start, char **error) { while (1) { + zbx_jsonobj_el_t el; + if ('"' != *ptr) return json_error("invalid object name", ptr, error); + jsonobj_el_init(&el); + /* cannot parse object name, failing */ - if (0 == (len = json_parse_string(ptr, error))) + if (0 == (len = json_parse_string(ptr, (NULL != obj ? &el.name : NULL), error))) return 0; ptr += len; @@ -377,11 +442,21 @@ static zbx_int64_t json_parse_object(const char *start, char **error) SKIP_WHITESPACE(ptr); if (':' != *ptr) + { + jsonobj_el_clear(&el); return json_error("invalid object name/value separator", ptr, error); + } + ptr++; - if (0 == (len = json_parse_value(ptr, error))) + if (0 == (len = json_parse_value(ptr, (NULL != obj ? &el.value : NULL), error))) + { + jsonobj_el_clear(&el); return 0; + } + + if (NULL != obj) + zbx_hashset_insert(&obj->data.object, &el, sizeof(el)); ptr += len; @@ -426,11 +501,11 @@ zbx_int64_t zbx_json_validate(const char *start, char **error) switch (*start) { case '{': - if (0 == (len = json_parse_object(start, error))) + if (0 == (len = json_parse_object(start, NULL, error))) return 0; break; case '[': - if (0 == (len = json_parse_array(start, error))) + if (0 == (len = json_parse_array(start, NULL, error))) return 0; break; default: diff --git a/src/libs/zbxjson/json_parser.h b/src/libs/zbxjson/json_parser.h index f9fb3bb8cf5..cd54939eb7d 100644 --- a/src/libs/zbxjson/json_parser.h +++ b/src/libs/zbxjson/json_parser.h @@ -21,9 +21,15 @@ #define ZABBIX_JSON_PARSER_H #include "zbxtypes.h" +#include "jsonobj.h" zbx_int64_t zbx_json_validate(const char *start, char **error); -zbx_int64_t json_parse_value(const char *start, char **error); +zbx_int64_t json_parse_value(const char *start, zbx_jsonobj_t *obj, char **error); + +zbx_int64_t json_error(const char *message, const char *ptr, char **error); + +zbx_int64_t json_parse_object(const char *start, zbx_jsonobj_t *obj, char **error); +zbx_int64_t json_parse_array(const char *start, zbx_jsonobj_t *obj, char **error); #endif diff --git a/src/libs/zbxjson/jsonobj.c b/src/libs/zbxjson/jsonobj.c new file mode 100644 index 00000000000..165738a4b3b --- /dev/null +++ b/src/libs/zbxjson/jsonobj.c @@ -0,0 +1,365 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "jsonobj.h" + +#include "json_parser.h" +#include "json.h" + +ZBX_PTR_VECTOR_IMPL(jsonobj_ptr, zbx_jsonobj_t *) +ZBX_VECTOR_IMPL(jsonobj_ref, zbx_jsonobj_ref_t) + +/* jsonobject index hashset support */ + +static zbx_hash_t jsonobj_index_el_hash(const void *v) +{ + const zbx_jsonobj_index_el_t *el = (const zbx_jsonobj_index_el_t *)v; + + return ZBX_DEFAULT_STRING_HASH_FUNC(el->value); +} + +static int jsonobj_index_el_compare(const void *v1, const void *v2) +{ + const zbx_jsonobj_index_el_t *el1 = (const zbx_jsonobj_index_el_t *)v1; + const zbx_jsonobj_index_el_t *el2 = (const zbx_jsonobj_index_el_t *)v2; + + return strcmp(el1->value, el2->value); +} + +/* jsonobject values hashset support */ + +static zbx_hash_t jsonobj_el_hash(const void *v) +{ + const zbx_jsonobj_el_t *el = (const zbx_jsonobj_el_t *)v; + + return ZBX_DEFAULT_STRING_HASH_FUNC(el->name); +} + +static int jsonobj_el_compare(const void *v1, const void *v2) +{ + const zbx_jsonobj_el_t *el1 = (const zbx_jsonobj_el_t *)v1; + const zbx_jsonobj_el_t *el2 = (const zbx_jsonobj_el_t *)v2; + + return strcmp(el1->name, el2->name); +} + +/****************************************************************************** + * * + * Purpose: initialize json object structure * + * * + * Parameters: obj - [IN/OUT] the json object to initialize * + * type - [IN] the json object type * + * * + ******************************************************************************/ +void jsonobj_init(zbx_jsonobj_t *obj, zbx_json_type_t type) +{ + obj->type = type; + + switch (type) + { + case ZBX_JSON_TYPE_ARRAY: + zbx_vector_jsonobj_ptr_create(&obj->data.array); + break; + case ZBX_JSON_TYPE_OBJECT: + zbx_hashset_create(&obj->data.object, 0, jsonobj_el_hash, jsonobj_el_compare); + break; + default: + memset(&obj->data, 0, sizeof(obj->data)); + break; + } + + obj->index = NULL; +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object index element * + * * + * Parameters: v - [IN] the json index element * + * * + ******************************************************************************/ +static void jsonobj_index_el_clear(void *v) +{ + zbx_jsonobj_index_el_t *el = (zbx_jsonobj_index_el_t *)v; + int i; + + zbx_free(el->value); + for (i = 0; i < el->objects.values_num; i++) + { + zbx_free(el->objects.values[i].name); + + if (0 != el->objects.values[i].external) + { + zbx_jsonobj_clear(el->objects.values[i].value); + zbx_free(el->objects.values[i].value); + } + } + + zbx_vector_jsonobj_ref_destroy(&el->objects); +} + +/****************************************************************************** + * * + * Purpose: initialize json object index * + * * + * Parameters: obj - [IN/OUT] the json object * + * path - [IN] the indexed relative path * + * * + ******************************************************************************/ +void jsonobj_init_index(zbx_jsonobj_t *obj, const char *path) +{ + obj->index = (zbx_jsonobj_index_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_index_t)); + obj->index->path = zbx_strdup(NULL, path); + zbx_hashset_create_ext(&obj->index->objects, 0, jsonobj_index_el_hash, jsonobj_index_el_compare, + jsonobj_index_el_clear, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, + ZBX_DEFAULT_MEM_FREE_FUNC); +} + +/****************************************************************************** + * * + * Purpose: set string value to json object * + * * + ******************************************************************************/ +void jsonobj_set_string(zbx_jsonobj_t *obj, char *str) +{ + obj->type = ZBX_JSON_TYPE_STRING; + obj->data.string = str; +} + +/****************************************************************************** + * * + * Purpose: set numeric value to json object * + * * + ******************************************************************************/ +void jsonobj_set_number(zbx_jsonobj_t *obj, double number) +{ + obj->type = ZBX_JSON_TYPE_NUMBER; + obj->data.number = number; +} + +/****************************************************************************** + * * + * Purpose: set true value to json object * + * * + ******************************************************************************/ +void jsonobj_set_true(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_TRUE; +} + +/****************************************************************************** + * * + * Purpose: set false value to json object * + * * + ******************************************************************************/ +void jsonobj_set_false(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_FALSE; +} + +/****************************************************************************** + * * + * Purpose: set null value to json object * + * * + ******************************************************************************/ +void jsonobj_set_null(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_NULL; +} + +/****************************************************************************** + * * + * Purpose: initialize json object element * + * * + ******************************************************************************/ +void jsonobj_el_init(zbx_jsonobj_el_t *el) +{ + el->name = NULL; + jsonobj_init(&el->value, ZBX_JSON_TYPE_UNKNOWN); +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object element * + * * + ******************************************************************************/ +void jsonobj_el_clear(zbx_jsonobj_el_t *el) +{ + zbx_free(el->name); + zbx_jsonobj_clear(&el->value); +} + +/****************************************************************************** + * * + * Purpose: free json object * + * * + ******************************************************************************/ +static void jsonobj_free(zbx_jsonobj_t *obj) +{ + zbx_jsonobj_clear(obj); + zbx_free(obj); +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object * + * * + ******************************************************************************/ +void zbx_jsonobj_clear(zbx_jsonobj_t *obj) +{ + zbx_jsonobj_el_t *el; + zbx_hashset_iter_t iter; + + switch (obj->type) + { + case ZBX_JSON_TYPE_STRING: + zbx_free(obj->data.string); + break; + case ZBX_JSON_TYPE_ARRAY: + zbx_vector_jsonobj_ptr_clear_ext(&obj->data.array, jsonobj_free); + zbx_vector_jsonobj_ptr_destroy(&obj->data.array); + break; + case ZBX_JSON_TYPE_OBJECT: + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + zbx_free(el->name); + zbx_jsonobj_clear(&el->value); + } + zbx_hashset_destroy(&obj->data.object); + break; + default: + break; + } + + if (NULL != obj->index) + { + zbx_free(obj->index->path); + zbx_hashset_destroy(&obj->index->objects); + zbx_free(obj->index); + } +} + +/****************************************************************************** + * * + * Purpose: convert json object to text format * + * * + ******************************************************************************/ +int zbx_jsonobj_to_string(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj) +{ + char *tmp, buf[32], delim; + int i; + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; + + switch (obj->type) + { + case ZBX_JSON_TYPE_TRUE: + zbx_strcpy_alloc(str, str_alloc, str_offset, "true"); + break; + case ZBX_JSON_TYPE_FALSE: + zbx_strcpy_alloc(str, str_alloc, str_offset, "false"); + break; + case ZBX_JSON_TYPE_NULL: + zbx_strcpy_alloc(str, str_alloc, str_offset, "null"); + break; + case ZBX_JSON_TYPE_STRING: + tmp = zbx_strdup(NULL, obj->data.string); + zbx_json_escape(&tmp); + zbx_snprintf_alloc(str, str_alloc, str_offset, "\"%s\"", tmp); + zbx_free(tmp); + break; + case ZBX_JSON_TYPE_NUMBER: + zbx_print_double(buf, sizeof(buf), obj->data.number); + zbx_strcpy_alloc(str, str_alloc, str_offset, buf); + break; + case ZBX_JSON_TYPE_ARRAY: + delim = '['; + for (i = 0; i < obj->data.array.values_num; i++) + { + zbx_chrcpy_alloc(str, str_alloc, str_offset, delim); + delim = ','; + zbx_jsonobj_to_string(str, str_alloc, str_offset, obj->data.array.values[i]); + } + zbx_chrcpy_alloc(str, str_alloc, str_offset, ']'); + break; + case ZBX_JSON_TYPE_OBJECT: + delim = '{'; + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + zbx_chrcpy_alloc(str, str_alloc, str_offset, delim); + delim = ','; + + tmp = zbx_strdup(NULL, el->name); + zbx_json_escape(&tmp); + zbx_snprintf_alloc(str, str_alloc, str_offset, "\"%s\"", tmp); + zbx_chrcpy_alloc(str, str_alloc, str_offset, ':'); + zbx_free(tmp); + + zbx_jsonobj_to_string(str, str_alloc, str_offset, &el->value); + } + zbx_chrcpy_alloc(str, str_alloc, str_offset, '}'); + break; + default: + zbx_set_json_strerror("unknown json object with type: %u", obj->type); + return FAIL; + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Purpose: parses json formatted data into json object structure * + * * + ******************************************************************************/ +int zbx_jsonobj_open(const char *data, zbx_jsonobj_t *obj) +{ + int ret = FAIL; + char *error = NULL; + + switch (*data) + { + case '{': + if (0 == json_parse_object(data, obj, &error)) + goto out; + break; + case '[': + if (0 == json_parse_array(data, obj, &error)) + goto out; + break; + default: + /* not json data, failing */ + jsonobj_init(obj, ZBX_JSON_TYPE_UNKNOWN); + (void)json_error("invalid object format, expected opening character '{' or '['", data, &error); + goto out; + } + + ret = SUCCEED; +out: + if (FAIL == ret) + { + zbx_jsonobj_clear(obj); + zbx_set_json_strerror("%s", error); + zbx_free(error); + } + + return ret; +} diff --git a/src/libs/zbxjson/jsonobj.h b/src/libs/zbxjson/jsonobj.h new file mode 100644 index 00000000000..dfd12df6b9b --- /dev/null +++ b/src/libs/zbxjson/jsonobj.h @@ -0,0 +1,56 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#ifndef ZABBIX_JSONOBJ_H +#define ZABBIX_JSONOBJ_H + +#include "zbxjson.h" + +typedef struct +{ + char *name; + zbx_jsonobj_t *value; + unsigned char external; /* 1 - the reference is to an external object. */ + /* 0 - the reference is to a local object which must be freed */ + /* when reference is destroyed */ +} +zbx_jsonobj_ref_t; + +ZBX_VECTOR_DECL(jsonobj_ref, zbx_jsonobj_ref_t) + +typedef struct +{ + char *value; /* the value found at indexed path */ + zbx_vector_jsonobj_ref_t objects; /* the objects matching value at indexed path */ +} +zbx_jsonobj_index_el_t; + +void jsonobj_init(zbx_jsonobj_t *obj, zbx_json_type_t type); + +void jsonobj_el_init(zbx_jsonobj_el_t *el); +void jsonobj_init_index(zbx_jsonobj_t *obj, const char *path); +void jsonobj_el_clear(zbx_jsonobj_el_t *el); + +void jsonobj_set_string(zbx_jsonobj_t *obj, char *str); +void jsonobj_set_number(zbx_jsonobj_t *obj, double number); +void jsonobj_set_true(zbx_jsonobj_t *obj); +void jsonobj_set_false(zbx_jsonobj_t *obj); +void jsonobj_set_null(zbx_jsonobj_t *obj); + +#endif diff --git a/src/libs/zbxjson/jsonpath.c b/src/libs/zbxjson/jsonpath.c index fdb7234004e..fc81cf646bc 100644 --- a/src/libs/zbxjson/jsonpath.c +++ b/src/libs/zbxjson/jsonpath.c @@ -19,27 +19,21 @@ #include "jsonpath.h" -#include "common.h" #include "zbxregexp.h" -#include "zbxjson.h" +#include "zbxvariant.h" #include "json.h" #include "json_parser.h" -#include "zbxvariant.h" +#include "jsonobj.h" typedef struct { - char *name; - const char *value; + zbx_jsonobj_t *root; /* the root object */ + zbx_jsonpath_t *path; + unsigned char found; /* set to 1 when one object was matched and */ + /* no more matches are required */ + zbx_vector_jsonobj_ref_t objects; /* the matched objects */ } -zbx_json_element_t; - -ZBX_VECTOR_DECL(json, zbx_json_element_t) -ZBX_VECTOR_IMPL(json, zbx_json_element_t) - -static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects); -static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects); +zbx_jsonpath_context_t; typedef struct { @@ -48,6 +42,10 @@ typedef struct } zbx_jsonpath_token_def_t; +static int jsonpath_query_object(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth); +static int jsonpath_query_array(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *array, int path_depth); +static int jsonpath_str_copy_value(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj); + /* define token groups and precedence */ static zbx_jsonpath_token_def_t jsonpath_tokens[] = { {0, 0}, @@ -73,6 +71,7 @@ static zbx_jsonpath_token_def_t jsonpath_tokens[] = { {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7} /* ZBX_JSONPATH_TOKEN_OP_REGEXP */ }; + static int jsonpath_token_precedence(int type) { return jsonpath_tokens[type].precedence; @@ -83,31 +82,118 @@ static int jsonpath_token_group(int type) return jsonpath_tokens[type].group; } -/* json element vector support */ -static void zbx_vector_json_add_element(zbx_vector_json_t *elements, const char *name, const char *value) +/****************************************************************************** + * * + * Purpose: add external json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * name - [IN] the json object name or array index * + * value - [IN] the json object * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add_object(zbx_vector_jsonobj_ref_t *refs, const char *name, + zbx_jsonobj_t *value) +{ + zbx_jsonobj_ref_t ref; + + ref.name = zbx_strdup(NULL, name); + ref.external = 1; + + ref.value = value; + zbx_vector_jsonobj_ref_append(refs, ref); +} + +/****************************************************************************** + * * + * Purpose: add internal json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * name - [IN] the json object name or array index * + * str - [IN] the string value of the object * + * * + * Comments: This function will create json object and add internal reference,* + * meaning the object will be destroyed together with its reference.* + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add_string(zbx_vector_jsonobj_ref_t *refs, const char *name, + const char *str) { - zbx_json_element_t el; + zbx_jsonobj_ref_t ref; + + ref.name = zbx_strdup(NULL, name); + ref.external = 0; + + ref.value = (zbx_jsonobj_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + jsonobj_init(ref.value, ZBX_JSON_TYPE_STRING); + jsonobj_set_string(ref.value, zbx_strdup(NULL, str)); - el.name = zbx_strdup(NULL, name); - el.value = value; - zbx_vector_json_append(elements, el); + zbx_vector_jsonobj_ref_append(refs, ref); } -static void zbx_vector_json_copy(zbx_vector_json_t *dst, const zbx_vector_json_t *src) +/****************************************************************************** + * * + * Purpose: add a copy of json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * ref - [IN] the json object reference * + * * + * Comments: For internal references a new internal json object will be * + * created. * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add(zbx_vector_jsonobj_ref_t *refs, zbx_jsonobj_ref_t *ref) +{ + if (0 != ref->external) + zbx_vector_jsonobj_ref_add_object(refs, ref->name, ref->value); + else + zbx_vector_jsonobj_ref_add_string(refs, ref->name, ref->value->data.string); +} + +/****************************************************************************** + * * + * Purpose: copy json object references from one vector to other * + * * + * Parameters: dst - [IN/OUT] the destination json object reference vector * + * src - [IN] the source json object reference vector * + * * + * Comments: For internal references a new internal json object will be * + * created. * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_copy(zbx_vector_jsonobj_ref_t *dst, const zbx_vector_jsonobj_ref_t *src) { int i; for (i = 0; i < src->values_num; i++) - zbx_vector_json_add_element(dst, src->values[i].name, src->values[i].value); + { + if (0 != src->values[i].external) + zbx_vector_jsonobj_ref_add_object(dst, src->values[i].name, src->values[i].value); + else + zbx_vector_jsonobj_ref_add_string(dst, src->values[i].name, src->values[i].value->data.string); + } } -static void zbx_vector_json_clear_ext(zbx_vector_json_t *elements) +/****************************************************************************** + * * + * Purpose: free resources allocated by json object reference * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_clear_ext(zbx_vector_jsonobj_ref_t *refs) { int i; - for (i = 0; i < elements->values_num; i++) - zbx_free(elements->values[i].name); - zbx_vector_json_clear(elements); + for (i = 0; i < refs->values_num; i++) + { + zbx_jsonobj_ref_t *ref = &refs->values[i]; + + zbx_free(ref->name); + if (0 == ref->external) + { + zbx_jsonobj_clear(ref->value); + zbx_free(ref->value); + } + } + zbx_vector_jsonobj_ref_clear(refs); } /****************************************************************************** @@ -214,7 +300,62 @@ static void jsonpath_list_free(zbx_jsonpath_list_node_t *list) /****************************************************************************** * * - * Purpose: create jsonpath and compile json path * + * Purpose: append array index to list * + * * + ******************************************************************************/ +static zbx_jsonpath_list_node_t *jsonpath_list_append_index(zbx_jsonpath_list_node_t *head, int index, + int check_duplicate) +{ + zbx_jsonpath_list_node_t *node; + + if (0 != check_duplicate) + { + for (node = head; NULL != node; node = node->next) + { + int query_index; + + memcpy(&query_index, node->data, sizeof(query_index)); + if (query_index == index) + return head; + } + } + + node = jsonpath_list_create_node(sizeof(int)); + node->next = head; + memcpy(node->data, &index, sizeof(int)); + + return node; +} + +/****************************************************************************** + * * + * Purpose: append name to list * + * * + ******************************************************************************/ +static zbx_jsonpath_list_node_t *jsonpath_list_append_name(zbx_jsonpath_list_node_t *head, const char *name, size_t len) +{ + zbx_jsonpath_list_node_t *node, *new_node; + + new_node = jsonpath_list_create_node(len + 1); + jsonpath_unquote(new_node->data, name, len + 1); + + for (node = head; NULL != node; node = node->next) + { + if (0 == strcmp((char *)new_node->data, (char *)node->data)) + { + zbx_free(new_node); + return head; + } + } + + new_node->next = head; + + return new_node; +} + +/****************************************************************************** + * * + * Purpose: create jsonpath structure and compile json path * * * ******************************************************************************/ static zbx_jsonpath_t *jsonpath_create_token_jsonpath(const char *text, size_t len) @@ -256,8 +397,6 @@ out: } /****************************************************************************** - * * - * Function: jsonpath_create_token * * * * Purpose: create jsonpath expression token * * * @@ -265,10 +404,12 @@ out: * expression - [IN] the expression * * loc - [IN] the token location in the expression * * * - * Return value: The created token (must be freed by the caller). * + * Return value: The created token (must be freed by the caller) or * + * NULL in the case of error. * * * ******************************************************************************/ -static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *expression, const zbx_strloc_t *loc) +static zbx_jsonpath_token_t *jsonpath_create_token(unsigned char type, const char *expression, + const zbx_strloc_t *loc) { zbx_jsonpath_token_t *token; @@ -303,6 +444,11 @@ static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *express return token; } +/****************************************************************************** + * * + * Purpose: free jsonpath expression token * + * * + ******************************************************************************/ static void jsonpath_token_free(zbx_jsonpath_token_t *token) { zbx_free(token->text); @@ -336,16 +482,21 @@ static void jsonpath_reserve(zbx_jsonpath_t *jsonpath, int num) jsonpath->segments_alloc *= 2; jsonpath->segments = (zbx_jsonpath_segment_t *)zbx_realloc(jsonpath->segments, - sizeof(zbx_jsonpath_segment_t) * jsonpath->segments_alloc); + sizeof(zbx_jsonpath_segment_t) * (size_t)jsonpath->segments_alloc); /* Initialize the memory allocated for new segments, as parser can set */ /* detached flag for the next segment, so the memory cannot be initialized */ /* when creating a segment. */ memset(jsonpath->segments + old_alloc, 0, - (jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t)); + (size_t)(jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t)); } } +/****************************************************************************** + * * + * Purpose: free resource allocated by jsonpath segment * + * * + ******************************************************************************/ static void jsonpath_segment_clear(zbx_jsonpath_segment_t *segment) { switch (segment->type) @@ -453,7 +604,7 @@ static int jsonpath_next(const char **pnext) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_parse_substring(const char *start, int *len) +static int jsonpath_parse_substring(const char *start, size_t *len) { const char *ptr; char quotes; @@ -462,7 +613,7 @@ static int jsonpath_parse_substring(const char *start, int *len) { if (*ptr == quotes) { - *len = ptr - start + 1; + *len = (size_t)(ptr - start + 1); return SUCCEED; } @@ -491,7 +642,7 @@ static int jsonpath_parse_substring(const char *start, int *len) * jsonpath filter expressions. * * * ******************************************************************************/ -static int jsonpath_parse_path(const char *start, int *len) +static int jsonpath_parse_path(const char *start, size_t *len) { const char *ptr = start + 1; @@ -501,7 +652,7 @@ static int jsonpath_parse_path(const char *start, int *len) return FAIL; } - *len = ptr - start; + *len = (size_t)(ptr - start); return SUCCEED; } @@ -516,7 +667,7 @@ static int jsonpath_parse_path(const char *start, int *len) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_parse_number(const char *start, int *len) +static int jsonpath_parse_number(const char *start, size_t *len) { const char *ptr = start; char *end; @@ -536,7 +687,7 @@ static int jsonpath_parse_number(const char *start, int *len) if (ptr != end || HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno) return FAIL; - *len = (int)(ptr - start); + *len = (size_t)(ptr - start); return SUCCEED; } @@ -556,14 +707,14 @@ static int jsonpath_parse_number(const char *start, int *len) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_expression_next_token(const char *expression, int pos, int prev_group, +static int jsonpath_expression_next_token(const char *expression, size_t pos, int prev_group, zbx_jsonpath_token_type_t *type, zbx_strloc_t *loc) { - int len; + size_t len; const char *ptr = expression + pos; SKIP_WHITESPACE(ptr); - loc->l = ptr - expression; + loc->l = (size_t)(ptr - expression); switch (*ptr) { @@ -695,6 +846,139 @@ out: return zbx_jsonpath_error(ptr); } +/* value types on index stack */ +typedef enum +{ + ZBX_JSONPATH_CONST = 1, /* constant value - string or number */ + ZBX_JSONPATH_VALUE, /* result of an operation after which cannot be used in index */ + ZBX_JSONPATH_PATH, /* relative jsonpath - @.a.b.c */ + ZBX_JSONPATH_PATH_OP /* result of an operation with jsonpath which still can be used in index */ +} +zbx_jsonpath_index_value_type_t; + +typedef struct +{ + zbx_jsonpath_index_value_type_t type; + zbx_jsonpath_token_t *index_token; + zbx_jsonpath_token_t *value_token; +} +zbx_jsonpath_index_value_t; + +ZBX_VECTOR_DECL(jpi_value, zbx_jsonpath_index_value_t) +ZBX_VECTOR_IMPL(jpi_value, zbx_jsonpath_index_value_t) + +/****************************************************************************** + * * + * Purpose: analyze expression and set indexing fields if possible * + * * + * Comments: Expression can be indexed if it contains relative json path * + * comparison with constant that is used in and operations. * + * This is tested by doing a pseudo evaluation by operand types * + * and checking the result type. * + * * + * So expressions like ?(@.a.b == 1), ?(@.a == "A" and @.b == "B") * + * can be indexed (by @.a.b and by @.a) while expressions like * + * ?(@.a == @.b), ?(@.a == "A" or @.b == "B") cannot. * + * * + ******************************************************************************/ +static void jsonpath_expression_prepare_index(zbx_jsonpath_expression_t *exp) +{ + int i; + zbx_vector_jpi_value_t stack; + zbx_jsonpath_index_value_t *left, *right; + + zbx_vector_jpi_value_create(&stack); + + for (i = 0; i < exp->tokens.values_num; i++) + { + zbx_jsonpath_token_t *token = (zbx_jsonpath_token_t *)exp->tokens.values[i]; + zbx_jsonpath_index_value_t jpi = {0}; + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_OP_NOT: + if (1 > stack.values_num) + goto out; + stack.values[stack.values_num - 1].type = ZBX_JSONPATH_VALUE; + stack.values[stack.values_num - 1].index_token = NULL; + stack.values[stack.values_num - 1].value_token = NULL; + continue; + case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: + jpi.index_token = token; + jpi.type = ZBX_JSONPATH_PATH; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: + jpi.type = ZBX_JSONPATH_VALUE; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + case ZBX_JSONPATH_TOKEN_CONST_STR: + case ZBX_JSONPATH_TOKEN_CONST_NUM: + jpi.value_token = token; + jpi.type = ZBX_JSONPATH_CONST; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + } + + if (2 > stack.values_num) + goto out; + + left = &stack.values[stack.values_num - 2]; + right = &stack.values[stack.values_num - 1]; + stack.values_num--; + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_OP_EQ: + if ((ZBX_JSONPATH_PATH == left->type || ZBX_JSONPATH_PATH == right->type) && + (ZBX_JSONPATH_CONST == left->type || ZBX_JSONPATH_CONST == right->type)) + { + left->type = ZBX_JSONPATH_PATH_OP; + + if (ZBX_JSONPATH_CONST == right->type) + left->value_token = right->value_token; + else + left->index_token = right->index_token; + } + else + left->type = ZBX_JSONPATH_VALUE; + continue; + case ZBX_JSONPATH_TOKEN_OP_AND: + if (ZBX_JSONPATH_PATH == left->type) + left->type = ZBX_JSONPATH_VALUE; + + if (ZBX_JSONPATH_PATH == right->type) + right->type = ZBX_JSONPATH_VALUE; + + if (ZBX_JSONPATH_PATH_OP == left->type && ZBX_JSONPATH_PATH_OP == right->type) + continue; + + if ((ZBX_JSONPATH_PATH_OP == left->type || ZBX_JSONPATH_PATH_OP == right->type) && + (ZBX_JSONPATH_VALUE == left->type || ZBX_JSONPATH_VALUE == right->type)) + { + if (ZBX_JSONPATH_PATH_OP != left->type) + *left = *right; + } + else + left->type = ZBX_JSONPATH_VALUE; + continue; + default: + left->type = ZBX_JSONPATH_VALUE; + left->index_token = NULL; + left->value_token = NULL; + break; + } + } + + if (1 == stack.values_num && ZBX_JSONPATH_PATH_OP == stack.values[0].type) + { + exp->index_token = stack.values[0].index_token; + exp->value_token = stack.values[0].value_token; + } +out: + zbx_vector_jpi_value_destroy(&stack); +} + /****************************************************************************** * * * Purpose: parse jsonpath filter expression in format * @@ -887,6 +1171,10 @@ out: zbx_vector_ptr_create(&segment->data.expression.tokens); zbx_vector_ptr_append_array(&segment->data.expression.tokens, output.values, output.values_num); + /* index only json path that has been definite until this point */ + if (0 != jsonpath->definite) + jsonpath_expression_prepare_index(&segment->data.expression); + jsonpath->definite = 0; } cleanup: @@ -940,18 +1228,13 @@ static int jsonpath_parse_names(const char *list, zbx_jsonpath_t *jsonpath, cons } else if (*start == *end) { - zbx_jsonpath_list_node_t *node; - if (start + 1 == end) { ret = zbx_jsonpath_error(start); goto out; } - node = jsonpath_list_create_node(end - start + 1); - jsonpath_unquote(node->data, start, end - start + 1); - node->next = head; - head = node; + head = jsonpath_list_append_name(head, start, (size_t)(end - start)); parsed_name = 1; start = NULL; } @@ -1058,19 +1341,13 @@ static int jsonpath_parse_indexes(const char *list, zbx_jsonpath_t *jsonpath, co if (NULL != start) { - int value; - if ('-' == *start && end == start + 1) { ret = zbx_jsonpath_error(start); goto out; } - node = jsonpath_list_create_node(sizeof(int)); - node->next = head; - head = node; - value = atoi(start); - memcpy(node->data, &value, sizeof(int)); + head = jsonpath_list_append_index(head, atoi(start), type == ZBX_JSONPATH_SEGMENT_MATCH_LIST); start = NULL; parsed_index = 1; } @@ -1234,7 +1511,7 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat { zbx_jsonpath_segment_t *segment; const char *ptr; - int len; + size_t len; segment = &jsonpath->segments[jsonpath->segments_num]; jsonpath->segments_num++; @@ -1250,6 +1527,8 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat for (ptr = start; 0 != isalnum((unsigned char)*ptr) || '_' == *ptr;) ptr++; + len = (size_t)(ptr - start); + if ('(' == *ptr) { const char *end = ptr + 1; @@ -1257,28 +1536,28 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat SKIP_WHITESPACE(end); if (')' == *end) { - if (ZBX_CONST_STRLEN("min") == ptr - start && 0 == strncmp(start, "min", ptr - start)) + if (ZBX_CONST_STRLEN("min") == len && 0 == strncmp(start, "min", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_MIN; } - else if (ZBX_CONST_STRLEN("max") == ptr - start && 0 == strncmp(start, "max", ptr - start)) + else if (ZBX_CONST_STRLEN("max") == len && 0 == strncmp(start, "max", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_MAX; } - else if (ZBX_CONST_STRLEN("avg") == ptr - start && 0 == strncmp(start, "avg", ptr - start)) + else if (ZBX_CONST_STRLEN("avg") == len && 0 == strncmp(start, "avg", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_AVG; } - else if (ZBX_CONST_STRLEN("length") == ptr - start && 0 == strncmp(start, "length", ptr - start)) + else if (ZBX_CONST_STRLEN("length") == len && 0 == strncmp(start, "length", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_LENGTH; } - else if (ZBX_CONST_STRLEN("first") == ptr - start && 0 == strncmp(start, "first", ptr - start)) + else if (ZBX_CONST_STRLEN("first") == len && 0 == strncmp(start, "first", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_FIRST; jsonpath->first_match = 1; } - else if (ZBX_CONST_STRLEN("sum") == ptr - start && 0 == strncmp(start, "sum", ptr - start)) + else if (ZBX_CONST_STRLEN("sum") == len && 0 == strncmp(start, "sum", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_SUM; } @@ -1291,7 +1570,7 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat } } - if (0 < (len = ptr - start)) + if (0 < len) { segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST; segment->data.list.type = ZBX_JSONPATH_LIST_NAME; @@ -1329,125 +1608,86 @@ static int jsonpath_parse_name_reference(const char *start, zbx_jsonpath_t *json return SUCCEED; } -/****************************************************************************** - * * - * Purpose: convert a pointer to an object/array/value in json data to * - * json parse structure * - * * - * Parameters: pnext - [IN] a pointer to object/array/value data * - * jp - [OUT] json parse data with start/end set * - * * - * Return value: SUCCEED - pointer was converted successfully * - * FAIL - otherwise * - * * - ******************************************************************************/ -static int jsonpath_pointer_to_jp(const char *pnext, struct zbx_json_parse *jp) -{ - if ('[' == *pnext || '{' == *pnext) - { - return zbx_json_brackets_open(pnext, jp); - } - else - { - jp->start = pnext; - jp->end = pnext + json_parse_value(pnext, NULL) - 1; - return SUCCEED; - } -} - /****************************************************************************** * * * Purpose: perform the rest of jsonpath query on json data * * * - * Parameters: jp_root - [IN] the document root * - * pnext - [IN] a pointer to object/array/value in json data * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the data were queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_contents(const struct zbx_json_parse *jp_root, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_query_contents(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) { - struct zbx_json_parse jp_child; + int ret; - switch (*pnext) + switch (obj->type) { - case '{': - if (FAIL == zbx_json_brackets_open(pnext, &jp_child)) - return FAIL; - - return jsonpath_query_object(jp_root, &jp_child, jsonpath, path_depth, done, objects); - case '[': - if (FAIL == zbx_json_brackets_open(pnext, &jp_child)) - return FAIL; - - return jsonpath_query_array(jp_root, &jp_child, jsonpath, path_depth, done, objects); + case ZBX_JSON_TYPE_OBJECT: + ret = jsonpath_query_object(ctx, obj, path_depth); + break; + case ZBX_JSON_TYPE_ARRAY: + ret = jsonpath_query_array(ctx, obj, path_depth); + break; + default: + ret = SUCCEED; } - return SUCCEED; + + return ret; } /****************************************************************************** * * * Purpose: query next segment * * * - * Parameters: jp_root - [IN] the document root * + * Parameters: ctx - [IN] the jsonpath query context * * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to object/array/value in json data * - * jsonpath - [IN] the jsonpath * + * obj - [IN] the current json object * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the segment was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_next_segment(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_query_next_segment(zbx_jsonpath_context_t *ctx, const char *name, zbx_jsonobj_t *obj, + int path_depth) { /* check if jsonpath end has been reached, so we have found matching data */ /* (functions are processed afterwards) */ - if (++path_depth == jsonpath->segments_num || - ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath->segments[path_depth].type) + if (++path_depth == ctx->path->segments_num || + ZBX_JSONPATH_SEGMENT_FUNCTION == ctx->path->segments[path_depth].type) { - if (1 == jsonpath->first_match) - *done = 1; + if (1 == ctx->path->first_match) + ctx->found = 1; - zbx_vector_json_add_element(objects, name, pnext); + zbx_vector_jsonobj_ref_add_object(&ctx->objects, name, obj); return SUCCEED; } - /* continue by matching found data against the rest of jsonpath segments */ - return jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, done, objects); + /* continue by matching found object against the rest of jsonpath segments */ + return jsonpath_query_contents(ctx, obj, path_depth); } /****************************************************************************** * * - * Purpose: match object value name against jsonpath segment name list * + * Purpose: match object contents against jsonpath segment name list * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to object value with the specified * - * name * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] parent json object * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_match_name(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; const zbx_jsonpath_list_node_t *node; + zbx_jsonobj_el_t el_local, *el; /* object contents can match only name list */ if (ZBX_JSONPATH_LIST_NAME != segment->data.list.type) @@ -1455,14 +1695,11 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char for (node = segment->data.list.values; NULL != node; node = node->next) { - if (0 == strcmp(name, node->data)) + el_local.name = (char *)node->data; + if (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_search(&parent->data.object, &el_local))) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, - objects)) - { + if (FAIL == jsonpath_query_next_segment(ctx, el->name, &el->value, path_depth)) return FAIL; - } - break; } } @@ -1473,8 +1710,8 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char * * * Purpose: extract value from json data by the specified path * * * - * Parameters: jp - [IN] the parent object * - * path - [IN] the jsonpath (definite) * + * Parameters: obj - [IN] the parent object * + * path - [IN] the jsonpath * * value - [OUT] the extracted value * * * * Return value: SUCCEED - the value was extracted successfully * @@ -1482,28 +1719,29 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char * extract * * * ******************************************************************************/ -static int jsonpath_extract_value(const struct zbx_json_parse *jp, const zbx_jsonpath_t *path, - zbx_variant_t *value) +static int jsonpath_extract_value(zbx_jsonobj_t *obj, zbx_jsonpath_t *path, zbx_variant_t *value) { - struct zbx_json_parse jp_child; - char *data = NULL; - size_t data_alloc = 0; - int ret = FAIL; + int ret = FAIL; + zbx_jsonpath_context_t ctx; - if (FAIL == json_open_path(jp, path, &jp_child)) - goto out; + ctx.path = path; + ctx.found = 0; + ctx.root = obj; + zbx_vector_jsonobj_ref_create(&ctx.objects); - if (NULL == zbx_json_decodevalue_dyn(jp_child.start, &data, &data_alloc, NULL)) + if (SUCCEED == jsonpath_query_contents(&ctx, obj, 0) && 0 != ctx.objects.values_num) { - size_t len = jp_child.end - jp_child.start + 2; + char *str = NULL; + size_t str_alloc = 0, str_offset = 0; - data = (char *)zbx_malloc(NULL, len); - zbx_strlcpy(data, jp_child.start, len); + jsonpath_str_copy_value(&str, &str_alloc, &str_offset, ctx.objects.values[0].value); + zbx_variant_set_str(value, str); + ret = SUCCEED; } - zbx_variant_set_str(value, data); - ret = SUCCEED; -out: + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + zbx_vector_jsonobj_ref_destroy(&ctx.objects); + return ret; } @@ -1520,15 +1758,9 @@ out: ******************************************************************************/ static char *jsonpath_expression_to_str(zbx_jsonpath_expression_t *expression) { - int i; - char *str = NULL; - size_t str_alloc = 0, str_offset = 0; - - if (0 == expression->tokens.values_num) - { - THIS_SHOULD_NEVER_HAPPEN; - return zbx_strdup(NULL, "?"); - } + int i; + char *str = NULL; + size_t str_alloc = 0, str_offset = 0; for (i = 0; i < expression->tokens.values_num; i++) { @@ -1693,38 +1925,125 @@ static int jsonpath_regexp_match(const char *text, const char *pattern, double * return SUCCEED; } +/****************************************************************************** + * * + * Purpose: add matched object to the index * + * * + * Parameters: index - [IN] the parent object index * + * name - [IN] the name of objec to add to index * + * obj - [IN] the object to add to index * + * value - [IN] the object matched by index path * + * * + ******************************************************************************/ +static void jsonpath_index_append_result(zbx_hashset_t *index, const char *name, zbx_jsonobj_t *obj, + zbx_jsonobj_t *value) +{ + zbx_jsonobj_index_el_t el_local = {.value = NULL}, *el; + size_t value_alloc = 0, value_offset = 0; + zbx_jsonobj_ref_t ref; + + jsonpath_str_copy_value(&el_local.value, &value_alloc, &value_offset, value); + + if (NULL == (el = (zbx_jsonobj_index_el_t *)zbx_hashset_search(index, &el_local))) + { + el = (zbx_jsonobj_index_el_t *)zbx_hashset_insert(index, &el_local, sizeof(el_local)); + zbx_vector_jsonobj_ref_create(&el->objects); + } + else + zbx_free(el_local.value); + + ref.name = zbx_strdup(NULL, name); + ref.value = obj; + ref.external = 0; + zbx_vector_jsonobj_ref_append(&el->objects, ref); +} + +/****************************************************************************** + * * + * Purpose: index json object using the expression token index path * + * * + * Parameters: obj - [IN] the object to index * + * index_token - [IN] the expression index token (relative path) * + * * + ******************************************************************************/ +static void jsonpath_create_index(zbx_jsonobj_t *obj, zbx_jsonpath_token_t *index_token) +{ + zbx_jsonpath_context_t ctx; + + jsonobj_init_index(obj, index_token->text); + + ctx.root = obj; + ctx.path = index_token->path; + zbx_vector_jsonobj_ref_create(&ctx.objects); + + if (ZBX_JSON_TYPE_OBJECT == obj->type) + { + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; + + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + ctx.found = 0; + if (SUCCEED == jsonpath_query_contents(&ctx, &el->value, 0) && 1 == ctx.objects.values_num) + { + jsonpath_index_append_result(&obj->index->objects, el->name, &el->value, + ctx.objects.values[0].value); + } + + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + } + } + else + { + int i; + + for (i = 0; i < obj->data.array.values_num; i++) + { + char name[MAX_ID_LEN + 1]; + zbx_jsonobj_t *value = obj->data.array.values[i]; + + zbx_snprintf(name, sizeof(name), "%d", i); + + ctx.found = 0; + if (SUCCEED == jsonpath_query_contents(&ctx, value, 0) && 1 == ctx.objects.values_num) + { + jsonpath_index_append_result(&obj->index->objects, name, value, + ctx.objects.values[0].value); + } + + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + } + } + + zbx_vector_jsonobj_ref_destroy(&ctx.objects); +} + /****************************************************************************** * * * Purpose: match json array element/object value against jsonpath expression * * * - * Parameters: jp_root - [IN] the document root * + * Parameters: ctx - [IN] the jsonpath query context * * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to array element/object value * - * jsonpath - [IN] the jsonpath * + * obj - [IN] the jsonobject to match * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_match_expression(zbx_jsonpath_context_t *ctx, const char *name, zbx_jsonobj_t *obj, + int path_depth) { - struct zbx_json_parse jp; zbx_vector_var_t stack; int i, ret = SUCCEED; zbx_jsonpath_segment_t *segment; zbx_variant_t value, *right; double res; - if (SUCCEED != jsonpath_pointer_to_jp(pnext, &jp)) - return FAIL; - zbx_vector_var_create(&stack); - segment = &jsonpath->segments[path_depth]; + segment = &ctx->path->segments[path_depth]; for (i = 0; i < segment->data.expression.tokens.values_num; i++) { @@ -1871,16 +2190,16 @@ static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const switch (token->type) { case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: - if (FAIL == jsonpath_extract_value(jp_root, token->path, &value)) + if (FAIL == jsonpath_extract_value(ctx->root, token->path, &value)) zbx_variant_set_none(&value); zbx_vector_var_append_ptr(&stack, &value); break; case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: /* relative path can be applied only to array or object */ - if ('[' != *jp.start && '{' != *jp.start) + if (ZBX_JSON_TYPE_ARRAY != obj->type && ZBX_JSON_TYPE_OBJECT != obj->type) goto out; - if (FAIL == jsonpath_extract_value(&jp, token->path, &value)) + if (FAIL == jsonpath_extract_value(obj, token->path, &value)) zbx_variant_set_none(&value); zbx_vector_var_append_ptr(&stack, &value); break; @@ -1916,7 +2235,7 @@ static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const jsonpath_variant_to_boolean(&stack.values[0]); if (SUCCEED != zbx_double_compare(stack.values[0].data.dbl, 0.0)) - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, objects); + ret = jsonpath_query_next_segment(ctx, name, obj, path_depth); out: for (i = 0; i < stack.values_num; i++) zbx_variant_clear(&stack.values[i]); @@ -1925,52 +2244,93 @@ out: return ret; } +/****************************************************************************** + * * + * Purpose: query indexed object fields for jsonpath segment match * + * * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object to query * + * path_depth - [IN] the jsonpath segment to match * + * * + * Return value: SUCCEED - the object was queried successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_match_indexed_expression(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) +{ + zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; + zbx_jsonobj_index_el_t index_local, *el; + + index_local.value = segment->data.expression.value_token->text; + + if (NULL != (el = (zbx_jsonobj_index_el_t *)zbx_hashset_search(&obj->index->objects, &index_local))) + { + int i; + + for (i = 0; i < el->objects.values_num; i++) + { + jsonpath_match_expression(ctx, el->objects.values[i].name, el->objects.values[i].value, + path_depth); + } + } + + return SUCCEED; +} + /****************************************************************************** * * * Purpose: query object fields for jsonpath segment match * * * - * Parameters: jp_root - [IN] the document root * - * jp - [IN] the json object to query * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object to query * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the object was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_query_object(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) { - const char *pnext = NULL; - char name[MAX_STRING_LEN]; const zbx_jsonpath_segment_t *segment; int ret = SUCCEED; + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; - segment = &jsonpath->segments[path_depth]; + segment = &ctx->path->segments[path_depth]; - while (NULL != (pnext = zbx_json_pair_next(jp, pnext, name, sizeof(name))) && SUCCEED == ret && 0 == *done) + if (ZBX_JSONPATH_SEGMENT_MATCH_LIST == segment->type) + { + ret = jsonpath_match_name(ctx, obj, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + } + else if (ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION == segment->type && NULL != segment->data.expression.index_token) + { + if (NULL == obj->index) + jsonpath_create_index(obj, segment->data.expression.index_token); + + if (0 == strcmp(obj->index->path, segment->data.expression.index_token->text)) + return jsonpath_match_indexed_expression(ctx, obj, path_depth); + } + + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter)) && SUCCEED == ret && + 0 == ctx->found) { switch (segment->type) { case ZBX_JSONPATH_SEGMENT_MATCH_ALL: - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, - objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_LIST: - ret = jsonpath_match_name(jp_root, name, pnext, jsonpath, path_depth, done, objects); + ret = jsonpath_query_next_segment(ctx, el->name, &el->value, path_depth); break; case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: - ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, done, - objects); + ret = jsonpath_match_expression(ctx, el->name, &el->value, path_depth); break; default: break; } if (1 == segment->detached) - ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, done, objects); + ret = jsonpath_query_contents(ctx, &el->value, path_depth); } return ret; @@ -1978,27 +2338,19 @@ static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const str /****************************************************************************** * * - * Purpose: match array element against segment index list * + * Purpose: array elements against segment index list * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] the json element name (index) * - * pnext - [IN] a pointer to an array element * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] the json array * * path_depth - [IN] the jsonpath segment to match * - * index - [IN] the array element index * - * elements_num - [IN] the total number of elements in array * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, unsigned char *done, - zbx_vector_json_t *objects) +static int jsonpath_match_index(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; const zbx_jsonpath_list_node_t *node; /* array contents can match only index list */ @@ -2011,14 +2363,20 @@ static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char memcpy(&query_index, node->data, sizeof(query_index)); - if ((query_index >= 0 && index == query_index) || index == elements_num + query_index) + if (0 > query_index) + query_index += parent->data.array.values_num; + + if (query_index >= 0 && query_index < parent->data.array.values_num) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, - objects)) + char name[MAX_ID_LEN + 1]; + + zbx_snprintf(name, sizeof(name), "%d", query_index); + + if (FAIL == jsonpath_query_next_segment(ctx, name, parent->data.array.values[query_index], + path_depth)) { return FAIL; } - break; } } @@ -2027,40 +2385,37 @@ static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char /****************************************************************************** * * - * Purpose: match array element against segment index range * + * Purpose: match array elements against segment index range * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] the json element name (index) * - * pnext - [IN] a pointer to an array element * - * jsonpath - [IN] the jsonpath * - * path_depth - [IN] the jsonpath segment to match * - * index - [IN] the array element index * - * elements_num - [IN] the total number of elements in array * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] the json array * + * path_depth - [IN] the jsonpath segment to match * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, unsigned char *done, - zbx_vector_json_t *objects) +static int jsonpath_match_range(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - int start_index, end_index; - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + int i, start_index, end_index, values_num; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; + values_num = parent->data.array.values_num; start_index = (0 != (segment->data.range.flags & 0x01) ? segment->data.range.start : 0); - end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : elements_num); + end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : values_num); if (0 > start_index) - start_index += elements_num; + start_index += values_num; if (0 > end_index) - end_index += elements_num; + end_index += values_num; - if (start_index <= index && end_index > index) + for (i = start_index; i < end_index; i++) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, objects)) + char name[MAX_ID_LEN + 1]; + + zbx_snprintf(name, sizeof(name), "%d", i); + + if (FAIL == jsonpath_query_next_segment(ctx, name, parent->data.array.values[i], path_depth)) return FAIL; } @@ -2069,62 +2424,69 @@ static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char /****************************************************************************** * * - * Purpose: query array elements for jsonpath segment match * + * Purpose: query json array for jsonpath segment match * * * - * Parameters: jp_root - [IN] the document root * - * jp - [IN] the json array to query * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * array - [IN] the json array to query * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the array was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_query_array(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *array, int path_depth) { - const char *pnext = NULL; - int index = 0, elements_num = 0, ret = SUCCEED; + int ret = SUCCEED, i; zbx_jsonpath_segment_t *segment; - segment = &jsonpath->segments[path_depth]; + segment = &ctx->path->segments[path_depth]; - while (NULL != (pnext = zbx_json_next(jp, pnext))) - elements_num++; + switch (segment->type) + { + case ZBX_JSONPATH_SEGMENT_MATCH_LIST: + ret = jsonpath_match_index(ctx, array, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + break; + case ZBX_JSONPATH_SEGMENT_MATCH_RANGE: + ret = jsonpath_match_range(ctx, array, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + break; + case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: + if (NULL != segment->data.expression.index_token) + { + if (NULL == array->index) + jsonpath_create_index(array, segment->data.expression.index_token); - while (NULL != (pnext = zbx_json_next(jp, pnext)) && SUCCEED == ret && 0 == *done) + if (0 == strcmp(array->index->path, segment->data.expression.index_token->text)) + return jsonpath_match_indexed_expression(ctx, array, path_depth); + } + break; + default: + break; + } + + for (i = 0; i < array->data.array.values_num && SUCCEED == ret && 0 == ctx->found; i++) { char name[MAX_ID_LEN + 1]; - zbx_snprintf(name, sizeof(name), "%d", index); + zbx_snprintf(name, sizeof(name), "%d", i); + switch (segment->type) { case ZBX_JSONPATH_SEGMENT_MATCH_ALL: - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, - objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_LIST: - ret = jsonpath_match_index(jp_root, name, pnext, jsonpath, path_depth, index, - elements_num, done, objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_RANGE: - ret = jsonpath_match_range(jp_root, name, pnext, jsonpath, path_depth, index, - elements_num, done, objects); + ret = jsonpath_query_next_segment(ctx, name, array->data.array.values[i], path_depth); break; case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: - ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, done, - objects); + ret = jsonpath_match_expression(ctx, name, array->data.array.values[i], path_depth); break; default: break; } if (1 == segment->detached) - ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, done, objects); - - index++; + ret = jsonpath_query_contents(ctx, array->data.array.values[i], path_depth); } return ret; @@ -2132,123 +2494,99 @@ static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const stru /****************************************************************************** * * - * Purpose: extract JSON element value from data * - * * - * Parameters: ptr - [IN] pointer to the element to extract * - * element - [OUT] the extracted element * + * Purpose: get numeric value from json data * * * - * Return value: SUCCEED - the element was extracted successfully * - * FAIL - the pointer was not pointing to a JSON element * + * Parameters: obj - [IN] json object * + * value - [OUT] the extracted value * * * - * Comments: String value element is unquoted, other elements are copied as * - * is. * + * Return value: SUCCEED - the value was extracted successfully * + * FAIL - the pointer was not pointing at numeric value * * * ******************************************************************************/ -static int jsonpath_extract_element(const char *ptr, char **element) +static int jsonpath_get_numeric_value(const zbx_jsonobj_t *obj, double *value) { - size_t element_size = 0; - - if (NULL == zbx_json_decodevalue_dyn(ptr, element, &element_size, NULL)) + switch (obj->type) { - struct zbx_json_parse jp; - - if (SUCCEED != zbx_json_brackets_open(ptr, &jp)) + case ZBX_JSON_TYPE_NUMBER: + *value = obj->data.number; + return SUCCEED; + case ZBX_JSON_TYPE_STRING: + if (SUCCEED == is_double(obj->data.string, value)) + return SUCCEED; + zbx_set_json_strerror("array value is not a number or out of range: %s", obj->data.string); + return FAIL; + default: + zbx_set_json_strerror("array value type is not a number or string"); return FAIL; - - *element = jsonpath_strndup(jp.start, jp.end - jp.start + 1); } - - return SUCCEED; } /****************************************************************************** * * - * Purpose: extract numeric value from json data * - * * - * Parameters: ptr - [IN] pointer to the value to extract * - * value - [OUT] the extracted value * + * Purpose: get value from json data * * * * Return value: SUCCEED - the value was extracted successfully * * FAIL - the pointer was not pointing at numeric value * * * ******************************************************************************/ -static int jsonpath_extract_numeric_value(const char *ptr, double *value) +static int jsonpath_str_copy_value(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj) { - char buffer[MAX_STRING_LEN]; - - if (NULL == zbx_json_decodevalue(ptr, buffer, sizeof(buffer), NULL) || - SUCCEED != is_double(buffer, value)) + switch (obj->type) { - zbx_set_json_strerror("array value is not a number or out of range starting with: %s", ptr); - return FAIL; + case ZBX_JSON_TYPE_STRING: + zbx_strcpy_alloc(str, str_alloc, str_offset, obj->data.string); + return SUCCEED; + break; + default: + return zbx_jsonobj_to_string(str, str_alloc, str_offset, obj); } - - return SUCCEED; } /****************************************************************************** * * * Purpose: apply jsonpath function to the extracted object list * * * - * Parameters: objects - [IN] the matched json elements (name, value) * + * Parameters: in - [IN] the matched objects * * type - [IN] the function type * - * definite_path - [IN] 1 - if the path is definite (pointing at * - * single object) * - * 0 - otherwise * - * output - [OUT] the output value * + * definite_path - [IN/OUT] 1 - if the path is definite (pointing * + * at single object) * + * 0 - otherwise * + * out - [OUT] the result objects * * * * Return value: SUCCEED - the function was applied successfully * * FAIL - invalid input data for the function or internal * * json error * * * ******************************************************************************/ -static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpath_function_type_t type, - int definite_path, char **output) +static int jsonpath_apply_function(const zbx_vector_jsonobj_ref_t *in, zbx_jsonpath_function_type_t type, + int *definite_path, zbx_vector_jsonobj_ref_t *out) { - int i, ret = FAIL; - zbx_vector_json_t objects_tmp; - double result; + int i, ret = FAIL; + double result; + zbx_vector_jsonobj_ref_t tmp; + char buffer[64]; - zbx_vector_json_create(&objects_tmp); + zbx_vector_jsonobj_ref_create(&tmp); if (ZBX_JSONPATH_FUNCTION_NAME == type) { - if (0 == objects->values_num) + if (0 == in->values_num) { zbx_set_json_strerror("cannot extract name from empty result"); goto out; } - /* For definite paths we have single output value, so return its name. */ - /* Otherwise return array of all output element names. */ - if (0 == definite_path) - { - struct zbx_json j; - - /* reserve some space for output json, 1k being large enough to satisfy most queries */ - zbx_json_initarray(&j, 1024); - for (i = 0; i < objects->values_num; i++) - zbx_json_addstring(&j, NULL, objects->values[i].name, ZBX_JSON_TYPE_STRING); - - zbx_json_close(&j); - *output = zbx_strdup(NULL, j.buffer); - zbx_json_clean(&j); - } - else - *output = zbx_strdup(NULL, objects->values[0].name); + for (i = 0; i < in->values_num; i++) + zbx_vector_jsonobj_ref_add_string(out, "", in->values[i].name); ret = SUCCEED; goto out; } /* convert definite path result to object array if possible */ - if (0 != definite_path) + if (0 != *definite_path) { - const char *pnext; - struct zbx_json_parse jp; - int index = 0; - - if (0 == objects->values_num || '[' != *objects->values[0].value) + if (0 == in->values_num || ZBX_JSON_TYPE_ARRAY != in->values[0].value->type) { /* all functions can be applied only to arrays */ /* attempt to apply a function to non-array will fail */ @@ -2256,51 +2594,51 @@ static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpat goto out; } - if (FAIL == zbx_json_brackets_open(objects->values[0].value, &jp)) - goto out; - - for (pnext = NULL; NULL != (pnext = zbx_json_next(&jp, pnext));) + for (i = 0; i < in->values[0].value->data.array.values_num; i++) { char name[MAX_ID_LEN + 1]; - zbx_snprintf(name, sizeof(name), "%d", index++); - zbx_vector_json_add_element(&objects_tmp, name, pnext); + zbx_snprintf(name, sizeof(name), "%d", i); + zbx_vector_jsonobj_ref_add_object(&tmp, name, in->values[0].value->data.array.values[i]); } - objects = &objects_tmp; + in = &tmp; + *definite_path = 0; } if (ZBX_JSONPATH_FUNCTION_LENGTH == type) { - *output = zbx_dsprintf(NULL, "%d", objects->values_num); + zbx_snprintf(buffer, sizeof(buffer), "%d", in->values_num); + zbx_vector_jsonobj_ref_add_string(out, "", buffer); + *definite_path = 1; ret = SUCCEED; goto out; } if (ZBX_JSONPATH_FUNCTION_FIRST == type) { - if (0 < objects->values_num) - ret = jsonpath_extract_element(objects->values[0].value, output); - else - ret = SUCCEED; + if (0 < in->values_num) + zbx_vector_jsonobj_ref_add(out, &in->values[0]); + *definite_path = 1; + ret = SUCCEED; goto out; } - if (0 == objects->values_num) + if (0 == in->values_num) { zbx_set_json_strerror("cannot apply aggregation function to empty array"); goto out; } - if (FAIL == jsonpath_extract_numeric_value(objects->values[0].value, &result)) + if (FAIL == jsonpath_get_numeric_value(in->values[0].value, &result)) goto out; - for (i = 1; i < objects->values_num; i++) + for (i = 1; i < in->values_num; i++) { double value; - if (FAIL == jsonpath_extract_numeric_value(objects->values[i].value, &value)) + if (FAIL == jsonpath_get_numeric_value(in->values[i].value, &value)) goto out; switch (type) @@ -2323,19 +2661,22 @@ static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpat } if (ZBX_JSONPATH_FUNCTION_AVG == type) - result /= objects->values_num; + result /= in->values_num; - *output = zbx_dsprintf(NULL, ZBX_FS_DBL, result); - if (SUCCEED != is_double(*output, NULL)) + zbx_print_double(buffer, sizeof(buffer), result); + if (SUCCEED != is_double(buffer, NULL)) { - zbx_set_json_strerror("invalid function result: %s", *output); + zbx_set_json_strerror("invalid function result: %s", buffer); goto out; } - del_zeros(*output); + + del_zeros(buffer); + zbx_vector_jsonobj_ref_add_string(out, "", buffer); + *definite_path = 1; ret = SUCCEED; out: - zbx_vector_json_clear_ext(&objects_tmp); - zbx_vector_json_destroy(&objects_tmp); + zbx_vector_jsonobj_ref_clear_ext(&tmp); + zbx_vector_jsonobj_ref_destroy(&tmp); return ret; } @@ -2344,58 +2685,47 @@ out: * * * Purpose: apply jsonpath function to the extracted object list * * * - * Parameters: jp_root - [IN] the document root * - * objects - [IN] the matched json elements (name, value) * - * jsonpath - [IN] the jsonpath * - * path_depth - [IN] the jsonpath segment to match * - * output - [OUT] the output value * + * Parameters: ctx - [IN] the jsonpath query context * + * path_depth - [IN] the jsonpath segment to match * + * definite_path - [IN/OUT] * + * out - [OUT] the result object * * * * Return value: SUCCEED - the function was applied successfully * * FAIL - invalid input data for the function or internal * * json error * * * ******************************************************************************/ -static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const zbx_vector_json_t *objects, - const zbx_jsonpath_t *jsonpath, int path_depth, char **output) +static int jsonpath_apply_functions(zbx_jsonpath_context_t *ctx, int path_depth, int *definite_path, + zbx_vector_jsonobj_ref_t *out) { - int ret, definite_path; - zbx_vector_json_t input; - char *input_json = NULL; + int ret; + zbx_vector_jsonobj_ref_t in; - zbx_vector_json_create(&input); + zbx_vector_jsonobj_ref_create(&in); /* when functions are applied directly to the json document (at the start of the jsonpath ) */ /* it makes all document as input object */ if (0 == path_depth) - zbx_vector_json_add_element(&input, "", jp_root->start); + zbx_vector_jsonobj_ref_add_object(&in, "", ctx->root); else - zbx_vector_json_copy(&input, objects); - - definite_path = jsonpath->definite; + zbx_vector_jsonobj_ref_copy(&in, &ctx->objects); for (;;) { - ret = jsonpath_apply_function(&input, jsonpath->segments[path_depth++].data.function.type, - definite_path, output); + ret = jsonpath_apply_function(&in, ctx->path->segments[path_depth++].data.function.type, + definite_path, out); - zbx_vector_json_clear_ext(&input); - zbx_free(input_json); + zbx_vector_jsonobj_ref_clear_ext(&in); - if (SUCCEED != ret || path_depth == jsonpath->segments_num) + if (SUCCEED != ret || path_depth == ctx->path->segments_num) break; - if (NULL != *output) - { - zbx_vector_json_add_element(&input, "", *output); - input_json = *output; - *output = NULL; - } - - /* functions return single value, so for the next functions path becomes definite */ - definite_path = 1; + zbx_vector_jsonobj_ref_copy(&in, out); + zbx_vector_jsonobj_ref_clear_ext(out); } - zbx_vector_json_destroy(&input); + zbx_vector_jsonobj_ref_clear_ext(&in); + zbx_vector_jsonobj_ref_destroy(&in); return ret; } @@ -2404,49 +2734,37 @@ static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const * * * Purpose: format query result, depending on jsonpath type * * * - * Parameters: objects - [IN] the matched json elements (name, value) * - * jsonpath - [IN] the jsonpath used to acquire result * - * output - [OUT] the output value * + * Parameters: objects - [IN] the matched json refs (name, value) * + * definite_path - [IN] the jsonpath definite flag * + * output - [OUT] the output value * * * * Return value: SUCCEED - the result was formatted successfully * * FAIL - invalid result data (internal json error) * * * ******************************************************************************/ -static int jsonpath_format_query_result(const zbx_vector_json_t *objects, zbx_jsonpath_t *jsonpath, char **output) +static int jsonpath_format_query_result(const zbx_vector_jsonobj_ref_t *objects, int definite_path, char **output) { size_t output_offset = 0, output_alloc; int i; + char delim; if (0 == objects->values_num) return SUCCEED; - if (1 == jsonpath->definite) - { - return jsonpath_extract_element(objects->values[0].value, output); - } + if (1 == definite_path) + return jsonpath_str_copy_value(output, &output_alloc, &output_offset, objects->values[0].value); /* reserve 32 bytes per returned object plus array start/end [] and terminating zero */ - output_alloc = objects->values_num * 32 + 3; + output_alloc = (size_t)objects->values_num * 32 + 3; *output = (char *)zbx_malloc(NULL, output_alloc); - zbx_chrcpy_alloc(output, &output_alloc, &output_offset, '['); + delim = '['; for (i = 0; i < objects->values_num; i++) { - struct zbx_json_parse jp; - - if (FAIL == jsonpath_pointer_to_jp(objects->values[i].value, &jp)) - { - zbx_set_json_strerror("cannot format query result, unrecognized json part starting with: %s", - objects->values[i].value); - zbx_free(*output); - return FAIL; - } - - if (0 != i) - zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ','); - - zbx_strncpy_alloc(output, &output_alloc, &output_offset, jp.start, jp.end - jp.start + 1); + zbx_chrcpy_alloc(output, &output_alloc, &output_offset, delim); + zbx_jsonobj_to_string(output, &output_alloc, &output_offset, objects->values[i].value); + delim = ','; } zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ']'); @@ -2469,7 +2787,7 @@ void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath) * Purpose: compile jsonpath to be used in queries * * * * Parameters: path - [IN] the path to parse * - * jsonpath - [IN/OUT] the compiled jsonpath * + * jsonpath - [IN/OUT] the compiled jsonpath * * * * Return value: SUCCEED - the segment was parsed successfully * * FAIL - otherwise * @@ -2581,38 +2899,89 @@ int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath) * being counted as successful query) * * FAIL - otherwise * * * + * Comments: This function is for compatibility purposes. Where possible the * + * zbx_jsonobj_query() function must be used. * + * * ******************************************************************************/ int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output) { + int ret; + zbx_jsonobj_t obj; + + if (SUCCEED != zbx_jsonobj_open(jp->start, &obj)) + return FAIL; + + ret = zbx_jsonobj_query(&obj, path, output); + + zbx_jsonobj_clear(&obj); + + return ret; +} + +/****************************************************************************** + * * + * Purpose: perform jsonpath query on the specified json object * + * * + * Parameters: obj - [IN] the json object * + * path - [IN] the jsonpath * + * output - [OUT] the output value * + * * + * Return value: SUCCEED - the query was performed successfully (empty result * + * being counted as successful query) * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_jsonobj_query(zbx_jsonobj_t *obj, const char *path, char **output) +{ + zbx_jsonpath_context_t ctx; zbx_jsonpath_t jsonpath; - int path_depth = 0, ret = SUCCEED; - zbx_vector_json_t objects; - unsigned char done = 0; + int ret = SUCCEED; if (FAIL == zbx_jsonpath_compile(path, &jsonpath)) return FAIL; - zbx_vector_json_create(&objects); + ctx.found = 0; + ctx.root = obj; + ctx.path = &jsonpath; + zbx_vector_jsonobj_ref_create(&ctx.objects); - if ('{' == *jp->start) - ret = jsonpath_query_object(jp, jp, &jsonpath, path_depth, &done, &objects); - else if ('[' == *jp->start) - ret = jsonpath_query_array(jp, jp, &jsonpath, path_depth, &done, &objects); + switch (obj->type) + { + case ZBX_JSON_TYPE_OBJECT: + ret = jsonpath_query_object(&ctx, obj, 0); + break; + case ZBX_JSON_TYPE_ARRAY: + ret = jsonpath_query_array(&ctx, obj, 0); + break; + default: + break; + } if (SUCCEED == ret) { + zbx_vector_jsonobj_ref_t out; + int definite_path = jsonpath.definite, path_depth; + + zbx_vector_jsonobj_ref_create(&out); + path_depth = jsonpath.segments_num; while (0 < path_depth && ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath.segments[path_depth - 1].type) path_depth--; if (path_depth < jsonpath.segments_num) - ret = jsonpath_apply_functions(jp, &objects, &jsonpath, path_depth, output); + { + if (SUCCEED == (ret = jsonpath_apply_functions(&ctx, path_depth, &definite_path, &out))) + ret = jsonpath_format_query_result(&out, definite_path, output); + } else - ret = jsonpath_format_query_result(&objects, &jsonpath, output); + ret = jsonpath_format_query_result(&ctx.objects, definite_path, output); + + zbx_vector_jsonobj_ref_clear_ext(&out); + zbx_vector_jsonobj_ref_destroy(&out); } - zbx_vector_json_clear_ext(&objects); - zbx_vector_json_destroy(&objects); + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + zbx_vector_jsonobj_ref_destroy(&ctx.objects); zbx_jsonpath_clear(&jsonpath); return ret; diff --git a/src/libs/zbxjson/jsonpath.h b/src/libs/zbxjson/jsonpath.h index ae3262a1009..644aef6aa4a 100644 --- a/src/libs/zbxjson/jsonpath.h +++ b/src/libs/zbxjson/jsonpath.h @@ -20,9 +20,10 @@ #ifndef ZABBIX_JSONPATH_H #define ZABBIX_JSONPATH_H -#include "zbxalgo.h" #include "zbxjson.h" +typedef struct zbx_jsonpath_token zbx_jsonpath_token_t; + typedef enum { ZBX_JSONPATH_SEGMENT_UNKNOWN, @@ -79,10 +80,19 @@ typedef struct } zbx_jsonpath_range_t; +typedef enum +{ + ZBX_JSONPATH_EXPRESSION_INDEX_TRUE, + ZBX_JSONPATH_EXPRESSION_INDEX_FALSE, +} +zbx_json_path_expression_index_t; + /* expression tokens in postfix notation */ typedef struct { zbx_vector_ptr_t tokens; + zbx_jsonpath_token_t *index_token; /* relative path token that is used to index parent object */ + zbx_jsonpath_token_t *value_token; /* the index value token */ } zbx_jsonpath_expression_t; @@ -151,12 +161,11 @@ typedef enum } zbx_jsonpath_token_type_t; -typedef struct +struct zbx_jsonpath_token { unsigned char type; char *text; zbx_jsonpath_t *path; -} -zbx_jsonpath_token_t; +}; #endif diff --git a/src/zabbix_agent/Makefile.am b/src/zabbix_agent/Makefile.am index 86c188a6bd9..56baebcc76e 100644 --- a/src/zabbix_agent/Makefile.am +++ b/src/zabbix_agent/Makefile.am @@ -54,7 +54,6 @@ zabbix_agentd_LDADD = \ $(top_builddir)/src/libs/zbxsysinfo/alias/libalias.a \ $(top_builddir)/src/libs/zbxlog/libzbxlog.a \ $(top_builddir)/src/libs/zbxregexp/libzbxregexp.a \ - $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_builddir)/src/libs/zbxmutexs/libzbxmutexs.a \ $(top_builddir)/src/libs/zbxnix/libzbxnix.a \ @@ -63,6 +62,7 @@ zabbix_agentd_LDADD = \ $(top_builddir)/src/libs/zbxconf/libzbxconf.a \ $(top_builddir)/src/libs/zbxjson/libzbxjson.a \ $(top_builddir)/src/libs/zbxvariant/libzbxvariant.a \ + $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_builddir)/src/libs/zbxgetopt/libzbxgetopt.a \ $(top_builddir)/src/libs/zbxhash/libzbxhash.a \ diff --git a/src/zabbix_server/Makefile.am b/src/zabbix_server/Makefile.am index bb8e7671b56..81669b27a1c 100644 --- a/src/zabbix_server/Makefile.am +++ b/src/zabbix_server/Makefile.am @@ -104,9 +104,9 @@ zabbix_server_LDADD = \ $(top_builddir)/src/libs/zbxself/libzbxself.a \ $(top_builddir)/src/libs/zbxself/libzbxself_server.a \ $(top_builddir)/src/libs/zbxnix/libzbxnix.a \ - $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_builddir)/src/libs/zbxmutexs/libzbxmutexs.a \ + $(top_builddir)/src/libs/zbxipcservice/libzbxipcservice.a \ $(top_builddir)/src/libs/zbxconf/libzbxconf.a \ $(top_builddir)/src/libs/zbxmedia/libzbxmedia.a \ $(top_builddir)/src/libs/zbxaudit/libzbxaudit.a \ @@ -117,6 +117,8 @@ zabbix_server_LDADD = \ $(top_builddir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_builddir)/src/libs/zbxcommshigh/libzbxcommshigh.a \ $(top_builddir)/src/libs/zbxjson/libzbxjson.a \ + $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ + $(top_builddir)/src/libs/zbxvault/libzbxvault.a \ $(top_builddir)/src/libs/zbxhttp/libzbxhttp.a \ $(top_builddir)/src/libs/zbxipcservice/libzbxipcservice.a \ $(top_builddir)/src/libs/zbxexec/libzbxexec.a \ diff --git a/src/zabbix_server/preprocessor/item_preproc.c b/src/zabbix_server/preprocessor/item_preproc.c index 12048b81261..4beee0be852 100644 --- a/src/zabbix_server/preprocessor/item_preproc.c +++ b/src/zabbix_server/preprocessor/item_preproc.c @@ -819,7 +819,8 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char ** * * * Purpose: execute jsonpath query * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -827,18 +828,59 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char ** * FAIL - otherwise * * * ******************************************************************************/ -static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, char **errmsg) +static int item_preproc_jsonpath_op(zbx_preproc_cache_t *cache, zbx_variant_t *value, const char *params, + char **errmsg) { - struct zbx_json_parse jp; - char *data = NULL; + char *data = NULL; - if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) - return FAIL; + if (NULL == cache) + { + zbx_jsonobj_t obj; + + if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) + return FAIL; - if (FAIL == zbx_json_open(value->data.str, &jp) || FAIL == zbx_jsonpath_query(&jp, params, &data)) + if (FAIL == zbx_jsonobj_open(value->data.str, &obj)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } + + if (FAIL == zbx_jsonobj_query(&obj, params, &data)) + { + zbx_jsonobj_clear(&obj); + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } + + zbx_jsonobj_clear(&obj); + } + else { - *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); - return FAIL; + zbx_jsonobj_t *obj; + + if (NULL == (obj = (zbx_jsonobj_t *)zbx_preproc_cache_get(cache, ZBX_PREPROC_JSONPATH))) + { + if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) + return FAIL; + + obj = (zbx_jsonobj_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + + if (SUCCEED != zbx_jsonobj_open(value->data.str, obj)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + zbx_free(obj); + return FAIL; + } + + zbx_preproc_cache_put(cache, ZBX_PREPROC_JSONPATH, obj); + } + + if (FAIL == zbx_jsonobj_query(obj, params, &data)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } } if (NULL == data) @@ -857,7 +899,8 @@ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, ch * * * Purpose: execute jsonpath query * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -865,11 +908,12 @@ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, ch * FAIL - otherwise * * * ******************************************************************************/ -static int item_preproc_jsonpath(zbx_variant_t *value, const char *params, char **errmsg) +static int item_preproc_jsonpath(zbx_preproc_cache_t *cache, zbx_variant_t *value, const char *params, + char **errmsg) { char *err = NULL; - if (SUCCEED == item_preproc_jsonpath_op(value, params, &err)) + if (SUCCEED == item_preproc_jsonpath_op(cache, value, params, &err)) return SUCCEED; *errmsg = zbx_dsprintf(*errmsg, "cannot extract value from json by path \"%s\": %s", params, err); @@ -1456,7 +1500,8 @@ fail: * * * Purpose: parse Prometheus format metrics * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -2033,7 +2078,7 @@ int zbx_item_preproc(zbx_preproc_cache_t *cache, unsigned char value_type, zbx_v ret = item_preproc_xpath(value, op->params, error); break; case ZBX_PREPROC_JSONPATH: - ret = item_preproc_jsonpath(value, op->params, error); + ret = item_preproc_jsonpath(cache, value, op->params, error); break; case ZBX_PREPROC_VALIDATE_RANGE: ret = item_preproc_validate_range(value_type, value, op->params, error); diff --git a/src/zabbix_server/preprocessor/preproc_cache.c b/src/zabbix_server/preprocessor/preproc_cache.c index 9b7cff0731e..556f58e53e9 100644 --- a/src/zabbix_server/preprocessor/preproc_cache.c +++ b/src/zabbix_server/preprocessor/preproc_cache.c @@ -95,6 +95,11 @@ void zbx_preproc_cache_clear(zbx_preproc_cache_t *cache) case ZBX_PREPROC_PROMETHEUS_PATTERN: zbx_prometheus_clear((zbx_prometheus_t *)cache->refs.values[i].impl); zbx_free(cache->refs.values[i].impl); + break; + case ZBX_PREPROC_JSONPATH: + zbx_jsonobj_clear((zbx_jsonobj_t *)cache->refs.values[i].impl); + zbx_free(cache->refs.values[i].impl); + break; } } diff --git a/tests/libs/zbxcommon/Makefile.am b/tests/libs/zbxcommon/Makefile.am index 113b999e0f6..ad63030a5fc 100644 --- a/tests/libs/zbxcommon/Makefile.am +++ b/tests/libs/zbxcommon/Makefile.am @@ -76,7 +76,6 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxshmem/libzbxshmem.a \ $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ $(top_srcdir)/src/libs/zbxself/libzbxself.a \ - $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ $(top_srcdir)/src/libs/zbxmedia/libzbxmedia.a \ $(top_srcdir)/src/libs/zbxxml/libzbxxml.a \ @@ -89,6 +88,7 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxvariant/libzbxvariant.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ diff --git a/tests/libs/zbxjson/Makefile.am b/tests/libs/zbxjson/Makefile.am index 8a1885c6549..99954f783f1 100644 --- a/tests/libs/zbxjson/Makefile.am +++ b/tests/libs/zbxjson/Makefile.am @@ -3,7 +3,7 @@ noinst_PROGRAMS = \ zbx_json_decodevalue \ zbx_json_decodevalue_dyn \ zbx_jsonpath_compile \ - zbx_jsonpath_query + zbx_jsonobj_query JSON_LIBS = \ $(top_srcdir)/tests/libzbxmocktest.a \ @@ -102,22 +102,22 @@ endif zbx_jsonpath_compile_CFLAGS = -I@top_srcdir@/tests -# zbx_jsonpath_query +# zbx_jsonobj_query -zbx_jsonpath_query_SOURCES = \ - zbx_jsonpath_query.c \ +zbx_jsonobj_query_SOURCES = \ + zbx_jsonobj_query.c \ ../../zbxmocktest.h -zbx_jsonpath_query_LDADD = $(JSON_LIBS) +zbx_jsonobj_query_LDADD = $(JSON_LIBS) if SERVER -zbx_jsonpath_query_LDADD += @SERVER_LIBS@ -zbx_jsonpath_query_LDFLAGS = @SERVER_LDFLAGS@ +zbx_jsonobj_query_LDADD += @SERVER_LIBS@ +zbx_jsonobj_query_LDFLAGS = @SERVER_LDFLAGS@ else if PROXY -zbx_jsonpath_query_LDADD += @PROXY_LIBS@ -zbx_jsonpath_query_LDFLAGS = @PROXY_LDFLAGS@ +zbx_jsonobj_query_LDADD += @PROXY_LIBS@ +zbx_jsonobj_query_LDFLAGS = @PROXY_LDFLAGS@ endif endif -zbx_jsonpath_query_CFLAGS = -I@top_srcdir@/tests +zbx_jsonobj_query_CFLAGS = -I@top_srcdir@/tests diff --git a/tests/libs/zbxjson/zbx_jsonobj_query.c b/tests/libs/zbxjson/zbx_jsonobj_query.c new file mode 100644 index 00000000000..ce8d603e1a6 --- /dev/null +++ b/tests/libs/zbxjson/zbx_jsonobj_query.c @@ -0,0 +1,114 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "zbxmocktest.h" +#include "zbxmockdata.h" +#include "zbxmockassert.h" +#include "zbxmockutil.h" +#include "zbxmockjson.h" + +#include "common.h" +#include "zbxjson.h" +#include "../../../src/libs/zbxjson/json.h" + +static void check_definite_path_result(zbx_mock_handle_t handle, const char *returned_output) +{ + const char *expected_output; + struct zbx_json_parse jp; + + if (ZBX_MOCK_SUCCESS != zbx_mock_string(handle, &expected_output)) + fail_msg("Invalid test case out.value parameter"); + + if (FAIL == zbx_json_open(expected_output, &jp)) + zbx_mock_assert_str_eq("Definite query result", expected_output, returned_output); + else + zbx_mock_assert_json_eq("Indefinite query result", expected_output, returned_output); +} + +static void check_indefinite_path_result(zbx_mock_handle_t handle, const char *returned_output) +{ + const char *expected_output; + + if (ZBX_MOCK_SUCCESS != zbx_mock_string(handle, &expected_output)) + fail_msg("Invalid test case out.values parameter"); + + zbx_mock_assert_json_eq("Indefinite query result", expected_output, returned_output); +} + +static void test_query(zbx_jsonobj_t *obj, const char *path, int expected_ret) +{ + char *output = NULL; + int returned_ret; + zbx_mock_handle_t handle; + + returned_ret = zbx_jsonobj_query(obj, path, &output); + + if (FAIL == returned_ret) + printf("\tzbx_jsonpath_query() failed with: %s\n", zbx_json_strerror()); + + zbx_mock_assert_result_eq("zbx_jsonpath_query() return value", expected_ret, returned_ret); + + if (SUCCEED == returned_ret) + { + printf("\tzbx_jsonpath_query() query result: %s\n", ZBX_NULL2EMPTY_STR(output)); + if (ZBX_MOCK_SUCCESS == zbx_mock_parameter("out.value", &handle)) + { + zbx_mock_assert_ptr_ne("Query result", NULL, output); + check_definite_path_result(handle, output); + } + else if (ZBX_MOCK_SUCCESS == zbx_mock_parameter("out.values", &handle)) + { + zbx_mock_assert_ptr_ne("Query result", NULL, output); + check_indefinite_path_result(handle, output); + } + else + zbx_mock_assert_ptr_eq("Query result", NULL, output); + } + else + zbx_mock_assert_str_ne("tzbx_jsonpath_query() error", "", zbx_json_strerror()); + + zbx_free(output); + +} + +void zbx_mock_test_entry(void **state) +{ + const char *data, *path; + int expected_ret; + zbx_jsonobj_t obj; + + ZBX_UNUSED(state); + + /* reset json error to check if compilation will set it */ + zbx_set_json_strerror("%s", ""); + + data = zbx_mock_get_parameter_string("in.data"); + if (FAIL == zbx_jsonobj_open(data, &obj)) + fail_msg("Invalid json data: %s", zbx_json_strerror()); + + path = zbx_mock_get_parameter_string("in.path"); + expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.return")); + + test_query(&obj, path, expected_ret); + + /* query second time to check index reuse */ + test_query(&obj, path, expected_ret); + + zbx_jsonobj_clear(&obj); +} diff --git a/tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml b/tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml new file mode 100644 index 00000000000..456f49ebe28 --- /dev/null +++ b/tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml @@ -0,0 +1,77 @@ +--- +| + { + "books": [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95, + "id": 1 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99, + "id": 2 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99, + "id": 3 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99, + "id": 4 + } + ], + "services": { + "delivery": { + "servicegroup": 1000, + "description": "Next day delivery in local town", + "active": true, + "price": 5 + }, + "bookbinding": { + "servicegroup": 1001, + "description": "Printing and assembling book in A5 format", + "active": true, + "price": 154.99 + }, + "restoration": { + "servicegroup": 1002, + "description": "Various restoration methods", + "active": false, + "methods": [ + { + "description": "Checmical cleaning", + "price": 46 + }, + { + "description": "Pressing pages damaged by moisture", + "price": 24.5 + }, + { + "description": "Rebinding torn book", + "price": 99.49 + } + ] + } + }, + "filters": { + "price": 10, + "category": "fiction", + "no filters": "no \"filters\"" + }, + "closed message": "Store is closed", + "tags": ["a", "b", "c", "d", "e" ] + } +... diff --git a/tests/libs/zbxjson/zbx_jsonobj_query.yaml b/tests/libs/zbxjson/zbx_jsonobj_query.yaml new file mode 100644 index 00000000000..7e9aae0044d --- /dev/null +++ b/tests/libs/zbxjson/zbx_jsonobj_query.yaml @@ -0,0 +1,800 @@ +--- +test case: Query $[0] from ["a", "b"] +in: + data: '["a", "b"]' + path: $[0] +out: + return: SUCCEED + value: a +--- +test case: Query $[4] from ["a", "b"] +in: + data: '["a", "b"]' + path: $[4] +out: + return: SUCCEED +--- +test case: Query $[1] from ["a", "b"] +in: + data: '["a", "b"]' + path: $[1] +out: + return: SUCCEED + value: b +--- +test case: Query $[2] from ["a", "b"] +in: + data: '["a", "b"]' + path: $[2] +out: + return: SUCCEED +--- +test case: Query $[2].* from ["a", "b"] +in: + data: '["a", "b"]' + path: $[2].* +out: + return: SUCCEED +--- +test case: Query $.filters.price +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.filters.price +out: + return: SUCCEED + value: 10 +--- +test case: Query $.filters.category +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.filters.category +out: + return: SUCCEED + value: fiction +--- +test case: Query $.filters.id +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.filters.id +out: + return: SUCCEED +--- +test case: Query $.books[1].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[1].title +out: + return: SUCCEED + value: Sword of Honour +--- +test case: Query $['closed message'] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $['closed message'] +out: + return: SUCCEED + value: Store is closed +--- +test case: Query $.books[-1].author +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[-1].author +out: + return: SUCCEED + value: J. R. R. Tolkien +--- +test case: Query $.filters +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.filters +out: + return: SUCCEED + value: | + { + "price": 10, + "category": "fiction", + "no filters": "no \"filters\"" + } +--- +test case: Query $.books.length() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books.length() +out: + return: SUCCEED + value: 4 +--- +test case: Query $.tags[:] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.tags[:] +out: + return: SUCCEED + value: '["a", "b", "c", "d", "e" ]' +--- +test case: Query $.tags[2:] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.tags[2:] +out: + return: SUCCEED + value: '["c", "d", "e" ]' +--- +test case: Query $.tags[:2] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.tags[:2] +out: + return: SUCCEED + value: '["a", "b"]' +--- +test case: Query $.tags[1:4] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.tags[1:4] +out: + return: SUCCEED + value: '["b", "c", "d"]' +--- +test case: Query $.tags[-2:] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.tags[-2:] +out: + return: SUCCEED + value: '["d", "e"]' +--- +test case: Query $.tags[:-3] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.tags[:-3] +out: + return: SUCCEED + value: '["a", "b"]' +--- +test case: Query $.tags[:-3].length() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.tags[:-3].length() +out: + return: SUCCEED + value: 2 +--- +test case: Query $.books[0, 2].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[0, 2].title +out: + return: SUCCEED + value: '["Moby Dick", "Sayings of the Century"]' +--- +test case: Query $.books[1]['author', "title"] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[1]['author', "title"] +out: + return: SUCCEED + value: '["Sword of Honour", "Evelyn Waugh"]' +--- +test case: Query $..id +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $..id +out: + return: SUCCEED + value: '[1, 2, 3, 4]' +--- +test case: Query $.services..price +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.services..price +out: + return: SUCCEED + value: '[154.99, 5, 46, 24.5, 99.49]' +--- +test case: Query $.books[?(@.id == 1 + 1)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 1 + 1)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 4 / 2)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 4 / 2)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 7 - 5)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 7 - 5)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 0.4 * 5)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 0.4 * 5)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 4 - 0.4 * 5)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 4 - 0.4 * 5)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == -0.4 * 5 + 4)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == -0.4 * 5 + 4)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 0.4 * (-5) + 4)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 0.4 * (-5) + 4)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 2 || @.id == 4)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 2 || @.id == 4)].title +out: + return: SUCCEED + value: '["Sword of Honour", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 2 == 1)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 2 == 1)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(!(@.id == 2))].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(!(@.id == 2))].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.id != 2)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id != 2)].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.title =~ " of ")].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.title =~ " of ")].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Sword of Honour", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.price > 12.99)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.price > 12.99)].title +out: + return: SUCCEED + value: '["The Lord of the Rings"]' +--- +test case: Query $.books[?(@.price >= 12.99)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.price >= 12.99)].title +out: + return: SUCCEED + value: '["Sword of Honour", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.price < 12.99)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.price < 12.99)].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick"]' +--- +test case: Query $.books[?(@.price <= 12.99)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.price <= 12.99)].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Sword of Honour", "Moby Dick"]' +--- +test case: Query $.books[?(@.author > "Herman Melville")].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.author > "Herman Melville")].title +out: + return: SUCCEED + value: '["Sayings of the Century", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.author >= "Herman Melville")].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.author >= "Herman Melville")].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.author < "Herman Melville")].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.author < "Herman Melville")].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.author <= "Herman Melville")].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.author <= "Herman Melville")].title +out: + return: SUCCEED + value: '["Sword of Honour", "Moby Dick"]' +--- +test case: Query $.books[?(@.price > $.filters.price)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.price > $.filters.price)].title +out: + return: SUCCEED + value: '["Sword of Honour", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.category == $.filters.category)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.category == $.filters.category)].title +out: + return: SUCCEED + value: '["Sword of Honour","Moby Dick","The Lord of the Rings"]' +--- +test case: Query $.books[?(@.category != $.filters.category)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.category != $.filters.category)].title +out: + return: SUCCEED + value: '["Sayings of the Century"]' +--- +test case: Query $..[?(@.id)] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $..[?(@.id)] +out: + return: SUCCEED + value: | + [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95, + "id": 1 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99, + "id": 2 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99, + "id": 3 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99, + "id": 4 + } + ] +--- +test case: Query $.services..[?(@.price > 50)].description +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.services..[?(@.price > 50)].description +out: + return: SUCCEED + value: '["Printing and assembling book in A5 format", "Rebinding torn book"]' +--- +test case: Query $..id.length() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $..id.length() +out: + return: SUCCEED + value: 4 +--- +test case: Query $.books[?(@.price >= 12.99)].length() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.price >= 12.99)].length() +out: + return: SUCCEED + value: 2 +--- +test case: Query $.books[?(@.id == 2)].title.first() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 2)].title.first() +out: + return: SUCCEED + value: Sword of Honour +--- +test case: Query $..tags.first().length() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $..tags.first().length() +out: + return: SUCCEED + value: 5 +--- +test case: Query $.bad.path.first().length() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.bad.path.first().length() +out: + return: FAIL +--- +test case: Query $.[?(@.ElementName == "test")].values.first().length() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.[?(@.ElementName == "test")].values.first().length() +out: + return: FAIL +--- +test case: Query $.length() from ["a", "b"] +in: + data: '["a", "b"]' + path: $.length() +out: + return: SUCCEED + value: 2 +--- +test case: Query $.first() from ["a", "b"] +in: + data: '["a", "b"]' + path: $.first() +out: + return: SUCCEED + value: a +--- +test case: Query $.first().first() from [["a", "b"]] +in: + data: '[["a", "b"]]' + path: $.first().first() +out: + return: SUCCEED + value: a +--- +test case: Query $.first().first().first() from [[["a", "b"]]] +in: + data: '[[["a", "b"]]]' + path: $.first().first().first() +out: + return: SUCCEED + value: a +--- +test case: Query $.books[*].price.min() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[*].price.min() +out: + return: SUCCEED + value: 8.95 +--- +test case: Query $..price.max() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $..price.max() +out: + return: SUCCEED + value: 154.99 +--- +test case: Query $.books[?(@.category == "fiction")].price.avg() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.category == "fiction")].price.avg() +out: + return: SUCCEED + value: 14.99 +--- +test case: Query $.books[?(@.category == $.filters.xyz)].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.category == $.filters.xyz)].title +out: + return: SUCCEED +--- +test case: Query $.filters['no filters'] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.filters['no filters'] +out: + return: SUCCEED + value: no "filters" +--- +test case: Query $.services[?(@.active=="true")].servicegroup +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.services[?(@.active=="true")].servicegroup +out: + return: SUCCEED + value: '[1001,1000]' +--- +test case: Query $.services[?(@.active=="false")].servicegroup +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.services[?(@.active=="false")].servicegroup +out: + return: SUCCEED + value: '[1002]' +--- +test case: Query $.books[?(@.title =~ "[a-z")].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.title =~ "[a-z")].title +out: + return: FAIL +--- +test case: $..books[?(!@.isbn)] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $..books[?(!@.isbn)] +out: + return: SUCCEED + value: | + [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95, + "id": 1 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99, + "id": 2 + } + ] +--- +test case: $..books[?(@.isbn)] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $..books[?(@.isbn)] +out: + return: SUCCEED + value: | + [ + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99, + "id": 3 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99, + "id": 4 + } + ] +--- +test case: Query $.books[*].price.sum() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[*].price.sum() +out: + return: SUCCEED + value: 53.92 +--- +test case: Query $[?(@.a)].id from [{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}] +in: + data: '[{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}]' + path: $[?(@.a)].id +out: + return: SUCCEED + values: '[1]' +--- +test case: Query $[?(!@.a)].id from [{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}] +in: + data: '[{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}]' + path: $[?(!@.a)].id +out: + return: SUCCEED + values: '[2]' +--- +test case: Query $[?(@.a)].id from [{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}] +in: + data: '[{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}]' + path: $[?(@.a)].id +out: + return: SUCCEED + values: '[1]' +--- +test case: Query $[?(!@.a)].id from [{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}] +in: + data: '[{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}]' + path: $[?(!@.a)].id +out: + return: SUCCEED + values: '[2]' +--- +test case: Query $.*~ +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.*~ +out: + return: SUCCEED + value: '["filters", "services", "tags", "books", "closed message"]' +--- +test case: Query $.*~.first() +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.*~.first() +out: + return: SUCCEED + value: 'filters' +--- +test case: Query $.services[?(@.servicegroup=="1002")]~ +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.services[?(@.servicegroup=="1002")]~ +out: + return: SUCCEED + value: '["restoration"]' +--- +test case: Query $.books[?(@.category=="fiction")]~ +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.category=="fiction")]~ +out: + return: SUCCEED + value: '["1","2","3"]' +--- +test case: Query $.books[?(@.category=="reference")].price +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[?(@.category=="reference")].price +out: + return: SUCCEED + value: '[8.95]' +--- +test case: Query $.books[1,1].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[1,1].title +out: + return: SUCCEED + value: Sword of Honour +--- +test case: Query $.books[1:2].title +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[1:2].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[1]["title","title"] +include: &include zbx_jsonobj_query.inc.yaml +in: + data: *include + path: $.books[1].["title","title"] +out: + return: SUCCEED + value: Sword of Honour +... + + diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.c b/tests/libs/zbxjson/zbx_jsonpath_query.c deleted file mode 100644 index 0beb97d1503..00000000000 --- a/tests/libs/zbxjson/zbx_jsonpath_query.c +++ /dev/null @@ -1,100 +0,0 @@ -/* -** Zabbix -** Copyright (C) 2001-2022 Zabbix SIA -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -**/ - -#include "zbxmocktest.h" -#include "zbxmockdata.h" -#include "zbxmockassert.h" -#include "zbxmockutil.h" -#include "zbxmockjson.h" - -#include "common.h" -#include "zbxjson.h" -#include "../../../src/libs/zbxjson/json.h" - -static void check_definite_path_result(zbx_mock_handle_t handle, const char *returned_output) -{ - const char *expected_output; - struct zbx_json_parse jp; - - if (ZBX_MOCK_SUCCESS != zbx_mock_string(handle, &expected_output)) - fail_msg("Invalid test case out.value parameter"); - - if (FAIL == zbx_json_open(expected_output, &jp)) - zbx_mock_assert_str_eq("Definite query result", expected_output, returned_output); - else - zbx_mock_assert_json_eq("Indefinite query result", expected_output, returned_output); -} - -static void check_indefinite_path_result(zbx_mock_handle_t handle, const char *returned_output) -{ - const char *expected_output; - - if (ZBX_MOCK_SUCCESS != zbx_mock_string(handle, &expected_output)) - fail_msg("Invalid test case out.values parameter"); - - zbx_mock_assert_json_eq("Indefinite query result", expected_output, returned_output); -} - -void zbx_mock_test_entry(void **state) -{ - const char *data, *path; - struct zbx_json_parse jp; - char *output = NULL; - int expected_ret, returned_ret; - zbx_mock_handle_t handle; - - ZBX_UNUSED(state); - - /* reset json error to check if compilation will set it */ - zbx_set_json_strerror("%s", ""); - - data = zbx_mock_get_parameter_string("in.data"); - if (FAIL == zbx_json_open(data, &jp)) - fail_msg("Invalid json data: %s", zbx_json_strerror()); - - path = zbx_mock_get_parameter_string("in.path"); - returned_ret = zbx_jsonpath_query(&jp, path, &output); - expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.return")); - - if (FAIL == returned_ret) - printf("\tzbx_jsonpath_query() failed with: %s\n", zbx_json_strerror()); - - zbx_mock_assert_result_eq("zbx_jsonpath_query() return value", expected_ret, returned_ret); - - if (SUCCEED == returned_ret) - { - printf("\tzbx_jsonpath_query() query result: %s\n", ZBX_NULL2EMPTY_STR(output)); - if (ZBX_MOCK_SUCCESS == zbx_mock_parameter("out.value", &handle)) - { - zbx_mock_assert_ptr_ne("Query result", NULL, output); - check_definite_path_result(handle, output); - } - else if (ZBX_MOCK_SUCCESS == zbx_mock_parameter("out.values", &handle)) - { - zbx_mock_assert_ptr_ne("Query result", NULL, output); - check_indefinite_path_result(handle, output); - } - else - zbx_mock_assert_ptr_eq("Query result", NULL, output); - } - else - zbx_mock_assert_str_ne("tzbx_jsonpath_query() error", "", zbx_json_strerror()); - - zbx_free(output); -} diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml b/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml deleted file mode 100644 index 456f49ebe28..00000000000 --- a/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml +++ /dev/null @@ -1,77 +0,0 @@ ---- -| - { - "books": [ - { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95, - "id": 1 - }, - { - "category": "fiction", - "author": "Evelyn Waugh", - "title": "Sword of Honour", - "price": 12.99, - "id": 2 - }, - { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99, - "id": 3 - }, - { - "category": "fiction", - "author": "J. R. R. Tolkien", - "title": "The Lord of the Rings", - "isbn": "0-395-19395-8", - "price": 22.99, - "id": 4 - } - ], - "services": { - "delivery": { - "servicegroup": 1000, - "description": "Next day delivery in local town", - "active": true, - "price": 5 - }, - "bookbinding": { - "servicegroup": 1001, - "description": "Printing and assembling book in A5 format", - "active": true, - "price": 154.99 - }, - "restoration": { - "servicegroup": 1002, - "description": "Various restoration methods", - "active": false, - "methods": [ - { - "description": "Checmical cleaning", - "price": 46 - }, - { - "description": "Pressing pages damaged by moisture", - "price": 24.5 - }, - { - "description": "Rebinding torn book", - "price": 99.49 - } - ] - } - }, - "filters": { - "price": 10, - "category": "fiction", - "no filters": "no \"filters\"" - }, - "closed message": "Store is closed", - "tags": ["a", "b", "c", "d", "e" ] - } -... diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.yaml b/tests/libs/zbxjson/zbx_jsonpath_query.yaml deleted file mode 100644 index a4b784b90d9..00000000000 --- a/tests/libs/zbxjson/zbx_jsonpath_query.yaml +++ /dev/null @@ -1,762 +0,0 @@ ---- -test case: Query $[0] from ["a", "b"] -in: - data: '["a", "b"]' - path: $[0] -out: - return: SUCCEED - value: a ---- -test case: Query $[4] from ["a", "b"] -in: - data: '["a", "b"]' - path: $[4] -out: - return: SUCCEED ---- -test case: Query $[1] from ["a", "b"] -in: - data: '["a", "b"]' - path: $[1] -out: - return: SUCCEED - value: b ---- -test case: Query $[2] from ["a", "b"] -in: - data: '["a", "b"]' - path: $[2] -out: - return: SUCCEED ---- -test case: Query $[2].* from ["a", "b"] -in: - data: '["a", "b"]' - path: $[2].* -out: - return: SUCCEED ---- -test case: Query $.filters.price -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.filters.price -out: - return: SUCCEED - value: 10 ---- -test case: Query $.filters.category -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.filters.category -out: - return: SUCCEED - value: fiction ---- -test case: Query $.filters.id -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.filters.id -out: - return: SUCCEED ---- -test case: Query $.books[1].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[1].title -out: - return: SUCCEED - value: Sword of Honour ---- -test case: Query $['closed message'] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $['closed message'] -out: - return: SUCCEED - value: Store is closed ---- -test case: Query $.books[-1].author -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[-1].author -out: - return: SUCCEED - value: J. R. R. Tolkien ---- -test case: Query $.filters -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.filters -out: - return: SUCCEED - value: | - { - "price": 10, - "category": "fiction", - "no filters": "no \"filters\"" - } ---- -test case: Query $.books.length() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books.length() -out: - return: SUCCEED - value: 4 ---- -test case: Query $.tags[:] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.tags[:] -out: - return: SUCCEED - value: '["a", "b", "c", "d", "e" ]' ---- -test case: Query $.tags[2:] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.tags[2:] -out: - return: SUCCEED - value: '["c", "d", "e" ]' ---- -test case: Query $.tags[:2] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.tags[:2] -out: - return: SUCCEED - value: '["a", "b"]' ---- -test case: Query $.tags[1:4] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.tags[1:4] -out: - return: SUCCEED - value: '["b", "c", "d"]' ---- -test case: Query $.tags[-2:] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.tags[-2:] -out: - return: SUCCEED - value: '["d", "e"]' ---- -test case: Query $.tags[:-3] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.tags[:-3] -out: - return: SUCCEED - value: '["a", "b"]' ---- -test case: Query $.tags[:-3].length() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.tags[:-3].length() -out: - return: SUCCEED - value: 2 ---- -test case: Query $.books[0, 2].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[0, 2].title -out: - return: SUCCEED - value: '["Sayings of the Century", "Moby Dick"]' ---- -test case: Query $.books[1]['author', "title"] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[1]['author', "title"] -out: - return: SUCCEED - value: '["Evelyn Waugh", "Sword of Honour"]' ---- -test case: Query $..id -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $..id -out: - return: SUCCEED - value: '[1, 2, 3, 4]' ---- -test case: Query $.services..price -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.services..price -out: - return: SUCCEED - value: '[5, 154.99, 46, 24.5, 99.49]' ---- -test case: Query $.books[?(@.id == 1 + 1)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 1 + 1)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.id == 4 / 2)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 4 / 2)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.id == 7 - 5)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 7 - 5)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.id == 0.4 * 5)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 0.4 * 5)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.id == 4 - 0.4 * 5)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 4 - 0.4 * 5)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.id == -0.4 * 5 + 4)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == -0.4 * 5 + 4)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.id == 0.4 * (-5) + 4)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 0.4 * (-5) + 4)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.id == 2 || @.id == 4)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 2 || @.id == 4)].title -out: - return: SUCCEED - value: '["Sword of Honour", "The Lord of the Rings"]' ---- -test case: Query $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.id == 2 == 1)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 2 == 1)].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(!(@.id == 2))].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(!(@.id == 2))].title -out: - return: SUCCEED - value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' ---- -test case: Query $.books[?(@.id != 2)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id != 2)].title -out: - return: SUCCEED - value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' ---- -test case: Query $.books[?(@.title =~ " of ")].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.title =~ " of ")].title -out: - return: SUCCEED - value: '["Sayings of the Century", "Sword of Honour", "The Lord of the Rings"]' ---- -test case: Query $.books[?(@.price > 12.99)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.price > 12.99)].title -out: - return: SUCCEED - value: '["The Lord of the Rings"]' ---- -test case: Query $.books[?(@.price >= 12.99)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.price >= 12.99)].title -out: - return: SUCCEED - value: '["Sword of Honour", "The Lord of the Rings"]' ---- -test case: Query $.books[?(@.price < 12.99)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.price < 12.99)].title -out: - return: SUCCEED - value: '["Sayings of the Century", "Moby Dick"]' ---- -test case: Query $.books[?(@.price <= 12.99)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.price <= 12.99)].title -out: - return: SUCCEED - value: '["Sayings of the Century", "Sword of Honour", "Moby Dick"]' ---- -test case: Query $.books[?(@.author > "Herman Melville")].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.author > "Herman Melville")].title -out: - return: SUCCEED - value: '["Sayings of the Century", "The Lord of the Rings"]' ---- -test case: Query $.books[?(@.author >= "Herman Melville")].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.author >= "Herman Melville")].title -out: - return: SUCCEED - value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' ---- -test case: Query $.books[?(@.author < "Herman Melville")].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.author < "Herman Melville")].title -out: - return: SUCCEED - value: '["Sword of Honour"]' ---- -test case: Query $.books[?(@.author <= "Herman Melville")].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.author <= "Herman Melville")].title -out: - return: SUCCEED - value: '["Sword of Honour", "Moby Dick"]' ---- -test case: Query $.books[?(@.price > $.filters.price)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.price > $.filters.price)].title -out: - return: SUCCEED - value: '["Sword of Honour", "The Lord of the Rings"]' ---- -test case: Query $.books[?(@.category == $.filters.category)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.category == $.filters.category)].title -out: - return: SUCCEED - value: '["Sword of Honour","Moby Dick","The Lord of the Rings"]' ---- -test case: Query $.books[?(@.category != $.filters.category)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.category != $.filters.category)].title -out: - return: SUCCEED - value: '["Sayings of the Century"]' ---- -test case: Query $..[?(@.id)] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $..[?(@.id)] -out: - return: SUCCEED - value: | - [ - { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95, - "id": 1 - }, - { - "category": "fiction", - "author": "Evelyn Waugh", - "title": "Sword of Honour", - "price": 12.99, - "id": 2 - }, - { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99, - "id": 3 - }, - { - "category": "fiction", - "author": "J. R. R. Tolkien", - "title": "The Lord of the Rings", - "isbn": "0-395-19395-8", - "price": 22.99, - "id": 4 - } - ] ---- -test case: Query $.services..[?(@.price > 50)].description -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.services..[?(@.price > 50)].description -out: - return: SUCCEED - value: '["Printing and assembling book in A5 format", "Rebinding torn book"]' ---- -test case: Query $..id.length() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $..id.length() -out: - return: SUCCEED - value: 4 ---- -test case: Query $.books[?(@.price >= 12.99)].length() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.price >= 12.99)].length() -out: - return: SUCCEED - value: 2 ---- -test case: Query $.books[?(@.id == 2)].title.first() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.id == 2)].title.first() -out: - return: SUCCEED - value: Sword of Honour ---- -test case: Query $..tags.first().length() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $..tags.first().length() -out: - return: SUCCEED - value: 5 ---- -test case: Query $.bad.path.first().length() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.bad.path.first().length() -out: - return: FAIL ---- -test case: Query $.[?(@.ElementName == "test")].values.first().length() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.[?(@.ElementName == "test")].values.first().length() -out: - return: FAIL ---- -test case: Query $.length() from ["a", "b"] -in: - data: '["a", "b"]' - path: $.length() -out: - return: SUCCEED - value: 2 ---- -test case: Query $.first() from ["a", "b"] -in: - data: '["a", "b"]' - path: $.first() -out: - return: SUCCEED - value: a ---- -test case: Query $.first().first() from [["a", "b"]] -in: - data: '[["a", "b"]]' - path: $.first().first() -out: - return: SUCCEED - value: a ---- -test case: Query $.first().first().first() from [[["a", "b"]]] -in: - data: '[[["a", "b"]]]' - path: $.first().first().first() -out: - return: SUCCEED - value: a ---- -test case: Query $.books[*].price.min() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[*].price.min() -out: - return: SUCCEED - value: 8.95 ---- -test case: Query $..price.max() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $..price.max() -out: - return: SUCCEED - value: 154.99 ---- -test case: Query $.books[?(@.category == "fiction")].price.avg() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.category == "fiction")].price.avg() -out: - return: SUCCEED - value: 14.99 ---- -test case: Query $.books[?(@.category == $.filters.xyz)].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.category == $.filters.xyz)].title -out: - return: SUCCEED ---- -test case: Query $.filters['no filters'] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.filters['no filters'] -out: - return: SUCCEED - value: no "filters" ---- -test case: Query $.services[?(@.active=="true")].servicegroup -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.services[?(@.active=="true")].servicegroup -out: - return: SUCCEED - value: '[1000,1001]' ---- -test case: Query $.services[?(@.active=="false")].servicegroup -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.services[?(@.active=="false")].servicegroup -out: - return: SUCCEED - value: '[1002]' ---- -test case: Query $.books[?(@.title =~ "[a-z")].title -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.title =~ "[a-z")].title -out: - return: FAIL ---- -test case: $..books[?(!@.isbn)] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $..books[?(!@.isbn)] -out: - return: SUCCEED - value: | - [ - { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95, - "id": 1 - }, - { - "category": "fiction", - "author": "Evelyn Waugh", - "title": "Sword of Honour", - "price": 12.99, - "id": 2 - } - ] ---- -test case: $..books[?(@.isbn)] -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $..books[?(@.isbn)] -out: - return: SUCCEED - value: | - [ - { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99, - "id": 3 - }, - { - "category": "fiction", - "author": "J. R. R. Tolkien", - "title": "The Lord of the Rings", - "isbn": "0-395-19395-8", - "price": 22.99, - "id": 4 - } - ] ---- -test case: Query $.books[*].price.sum() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[*].price.sum() -out: - return: SUCCEED - value: 53.92 ---- -test case: Query $[?(@.a)].id from [{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}] -in: - data: '[{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}]' - path: $[?(@.a)].id -out: - return: SUCCEED - values: '[1]' ---- -test case: Query $[?(!@.a)].id from [{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}] -in: - data: '[{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}]' - path: $[?(!@.a)].id -out: - return: SUCCEED - values: '[2]' ---- -test case: Query $[?(@.a)].id from [{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}] -in: - data: '[{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}]' - path: $[?(@.a)].id -out: - return: SUCCEED - values: '[1]' ---- -test case: Query $[?(!@.a)].id from [{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}] -in: - data: '[{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}]' - path: $[?(!@.a)].id -out: - return: SUCCEED - values: '[2]' ---- -test case: Query $.*~ -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.*~ -out: - return: SUCCEED - value: '["books","services","filters","closed message","tags"]' ---- -test case: Query $.*~.first() -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.*~.first() -out: - return: SUCCEED - value: 'books' ---- -test case: Query $.services[?(@.servicegroup=="1002")]~ -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.services[?(@.servicegroup=="1002")]~ -out: - return: SUCCEED - value: '["restoration"]' ---- -test case: Query $.books[?(@.category=="fiction")]~ -include: &include zbx_jsonpath_query.inc.yaml -in: - data: *include - path: $.books[?(@.category=="fiction")]~ -out: - return: SUCCEED - value: '["1","2","3"]' -... diff --git a/tests/libs/zbxsysinfo/Makefile.am b/tests/libs/zbxsysinfo/Makefile.am index f2f6f439d22..ba760a0a355 100644 --- a/tests/libs/zbxsysinfo/Makefile.am +++ b/tests/libs/zbxsysinfo/Makefile.am @@ -60,12 +60,12 @@ parse_item_key_LDADD = \ $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ $(top_srcdir)/src/libs/zbxself/libzbxself.a \ $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ - $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ $(top_srcdir)/src/libs/zbxmedia/libzbxmedia.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ @@ -107,14 +107,19 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxsysinfo/alias/libalias.a \ $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ +<<<<<<< HEAD $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \ +======= + $(top_srcdir)/src/libs/zbxsys/libzbxsys.a \ +>>>>>>> 3385fecdb61 (.......PS. [ZBXNEXT-8040] added object based json parsing and jsonpath optimizations) $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxhash/libzbxhash.a \ @@ -183,6 +188,7 @@ check_key_access_rules_SOURCES = \ check_key_access_rules_LDADD = $(COMMON_LIB_FILES) \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxsysinfo/common/libcommonsysinfo_http.a \ $(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a diff --git a/tests/libs/zbxsysinfo/common/Makefile.am b/tests/libs/zbxsysinfo/common/Makefile.am index 3a885dc332f..606afe52530 100644 --- a/tests/libs/zbxsysinfo/common/Makefile.am +++ b/tests/libs/zbxsysinfo/common/Makefile.am @@ -23,7 +23,6 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxsysinfo/alias/libalias.a \ $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ - $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \ $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ @@ -32,6 +31,7 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxvariant/libzbxvariant.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxhash/libzbxhash.a \ diff --git a/tests/libs/zbxsysinfo/linux/Makefile.am b/tests/libs/zbxsysinfo/linux/Makefile.am index 541fa1fa73a..71916f7d8e2 100644 --- a/tests/libs/zbxsysinfo/linux/Makefile.am +++ b/tests/libs/zbxsysinfo/linux/Makefile.am @@ -31,7 +31,6 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxsysinfo/alias/libalias.a \ $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ - $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \ $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ @@ -39,6 +38,7 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxhash/libzbxhash.a \ diff --git a/tests/zabbix_server/poller/Makefile.am b/tests/zabbix_server/poller/Makefile.am new file mode 100644 index 00000000000..31f478fa1bb --- /dev/null +++ b/tests/zabbix_server/poller/Makefile.am @@ -0,0 +1,61 @@ +if SERVER +SERVER_tests = zbx_poller_test + +noinst_PROGRAMS = $(SERVER_tests) + +COMMON_SRC_FILES = \ + ../../zbxmocktest.h + +POLLER_LIBS = \ + $(top_srcdir)/tests/libzbxmocktest.a \ + $(top_srcdir)/tests/libzbxmockdata.a \ + $(top_srcdir)/src/libs/zbxsysinfo/libzbxserversysinfo.a \ + $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ + $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ + $(top_srcdir)/src/libs/zbxsysinfo/common/libcommonsysinfo.a \ + $(top_srcdir)/src/libs/zbxsysinfo/common/libcommonsysinfo_httpmetrics.a \ + $(top_srcdir)/src/libs/zbxsysinfo/common/libcommonsysinfo_http.a \ + $(top_srcdir)/src/libs/zbxsysinfo/simple/libsimplesysinfo.a \ + $(top_srcdir)/src/libs/zbxthreads/libzbxthreads.a \ + $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ + $(top_srcdir)/src/libs/zbxsysinfo/alias/libalias.a \ + $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ + $(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \ + $(top_srcdir)/src/libs/zbxexec/libzbxexec.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ + $(top_srcdir)/src/libs/zbxhash/libzbxhash.a \ + $(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a \ + $(top_srcdir)/src/libs/zbxvariant/libzbxvariant.a \ + $(top_srcdir)/src/libs/zbxnum/libzbxnum.a \ + $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ + $(top_srcdir)/src/libs/zbxtime/libzbxtime.a \ + $(top_srcdir)/src/libs/zbxstr/libzbxstr.a \ + $(top_srcdir)/src/libs/zbxip/libzbxip.a \ + $(top_srcdir)/src/libs/zbxparam/libzbxparam.a \ + $(top_srcdir)/src/libs/zbxexpr/libzbxexpr.a \ + $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ + $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ + $(top_srcdir)/src/libs/zbxserialize/libzbxserialize.a \ + $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a + +zbx_poller_test_SOURCES = \ + ../../../src/zabbix_server/poller/checks_ssh.c \ + ../../../src/zabbix_server/poller/checks_telnet.c \ + zbx_poller_test.c \ + test_get_value_ssh.c \ + test_get_value_telnet.c \ + ../../zbxmockexit.c \ + ../../zbxmockfile.c \ + ../../zbxmocklog.c \ + ../../zbxmockdir.c + +zbx_poller_test_LDADD = $(POLLER_LIBS) +zbx_poller_test_LDADD += @SERVER_LIBS@ +zbx_poller_test_LDFLAGS = @SERVER_LDFLAGS@\ + -Wl,--wrap=ssh_run \ + -Wl,--wrap=telnet_run + +zbx_poller_test_CFLAGS = \ + -I@top_srcdir@/tests @LIBXML2_CFLAGS@ +endif diff --git a/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml b/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml index ef12178fa0a..640fcbef4b7 100644 --- a/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml +++ b/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml @@ -965,7 +965,7 @@ in: out: return: SUCCEED value: |- - {"b":[1, 2, 3]} + {"b":[1,2,3]} --- test case: jsonpath5 in: @@ -980,7 +980,7 @@ in: out: return: SUCCEED value: |- - [1, 2, 3] + [1,2,3] --- test case: jsonpath6 in: @@ -1008,7 +1008,7 @@ in: params: $.a['b c'] out: return: SUCCEED - value: '["one", "two", "three"]' + value: '["one","two","three"]' --- test case: jsonpath8 in: -- cgit v1.2.3 From 7f291321db339f39e58666ead8b7699626132fe4 Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Thu, 10 Nov 2022 14:52:15 +0200 Subject: .......... [ZBXNEXT-8040] fixed merge errors --- tests/libs/zbxsysinfo/Makefile.am | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/libs/zbxsysinfo/Makefile.am b/tests/libs/zbxsysinfo/Makefile.am index ba760a0a355..94ebab2fa0b 100644 --- a/tests/libs/zbxsysinfo/Makefile.am +++ b/tests/libs/zbxsysinfo/Makefile.am @@ -107,13 +107,8 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxsysinfo/alias/libalias.a \ $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ -<<<<<<< HEAD - $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxthreads/libzbxthreads.a \ $(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \ -======= - $(top_srcdir)/src/libs/zbxsys/libzbxsys.a \ ->>>>>>> 3385fecdb61 (.......PS. [ZBXNEXT-8040] added object based json parsing and jsonpath optimizations) $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ -- cgit v1.2.3 From ddf2a1991a9c63e2a7d1a470d00ce941bbbb86da Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Thu, 10 Nov 2022 14:59:40 +0200 Subject: .......... [ZBXNEXT-8040] fixed windows build --- build/win32/project/Makefile_agent | 1 - 1 file changed, 1 deletion(-) diff --git a/build/win32/project/Makefile_agent b/build/win32/project/Makefile_agent index 4d0bae5f60d..c96136657f7 100644 --- a/build/win32/project/Makefile_agent +++ b/build/win32/project/Makefile_agent @@ -37,7 +37,6 @@ ADD_RFLAGS = /d "ZABBIX_AGENT" OBJS = \ ..\..\..\src\libs\zbxalgo\algodefs.o \ ..\..\..\src\libs\zbxalgo\vector.o \ - ..\..\..\src\libs\zbxcommon\alias.o \ ..\..\..\src\libs\zbxalgo\hashset.o \ ..\..\..\src\libs\zbxcommon\comms.o \ ..\..\..\src\libs\zbxcommon\iprange.o \ -- cgit v1.2.3 From 3b203a011bf893be4f64103a83795d993a024eb3 Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Thu, 10 Nov 2022 15:16:26 +0200 Subject: .......... [ZBXNEXT-8040] fixed agent2 windows build --- build/mingw/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/mingw/Makefile b/build/mingw/Makefile index 3fa89b23d1c..2923e572a71 100644 --- a/build/mingw/Makefile +++ b/build/mingw/Makefile @@ -147,9 +147,6 @@ $(OUTPUTDIR)\sha256crypt.o: $(TOPDIR)\src\libs\zbxhash\sha256crypt.c $(OUTPUTDIR)\jsonobj.o: $(TOPDIR)\src\libs\zbxjson\jsonobj.c $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ -$(OUTPUTDIR)\sha256crypt.o: $(TOPDIR)\src\libs\zbxcrypto\sha256crypt.c - $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ - $(OUTPUTDIR)\variant.o: $(TOPDIR)\src\libs\zbxvariant\variant.c $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@ -- cgit v1.2.3