Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 96 additions & 36 deletions src/path.c
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,95 @@ ly_path_append(const struct ly_ctx *ctx, const struct ly_path *src, struct ly_pa
return ret;
}

/**
* @brief Compile deref XPath function into ly_path structure.
*
* @param[in] ctx libyang context.
* @param[in] cur_node Current (original context) node.
* @param[in] target_node The deref target schema node.
* @param[in] cur_type The currently evaluated type of deref target node.
* @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find
* the top-level node inside the extension instance instead of a module. Note that this is the case not only if
* the @p ctx_node is NULL, but also if the relative path starting in @p ctx_node reaches the document root
* via double dots.
* @param[in] expr Parsed path.
* @param[in] oper Oper option (@ref path_oper_options).
* @param[in] target Target option (@ref path_target_options).
* @param[in] format Format of the path.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
* @param[in] log Whether to generate log message or not
* @param[in,out] tok_idx Index in @p exp, is adjusted.
* @param[out] path Compiled path.
* @return LY_ERR value.
*/
static LY_ERR
ly_path_compile_deref_type(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lysc_node *target_node,
const struct lysc_type *cur_type, const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
LY_VALUE_FORMAT format, void *prefix_data, ly_bool log, uint32_t *tok_idx, struct ly_path **path)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr expr2;
struct ly_path *path2 = NULL;
const struct lysc_type_union *union_type;
const struct lysc_type_leafref *lref;
uint32_t cur_tok_idx = *tok_idx;
LY_ARRAY_COUNT_TYPE u;

if (cur_type->basetype == LY_TYPE_UNION) {
union_type = (const struct lysc_type_union *)cur_type;
ret = LY_EVALID;
LY_ARRAY_FOR(union_type->types, u) {
*tok_idx = cur_tok_idx;
if (ly_path_compile_deref_type(ctx, cur_node, target_node, union_type->types[u], top_ext, expr, oper, target, format, prefix_data, 0, tok_idx, path) == LY_SUCCESS) {
ret = LY_SUCCESS;
}
}
if (log && ret) {
LOGVAL_PATH(ctx, cur_node, target_node, LYVE_XPATH, "Deref function target node \"%s\" is union type with no leafrefs.", target_node->name);
}
goto cleanup;
} else if (cur_type->basetype != LY_TYPE_LEAFREF) {
if (log) {
LOGVAL_PATH(ctx, cur_node, target_node, LYVE_XPATH, "Deref function target node \"%s\" is not leafref.", target_node->name);
}
ret = LY_EVALID;
goto cleanup;
}
lref = (const struct lysc_type_leafref *)cur_type;

/* compile dereferenced leafref expression and append it to the path */
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, target_node, top_ext, lref->path, oper, target, format, prefix_data,
&path2), cleanup);
target_node = path2[LY_ARRAY_COUNT(path2) - 1].node;
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
ly_path_free(path2);
path2 = NULL;

/* properly parsed path must always continue with ')' and '/' */
assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
(*tok_idx)++;
assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_OPER_PATH));
(*tok_idx)++;

/* prepare expr representing rest of the path after deref */
expr2.tokens = &expr->tokens[*tok_idx];
expr2.tok_pos = &expr->tok_pos[*tok_idx];
expr2.tok_len = &expr->tok_len[*tok_idx];
expr2.repeat = &expr->repeat[*tok_idx];
expr2.used = expr->used - *tok_idx;
expr2.size = expr->size - *tok_idx;
expr2.expr = expr->expr;

/* compile rest of the path and append it to the path */
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, target_node, top_ext, &expr2, oper, target, format, prefix_data, &path2),
cleanup);
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);

cleanup:
ly_path_free(path2);
return ret;
}

/**
* @brief Compile deref XPath function into ly_path structure.
*
Expand Down Expand Up @@ -1049,7 +1138,6 @@ ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *cur_node
struct ly_path *path2 = NULL;
const struct lysc_node *node2;
const struct lysc_node_leaf *deref_leaf_node;
const struct lysc_type_leafref *lref;
uint32_t begin_token;

*path = NULL;
Expand Down Expand Up @@ -1083,50 +1171,22 @@ ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *cur_node
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, ctx_node, top_ext, &expr2, oper, target, format, prefix_data,
&path2), cleanup);
node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
if ((node2->nodetype != LYS_LEAF) && (node2->nodetype != LYS_LEAFLIST)) {
LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is not leaf nor leaflist.",
if (node2 == ctx_node) {
LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is node itself.",
node2->name);
ret = LY_EVALID;
goto cleanup;
}
deref_leaf_node = (const struct lysc_node_leaf *)node2;
if (deref_leaf_node->type->basetype != LY_TYPE_LEAFREF) {
LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is not leafref.", node2->name);
if ((node2->nodetype != LYS_LEAF) && (node2->nodetype != LYS_LEAFLIST)) {
LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is not leaf nor leaflist.",
node2->name);
ret = LY_EVALID;
goto cleanup;
}
lref = (const struct lysc_type_leafref *)deref_leaf_node->type;
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
ly_path_free(path2);
path2 = NULL;

/* compile dereferenced leafref expression and append it to the path */
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, node2, top_ext, lref->path, oper, target, format, prefix_data,
&path2), cleanup);
node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
ly_path_free(path2);
path2 = NULL;

