Experimental optimized XPath, multiple keys
This commit is contained in:
parent
7fb452f96e
commit
b340c36f79
17 changed files with 681 additions and 279 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@
|
|||
#include <clixon/clixon_xml_map.h>
|
||||
#include <clixon/clixon_datastore.h>
|
||||
#include <clixon/clixon_xpath_ctx.h>
|
||||
#include <clixon/clixon_xpath_optimize.h>
|
||||
#include <clixon/clixon_xpath.h>
|
||||
#include <clixon/clixon_json.h>
|
||||
#include <clixon/clixon_nacm.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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
46
lib/clixon/clixon_xpath_optimize.h
Normal file
46
lib/clixon/clixon_xpath_optimize.h
Normal file
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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</%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</%s>",
|
||||
cv_name_get(cvi),
|
||||
cv_string_get(cvi),
|
||||
cv_name_get(cvi));
|
||||
}
|
||||
cprintf(cb, "</%s>", 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;
|
||||
|
|
|
|||
|
|
@ -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 <name,val> 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,15 +397,52 @@ 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
|
||||
|
|
|
|||
|
|
@ -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: <name>[<keyname>=<keyval>]
|
||||
*/
|
||||
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; i<xc->xc_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)
|
||||
if ((ret = xpath_optimize_check(xs, xv, &x)) < 0)
|
||||
goto done;
|
||||
if (ret == 1){
|
||||
fprintf(stderr, "XPATH_LIST_OPTIMIZE\n");
|
||||
if (x)
|
||||
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 */
|
||||
|
||||
|
||||
|
|
|
|||
341
lib/src/clixon_xpath_optimize.c
Normal file
341
lib/src/clixon_xpath_optimize.c
Normal file
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <syslog.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h> /* NaN */
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* 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 <keyname>:<keyval> 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: <name>[<keyname>=<keyval>]
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +320,9 @@ yang_modules_state_get(clicon_handle h,
|
|||
/* Build a cb string: <modules-state>... */
|
||||
if (yms_build(h, yspec, msid, brief, cb) < 0)
|
||||
goto done;
|
||||
/* Parse cb, x is on the form: <top><modules-state>... */
|
||||
/* Parse cb, x is on the form: <top><modules-state>...
|
||||
* 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
31
test/mem.sh
31
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
|
||||
|
|
|
|||
|
|
@ -177,7 +177,6 @@ OK='^<rpc-reply><data><x xmlns="urn:example:nacm">0</x></data></rpc-reply>$'
|
|||
ERROR='^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>'
|
||||
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -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 <filename> \tYang filename or dir (load all files)\n"
|
||||
"\t-Y <dir> \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, <xpath> 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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue