data tree BUGFIX keep first sibling pointer correct
In case it is supposed to point to the first data sibling
of a module, do not break this.
diff --git a/src/tree_data.c b/src/tree_data.c
index f57c274..7cb0e45 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -354,8 +354,13 @@
(validate_options & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
+ /* our first module node pointer may no longer be the first */
+ while (*first2 && (*first2)->prev->next && (lyd_owner_module(*first2) == lyd_owner_module((*first2)->prev))) {
+ *first2 = (*first2)->prev;
+ }
+
/* finish incompletely validated terminal values/attributes and when conditions */
- ret = lyd_validate_unres(tree, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types, NULL);
+ ret = lyd_validate_unres(first2, mod, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types, NULL);
LY_CHECK_GOTO(ret, cleanup);
/* perform final validation that assumes the data tree is final */
@@ -1568,7 +1573,7 @@
}
/* resolve when and remove any invalid defaults */
- LY_CHECK_GOTO(ret = lyd_validate_unres(&tree, &node_when, NULL, NULL, diff), cleanup);
+ LY_CHECK_GOTO(ret = lyd_validate_unres(&tree, NULL, &node_when, NULL, NULL, diff), cleanup);
cleanup:
ly_set_erase(&node_when, NULL);
@@ -1634,7 +1639,7 @@
LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, NULL, &node_when, implicit_options, diff), cleanup);
/* resolve when and remove any invalid defaults */
- LY_CHECK_GOTO(ret = lyd_validate_unres(tree, &node_when, NULL, NULL, diff), cleanup);
+ LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, &node_when, NULL, NULL, diff), cleanup);
/* process nested nodes */
LY_LIST_FOR(*tree, root) {
diff --git a/src/tree_data.h b/src/tree_data.h
index ee28c41..883929e 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -1074,7 +1074,9 @@
/**
* @brief Add any missing implicit nodes. Default nodes with a false "when" are not added.
*
- * @param[in,out] tree Tree to add implicit nodes into.
+ * @param[in,out] tree Tree to add implicit nodes into. Note that in case a first top-level sibling is used,
+ * it may no longer be first if an implicit node was inserted before @p tree. Use ::lyd_first_sibling() to
+ * adjust @p tree in these cases.
* @param[in] ctx libyang context, must be set only if @p tree is an empty tree.
* @param[in] implicit_options Options for implicit node creation, see @ref implicitoptions.
* @param[out] diff Optional diff with any created nodes.
@@ -1085,7 +1087,9 @@
/**
* @brief Add any missing implicit nodes of one module. Default nodes with a false "when" are not added.
*
- * @param[in,out] tree Tree to add implicit nodes into.
+ * @param[in,out] tree Tree to add implicit nodes into. Note that in case a first top-level sibling is used,
+ * it may no longer be first if an implicit node was inserted before @p tree. Use ::lyd_first_sibling() to
+ * adjust @p tree in these cases.
* @param[in] module Module whose implicit nodes to create.
* @param[in] implicit_options Options for implicit node creation, see @ref implicitoptions.
* @param[out] diff Optional diff with any created nodes.
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index a379400..79d789c 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -397,6 +397,25 @@
}
void
+lyd_del_move_root(struct lyd_node **root, const struct lyd_node *to_del, const struct lys_module *mod)
+{
+ if (*root && (lyd_owner_module(*root) != mod)) {
+ /* there are no data of mod so this is simply the first top-level sibling */
+ mod = NULL;
+ }
+
+ if ((*root != to_del) || (*root)->parent) {
+ return;
+ }
+
+ *root = (*root)->next;
+ if (mod && *root && (lyd_owner_module(to_del) != lyd_owner_module(*root))) {
+ /* there are no more nodes from mod */
+ *root = lyd_first_sibling(*root);
+ }
+}
+
+void
ly_free_prefix_data(LY_PREFIX_FORMAT format, void *prefix_data)
{
struct ly_set *ns_list;
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index cae048e..71681c3 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -60,12 +60,14 @@
ly_bool lyb_has_schema_model(const struct lysc_node *sibling, const struct lys_module **models);
/**
- * @brief Check whether a node to be deleted is the first top-level sibling.
+ * @brief Check whether a node to be deleted is the root node, move it if it is.
*
- * @param[in] first First sibling.
+ * @param[in] root Root sibling.
* @param[in] to_del Node to be deleted.
+ * @param[in] mod If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
+ * the first top-level sibling.
*/
-#define LYD_DEL_IS_ROOT(first, to_del) (((first) == (to_del)) && !(first)->parent && !(first)->prev->next)
+void lyd_del_move_root(struct lyd_node **root, const struct lyd_node *to_del, const struct lys_module *mod);
/**
* @brief Get address of a node's child pointer if any.
diff --git a/src/validation.c b/src/validation.c
index f0320f6..bdfa571 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -152,13 +152,17 @@
* @brief Evaluate when conditions of collected unres nodes.
*
* @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
+ * @param[in] mod Module of the @p tree to take into consideration when deleting @p tree and moving it.
+ * If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
+ * the first top-level sibling.
* @param[in] node_when Set with nodes with "when" conditions.
* @param[in,out] diff Validation diff.
* @return LY_SUCCESS on success.
* @return LY_ERR value on error.
*/
static LY_ERR
-lyd_validate_unres_when(struct lyd_node **tree, struct ly_set *node_when, struct lyd_node **diff)
+lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
+ struct lyd_node **diff)
{
LY_ERR ret;
uint32_t i;
@@ -181,9 +185,7 @@
/* when false */
if (node->flags & LYD_WHEN_TRUE) {
/* autodelete */
- if (LYD_DEL_IS_ROOT(*tree, node)) {
- *tree = (*tree)->next;
- }
+ lyd_del_move_root(tree, node, mod);
if (diff) {
/* add into diff */
LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_DELETE, diff));
@@ -211,8 +213,8 @@
}
LY_ERR
-lyd_validate_unres(struct lyd_node **tree, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *meta_types,
- struct lyd_node **diff)
+lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
+ struct ly_set *node_types, struct ly_set *meta_types, struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
uint32_t i;
@@ -222,7 +224,7 @@
uint32_t prev_count;
do {
prev_count = node_when->count;
- LY_CHECK_RET(lyd_validate_unres_when(tree, node_when, diff));
+ LY_CHECK_RET(lyd_validate_unres_when(tree, mod, node_when, diff));
/* there must have been some when conditions resolved */
} while (prev_count > node_when->count);
@@ -325,12 +327,14 @@
* @brief Validate multiple case data existence with possible autodelete.
*
* @param[in,out] first First sibling to search in, is updated if needed.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
* @param[in] choic Choice node whose cases to check.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_cases(struct lyd_node **first, const struct lysc_node_choice *choic, struct lyd_node **diff)
+lyd_validate_cases(struct lyd_node **first, const struct lys_module *mod, const struct lysc_node_choice *choic,
+ struct lyd_node **diff)
{
const struct lysc_node *scase, *iter, *old_case = NULL, *new_case = NULL;
struct lyd_node *match, *to_del;
@@ -381,9 +385,8 @@
match = NULL;
to_del = NULL;
while ((match = lys_getnext_data(match, *first, &iter, old_case, NULL))) {
- if (LYD_DEL_IS_ROOT(*first, to_del)) {
- *first = (*first)->next;
- }
+ lyd_del_move_root(first, to_del, mod);
+
/* free previous node */
lyd_free_tree(to_del);
if (diff) {
@@ -392,9 +395,7 @@
}
to_del = match;
}
- if (LYD_DEL_IS_ROOT(*first, to_del)) {
- *first = (*first)->next;
- }
+ lyd_del_move_root(first, to_del, mod);
lyd_free_tree(to_del);
}
@@ -439,18 +440,17 @@
*
* @param[in,out] first First sibling, is updated if needed.
* @param[in] node Node instance to delete.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
* @param[in,out] next_p Temporary LY_LIST_FOR_SAFE next pointer, is updated if needed.
* @param[in,out] diff Validation diff.
*/
static void
-lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *node, struct lyd_node **next_p,
- struct lyd_node **diff)
+lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *node, const struct lys_module *mod,
+ struct lyd_node **next_p, struct lyd_node **diff)
{
struct lyd_node *iter;
- if (LYD_DEL_IS_ROOT(*first, node)) {
- *first = (*first)->next;
- }
+ lyd_del_move_root(first, node, mod);
if (node == *next_p) {
*next_p = (*next_p)->next;
}
@@ -473,11 +473,13 @@
*
* @param[in,out] first First sibling to search in, is updated if needed.
* @param[in] node New data node instance to check.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
* @param[in,out] next_p Temporary LY_LIST_FOR_SAFE next pointer, is updated if needed.
* @param[in,out] diff Validation diff.
*/
static void
-lyd_validate_autodel_dup(struct lyd_node **first, struct lyd_node *node, struct lyd_node **next_p, struct lyd_node **diff)
+lyd_validate_autodel_dup(struct lyd_node **first, struct lyd_node *node, const struct lys_module *mod,
+ struct lyd_node **next_p, struct lyd_node **diff)
{
struct lyd_node *match, *next;
@@ -488,7 +490,7 @@
LYD_LIST_FOR_INST_SAFE(*first, node->schema, next, match) {
if ((match->flags & LYD_DEFAULT) && !(match->flags & LYD_NEW)) {
/* default instance found, remove it */
- lyd_validate_autodel_node_del(first, match, next_p, diff);
+ lyd_validate_autodel_node_del(first, match, mod, next_p, diff);
/* remove only a single container/leaf default instance, if there are more, it is an error */
if (node->schema->nodetype & (LYS_LEAF | LYS_CONTAINER)) {
@@ -504,12 +506,13 @@
*
* @param[in,out] first First sibling to search in, is updated if needed.
* @param[in] node Default data node instance to check.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
* @param[in,out] next_p Temporary LY_LIST_FOR_SAFE next pointer, is updated if needed.
* @param[in,out] diff Validation diff.
*/
static void
-lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node *node, struct lyd_node **next_p,
- struct lyd_node **diff)
+lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node *node, const struct lys_module *mod,
+ struct lyd_node **next_p, struct lyd_node **diff)
{
struct lysc_node_choice *choic;
struct lyd_node *iter = NULL;
@@ -540,7 +543,7 @@
if (!iter) {
/* there are only default nodes of the case meaning it does not exist and neither should any default nodes
* of the case, remove this one default node */
- lyd_validate_autodel_node_del(first, node, next_p, diff);
+ lyd_validate_autodel_node_del(first, node, mod, next_p, diff);
}
}
@@ -556,7 +559,7 @@
while (*first && (snode = lys_getnext(snode, sparent, mod ? mod->compiled : NULL, LYS_GETNEXT_WITHCHOICE))) {
/* check case duplicites */
if (snode->nodetype == LYS_CHOICE) {
- LY_CHECK_RET(lyd_validate_cases(first, (struct lysc_node_choice *)snode, diff));
+ LY_CHECK_RET(lyd_validate_cases(first, mod, (struct lysc_node_choice *)snode, diff));
}
}
@@ -573,7 +576,7 @@
if (node->flags & LYD_NEW) {
/* remove old default(s) of the new node if it exists */
- lyd_validate_autodel_dup(first, node, &next, diff);
+ lyd_validate_autodel_dup(first, node, mod, &next, diff);
/* then check new node instance duplicities */
LY_CHECK_RET(lyd_validate_duplicates(*first, node));
@@ -584,7 +587,7 @@
if (node->flags & LYD_DEFAULT) {
/* remove leftover default nodes from a no-longer existing case */
- lyd_validate_autodel_case_dflt(first, node, &next, diff);
+ lyd_validate_autodel_case_dflt(first, node, mod, &next, diff);
}
}
@@ -1342,6 +1345,11 @@
LYD_IMPLICIT_NO_STATE : 0, diff);
LY_CHECK_GOTO(ret, cleanup);
+ /* our first module node pointer may no longer be the first */
+ while (*first2 && (*first2)->prev->next && (lyd_owner_module(*first2) == lyd_owner_module((*first2)->prev))) {
+ *first2 = (*first2)->prev;
+ }
+
/* process nested nodes */
LY_LIST_FOR(*first2, iter) {
ret = lyd_validate_subtree(iter, &node_types, &meta_types, &node_when, val_opts, diff);
@@ -1349,7 +1357,7 @@
}
/* finish incompletely validated terminal values/attributes and when conditions */
- ret = lyd_validate_unres(tree, &node_when, &node_types, &meta_types, diff);
+ ret = lyd_validate_unres(first2, mod, &node_when, &node_types, &meta_types, diff);
LY_CHECK_GOTO(ret, cleanup);
/* perform final validation that assumes the data tree is final */
@@ -1479,7 +1487,7 @@
LY_CHECK_GOTO(ret = lyd_validate_subtree(op_node, &type_check, &type_meta_check, &when_check, 0, diff), cleanup);
/* finish incompletely validated terminal values/attributes and when conditions on the full tree */
- LY_CHECK_GOTO(ret = lyd_validate_unres((struct lyd_node **)&tree, &when_check, &type_check, &type_meta_check,
+ LY_CHECK_GOTO(ret = lyd_validate_unres((struct lyd_node **)&tree, NULL, &when_check, &type_check, &type_meta_check,
diff), cleanup);
/* perform final validation of the operation/notification */
diff --git a/src/validation.h b/src/validation.h
index 9a7a6b0..ae286be 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -43,14 +43,17 @@
* !! It is assumed autodeleted nodes cannot be in the unresolved node type set !!
*
* @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
+ * @param[in] mod Module of the @p tree to take into consideration when deleting @p tree and moving it.
+ * If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
+ * the first top-level sibling.
* @param[in] node_when Set with nodes with "when" conditions, can be NULL.
* @param[in] node_types Set with nodes with unresolved types, can be NULL
* @param[in] meta_types Set with metdata with unresolved types, can be NULL.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
-LY_ERR lyd_validate_unres(struct lyd_node **tree, struct ly_set *node_when, struct ly_set *node_types,
- struct ly_set *meta_types, struct lyd_node **diff);
+LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
+ struct ly_set *node_types, struct ly_set *meta_types, struct lyd_node **diff);
/**
* @brief Validate new siblings. Specifically, check duplicated instances, autodelete default values and cases.
diff --git a/src/xpath.c b/src/xpath.c
index 1fd0e9d..35c29f4 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -8470,6 +8470,14 @@
return LY_EINVAL;
}
+ if (tree) {
+ /* adjust the pointer to be the first top-level sibling */
+ while (tree->parent) {
+ tree = lyd_parent(tree);
+ }
+ tree = lyd_first_sibling(tree);
+ }
+
/* prepare set for evaluation */
memset(set, 0, sizeof *set);
set->type = LYXP_SET_NODE_SET;
diff --git a/src/xpath.h b/src/xpath.h
index d53bceb..4c5db91 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -284,7 +284,7 @@
* @param[in] prefix_data Format-specific prefix data (see ::ly_resolve_prefix).
* @param[in] ctx_node Current (context) data node, NULL in case of the root node.
* @param[in] tree Data tree on which to perform the evaluation, it must include all the available data (including
- * the tree of @p ctx_node).
+ * the tree of @p ctx_node). Can be any node of the tree, it is adjusted.
* @param[out] set Result set.
* @param[in] options Whether to apply some evaluation restrictions.
* @return LY_EVALID for invalid argument types/count,