diff --git a/src/tree_data.c b/src/tree_data.c index f98150a85..d5a13d343 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -3825,17 +3825,65 @@ lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_te return LY_SUCCESS; } +/** + * @brief Traverse through data tree node suitable types and adds leafrefs links to the given nodes + * + * This API requires usage of ::LY_CTX_LEAFREF_LINKING context flag. + * + * @param[in] tree The data tree root node. + * @param[in] cur_node The current data node. + * @param[in] value The current node value. + * @param[in] type The leaf/leaf-list type of given data node. + * + * @return LY_SUCCESS on success. + * @return LY_ERR value on error. + */ +static LY_ERR +lyd_leafref_link_node_tree_type(const struct lyd_node *tree, const struct lyd_node *cur_node, struct lyd_value *value, const struct lysc_type *type) +{ + char *errmsg; + struct ly_set *targets = NULL; + LY_ERR ret = LY_SUCCESS; + struct lysc_type_leafref *lref; + struct lysc_type_union *un; + LY_ARRAY_COUNT_TYPE u; + uint32_t i; + struct lyd_node_term *leafref_node = (struct lyd_node_term *)cur_node; + + if (type->basetype == LY_TYPE_LEAFREF) { + lref = (struct lysc_type_leafref *)type; + ly_set_free(targets, NULL); + if (lyplg_type_resolve_leafref(lref, cur_node, value, tree, NULL, &targets, &errmsg)) { + /* leafref target not found */ + free(errmsg); + } else { + /* leafref target found, link it */ + for (i = 0; i < targets->count; ++i) { + if (targets->dnodes[i]->schema->nodetype & LYD_NODE_TERM) { + ret = lyd_link_leafref_node((struct lyd_node_term *)targets->dnodes[i], leafref_node); + LY_CHECK_GOTO(ret, cleanup); + } + } + } + } else if (type->basetype == LY_TYPE_UNION) { + un = (struct lysc_type_union *)type; + LY_ARRAY_FOR(un->types, u) { + ret = lyd_leafref_link_node_tree_type(tree, cur_node, &leafref_node->value.subvalue->value, un->types[u]); + LY_CHECK_GOTO(ret, cleanup) + } + } + +cleanup: + ly_set_free(targets, NULL); + return ret; +} + LIBYANG_API_DEF LY_ERR lyd_leafref_link_node_tree(const struct lyd_node *tree) { const struct lyd_node *sibling, *elem; - struct ly_set *targets = NULL; - char *errmsg; - struct lyd_node_term *leafref_node; + struct lyd_node_term *cur_node; struct lysc_node_leaf *leaf_schema; - struct lysc_type_leafref *lref; - LY_ERR ret = LY_SUCCESS; - uint32_t i; LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL); @@ -3846,33 +3894,15 @@ lyd_leafref_link_node_tree(const struct lyd_node *tree) LY_LIST_FOR(tree, sibling) { LYD_TREE_DFS_BEGIN(sibling, elem) { if (elem->schema && (elem->schema->nodetype & LYD_NODE_TERM)) { - leafref_node = (struct lyd_node_term *)elem; leaf_schema = (struct lysc_node_leaf *)elem->schema; - - if (leaf_schema->type->basetype == LY_TYPE_LEAFREF) { - lref = (struct lysc_type_leafref *)leaf_schema->type; - ly_set_free(targets, NULL); - if (lyplg_type_resolve_leafref(lref, elem, &leafref_node->value, tree, NULL, &targets, &errmsg)) { - /* leafref target not found */ - free(errmsg); - } else { - /* leafref target found, link it */ - for (i = 0; i < targets->count; ++i) { - if (targets->dnodes[i]->schema->nodetype & LYD_NODE_TERM) { - ret = lyd_link_leafref_node((struct lyd_node_term *)targets->dnodes[i], leafref_node); - LY_CHECK_GOTO(ret, cleanup); - } - } - } - } + cur_node = (struct lyd_node_term *)elem; + LY_CHECK_RET(lyd_leafref_link_node_tree_type(tree, elem, &cur_node->value, leaf_schema->type)); } LYD_TREE_DFS_END(sibling, elem); } } -cleanup: - ly_set_free(targets, NULL); - return ret; + return LY_SUCCESS; } LY_ERR diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c index fa06489e9..00b7b844e 100644 --- a/tests/utests/data/test_tree_data.c +++ b/tests/utests/data/test_tree_data.c @@ -596,7 +596,7 @@ static void test_data_leafref_nodes(void **state) { struct lyd_node *tree, *iter; - struct lyd_node_term *target_node = NULL, *leafref_node; + struct lyd_node_term *target_node = NULL, *leafref_node, *leafref_node2; const struct lyd_leafref_links_rec *rec; const char *schema, *data, *value; @@ -622,6 +622,14 @@ test_data_leafref_nodes(void **state) " path \"../ll\";" " }" " }" + " leaf ref3 {" + " type union {" + " type leafref {" + " path \"../ll\";" + " }" + " type string;" + " }" + " }" "}"; UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); @@ -630,11 +638,12 @@ test_data_leafref_nodes(void **state) "{" " \"test-data-hash:ll\": [\"qwe\", \"asd\"]," " \"test-data-hash:c1\": { \"ref1\": \"qwe\"}," - " \"test-data-hash:ref2\": \"asd\"" + " \"test-data-hash:ref2\": \"asd\"," + " \"test-data-hash:ref3\": \"asd\"" "}"; /* The run must not crash due to the assert that checks the hash. */ - CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); LY_LIST_FOR(tree, iter) { if (strcmp(iter->schema->name, "ll") == 0) { value = lyd_get_value(iter); @@ -645,46 +654,73 @@ test_data_leafref_nodes(void **state) if (strcmp(iter->schema->name, "ref2") == 0) { leafref_node = (struct lyd_node_term *)iter; } + if (strcmp(iter->schema->name, "ref3") == 0) { + leafref_node2 = (struct lyd_node_term *)iter; + } } /* verify state after leafref plugin validation */ assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); - assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); - assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_int_equal(2, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[1], leafref_node); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node2); assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); assert_ptr_equal(rec->target_nodes[0], target_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node2, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); + assert_ptr_equal(rec->target_nodes[0], target_node); /* value modification of target */ assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)target_node, "ASD")); assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node2, &rec)); /* change back to original value */ assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)target_node, "asd")); assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node2, &rec)); /* linking the whole tree again */ assert_int_equal(LY_SUCCESS, lyd_leafref_link_node_tree(tree)); assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); - assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_int_equal(2, LY_ARRAY_COUNT(rec->leafref_nodes)); assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_ptr_equal(rec->leafref_nodes[1], leafref_node2); assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); assert_ptr_equal(rec->target_nodes[0], target_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node2, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); + assert_ptr_equal(rec->target_nodes[0], target_node); /* value modification of leafref */ assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)leafref_node, "qwe")); - assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node2); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node2, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); + assert_ptr_equal(rec->target_nodes[0], target_node); assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)leafref_node, "asd")); - assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node2); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node2, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); + assert_ptr_equal(rec->target_nodes[0], target_node); /* linking the whole tree again */ assert_int_equal(LY_SUCCESS, lyd_leafref_link_node_tree(tree)); assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); - assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); - assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_int_equal(2, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[1], leafref_node); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node2); assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); assert_ptr_equal(rec->target_nodes[0], target_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node2, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); + assert_ptr_equal(rec->target_nodes[0], target_node); /* freeing whole tree */ lyd_free_all(tree); }