XPath: refactored XPath match, documented localonly and prefixonly api

This commit is contained in:
Olof hagsand 2024-12-15 11:27:58 +01:00
parent 6c73c36fb7
commit 081a541c6b
13 changed files with 384 additions and 105 deletions

View file

@ -656,6 +656,7 @@ get_list_pagination(clixon_handle h,
int j;
int ret;
dispatcher_entry_t *htable = NULL;
cvec *wherens = NULL;
// int extflag = 0;
#ifdef LIST_PAGINATION_REMAINING
cxobj *xcache;
@ -715,6 +716,8 @@ get_list_pagination(clixon_handle h,
(where = xml_body(x)) != NULL){
if (strcmp(where, "unfiltered") == 0)
where = NULL;
else if (xml_nsctx_node(x, &wherens) < 0)
goto done;
}
/* then the "sort-by" parameter (see Section 3.1.2) */
if ((x = xml_find_type(xe, NULL, "sort-by", CX_ELMNT)) != NULL){
@ -907,6 +910,8 @@ get_list_pagination(clixon_handle h,
ok:
retval = 0;
done:
if (wherens)
cvec_free(wherens);
if (xvec)
free(xvec);
if (cbmsg)

View file

@ -214,3 +214,13 @@
* worse case an overwrite.
*/
#undef SYSTEM_ONLY_CONFIG_CANDIDATE_CLEAR
/*! In full XPath namespace resolve, match even if namespace not resolved
*
* In the case of xpath lookup functions (eg xpath_vec_ctx) where nsc is defined, then
* matching with XML requires equal namespaces.
* However, some code is OK with the XPATH NSC being unresolved to NULL, even if the XML
* namespace is defined.
* This seems wrong and should be changed, but need further investigation
*/
#define XPATH_NS_ACCEPT_UNRESOLVED

View file

@ -153,7 +153,7 @@ static inline int clixon_debug_isset(unsigned n)
}
/* Is detail set ?, return detail level 0-7 */
static inline int clixon_debug_detail()
static inline int clixon_debug_detail(void)
{
unsigned level = clixon_debug_get();

View file

@ -60,7 +60,7 @@ int xml_nsctx_node(cxobj *x, cvec **ncp);
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp);
int xml_nsctx_cbuf(cbuf *cb, cvec *nsc);
int xml2ns(cxobj *x, char *localname, char **ns);
int xml2ns(cxobj *x, char *prefix, char **ns);
int xml2ns_recurse(cxobj *x);
int xmlns_set(cxobj *x, char *prefix, char *ns);
int xmlns_set_all(cxobj *x, cvec *nsc);

View file

@ -134,7 +134,7 @@ int xpath_tree_eq(xpath_tree *xt1, xpath_tree *xt2, xpath_tree ***vec, size_t
xpath_tree *xpath_tree_traverse(xpath_tree *xt, ...);
int xpath_tree_free(xpath_tree *xs);
int xpath_parse(const char *xpath, xpath_tree **xptree);
int xpath_vec_ctx(cxobj *xcur, cvec *nsc, const char *xpath, int localonly, xp_ctx **xrp);
int xpath_vec_ctx(cxobj *xcur, cvec *nsc, const char *xpath, int localonly, xp_ctx **xrp);
int xpath_vec_bool(cxobj *xcur, cvec *nsc, const char *xpformat, ...) __attribute__ ((format (printf, 3, 4)));
int xpath_vec_flag(cxobj *xcur, cvec *nsc, const char *xpformat, uint16_t flags,

View file

@ -58,7 +58,9 @@ enum xp_objtype{
XT_STRING
};
/* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the
/*! XPath context and result
*
* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the
* context is determined for XPath expressions used in XSLT and XPointer respectively. The
* context consists of:
* a node (the context node)

View file

@ -60,7 +60,10 @@
* of the xpath-tree, which is context-dependent.
* Best is to send it as a (read-only) parameter to the xp_eval family of functions
* as an exlicit namespace context.
*
* 3) localonly flag refers to https://www.w3.org/TR/REC-xml-names/, where :
* PrefixedName ::= Prefix ':' LocalPart
* "localonly" means to skip the "Prefix" part, ie namespaces
* see nodetest_eval_node() for the semantics of prefix/namespace processing
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
@ -328,8 +331,8 @@ xpath_tree2cbuf(xpath_tree *xs,
cprintf(xcb, "%s", clicon_int2str(xpopmap, xs->xs_int));
break;
case XP_PATHEXPR:
/* [19] PathExpr ::= | FilterExpr '/' RelativeLocationPath
| FilterExpr '//' RelativeLocationPath
/* [19] PathExpr ::= | FilterExpr '/' RelativeLocationPath
| FilterExpr '//' RelativeLocationPath
*/
if (xs->xs_s0)
cprintf(xcb, "%s", xs->xs_s0);
@ -608,7 +611,7 @@ xpath_parse(const char *xpath,
* @param[in] xcur XML-tree where to search
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPath 1.0 syntax
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @param[in] localonly Skip prefix and namespace tests
* @param[out] xrp Return XPath context
* @retval 0 OK
* @retval -1 Error

View file

@ -44,9 +44,9 @@
* select="/t:top/t:users/t:user[t:name='fred']"/>
* </get-config>
* We need to add namespace context to the cpath tree, typically in eval. How do
* we do that?
* we do that?
* One observation is that the namespace context is static, so it can not be a part
* of the xpath-tree, which is context-dependent.
* of the xpath-tree, which is context-dependent.
* Best is to send it as a (read-only) parameter to the xp_eval family of functions
* as an exlicit namespace context.
* For that you need an API to get/set namespaces: clixon_xml_nscache.c?
@ -86,6 +86,7 @@
#include "clixon_xml_sort.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h"
#include "clixon_string.h"
#include "clixon_xpath.h"
#include "clixon_xpath_optimize.h"
#include "clixon_xpath_function.h"
@ -110,99 +111,148 @@ const map_str2int xpopmap[] = {
{NULL, -1}
};
/*! Eval an XPath nodetest
/*! Eval an XPath nodetest with full namespace test
*
* @retval 1 Match
* @retval 0 No match
* @retval -1 Error XXX: retval -1 not properly handled
* XML x -> prefix1 + name1
* XPATH xs -> prefix2 + name2
* Unless name2=*, if name1 != name2 -> fail
* Lookup(prefix1, XML) -> ns1
* Lookup(prefix2, NSC) -> ns2
* if ns1 = NULL -> fail
* if ns2 = NULL -> fail (see XPATH_NS_ACCEPT_UNRESOLVED)
* if ns1 = ns2 -> match
* otherwise fail
* @param[in] x XML sub-tree given by the the context node
* @param[in] xs XPath stack of type XP_NODE or XP_NODE_FN
* @param[in] nsc XML Namespace context as given by xpath_vec_ctx()
* @retval 1 Match
* @retval 0 No match
* @retval -1 Error
*/
static int
nodetest_eval_node(cxobj *x,
xpath_tree *xs,
cvec *nsc)
nodetest_eval_namespace(cxobj *x,
xpath_tree *xs,
cvec *nsc)
{
int retval = -1;
char *name1 = xml_name(x);
char *prefix1 = xml_prefix(x);
char *nsxml = NULL; /* xml body namespace */
char *nsxpath = NULL; /* xpath context namespace */
char *prefix2 = NULL;
char *name2 = NULL;
int retval = -1;
char *name1;
char *prefix1;
char *prefix2;
char *name2;
char *ns1 = NULL; /* xml namespace */
char *ns2 = NULL; /* xpath namespace */
/* Namespaces is s0, name is s1 */
if (strcmp(xs->xs_s1, "*")==0)
return 1;
/* get namespace of xml tree */
if (xml2ns(x, prefix1, &nsxml) < 0)
goto done;
/* XML x -> prefix1 + name1 */
prefix1 = xml_prefix(x);
name1 = xml_name(x);
/* XPATH xs -> prefix2 + name2 */
prefix2 = xs->xs_s0;
name2 = xs->xs_s1;
/* Before going into namespaces, check name equality and filter out noteq */
if (strcmp(name1, name2) != 0){
retval = 0; /* no match */
clixon_debug(CLIXON_DBG_XPATH | CLIXON_DBG_DETAIL, "%s %s", name1, name2);
if (strcmp(name2, "*") != 0){
/* if name1 != name2 -> fail */
if (strcmp(name1, name2) != 0)
goto fail;
}
/* get namespace of xml tree */
if (xml2ns(x, prefix1, &ns1) < 0)
goto done;
}
/* Here names are equal
* Now look for namespaces
* 1) prefix1 and prefix2 point to same namespace <<-- try this first
* 2) prefix1 is equal to prefix2 <<-- then try this
* (1) is strict yang xml
* (2) without yang
*/
if (nsc != NULL) { /* solution (1) */
nsxpath = xml_nsctx_get(nsc, prefix2);
if (nsxml != NULL && nsxpath != NULL)
retval = (strcmp(nsxml, nsxpath) == 0);
else if (nsxpath == NULL){
/* We have a namespace from xml, but none in yang.
* This can happen in eg augments and ../foo, where foo is
* augmented from another namespace
*/
retval = 1;
}
else
retval = (nsxml == nsxpath); /* True only if both are NULL */
}
else{ /* solution (2) */
if (prefix1 == NULL && prefix2 == NULL)
retval = 1;
else if (prefix1 == NULL || prefix2 == NULL)
retval = 0;
else
retval = strcmp(prefix1, prefix2) == 0;
}
#if 0 /* debugging */
/* If retval == 0 here, then there is name match, but not ns match */
if (retval == 0){
fprintf(stderr, "%s NOMATCH xml: (%s)%s\n\t\t xpath: (%s)%s\n", __FUNCTION__,
name1, nsxml,
name2, nsxpath);
}
if (ns1 == NULL)
goto fail;
ns2 = xml_nsctx_get(nsc, prefix2);
if (ns2 == NULL){
#ifdef XPATH_NS_ACCEPT_UNRESOLVED
goto ok;
#else
goto fail;
#endif
}
else if (strcmp(ns1, ns2) != 0)
goto fail;
#ifdef XPATH_NS_ACCEPT_UNRESOLVED
ok:
#endif
retval = 1;
done: /* retval set in preceding statement */
return retval;
fail:
retval = 0;
goto done;
}
/*! Eval an XPath nodetest but skip namespace tests
*
* XML x -> prefix1 + name1
* XPATH xs -> prefix2 + name2
* Unless name2=*, if name1 != name2 -> fail
* if prefix1 and prefix2 are string-equal or both NULL -> match
* else -> fail
* @param[in] x XML node
* @param[in] xs XPath stack of type XP_NODE or XP_NODE_FN
* @retval 1 Match
* @retval 0 No match
* @retval -1 Error XXX: retval -1 not properly handled
*/
static int
nodetest_eval_prefixonly(cxobj *x,
xpath_tree *xs)
{
int retval = -1;
char *name1;
char *prefix1;
char *prefix2;
char *name2;
int ret;
/* XML x -> prefix1 + name1 */
prefix1 = xml_prefix(x);
name1 = xml_name(x);
/* XPATH xs -> prefix2 + name2 */
prefix2 = xs->xs_s0;
name2 = xs->xs_s1;
clixon_debug(CLIXON_DBG_XPATH | CLIXON_DBG_DETAIL, "%s:%s %s:%s", prefix1, name1, prefix2, name2);
if (strcmp(name2, "*") != 0){
/* if name1 != name2 -> fail */
if (strcmp(name1, name2) != 0)
goto fail;
}
ret = clicon_strcmp(prefix1, prefix2);
if (ret != 0)
goto fail;
retval = 1;
done: /* retval set in preceding statement */
return retval;
fail:
retval = 0;
goto done;
}
/*! Eval an XPath nodetest but skip prefix and namespace tests
*
* This is NOT according to standard
* If name2 = "*" -> match # A node test * is true for any node of the principal node type.
* If name1 = name2 -> match (string-equal or both NULL)
* @param[in] x XML node
* @param[in] xs XPath stack of type XP_NODE or XP_NODE_FN
* @retval 1 Match
* @retval 0 No match
* @retval -1 Error
*/
static int
nodetest_eval_node_localonly(cxobj *x,
xpath_tree *xs,
cvec *nsc)
nodetest_eval_localonly(cxobj *x,
xpath_tree *xs)
{
int retval = -1;
char *name1 = xml_name(x);
char *name2 = NULL;
char *name1;
char *name2;
/* Namespaces is s0, name is s1 */
if (strcmp(xs->xs_s1, "*")==0){
name1 = xml_name(x);
name2 = xs->xs_s1;
clixon_debug(CLIXON_DBG_XPATH | CLIXON_DBG_DETAIL, "%s %s", name1, name2);
if (strcmp(name2, "*")==0){
retval = 1;
goto done;
}
name2 = xs->xs_s1;
/* Before going into namespaces, check name equality and filter out noteq */
/* Check name only */
if (strcmp(name1, name2) == 0){
retval = 1;
goto done;
@ -219,7 +269,7 @@ nodetest_eval_node_localonly(cxobj *x,
* @param[in] nsc XML Namespace context
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @retval 1 Match
* @retval 0 No match
* @retval 0 No match
* @retval -1 Error
* - node() is true for any node of any type whatsoever.
* - text() is true for any text node.
@ -234,9 +284,11 @@ nodetest_eval(cxobj *x,
if (xs->xs_type == XP_NODE){
if (localonly)
retval = nodetest_eval_node_localonly(x, xs, nsc);
retval = nodetest_eval_localonly(x, xs);
else if (nsc == NULL)
retval = nodetest_eval_prefixonly(x, xs);
else
retval = nodetest_eval_node(x, xs, nsc);
retval = nodetest_eval_namespace(x, xs, nsc);
}
else if (xs->xs_type == XP_NODE_FN){
switch (xs->xs_int){
@ -295,7 +347,7 @@ nodetest_recursive(cxobj *xn,
retval = 0;
*vec0 = vec;
*vec0len = veclen;
done:
done:
return retval;
}
@ -309,7 +361,7 @@ nodetest_recursive(cxobj *xn,
* @retval 0 OK
* @retval -1 Error
*
* - A node test that is a QName is true if and only if the type of the node (see [5 Data Model])
* - A node test that is a QName is true if and only if the type of the node (see [5 Data Model])
* is the principal node type and has an expanded-name equal to the expanded-name specified by the QName.
* - A node test * is true for any node of the principal node type.
* - node() is true for any node of any type whatsoever.
@ -1019,7 +1071,7 @@ xp_relop(xp_ctx *xc1,
break;
default:
clixon_err(OE_XML, 0, "Operator %s not supported for nodeset and string", clicon_int2str(xpopmap,op));
goto done;
goto done;
break;
}
if (xr->xc_bool) /* enough to find a single node */
@ -1055,7 +1107,7 @@ xp_relop(xp_ctx *xc1,
break;
default:
clixon_err(OE_XML, 0, "Operator %s not supported for nodeset and number", clicon_int2str(xpopmap,op));
goto done;
goto done;
break;
}
if (xr->xc_bool) /* enough to find a single node */
@ -1203,7 +1255,7 @@ xp_eval(xp_ctx *xc,
goto ok;
break;
case XPATHFN_DEREF:
if (xp_function_deref(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
if (xp_function_deref(xc, xs->xs_c0, nsc, xrp) < 0)
goto done;
goto ok;
break;
@ -1223,7 +1275,7 @@ xp_eval(xp_ctx *xc,
goto ok;
break;
case XPATHFN_POSITION:
if (xp_function_position(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
if (xp_function_position(xc, xs->xs_c0, nsc, xrp) < 0)
goto done;
goto ok;
break;
@ -1288,12 +1340,12 @@ xp_eval(xp_ctx *xc,
goto ok;
break;
case XPATHFN_TRUE:
if (xp_function_true(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
if (xp_function_true(xc, xs->xs_c0, nsc, xrp) < 0)
goto done;
goto ok;
break;
case XPATHFN_FALSE:
if (xp_function_false(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
if (xp_function_false(xc, xs->xs_c0, nsc, xrp) < 0)
goto done;
goto ok;
break;

View file

@ -280,7 +280,6 @@ xp_function_re_match(xp_ctx *xc,
* @param[in] xc0 Incoming context
* @param[in] xs XPath node tree
* @param[in] nsc XML Namespace context
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @param[out] xrp Resulting context
* @retval 0 OK
* @retval -1 Error
@ -290,7 +289,6 @@ int
xp_function_deref(xp_ctx *xc0,
struct xpath_tree *xs,
cvec *nsc,
int localonly,
xp_ctx **xrp)
{
int retval = -1;
@ -587,7 +585,6 @@ xp_function_bit_is_set(xp_ctx *xc,
* @param[in] xc Incoming context
* @param[in] xs XPath node tree
* @param[in] nsc XML Namespace context
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @param[out] xrp Resulting context
* @retval 0 OK
* @retval -1 Error
@ -596,7 +593,6 @@ int
xp_function_position(xp_ctx *xc,
struct xpath_tree *xs,
cvec *nsc,
int localonly,
xp_ctx **xrp)
{
int retval = -1;
@ -1253,7 +1249,6 @@ int
xp_function_true(xp_ctx *xc,
struct xpath_tree *xs,
cvec *nsc,
int localonly,
xp_ctx **xrp)
{
int retval = -1;
@ -1280,7 +1275,6 @@ int
xp_function_false(xp_ctx *xc,
struct xpath_tree *xs,
cvec *nsc,
int localonly,
xp_ctx **xrp)
{
int retval = -1;

View file

@ -95,10 +95,10 @@ const char *xp_fnname_int2str(enum clixon_xpath_function code);
int xp_function_current(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_re_match(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_deref(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_deref(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
int xp_function_derived_from(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, int self, xp_ctx **xrp);
int xp_function_bit_is_set(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_position(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_position(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
int xp_function_count(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_name(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_string(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
@ -109,7 +109,7 @@ int xp_function_string_length(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int
int xp_function_translate(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_boolean(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_not(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_true(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_false(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
int xp_function_true(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
int xp_function_false(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
#endif /* _CLIXON_XPATH_FUNCTION_H */

View file

@ -242,17 +242,17 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
new "A.3.6.2. where, match on descendent string containing a substring"
# bob, eric, alice, lin, joe
# Confusing: all match
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where>.[contains (email-address,'@example.com')]</where></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>bob</member-id>.*<member-id>eric</member-id>.*<member-id>alice</member-id>.*<member-id>lin</member-id>.*<member-id>joe</member-id>"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where xmlns:ex=\"https://example.com/ns/example-social\">.[contains (es:email-address,'@example.com')]</where></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>bob</member-id>.*<member-id>eric</member-id>.*<member-id>alice</member-id>.*<member-id>lin</member-id>.*<member-id>joe</member-id>"
new "A.3.6.3. where, match on decendent timestamp starting with a substring"
# bob, eric, alice, joe,
# starts-with NYI, replaced with contains
# posts//post[starts-with(timestamp,'2020')]
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where>posts/post[contains(timestamp,'2020')]</where></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>bob</member-id>.*<member-id>eric</member-id>.*<member-id>alice</member-id>.*<member-id>joe</member-id>"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where xmlns:ex=\"https://example.com/ns/example-social\">es:posts/es:post[contains(es:timestamp,'2020')]</where></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>bob</member-id>.*<member-id>eric</member-id>.*<member-id>alice</member-id>.*<member-id>joe</member-id>"
new "A.3.9.1. All six parameters at once"
# eric, bob
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where>//post[contains(timestamp,'2020')]</where><sort-by>member-id</sort-by><direction>backwards</direction><offset>2</offset><limit>2</limit></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>eric</member-id>.*<member-id>bob</member-id>"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where xmlns:ex=\"https://example.com/ns/example-social\">//es:post[contains(es:timestamp,'2020')]</where><sort-by>member-id</sort-by><direction>backwards</direction><offset>2</offset><limit>2</limit></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>eric</member-id>.*<member-id>bob</member-id>"
if [ $BE -ne 0 ]; then
new "Kill backend"

View file

@ -447,7 +447,6 @@ new "xpath /root/*/*[.='111']"
expecteof "$clixon_util_xpath -D $DBG -f $xml4 -p /root/*/*[.='111']" 0 "" "nodeset:0:<a>111</a>1:<b>111</b>2:<a>111</a>"
# Try functionnames in place of node nc-names
new "xpath nodetest: node"
expectpart "$($clixon_util_xpath -D $DBG -f $xmlfn -p "count(/root/count)")" 0 "number:1"

214
test/test_xpath_crossref.sh Executable file
View file

@ -0,0 +1,214 @@
#!/usr/bin/env bash
# XPath crossref tests
# Load datastrore XML as follows:
# 1. Without prefix
# 2. With canonical prefix
# 3. With non-canonical prefix
# Access using XPath:
# 1. Without prefix
# 2. With canonical prefix
# 3. With non-canonical prefix
# With namespace context:
# 1. Yes
# 2. No
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
: ${clixon_util_xpath:=clixon_util_xpath}
# XML file (alt provide it in stdin after xpath)
xml1=$dir/xml1.xml
xml2=$dir/xml2.xml
xml3=$dir/xml3.xml
fyang=$dir/clixon-example.yang
cat <<EOF > $fyang
module clixon-example {
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
container table1 {
description "1. Without prefix";
list parameter {
key name;
leaf name {
type string;
}
}
}
container table2 {
description "2. With canonical prefix";
list parameter {
key name;
leaf name {
type string;
}
}
}
container table3 {
description "3. With non-canonical prefix";
list parameter {
key name;
leaf name {
type string;
}
}
}
}
EOF
#
cat <<EOF > $xml1
<table1 xmlns="urn:example:clixon">
<parameter>
<name>A</name>
</parameter>
</table1>
EOF
cat <<EOF > $xml2
<ex:table2 xmlns:ex="urn:example:clixon">
<ex:parameter>
<ex:name>B</ex:name>
</ex:parameter>
</ex:table2>
EOF
cat <<EOF > $xml3
<xe:table3 xmlns:xe="urn:example:clixon">
<xe:parameter>
<xe:name>C</xe:name>
</xe:parameter>
</xe:table3>
EOF
unset y
#-------------------------------------- Name only
prefix=xxx:
localonly="-L"
nsc=
xi=1
xml=$dir/xml$xi.xml
new "Name only $xi"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<parameter><name>A</name></parameter>"
new "Name only $xi"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}xxx $y $nsc)" 0 "nodeset:$"
xi=2
xml=$dir/xml$xi.xml
new "Name only $xi"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
xi=3
xml=$dir/xml$xi.xml
new "Name only $xi"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
#-------------------------------------- Prefix only
localonly=
nsc=
xi=1
xml=$dir/xml$xi.xml
unset prefix
new "Prefix only $xi prefix=$prefix"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<parameter><name>A</name></parameter>"
prefix=xe:
new "Prefix only $xi prefix=$prefix fail"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
xi=2
xml=$dir/xml$xi.xml
unset prefix
new "Prefix only $xi prefix=$prefix"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
prefix=ex:
new "Prefix only $xi prefix=$prefix"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
prefix=xe:
new "Prefix only $xi prefix=$prefix"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
xi=3
xml=$dir/xml$xi.xml
unset prefix
new "Prefix only $xi prefix=$prefix"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
prefix=ex:
new "Prefix only $xi prefix=$prefix"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
prefix=xe:
new "Prefix only $xi prefix=$prefix"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
#-------------------------------------- Namespace
localonly=
nsc="-n null:urn:example:clixon"
xi=1
xml=$dir/xml$xi.xml
prefix=
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<parameter><name>A</name></parameter>"
nsc="-n m:urn:example:clixon"
prefix=m:
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<parameter><name>A</name></parameter>"
if false; then # SEE XPATH_NS_ACCEPT_UNRESOLVED
nsc="-n n:urn:example:clixon"
prefix=m:
new "Namespace $xi prefix=$prefix nsc=$nsc"
#echo "$clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
fi
xi=2
xml=$dir/xml$xi.xml
nsc="-n null:urn:example:clixon"
prefix=
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
nsc="-n ex:urn:example:clixon"
prefix=ex:
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
nsc="-n xe:urn:example:clixon"
prefix=xe:
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
nsc="-n xe:urn:example:xxx"
prefix=xe:
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
xi=3
xml=$dir/xml$xi.xml
nsc="-n null:urn:example:clixon"
prefix=
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
nsc="-n ex:urn:example:clixon"
prefix=ex:
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
nsc="-n xe:urn:example:clixon"
prefix=xe:
new "Namespace $xi prefix=$prefix nsc=$nsc"
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
rm -rf $dir
new "endtest"
endtest