xpath BUGFIX faulty logic when moving all the nodes in a set
It is really difficult to make the change properly
in a single set and causes temporary duplicate
items so another set is used.
Fixes sysrepo/sysrepo#2659
diff --git a/src/xpath.c b/src/xpath.c
index 88c9163..fde0dfc 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -1272,31 +1272,6 @@
}
/**
- * @brief Replace a node in a set with another. Context position aware.
- *
- * @param[in] set Set to use.
- * @param[in] node Node to insert to @p set.
- * @param[in] pos Sort position of @p node. If left 0, it is filled just before sorting.
- * @param[in] node_type Node type of @p node.
- * @param[in] idx Index in @p set of the node to replace.
- */
-static void
-set_replace_node(struct lyxp_set *set, const struct lyd_node *node, uint32_t pos, enum lyxp_node_type node_type, uint32_t idx)
-{
- assert(set && (idx < set->used));
-
- if (set->val.nodes[idx].type == LYXP_NODE_ELEM) {
- set_remove_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
- }
- set->val.nodes[idx].node = (struct lyd_node *)node;
- set->val.nodes[idx].type = node_type;
- set->val.nodes[idx].pos = pos;
- if (set->val.nodes[idx].type == LYXP_NODE_ELEM) {
- set_insert_node_hash(set, set->val.nodes[idx].node, set->val.nodes[idx].type);
- }
-}
-
-/**
* @brief Set all nodes with ctx 1 to a new unique context value.
*
* @param[in] set Set to modify.
@@ -1726,33 +1701,6 @@
return ret - 1;
}
-/**
- * @brief Remove duplicate entries in a sorted node set.
- *
- * @param[in] set Sorted set to check.
- * @return LY_ERR (LY_EEXIST if some duplicates are found)
- */
-static LY_ERR
-set_sorted_dup_node_clean(struct lyxp_set *set)
-{
- uint32_t i = 0;
- LY_ERR ret = LY_SUCCESS;
-
- if (set->used > 1) {
- while (i < set->used - 1) {
- if ((set->val.nodes[i].node == set->val.nodes[i + 1].node) &&
- (set->val.nodes[i].type == set->val.nodes[i + 1].type)) {
- set_remove_node_none(set, i + 1);
- ret = LY_EEXIST;
- }
- ++i;
- }
- }
-
- set_remove_nodes_none(set);
- return ret;
-}
-
#endif
/**
@@ -5567,9 +5515,9 @@
static LY_ERR
moveto_node(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname, uint32_t options)
{
- uint32_t i;
+ LY_ERR r, rc = LY_SUCCESS;
const struct lyd_node *siblings, *sub;
- LY_ERR rc;
+ struct lyxp_set result;
if (options & LYXP_SKIP_EXPR) {
return LY_SUCCESS;
@@ -5580,9 +5528,10 @@
return LY_EVALID;
}
- for (i = 0; i < set->used; ) {
- ly_bool replaced = 0;
+ /* init result set */
+ set_init(&result, set);
+ for (uint32_t i = 0; i < set->used; ++i) {
if ((set->val.nodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.nodes[i].type == LYXP_NODE_ROOT)) {
assert(!set->val.nodes[i].node);
@@ -5594,27 +5543,26 @@
}
for (sub = siblings; sub; sub = sub->next) {
- rc = moveto_node_check(sub, set, ncname, moveto_mod, options);
- if (rc == LY_SUCCESS) {
- if (!replaced) {
- set_replace_node(set, sub, 0, LYXP_NODE_ELEM, i);
- replaced = 1;
- } else {
- set_insert_node(set, sub, 0, LYXP_NODE_ELEM, i);
- }
- ++i;
- } else if (rc == LY_EINCOMPLETE) {
- return rc;
+ r = moveto_node_check(sub, set, ncname, moveto_mod, options);
+ if (r == LY_SUCCESS) {
+ /* matching node */
+ set_insert_node(&result, sub, 0, LYXP_NODE_ELEM, result.used);
+ } else if (r == LY_EINCOMPLETE) {
+ rc = r;
+ goto cleanup;
}
}
-
- if (!replaced) {
- /* no match */
- set_remove_node(set, i);
- }
}
- return LY_SUCCESS;
+ /* move result to the set */
+ lyxp_set_free_content(set);
+ *set = result;
+ result.type = LYXP_SET_NUMBER;
+ assert(!set_sort(set));
+
+cleanup:
+ lyxp_set_free_content(&result);
+ return rc;
}
/**
@@ -5634,6 +5582,7 @@
LY_ERR ret = LY_SUCCESS;
uint32_t i;
const struct lyd_node *siblings;
+ struct lyxp_set result;
struct lyd_node *sub, *inst = NULL;
assert(scnode && (!(scnode->nodetype & (LYS_LIST | LYS_LEAFLIST)) || predicates));
@@ -5665,7 +5614,10 @@
LY_CHECK_GOTO(ret = lyd_create_term2(scnode, &predicates[0].value, &inst), cleanup);
}
- for (i = 0; i < set->used; ) {
+ /* init result set */
+ set_init(&result, set);
+
+ for (i = 0; i < set->used; ++i) {
siblings = NULL;
if ((set->val.nodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.nodes[i].type == LYXP_NODE_ROOT)) {
@@ -5693,15 +5645,18 @@
if (sub) {
/* pos filled later */
- set_replace_node(set, sub, 0, LYXP_NODE_ELEM, i);
- ++i;
- } else {
- /* no match */
- set_remove_node(set, i);
+ set_insert_node(&result, sub, 0, LYXP_NODE_ELEM, result.used);
}
}
+ /* move result to the set */
+ lyxp_set_free_content(set);
+ *set = result;
+ result.type = LYXP_SET_NUMBER;
+ assert(!set_sort(set));
+
cleanup:
+ lyxp_set_free_content(&result);
lyd_free_tree(inst);
return ret;
}
@@ -6401,9 +6356,10 @@
static LY_ERR
moveto_parent(struct lyxp_set *set, ly_bool all_desc, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
struct lyd_node *node, *new_node;
enum lyxp_node_type new_type;
+ struct lyxp_set result;
if (options & LYXP_SKIP_EXPR) {
return LY_SUCCESS;
@@ -6420,6 +6376,9 @@
LY_CHECK_RET(rc);
}
+ /* init result set */
+ set_init(&result, set);
+
for (uint32_t i = 0; i < set->used; ++i) {
node = set->val.nodes[i].node;
@@ -6430,39 +6389,45 @@
} else if (set->val.nodes[i].type == LYXP_NODE_META) {
new_node = set->val.meta[i].meta->parent;
if (!new_node) {
- LOGINT_RET(set->ctx);
+ LOGINT(set->ctx);
+ rc = LY_EINT;
+ goto cleanup;
}
} else {
/* root does not have a parent */
- set_remove_node_none(set, i);
continue;
}
/* when check */
- if (!(options & LYXP_IGNORE_WHEN) && new_node && lysc_has_when(new_node->schema) && !(new_node->flags & LYD_WHEN_TRUE)) {
- return LY_EINCOMPLETE;
+ if (!(options & LYXP_IGNORE_WHEN) && new_node && lysc_has_when(new_node->schema) &&
+ !(new_node->flags & LYD_WHEN_TRUE)) {
+ rc = LY_EINCOMPLETE;
+ goto cleanup;
}
if (!new_node) {
/* node already there can also be the root */
new_type = set->root_type;
-
} else {
/* node has a standard parent (it can equal the root, it's not the root yet since they are fake) */
new_type = LYXP_NODE_ELEM;
}
- if (set_dup_node_check(set, new_node, new_type, -1)) {
- set_remove_node_none(set, i);
- } else {
- set_replace_node(set, new_node, 0, new_type, i);
+ /* check for duplicates, several nodes may have the same parent */
+ if (!set_dup_node_check(&result, new_node, new_type, -1)) {
+ set_insert_node(&result, new_node, 0, new_type, result.used);
}
}
- set_remove_nodes_none(set);
- assert(!set_sort(set) && !set_sorted_dup_node_clean(set));
+ /* move result to the set */
+ lyxp_set_free_content(set);
+ *set = result;
+ result.type = LYXP_SET_NUMBER;
+ assert(!set_sort(set));
- return LY_SUCCESS;
+cleanup:
+ lyxp_set_free_content(&result);
+ return rc;
}
/**