From b340c36f79957b7cbb8fa9b162a370ecb0ec8d97 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 31 Dec 2019 13:29:25 +0100 Subject: [PATCH] Experimental optimized XPath, multiple keys --- CHANGELOG.md | 3 +- lib/clixon/clixon.h.in | 1 + lib/clixon/clixon_xml_sort.h | 2 +- lib/clixon/clixon_xpath.h | 18 +- lib/clixon/clixon_xpath_optimize.h | 46 ++++ lib/src/Makefile.in | 4 +- lib/src/clixon_xml.c | 1 + lib/src/clixon_xml_map.c | 21 +- lib/src/clixon_xml_sort.c | 167 ++++++++------ lib/src/clixon_xpath.c | 141 ++++++++---- lib/src/clixon_xpath_eval.c | 139 ++---------- lib/src/clixon_xpath_optimize.c | 341 +++++++++++++++++++++++++++++ lib/src/clixon_yang_module.c | 4 +- test/lib.sh | 6 +- test/mem.sh | 31 +-- test/test_nacm_credentials.sh | 10 - util/clixon_util_xpath.c | 25 ++- 17 files changed, 681 insertions(+), 279 deletions(-) create mode 100644 lib/clixon/clixon_xpath_optimize.h create mode 100644 lib/src/clixon_xpath_optimize.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a0ddc86..ed7c174e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ ### Minor changes * C-API: Added `xpath_first_localonly()` as an xpath function that skips prefix and namespace checks. * Added experimental code for optizing XPath search using binary search. - * Enable with XPATH_LIST_OPTIMIZE + * Enable with XPATH_LIST_OPTIMIZE in include/clixon_custom.h + * Optimizes xpaths on the form: `a[b=c]` on sorted, yangified config lists. * Removed most assert.h includes * Added "canonical" global namespace context: `nsctx_global` * This is a normalized XML prefix:namespace pair vector computed from all loaded Yang modules. Useful when writing XML and XPATH expressions in callbacks. diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index 7de4a0d0..2601839b 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -85,6 +85,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/clixon/clixon_xml_sort.h b/lib/clixon/clixon_xml_sort.h index 83fcac63..d8da6fd8 100644 --- a/lib/clixon/clixon_xml_sort.h +++ b/lib/clixon/clixon_xml_sort.h @@ -45,6 +45,6 @@ int xml_sort(cxobj *x0, void *arg); int xml_insert(cxobj *xp, cxobj *xc, enum insert_type ins, char *key_val, cvec *nsckey); int xml_sort_verify(cxobj *x, void *arg); int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp); -int xml_binsearch(cxobj *xp, char *name, char *keyname, char *keyval, cxobj **xret); +int xml_binsearch(cxobj *xp, yang_stmt *yc, cvec *cvk, cxobj **xretp); #endif /* _CLIXON_XML_SORT_H */ diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index 5c7a76dc..6d4ad064 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -107,13 +107,14 @@ enum xp_type{ */ struct xpath_tree{ enum xp_type xs_type; - int xs_int; /* step-> axis_type */ - double xs_double; - char *xs_strnr; /* original string xs_double: numeric value */ - char *xs_s0; - char *xs_s1; - struct xpath_tree *xs_c0; /* child 0 */ - struct xpath_tree *xs_c1; /* child 1 */ + int xs_int; /* step-> axis_type */ + double xs_double; /* set if XP_PRIME_NR */ + char *xs_strnr; /* original string xs_double: numeric value */ + char *xs_s0; /* set if XP_PRIME_STR, XP_PRIME_FN, XP_NODE[_FN] prefix*/ + char *xs_s1; /* set if XP_NODE NAME */ + struct xpath_tree *xs_c0; /* child 0 */ + struct xpath_tree *xs_c1; /* child 1 */ + int xs_match; /* meta: match this node */ }; typedef struct xpath_tree xpath_tree; @@ -125,7 +126,8 @@ char* xpath_tree_int2str(int nodetype); int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs); int xpath_tree_print(FILE *f, xpath_tree *xs); int xpath_tree2cbuf(xpath_tree *xs, cbuf *xpathcb); -int xpath_tree_eq(xpath_tree *xt1, xpath_tree *xt2, cvec *match); +int xpath_tree_eq(xpath_tree *xt1, xpath_tree *xt2, xpath_tree ***vec, size_t *len); +xpath_tree *xpath_tree_traverse(xpath_tree *xt, ...); int xpath_tree_free(xpath_tree *xs); int xpath_parse(char *xpath, xpath_tree **xptree); int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, int localonly, xp_ctx **xrp); diff --git a/lib/clixon/clixon_xpath_optimize.h b/lib/clixon/clixon_xpath_optimize.h new file mode 100644 index 00000000..c91eebab --- /dev/null +++ b/lib/clixon/clixon_xpath_optimize.h @@ -0,0 +1,46 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2019 Olof Hagsand + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10 + * See XPATH_LIST_OPTIMIZE + */ +#ifndef _CLIXON_XPATH_OPTIMIZE_H +#define _CLIXON_XPATH_OPTIMIZE_H + + +int xpath_list_optimize_stats(int *hits); +int xpath_list_optimize_set(int enable); +void xpath_optimize_exit(void); +int xpath_optimize_check(); + +#endif /* _CLIXON_XPATH_OPTIMIZE_H */ diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index 6e8e5fda..ee84bee9 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -73,8 +73,8 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \ clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \ clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \ clixon_proto.c clixon_proto_client.c \ - clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_sha1.c \ - clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \ + clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_optimize.c \ + clixon_sha1.c clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \ clixon_datastore_tree.c \ clixon_netconf_lib.c clixon_stream.c clixon_nacm.c diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index c1a3c22c..b0982a2e 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -1474,6 +1474,7 @@ xml_find_type_value(cxobj *xt, * @param[in] xt xml tree node * @param[in] prefix Prefix (namespace local name) or NULL * @param[in] name name of xml tree node (eg attr name or "body") + * @param[in] type Matching type or -1 for any * @retval val Pointer to the name string * @retval NULL No such node or no value in node * @code diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 63daa827..4ac40c18 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -690,7 +690,7 @@ check_mandatory(cxobj *xt, if (yt->ys_keyword == Y_LIST && yc->ys_keyword == Y_KEY && yang_config(yt)){ - cvk = yt->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvk = yang_cvec_get(yt); /* Use Y_LIST cache, see ys_populate_list() */ cvi = NULL; while ((cvi = cvec_each(cvk, cvi)) != NULL) { keyname = cv_string_get(cvi); @@ -774,7 +774,7 @@ check_list_key(cxobj *xt, if (yt->ys_keyword == Y_LIST && yc->ys_keyword == Y_KEY && yang_config(yt)){ - cvk = yt->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvk = yang_cvec_get(yt); /* Use Y_LIST cache, see ys_populate_list() */ cvi = NULL; while ((cvi = cvec_each(cvk, cvi)) != NULL) { keyname = cv_string_get(cvi); @@ -1151,7 +1151,7 @@ xml_yang_validate_add(clicon_handle h, /* fall thru */ case Y_LEAF_LIST: /* validate value against ranges, etc */ - if ((cv = cv_dup(yt->ys_cv)) == NULL){ + if ((cv = cv_dup(yang_cv_get(yt))) == NULL){ clicon_err(OE_UNIX, errno, "cv_dup"); goto done; } @@ -1481,7 +1481,7 @@ xml2cvec(cxobj *xt, cv_free(cv); } } - else if ((ycv = ys->ys_cv) != NULL){ + else if ((ycv = yang_cv_get(ys)) != NULL){ if ((body = xml_body(xc)) != NULL){ if ((cv = cv_new(CGV_STRING)) == NULL){ clicon_err(OE_PLUGIN, errno, "cv_new"); @@ -1819,7 +1819,7 @@ yang2api_path_fmt_1(yang_stmt *ys, switch (yang_keyword_get(ys)){ case Y_LIST: - cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */ if (cvec_len(cvk)) cprintf(cb, "="); /* Iterate over individual keys */ @@ -2244,8 +2244,7 @@ xml_default(cxobj *xt, y = ys->ys_stmt[i]; if (y->ys_keyword != Y_LEAF) continue; - assert(y->ys_cv); - if (!cv_flag(y->ys_cv, V_UNSET)){ /* Default value exists */ + if (!cv_flag(yang_cv_get(y), V_UNSET)){ /* Default value exists */ if (!xml_find(xt, y->ys_argument)){ if ((xc = xml_new(y->ys_argument, NULL, y)) == NULL) goto done; @@ -2272,7 +2271,7 @@ xml_default(cxobj *xt, if ((xb = xml_new("body", xc, NULL)) == NULL) goto done; xml_type_set(xb, CX_BODY); - if ((str = cv2str_dup(y->ys_cv)) == NULL){ + if ((str = cv2str_dup(yang_cv_get(y))) == NULL){ clicon_err(OE_UNIX, errno, "cv2str_dup"); goto done; } @@ -2599,7 +2598,7 @@ api_path2xpath_cvv(cvec *api_path, } if ((valvec = clicon_strsep(val, ",", &nvalvec)) == NULL) goto done; - cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvk = yang_cvec_get(y); /* Use Y_LIST cache, see ys_populate_list() */ cvi = NULL; /* Iterate over individual yang keys */ cprintf(xpath, "/"); @@ -2860,7 +2859,7 @@ api_path2xml_vec(char **vec, goto done; break; case Y_LIST: - cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvk = yang_cvec_get(y); /* Use Y_LIST cache, see ys_populate_list() */ if (valvec){ /* loop, valvec may have been used before */ free(valvec); valvec = NULL; @@ -3173,7 +3172,7 @@ xml2api_path_1(cxobj *x, free(enc); break; case Y_LIST: - cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvk = yang_cvec_get(y); /* Use Y_LIST cache, see ys_populate_list() */ if (cvec_len(cvk)) cprintf(cb, "="); /* Iterate over individual keys */ diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index 368c1106..291d3118 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -367,18 +367,19 @@ xml_sort(cxobj *x, } /*! Special case search for ordered-by user where linear sort is used - * @param[in] xp Parent XML node (go through its childre) - * @param[in] x1 XML node to match - * @param[in] yangi Yang order number (according to spec) - * @param[in] mid Where to start from (may be in middle of interval) - * @retval NULL Not found - * @retval x XML element that matches x1 + * @param[in] xp Parent XML node (go through its childre) + * @param[in] x1 XML node to match + * @param[in] yangi Yang order number (according to spec) + * @param[in] mid Where to start from (may be in middle of interval) + * @param[out] xretp XML return object, or NULL + * @retval 0 OK, see xretp */ -static cxobj * +static int xml_search_userorder(cxobj *xp, cxobj *x1, int yangi, - int mid) + int mid, + cxobj **xretp) { int i; cxobj *xc; @@ -389,35 +390,44 @@ xml_search_userorder(cxobj *xp, yc = xml_spec(xc); if (yangi!=yang_order(yc)) break; - if (xml_cmp(xc, x1, 0) == 0) - return xc; + if (xml_cmp(xc, x1, 0) == 0){ + *xretp = xc; + goto done; + } } for (i=mid-1; i>=0; i--){ /* Then decrement */ xc = xml_child_i(xp, i); yc = xml_spec(xc); if (yangi!=yang_order(yc)) break; - if (xml_cmp(xc, x1, 0) == 0) - return xc; + if (xml_cmp(xc, x1, 0) == 0){ + *xretp = xc; + goto done; + } } - return NULL; /* Not found */ + *xretp = NULL; + done: + return 0; } -/*! - * @param[in] xp Parent xml node. - * @param[in] x1 Find this object among xp:s children - * @param[in] userorder If x1 is ordered by user - * @param[in] yangi Yang order - * @param[in] low Lower bound of childvec search interval - * @param[in] upper Lower bound of childvec search interval +/*! Find XML child under xp matching x1 using binary search + * @param[in] xp Parent xml node. + * @param[in] x1 Find this object among xp:s children + * @param[in] userorder If x1 is ordered by user + * @param[in] yangi Yang order + * @param[in] low Lower bound of childvec search interval + * @param[in] upper Lower bound of childvec search interval + * @param[out] xretp XML return object, or NULL + * @retval 0 OK, see xretp */ -static cxobj * +static int xml_search1(cxobj *xp, cxobj *x1, int userorder, int yangi, int low, - int upper) + int upper, + cxobj **xretp) { int mid; int cmp; @@ -425,44 +435,52 @@ xml_search1(cxobj *xp, yang_stmt *y; if (upper < low) - return NULL; /* not found */ + goto notfound; mid = (low + upper) / 2; if (mid >= xml_child_nr(xp)) /* beyond range */ - return NULL; + goto notfound; xc = xml_child_i(xp, mid); if ((y = xml_spec(xc)) == NULL) - return NULL; + goto notfound; cmp = yangi-yang_order(y); /* Here is right yang order == same yang? */ if (cmp == 0){ cmp = xml_cmp(x1, xc, 0); - if (cmp && userorder) /* Ordered by user (if not equal) */ - return xml_search_userorder(xp, x1, yangi, mid); + if (cmp && userorder){ /* Ordered by user (if not equal) */ + xml_search_userorder(xp, x1, yangi, mid, xretp); + goto done; + } } if (cmp == 0) - return xc; + *xretp = xc; else if (cmp < 0) - return xml_search1(xp, x1, userorder, yangi, low, mid-1); + xml_search1(xp, x1, userorder, yangi, low, mid-1, xretp); else - return xml_search1(xp, x1, userorder, yangi, mid+1, upper); - return NULL; + xml_search1(xp, x1, userorder, yangi, mid+1, upper, xretp); + done: + return 0; + notfound: + *xretp = NULL; + goto done; } /*! Find XML child under xp matching x1 using binary search - * @param[in] xp Parent xml node. - * @param[in] x1 Find this object among xp:s children - * @param[in] yc Yang spec of x1 + * @param[in] xp Parent xml node. + * @param[in] x1 Find this object among xp:s children + * @param[in] yc Yang spec of x1 + * @param[out] xretp XML return object, or NULL + * @retval 0 OK, see xretp */ -static cxobj * +static int xml_search(cxobj *xp, cxobj *x1, - yang_stmt *yc) + yang_stmt *yc, + cxobj **xretp) { cxobj *xa; int low = 0; int upper = xml_child_nr(xp); int userorder=0; - cxobj *xret = NULL; int yangi; /* Assume if there are any attributes, they are first in the list, mask @@ -476,8 +494,7 @@ xml_search(cxobj *xp, else if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) userorder = (yang_find(yc, Y_ORDERED_BY, "user") != NULL); yangi = yang_order(yc); - xret = xml_search1(xp, x1, userorder, yangi, low, upper); - return xret; + return xml_search1(xp, x1, userorder, yangi, low, upper, xretp); } /*! Insert xn in xp:s sorted child list (special case of ordered-by user) @@ -829,7 +846,7 @@ match_base_child(cxobj *x0, break; } /* Get match. */ - x0c = xml_search(x0, x1c, yc); + xml_search(x0, x1c, yc, &x0c); ok: *x0cp = x0c; retval = 0; @@ -837,53 +854,73 @@ match_base_child(cxobj *x0, } /*! Experimental API for binary search + * + * Create a temporary search object: a list (xc) with a key (xk) and call the binary search. * @param[in] xp Parent xml node. - * @param[in] name Node name of child (list) - * @param[in] keyname Yang list key name - * @param[in] keyval XML key value + * @param[in] yc Yang spec of list child + * @param[in] cvk List of keys and values as CLIgen vector * @param[out] xret Found XML object, NULL if not founs * @retval 0 OK, see xret + * @code + * cvec *cvk = NULL; vector of index keys + * ... Populate cvk with key/values eg a:5 b:6 + * if (xml_binsearch(xp, yc, cvk, xp) < 0) + * err; + * @endcode * @retval -1 Error - * Multiple keys? * Can extend to leaf-list? */ int -xml_binsearch(cxobj *xp, - char *name, - char *keyname, - char *keyval, - cxobj **xretp) +xml_binsearch(cxobj *xp, + yang_stmt *yc, + cvec *cvk, + cxobj **xretp) { int retval = -1; cxobj *xc = NULL; - // cxobj *xa = NULL; - cxobj *xret = NULL; - yang_stmt *yp; - yang_stmt *yc; + cxobj *xk; + cg_var *cvi = NULL; + cbuf *cb = NULL; + yang_stmt *yk; + char *name; - if ((yp = xml_spec(xp)) == NULL){ + if (yc == NULL || xml_spec(xp) == NULL){ clicon_err(OE_YANG, ENOENT, "yang spec not found"); goto done; } - if ((yc = yang_find(yp, 0, name)) == NULL){ - clicon_err(OE_YANG, ENOENT, "yang not found"); + name = yang_argument_get(yc); + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - if (xml_parse_va(&xc, yc, "<%s><%s>%s", name, keyname, keyval, keyname, name) < 0) + cprintf(cb, "<%s>", name); + cvi = NULL; + while ((cvi = cvec_each(cvk, cvi)) != NULL) { + cprintf(cb, "<%s>%s", + cv_name_get(cvi), + cv_string_get(cvi), + cv_name_get(cvi)); + } + cprintf(cb, "", name); + if (xml_parse_string(cbuf_get(cb), yc, &xc) < 0) goto done; if (xml_rootchild(xc, 0, &xc) < 0) goto done; -#if 0 - if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, ys_spec(yc)) < 0) - goto done; -#else if (xml_spec_set(xc, yc) < 0) goto done; -#endif - xret = xml_search(xp, xc, yc); - *xretp = xret; /* XXX possibly use *xretp directly */ - retval = 0; + xk = NULL; + while ((xk = xml_child_each(xc, xk, CX_ELMNT)) != NULL) { + if ((yk = yang_find(yc, Y_LEAF, xml_name(xk))) == NULL){ + clicon_err(OE_YANG, ENOENT, "yang spec of key %s not found", xml_name(xk)); + goto done; + } + if (xml_spec_set(xk, yk) < 0) + goto done; + } + retval = xml_search(xp, xc, yc, xretp); done: + if (cb) + cbuf_free(cb); if (xc) xml_free(xc); return retval; diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 60f4a07d..9f658c2f 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -306,56 +306,76 @@ xpath_tree2cbuf(xpath_tree *xs, return retval; } +static int +xpath_tree_append(xpath_tree *xt, + xpath_tree ***vec, + size_t *len) +{ + int retval = -1; + + if ((*vec = realloc(*vec, sizeof(xpath_tree *) * (*len+1))) == NULL){ + clicon_err(OE_XML, errno, "realloc"); + goto done; + } + (*vec)[(*len)++] = xt; + retval = 0; + done: + return retval; +} /*! Check if two xpath-trees (parsed xpaths) ar equal * * @param[in] xt1 XPath parse 1 * @param[in] xt2 XPath parse 2 - * @param[in,out] mv Match vector consisting of pairs - * @retval 0 If equal - * @retval -1 If not equal - * The function returns 0 if the two trees are equal, otherwise -1 - * But the "mv" parameter is a way to add pattern matching to some variables - * On input, mv contains a vector of CLIgen string variables with names that may match string - * fields s0 and s1 in an xpath_tree. If the names match the values of s0 or s1, the field is - * considered equal and is stored as value in the CLIgen variable vector. - * Example: - * xt1: _x[_y='_z'] - * xt2: y[k='3'] - * match[in]: {_x, _y, _z} - * match[out]: {_x:y, _y:k, _z:3} + * @param[in,out] vec XPath tree vector + * @param[in,out] len Length of XML XPath vector + * @retval -1 Error + * @retval 0 Not equal + * @retval 1 Equal */ int -xpath_tree_eq(xpath_tree *xt1, - xpath_tree *xt2, - cvec *match) +xpath_tree_eq(xpath_tree *xt1, /* pattern */ + xpath_tree *xt2, + xpath_tree ***vec, + size_t *len) { - int retval = -1; /* not equal */ + int retval = -1; /* Error */ xpath_tree *xc1; xpath_tree *xc2; - cg_var *cv; + int ret; - /* node itself */ - if (xt1->xs_type != xt2->xs_type) + /* node type */ + if (xt1->xs_type != xt2->xs_type +#if 1 /* special case that they are expressions but of different types */ + && !((xt1->xs_type == XP_PRIME_NR || xt1->xs_type == XP_PRIME_STR) && + (xt2->xs_type == XP_PRIME_NR || xt2->xs_type == XP_PRIME_STR)) +#endif + ){ + fprintf(stderr, "%s type %s vs %s\n", __FUNCTION__, + xpath_tree_int2str(xt1->xs_type), + xpath_tree_int2str(xt2->xs_type)); goto neq; - if (xt1->xs_int != xt2->xs_int) + } + /* check match, if set, store and go directly to ok */ + if (xt1->xs_match){ + if (xpath_tree_append(xt2, vec, len) < 0) + goto done; + goto eq; + } + if (xt1->xs_int != xt2->xs_int){ + fprintf(stderr, "%s int\n", __FUNCTION__); goto neq; - if (xt1->xs_double != xt2->xs_double) + } + if (xt1->xs_double != xt2->xs_double){ + fprintf(stderr, "%s double\n", __FUNCTION__); goto neq; + } if (clicon_strcmp(xt1->xs_s0, xt2->xs_s0)){ - if (xt1->xs_s0 && xt2->xs_s0 && - (cv = cvec_find(match, xt1->xs_s0)) != NULL){ - cv_string_set(cv, xt2->xs_s0); - goto ok; - } + fprintf(stderr, "%s s0\n", __FUNCTION__); goto neq; } if (clicon_strcmp(xt1->xs_s1, xt2->xs_s1)){ - if (xt1->xs_s1 && xt2->xs_s1 && - (cv = cvec_find(match, xt1->xs_s1)) != NULL){ - cv_string_set(cv, xt2->xs_s1); - goto ok; - } + fprintf(stderr, "%s s1\n", __FUNCTION__); goto neq; } xc1 = xt1->xs_c0; @@ -363,9 +383,13 @@ xpath_tree_eq(xpath_tree *xt1, if (xc1 == NULL && xc2 == NULL) ; else{ - if (xc1 == NULL || xc2 == NULL) + if (xc1 == NULL || xc2 == NULL){ + fprintf(stderr, "%s NULL\n", __FUNCTION__); goto neq; - if (xpath_tree_eq(xc1, xc2, match)) + } + if ((ret = xpath_tree_eq(xc1, xc2, vec, len)) < 0) + goto done; + if (ret == 0) goto neq; } xc1 = xt1->xs_c1; @@ -373,17 +397,54 @@ xpath_tree_eq(xpath_tree *xt1, if (xc1 == NULL && xc2 == NULL) ; else{ - if (xc1 == NULL || xc2 == NULL) + if (xc1 == NULL || xc2 == NULL){ + fprintf(stderr, "%s NULL\n", __FUNCTION__); goto neq; - if (xpath_tree_eq(xc1, xc2, match)) + } + if ((ret = xpath_tree_eq(xc1, xc2, vec, len)) < 0) + goto done; + if (ret == 0) goto neq; } - ok: - retval = 0; /* equal */ - neq: + eq: + retval = 1; /* equal */ + done: return retval; + neq: + retval = 0; /* not equal */ + goto done; } - + +/*! Traverse through an xpath-tree using indexes + * @param[in] xt Start-tree + * @param[in] i List of indexes terminated by -1: 0 means c0, 1 means c1 + * @retval xc End-tree + */ +xpath_tree * +xpath_tree_traverse(xpath_tree *xt, + ...) +{ + va_list ap; + int i; + xpath_tree *xs = xt; + + va_start(ap, xt); + for (i = va_arg(ap, int); i >= 0; i = va_arg(ap, int)){ + switch (i){ + case 0: + xs = xs->xs_c0; + break; + case 1: + xs = xs->xs_c1; + break; + default: + break; + } + } + va_end(ap); + return xs; +} + /*! Free a xpath_tree * @param[in] xs XPATH tree * @see xpath_parse creates a xpath_tree diff --git a/lib/src/clixon_xpath_eval.c b/lib/src/clixon_xpath_eval.c index 2cf56644..60ee0172 100644 --- a/lib/src/clixon_xpath_eval.c +++ b/lib/src/clixon_xpath_eval.c @@ -85,6 +85,7 @@ #include "clixon_xml_nsctx.h" #include "clixon_xpath_ctx.h" #include "clixon_xpath.h" +#include "clixon_xpath_optimize.h" #include "clixon_xpath_eval.h" /* Mapping between XPATH operator string <--> int */ @@ -277,107 +278,6 @@ nodetest_recursive(cxobj *xn, return retval; } -#ifdef XPATH_LIST_OPTIMIZE -/*! Pattern matching to find fastpath - * - * @param[in] xt XPath tree - * @param[in] xv XML base node - * @param[out] xp XML found node (retval == 1) - * @param[out] key - * @param[out] keyval - * @retval 0 Match - * @retval -1 No match - XPath: - y[k=3] # corresponds to: [=] - */ -static int -xpath_list_optimize(xpath_tree *xt, - cxobj *xv, - cxobj **xp) -{ - int retval = -1; - xpath_tree *xmtop = NULL; /* pattern match tree top */ - xpath_tree *xm = NULL; - cg_var *cv0; - cg_var *cv1; - cg_var *cv2; - cvec *match = NULL; - char *name; - char *keyname; - char *keyval; - yang_stmt *yp; - yang_stmt *yc; - - /* Parse */ - if (xpath_parse("_x[_y='_z']", &xmtop) < 0) /* XXX: "y[k=3]" */ - goto done; - /* Go down two steps */ - if (xmtop && (xm = xmtop->xs_c0) && (xm = xm->xs_c0)) - ; - if (xm == NULL) - goto ok; - /* Create a cvec match vector and initialize variables */ - if ((match = cvec_new(3)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_new"); - goto done; - } - cv0 = cvec_i(match, 0); - cv_name_set(cv0, "_x"); - cv_type_set(cv0, CGV_STRING); - cv1 = cvec_i(match, 1); - cv_name_set(cv1, "_y"); - cv_type_set(cv1, CGV_STRING); - cv2 = cvec_i(match, 2); - cv_name_set(cv2, "_z"); - cv_type_set(cv2, CGV_STRING); - - /* Check if equal */ - if (xpath_tree_eq(xm, xt, match) != 0) - goto ok; /* no match */ - /* Extract variables XXX strdups */ - name = cv_string_get(cv0); - keyname = cv_string_get(cv1); - keyval = cv_string_get(cv2); - assert(name && keyname && keyval); - /* Check yang and that only a list with key as index is a special case can do bin search */ - if ((yp = xml_spec(xv)) == NULL) - goto ok; - if ((yc = yang_find(yp, 0, name)) == NULL) - goto ok; - if (yang_keyword_get(yc) != Y_LIST) - goto ok; -#if 0 - { - cvec *cvv = NULL; - if ((cvv = yang_cvec_get(yc)) == NULL) - goto ok; - if (cvec_len(cvv) != 1) - goto ok; - } -#else - { - int ret; - if ((ret = yang_key_match(yc, keyname)) < 0) - goto done; - if (ret != 1) - goto ok; - } -#endif - if (xml_binsearch(xv, name, keyname, keyval, xp) < 0) - goto done; - retval = 1; /* match */ - done: - if (match) - cvec_free(match); - if (xmtop) - xpath_tree_free(xmtop); - return retval; - ok: /* no match, not special case */ - retval = 0; - goto done; -} -#endif /* XPATH_LIST_OPTIMIZE */ - /*! Evaluate xpath step rule of an XML tree * * @param[in] xc0 Incoming context @@ -408,9 +308,7 @@ xp_eval_step(xp_ctx *xc0, size_t veclen = 0; xpath_tree *nodetest = xs->xs_c0; xp_ctx *xc = NULL; -#ifdef XPATH_LIST_OPTIMIZE int ret; -#endif /* Create new xc */ if ((xc = ctx_dup(xc0)) == NULL) @@ -441,29 +339,25 @@ xp_eval_step(xp_ctx *xc0, else for (i=0; ixc_size; i++){ xv = xc->xc_nodeset[i]; x = NULL; -#ifdef XPATH_LIST_OPTIMIZE - /* Identify XPATH special cases and if match, use binary search. - * it returns: -1 fatal error, quit - * 0: not special case, do normal processing - * 1: special case, use x (found if != NULL) - */ - if ((ret = xpath_list_optimize(xs, xv, &x)) < 0) - goto done; - if (ret == 1){ - fprintf(stderr, "XPATH_LIST_OPTIMIZE\n"); - if (x) + if ((ret = xpath_optimize_check(xs, xv, &x)) < 0) + goto done; + switch (ret){ + case 1: /* optimized */ + if (x) /* keep only x */ if (cxvec_append(x, &vec, &veclen) < 0) goto done; - } - else -#endif - while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) { - /* xs->xs_c0 is nodetest */ - if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){ - if (cxvec_append(x, &vec, &veclen) < 0) - goto done; + break; + case 0: /* regular code */ + if (ret == 0){ /* No optimization made */ + while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) { + /* xs->xs_c0 is nodetest */ + if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){ + if (cxvec_append(x, &vec, &veclen) < 0) + goto done; + } } } + } /* switch */ } } ctx_nodeset_replace(xc, vec, veclen); @@ -1207,3 +1101,4 @@ xp_eval(xp_ctx *xc, return retval; } /* xp_eval */ + diff --git a/lib/src/clixon_xpath_optimize.c b/lib/src/clixon_xpath_optimize.c new file mode 100644 index 00000000..cdfceafd --- /dev/null +++ b/lib/src/clixon_xpath_optimize.c @@ -0,0 +1,341 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, indicate + your decision by deleting the provisions above and replace them with the + notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10 + * See XPATH_LIST_OPTIMIZE + */ + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* NaN */ + +/* cligen */ +#include + +/* clicon */ +#include "clixon_err.h" +#include "clixon_log.h" +#include "clixon_string.h" +#include "clixon_queue.h" +#include "clixon_hash.h" +#include "clixon_handle.h" +#include "clixon_yang.h" +#include "clixon_xml.h" +#include "clixon_xml_sort.h" +#include "clixon_xml_nsctx.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" +#include "clixon_xpath_optimize.h" + + +#ifdef XPATH_LIST_OPTIMIZE +static xpath_tree *_xmtop = NULL; /* pattern match tree top */ +static xpath_tree *_xm = NULL; +static xpath_tree *_xe = NULL; +static int _optimize_enable = 1; +static int _optimize_hits = 0; +#endif /* XPATH_LIST_OPTIMIZE */ + + +/* XXX development in clixon_xpath_eval */ +int +xpath_list_optimize_stats(int *hits) +{ +#ifdef XPATH_LIST_OPTIMIZE + *hits = _optimize_hits; + _optimize_hits = 0; +#endif + return 0; +} + +int +xpath_list_optimize_set(int enable) +{ +#ifdef XPATH_LIST_OPTIMIZE + _optimize_enable = enable; +#endif + return 0; +} + +void +xpath_optimize_exit(void) +{ +#ifdef XPATH_LIST_OPTIMIZE + if (_xmtop) + xpath_tree_free(_xmtop); +#endif +} + +#ifdef XPATH_LIST_OPTIMIZE +/*! Initialize xpath module + * XXX move to clixon_xpath.c + * @see loop_preds + */ +int +xpath_optimize_init(xpath_tree **xm, + xpath_tree **xe) +{ + int retval = -1; + xpath_tree *xs; + + if (_xm == NULL){ + /* Initialize xpath-tree */ + if (xpath_parse("_x[_y='_z']", &_xmtop) < 0) + goto done; + /* Go down two steps */ + if ((_xm = xpath_tree_traverse(_xmtop, 0, 0, -1)) == NULL) + goto done; + /* get nodetest tree (_x) */ + if ((xs = xpath_tree_traverse(_xm, 0, -1)) == NULL) + goto done; + xs->xs_match++; + /* get predicates [_y=_z][z=2] */ + if ((xs = xpath_tree_traverse(_xm, 1, -1)) == NULL) + goto done; + xs->xs_match++; + /* get expression [_y=_z] */ + if ((_xe = xpath_tree_traverse(xs, 1, -1)) == NULL) + goto done; + /* get keyname (_y) */ + if ((xs = xpath_tree_traverse(_xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1)) == NULL) + goto done; + xs->xs_match++; /* in loop_preds get name in xs_s1 XXX: leaf-list is different */ + /* get keyval (_z) */ + if ((xs = xpath_tree_traverse(_xe, 0, 0, 1, 0, 0, 0, -1)) == NULL) + goto done; + xs->xs_match++; /* in loop_preds get value in xs_s0 or xs_strnr */ + } + *xm = _xm; + *xe = _xe; + retval = 0; + done: + return retval; +} + + +/*! Recursive function to loop over all EXPR and pattern match them + * + * @param[in] xt XPath tree of type PRED + * @param[in] xepat Pattern matching XPath tree of type EXPR + * @param[out] cvk Vector of : pairs + * @retval -1 Error + * @retval 0 No match + * @retval 1 Match + * @see xpath_optimize_init + */ +static int +loop_preds(xpath_tree *xt, + xpath_tree *xepat, + cvec *cvk) +{ + int retval = -1; + int ret; + xpath_tree *xe; + xpath_tree **vec = NULL; + size_t veclen = 0; + cg_var *cvi; + + if (xt->xs_type == XP_PRED && xt->xs_c0){ + if ((ret = loop_preds(xt->xs_c0, xepat, cvk)) < 0) + goto done; + if (ret == 0) + goto ok; + } + if ((xe = xt->xs_c1) && (xe->xs_type == XP_EXP)){ + if ((ret = xpath_tree_eq(xepat, xe, &vec, &veclen)) < 0) + goto done; + if (ret == 0) + goto ok; + if (veclen != 2) + goto ok; + if ((cvi = cvec_add(cvk, CGV_STRING)) == NULL){ + clicon_err(OE_XML, errno, "cvec_add"); + goto done; + } + cv_name_set(cvi, vec[0]->xs_s1); + if (vec[1]->xs_type == XP_PRIME_NR) + cv_string_set(cvi, vec[1]->xs_strnr); + else + cv_string_set(cvi, vec[1]->xs_s0); + } + retval = 1; + done: + if (vec) + free(vec); + return retval; + ok: /* no match, not special case */ + retval = 0; + goto done; +} + +/*! Pattern matching to find fastpath + * + * @param[in] xt XPath tree + * @param[in] xv XML base node + * @param[out] xp XML found node (retval == 1) + * @param[out] key + * @param[out] keyval + * @retval -1 Error + * @retval 0 No match - use non-optimized lookup + * @retval 1 Match + * XPath: + * y[k=3] # corresponds to: [=] + */ +static int +xpath_list_optimize_fn(xpath_tree *xt, + cxobj *xv, + cxobj **xp) +{ + int retval = -1; + xpath_tree *xm = NULL; + xpath_tree *xem = NULL; + char *name; + yang_stmt *yp; + yang_stmt *yc; + cvec *cvv = NULL; + xpath_tree **vec = NULL; + size_t veclen = 0; + xpath_tree *xtp; + int ret; + cvec *cvk = NULL; /* vector of index keys */ + cg_var *cvi; + int i; + + /* revert to non-optimized if no yang */ + if ((yp = xml_spec(xv)) == NULL) + goto ok; + /* or if not config data (state data should not be ordered) */ + if (yang_config(yp) == 0) + goto ok; + /* Check yang and that only a list with key as index is a special case can do bin search + * That is, ONLY check optimize cases of this type:_x[_y='_z'] + * Should we extend this simple example and have more cases (all cases?) + */ + xpath_optimize_init(&xm, &xem); + /* Here is where pattern is checked for equality and where variable binding is made (if + * equal) */ + if ((ret = xpath_tree_eq(xm, xt, &vec, &veclen)) < 0) + goto done; + if (ret == 0) + goto ok; /* no match */ + if (veclen != 2) + goto ok; + name = vec[0]->xs_s1; + /* Extract variables XXX strdups */ + if ((yc = yang_find(yp, Y_LIST, name)) == NULL) +#ifdef NOTYET + if ((yc = yang_find(yp, Y_LEAF_LIST, name)) == NULL) /* XXX */ +#endif + goto ok; + /* Validate keys */ + if ((cvv = yang_cvec_get(yc)) == NULL) + goto ok; + xtp = vec[1]; + if ((cvk = cvec_new(0)) == NULL){ + clicon_err(OE_YANG, errno, "cvec_new"); + goto done; + } + if ((ret = loop_preds(xtp, xem, cvk)) < 0) + goto done; + if (ret == 0) + goto ok; + + if (cvec_len(cvv) != cvec_len(cvk)) + goto ok; + i = 0; + cvi = NULL; + while ((cvi = cvec_each(cvk, cvi)) != NULL) { + if (strcmp(cv_name_get(cvi), cv_string_get(cvec_i(cvv,i)))){ + fprintf(stderr, "%s key name mismatch %s vs %s\n", + __FUNCTION__, cv_name_get(cvi), cv_string_get(cvec_i(cvv,i))); + goto ok; + } + i++; + } + if (xml_binsearch(xv, yc, cvk, xp) < 0) + goto done; + retval = 1; /* match */ + done: + if (vec) + free(vec); + if (cvk) + cvec_free(cvk); + return retval; + ok: /* no match, not special case */ + retval = 0; + goto done; +} +#endif /* XPATH_LIST_OPTIMIZE */ + +/*! Identify XPATH special cases and if match, use binary search. + * + * @retval -1 Error + * @retval 0 Dont optimize: not special case, do normal processing + * @retval 1 Optimization made, special case, use x (found if != NULL) + */ +int +xpath_optimize_check(xpath_tree *xs, + cxobj *xv, + cxobj **xp) + +{ +#ifdef XPATH_LIST_OPTIMIZE + int ret; + + if (!_optimize_enable) + return 0; /* use regular code */ + if ((ret = xpath_list_optimize_fn(xs, xv, xp)) < 0) + return -1; + if (ret == 1){ + _optimize_hits++; + return 1; /* Optimized */ + } + else + return 0; /* use regular code */ +#else + return 0; /* use regular code */ +#endif +} + diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index 5c9af091..4210fa6a 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -320,7 +320,9 @@ yang_modules_state_get(clicon_handle h, /* Build a cb string: ... */ if (yms_build(h, yspec, msid, brief, cb) < 0) goto done; - /* Parse cb, x is on the form: ... */ + /* Parse cb, x is on the form: ... + * Note, list is not sorted since it is state (should not be) + */ if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){ if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) goto done; diff --git a/test/lib.sh b/test/lib.sh index 3e8e3c32..e53e2774 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -78,7 +78,11 @@ testname= : ${RCLOG:=} # Wait after daemons (backend/restconf) start. See mem.sh for valgrind -: ${RCWAIT:=2} +if [ "$(arch)" = "armv7l" ]; then + : ${RCWAIT:=4} +else + : ${RCWAIT:=2} +fi # www user (on linux typically www-data, freebsd www) # could be taken from configure diff --git a/test/mem.sh b/test/mem.sh index 0122ae72..87c01919 100755 --- a/test/mem.sh +++ b/test/mem.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Run valgrind leak test for cli, restconf, netconf or background. # Stop on first error +# Typical run: ./mem.sh 2>&1 | tee mylog # Pattern to run tests, default is all, but you may want to narrow it down : ${pattern:=test_*.sh} @@ -66,6 +67,19 @@ memonce(){ fi } +# Print a line with ==== under +println(){ + str=$1 + echo "$str" + length=$(echo "$str" | wc -c) + let i=1 + while [ $i -lt $length ]; do + echo -n "=" + let i++ + done + echo +} + if [ -z "$*" ]; then cmds="backend restconf cli netconf" else @@ -86,20 +100,7 @@ done testnr=0 for c in $cmds; do if [ $testnr != 0 ]; then echo; fi - echo "Mem test $c begin" - length=$(echo "Mem test $c begin" | wc -c) - let i=1 - while [ $i -lt $length ]; do - echo -n "=" - let i++ - done - echo + println "Mem test $c begin" memonce $c - echo "Mem test $c done" - let i=1 - while [ $i -lt $length ]; do - echo -n "=" - let i++ - done - echo + println "Mem test $c done" done diff --git a/test/test_nacm_credentials.sh b/test/test_nacm_credentials.sh index d0bee929..f67c2bfa 100755 --- a/test/test_nacm_credentials.sh +++ b/test/test_nacm_credentials.sh @@ -177,7 +177,6 @@ OK='^0$' ERROR='^applicationaccess-deniederror' # UNIX socket, no user, loop mode. All fail since null user cant access anything -if true; then new "Credentials: mode=none, fam=UNIX user=none" testrun none "" UNIX $dir/backend.sock "$OK" "" @@ -186,10 +185,8 @@ testrun exact "" UNIX $dir/backend.sock "$OK" "" new "Credentials: mode=except, fam=UNIX user=none" testrun except "" UNIX $dir/backend.sock "$OK" "" -fi # UNIX socket, myuser, loop mode. All should work -if true; then new "Credentials: mode=none, fam=UNIX user=me" testrun none "$USER" UNIX $dir/backend.sock "$OK" "" @@ -198,10 +195,8 @@ testrun exact "$USER" UNIX $dir/backend.sock "$OK" "" new "Credentials: mode=except, fam=UNIX user=me" testrun except "$USER" UNIX $dir/backend.sock "$OK" "" -fi # UNIX socket, admin user. First should work -if true; then new "Credentials: mode=none, fam=UNIX user=admin" testrun none admin UNIX $dir/backend.sock "$OK" "" @@ -210,10 +205,8 @@ testrun exact admin UNIX $dir/backend.sock "$ERROR" "" new "Credentials: mode=except, fam=UNIX user=admin" testrun except admin UNIX $dir/backend.sock "$ERROR" "" -fi # UNIX socket, admin user. sudo self to root. First and last should work -if true; then new "Credentials: mode=none, fam=UNIX user=admin sudo" testrun none admin UNIX $dir/backend.sock "$OK" sudo @@ -222,10 +215,8 @@ testrun exact admin UNIX $dir/backend.sock "$ERROR" sudo new "Credentials: mode=except, fam=UNIX user=admin sudo" testrun except admin UNIX $dir/backend.sock "$OK" sudo -fi # IPv4 socket, admin user. First should work -if true; then new "Credentials: mode=none, fam=UNIX user=admin sudo" testrun none $USER IPv4 127.0.0.1 "$OK" "" @@ -234,6 +225,5 @@ testrun exact $USER IPv4 127.0.0.1 "$ERROR" "" new "Credentials: mode=except, fam=UNIX user=admin sudo" testrun except $USER IPv4 127.0.0.1 "$ERROR" "" -fi rm -rf $dir diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index 184bf9ff..7e9e30b1 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -57,6 +57,9 @@ See https://www.w3.org/TR/xpath/ /* clixon */ #include "clixon/clixon.h" +/* Command line options to be passed to getopt(3) */ +#define XPATH_OPTS "hD:f:p:i:n:cy:Y:x" + static int usage(char *argv0) { @@ -71,6 +74,7 @@ usage(char *argv0) "\t-c \t\tMap xpath to canonical form\n" "\t-y \tYang filename or dir (load all files)\n" "\t-Y \tYang dirs (can be several)\n" + "\t-x \t\tXPath optimize\n" "and the following extra rules:\n" "\tif -f is not given, XML input is expected on stdin\n" "\tif -p is not given, is expected as the first line on stdin\n" @@ -141,7 +145,7 @@ main(int argc, optind = 1; opterr = 0; - while ((c = getopt(argc, argv, "hD:f:p:i:n:cy:Y:")) != -1) + while ((c = getopt(argc, argv, XPATH_OPTS)) != -1) switch (c) { case 'h': usage(argv0); @@ -193,6 +197,10 @@ main(int argc, if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0) goto done; break; + case 'x': /* xpath optimize. Only if XPATH_LIST_OPTIMIZE is set */ + + xpath_list_optimize_set(1); + break; default: usage(argv[0]); break; @@ -315,10 +323,23 @@ main(int argc, } else x = x0; +#ifdef XPATH_LIST_OPTIMIZE /* Experimental */ + { + int hits = 0; + int j; - /* Parse XPATH (use nsc == NULL to indicate dont use) */ + xpath_list_optimize_stats(&hits); + for (j=0;j<1;j++){ + if (xpath_vec_ctx(x, nsc, xpath, 0, &xc) < 0) + return -1; + } + xpath_list_optimize_stats(&hits); + fprintf(stderr, "hits after:%d\n", hits); + } +#else if (xpath_vec_ctx(x, nsc, xpath, 0, &xc) < 0) return -1; +#endif /* Print results */ cb = cbuf_new(); ctx_print2(cb, xc);