/* properly parsed path must always continue with ')' and '/' */
assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
(*tok_idx)++;
assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_OPER_PATH));
(*tok_idx)++;

/* prepare expr representing rest of the path after deref */
expr2.tokens = &expr->tokens[*tok_idx];
expr2.tok_pos = &expr->tok_pos[*tok_idx];
expr2.tok_len = &expr->tok_len[*tok_idx];
expr2.repeat = &expr->repeat[*tok_idx];
expr2.used = expr->used - *tok_idx;
expr2.size = expr->size - *tok_idx;
expr2.expr = expr->expr;

/* compile rest of the path and append it to the path */
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, node2, top_ext, &expr2, oper, target, format, prefix_data, &path2),
cleanup);
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
deref_leaf_node = (const struct lysc_node_leaf *)node2;
ret = ly_path_compile_deref_type(ctx, cur_node, node2, deref_leaf_node->type, top_ext, expr, oper, target, format, prefix_data, 1, tok_idx, path);

cleanup:
ly_path_free(path2);
Expand Down
112 changes: 78 additions & 34 deletions src/xpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -3980,6 +3980,83 @@ xpath_current(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set,
return LY_SUCCESS;
}

/**
* @brief Executes deref function on specific type and value. It performs evaluation in recursive manner,
* which supports usage of union types as deref targets. Returns LYXP_SET_NODE_SET with either
* leafref or instance-identifier target node(s).
*
* @param[in] leaf The target deref data node
* @param[in] sleaf The target deref schema node
* @param[in] value The currently evaluated value of target deref node depending on current type
* @param[in] cur_type The currently evaluated type of target deref node
* @param[in] log Whether to generate log message or not
* @param[in,out] set Context and result set at the same time.
* @param[in] options XPath options.
* @return LY_ERR
*/
static LY_ERR
xpath_deref_type(struct lyd_node_term *leaf, struct lysc_node_leaf *sleaf, struct lyd_value *value, struct lysc_type *cur_type, ly_bool log, struct lyxp_set *set)
{
LY_ERR r;
LY_ERR ret = LY_SUCCESS;
char *errmsg = NULL;
struct lyd_node *node;
struct ly_set *targets = NULL;
uint32_t i;
const struct lysc_type_union *union_type;
LY_ARRAY_COUNT_TYPE u;

if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
if (cur_type->basetype == LY_TYPE_LEAFREF) {
/* find leafref target */
r = lyplg_type_resolve_leafref((struct lysc_type_leafref *)cur_type, &leaf->node, value, set->tree,
set->ext, &targets, &errmsg);
if (r) {
if (log) {
LOGERR(set->ctx, LY_EINVAL, "%s", errmsg);
}
free(errmsg);
ret = LY_EINVAL;
goto cleanup;
}

/* insert nodes into set */
for (i = 0; i < targets->count; ++i) {
set_insert_node(set, targets->dnodes[i], 0, LYXP_NODE_ELEM, 0);
}
} else if (cur_type->basetype == LY_TYPE_INST) {
if (ly_path_eval(value->target, set->tree, NULL, set->ext, &node)) {
if (log) {
LOGERR(set->ctx, LY_EINVAL, "Invalid instance-identifier \"%s\" value - required instance not found.",
lyd_get_value(&leaf->node));
}
ret = LY_EINVAL;
goto cleanup;
}

/* insert it */
set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
} else if (cur_type->basetype == LY_TYPE_UNION) {
union_type = (const struct lysc_type_union *)cur_type;
ret = LY_EINVAL;
LY_ARRAY_FOR(union_type->types, u) {
if (!xpath_deref_type(leaf, sleaf, &value->subvalue->value, union_type->types[u], 0, set)) {
ret = LY_SUCCESS;
goto cleanup;
}
}
if (log) {
LOGERR(set->ctx, LY_EINVAL, "Invalid leafref or instance-identifier \"%s\" value - required instance not found.",
lyd_get_value(&leaf->node));
}
}
}

cleanup:
ly_set_free(targets, NULL);
return ret;
}

/**
* @brief Execute the YANG 1.1 deref(node-set) function. Returns LYXP_SET_NODE_SET with either
* leafref or instance-identifier target node(s).
Expand All @@ -3998,13 +4075,9 @@ xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set
struct lysc_type_leafref *lref;
const struct lysc_node *target;
struct ly_path *p;
struct lyd_node *node;
char *errmsg = NULL;
uint8_t oper;
LY_ERR r;
LY_ERR ret = LY_SUCCESS;
struct ly_set *targets = NULL;
uint32_t i;

if (options & LYXP_SCNODE_ALL) {
if (args[0]->type != LYXP_SET_SCNODE_SET) {
Expand Down Expand Up @@ -4050,39 +4123,10 @@ xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set
if (args[0]->used) {
leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
sleaf = (struct lysc_node_leaf *)leaf->schema;
if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
if (sleaf->type->basetype == LY_TYPE_LEAFREF) {
/* find leafref target */
r = lyplg_type_resolve_leafref((struct lysc_type_leafref *)sleaf->type, &leaf->node, &leaf->value, set->tree,
set->ext, &targets, &errmsg);
if (r) {
LOGERR(set->ctx, LY_EINVAL, "%s", errmsg);
free(errmsg);
ret = LY_EINVAL;
goto cleanup;
}

/* insert nodes into set */
for (i = 0; i < targets->count; ++i) {
set_insert_node(set, targets->dnodes[i], 0, LYXP_NODE_ELEM, 0);
}
} else {
assert(sleaf->type->basetype == LY_TYPE_INST);
if (ly_path_eval(leaf->value.target, set->tree, NULL, set->ext, &node)) {
LOGERR(set->ctx, LY_EINVAL, "Invalid instance-identifier \"%s\" value - required instance not found.",
lyd_get_value(&leaf->node));
ret = LY_EINVAL;
goto cleanup;
}

/* insert it */
set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
}
}
ret = xpath_deref_type(leaf, sleaf, &leaf->value, sleaf->type, 1, set);
}

cleanup:
ly_set_free(targets, NULL);
return ret;
}

Expand Down
44 changes: 43 additions & 1 deletion tests/utests/types/leafref.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,45 @@ test_data_xpath_json(void **state)
lyd_free_all(tree);
}

static void
test_data_xpath_deref_union(void **state)
{
const char *schema, *data;
struct lyd_node *tree;

ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED);

/* json xpath test */
schema = MODULE_CREATE_YANG("xp_test",
"list l1 {key k; leaf k {type string;} leaf v {type string;}}"
"list l2 {key k; leaf k {type uint8;} leaf v {type string;}}"
"leaf ref1 {type union {type leafref {path \"../l1/k\";} type leafref {path \"../l2/k\";}}}"
"leaf ref2 {type leafref {path \"deref(../ref1)/../v\";}}"
"leaf ref3 {type union {type leafref {path \"../l1/k\";} type leafref {path \"../l2/k\";}}}"
"leaf ref4 {type leafref {path \"deref(../ref3)/../v\";}}"
"leaf ref5 {type union {type leafref {path \"../l1/k\";} type string;}}"
"leaf ref6 {type leafref {path \"deref(../ref5)/../v\";}}");

UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);

data = "{"
" \"xp_test:l1\": [{\"k\": \"key1\", \"v\": \"value1\"}, {\"k\": \"key2\", \"v\": \"value2\"}],"
" \"xp_test:l2\": [{\"k\": 1, \"v\": \"value3\" }, {\"k\": 2, \"v\": \"value4\"}],"
" \"xp_test:ref1\": \"key2\","
" \"xp_test:ref2\": \"value2\","
" \"xp_test:ref3\": 2,"
" \"xp_test:ref4\": \"value4\","
" \"xp_test:ref5\": \"key2\","
" \"xp_test:ref6\": \"value2\""
"}";
CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
lyd_free_all(tree);
}

static void
test_xpath_invalid_schema(void **state)
{
const char *schema1, *schema2;
const char *schema1, *schema2, *schema3;

ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED);
schema1 = MODULE_CREATE_YANG("xp_test",
Expand All @@ -308,6 +343,12 @@ test_xpath_invalid_schema(void **state)

UTEST_INVALID_MODULE(schema2, LYS_IN_YANG, NULL, LY_EVALID)
CHECK_LOG_CTX("Deref function target node \"r1\" is not leafref.", "/xp_test:r2", 0);

schema3 = MODULE_CREATE_YANG("xp_test",
"leaf v1 {type string;}"
"leaf r1 {type leafref {path \"deref(../r1)/../v1\";}}");
UTEST_INVALID_MODULE(schema3, LYS_IN_YANG, NULL, LY_EVALID)
CHECK_LOG_CTX("Deref function target node \"r1\" is node itself.", "/xp_test:r1", 0);
}

int
Expand All @@ -319,6 +360,7 @@ main(void)
UTEST(test_plugin_lyb),
UTEST(test_plugin_sort),
UTEST(test_data_xpath_json),
UTEST(test_data_xpath_deref_union),
UTEST(test_xpath_invalid_schema)
};

Expand Down