Skip to content

Commit f553dd4

Browse files
stewegmichalvasko
authored andcommitted
xpath UPDATE to support usage of union types as deref targets
This patch introduces ability to use union types, which internally uses leafrefs to be used as deref target nodes. This fullfils the last statement of RFC 7950 section 10.3.1. This behavior is also allowed within leafref paths which uses deref function in it. This patch also fixes segfault in case of using cyclic deref paths
1 parent 4ed27e0 commit f553dd4

File tree

3 files changed

+217
-71
lines changed

3 files changed

+217
-71
lines changed

src/path.c

Lines changed: 96 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,95 @@ ly_path_append(const struct ly_ctx *ctx, const struct ly_path *src, struct ly_pa
10201020
return ret;
10211021
}
10221022

1023+
/**
1024+
* @brief Compile deref XPath function into ly_path structure.
1025+
*
1026+
* @param[in] ctx libyang context.
1027+
* @param[in] cur_node Current (original context) node.
1028+
* @param[in] target_node The deref target schema node.
1029+
* @param[in] cur_type The currently evaluated type of deref target node.
1030+
* @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find
1031+
* the top-level node inside the extension instance instead of a module. Note that this is the case not only if
1032+
* the @p ctx_node is NULL, but also if the relative path starting in @p ctx_node reaches the document root
1033+
* via double dots.
1034+
* @param[in] expr Parsed path.
1035+
* @param[in] oper Oper option (@ref path_oper_options).
1036+
* @param[in] target Target option (@ref path_target_options).
1037+
* @param[in] format Format of the path.
1038+
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
1039+
* @param[in] log Whether to generate log message or not
1040+
* @param[in,out] tok_idx Index in @p exp, is adjusted.
1041+
* @param[out] path Compiled path.
1042+
* @return LY_ERR value.
1043+
*/
1044+
static LY_ERR
1045+
ly_path_compile_deref_type(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lysc_node *target_node,
1046+
const struct lysc_type *cur_type, const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
1047+
LY_VALUE_FORMAT format, void *prefix_data, ly_bool log, uint32_t *tok_idx, struct ly_path **path)
1048+
{
1049+
LY_ERR ret = LY_SUCCESS;
1050+
struct lyxp_expr expr2;
1051+
struct ly_path *path2 = NULL;
1052+
const struct lysc_type_union *union_type;
1053+
const struct lysc_type_leafref *lref;
1054+
uint32_t cur_tok_idx = *tok_idx;
1055+
LY_ARRAY_COUNT_TYPE u;
1056+
1057+
if (cur_type->basetype == LY_TYPE_UNION) {
1058+
union_type = (const struct lysc_type_union *)cur_type;
1059+
ret = LY_EVALID;
1060+
LY_ARRAY_FOR(union_type->types, u) {
1061+
*tok_idx = cur_tok_idx;
1062+
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) {
1063+
ret = LY_SUCCESS;
1064+
}
1065+
}
1066+
if (log && ret) {
1067+
LOGVAL_PATH(ctx, cur_node, target_node, LYVE_XPATH, "Deref function target node \"%s\" is union type with no leafrefs.", target_node->name);
1068+
}
1069+
goto cleanup;
1070+
} else if (cur_type->basetype != LY_TYPE_LEAFREF) {
1071+
if (log) {
1072+
LOGVAL_PATH(ctx, cur_node, target_node, LYVE_XPATH, "Deref function target node \"%s\" is not leafref.", target_node->name);
1073+
}
1074+
ret = LY_EVALID;
1075+
goto cleanup;
1076+
}
1077+
lref = (const struct lysc_type_leafref *)cur_type;
1078+
1079+
/* compile dereferenced leafref expression and append it to the path */
1080+
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, target_node, top_ext, lref->path, oper, target, format, prefix_data,
1081+
&path2), cleanup);
1082+
target_node = path2[LY_ARRAY_COUNT(path2) - 1].node;
1083+
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
1084+
ly_path_free(path2);
1085+
path2 = NULL;
1086+
1087+
/* properly parsed path must always continue with ')' and '/' */
1088+
assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
1089+
(*tok_idx)++;
1090+
assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_OPER_PATH));
1091+
(*tok_idx)++;
1092+
1093+
/* prepare expr representing rest of the path after deref */
1094+
expr2.tokens = &expr->tokens[*tok_idx];
1095+
expr2.tok_pos = &expr->tok_pos[*tok_idx];
1096+
expr2.tok_len = &expr->tok_len[*tok_idx];
1097+
expr2.repeat = &expr->repeat[*tok_idx];
1098+
expr2.used = expr->used - *tok_idx;
1099+
expr2.size = expr->size - *tok_idx;
1100+
expr2.expr = expr->expr;
1101+
1102+
/* compile rest of the path and append it to the path */
1103+
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, target_node, top_ext, &expr2, oper, target, format, prefix_data, &path2),
1104+
cleanup);
1105+
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
1106+
1107+
cleanup:
1108+
ly_path_free(path2);
1109+
return ret;
1110+
}
1111+
10231112
/**
10241113
* @brief Compile deref XPath function into ly_path structure.
10251114
*
@@ -1049,7 +1138,6 @@ ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *cur_node
10491138
struct ly_path *path2 = NULL;
10501139
const struct lysc_node *node2;
10511140
const struct lysc_node_leaf *deref_leaf_node;
1052-
const struct lysc_type_leafref *lref;
10531141
uint32_t begin_token;
10541142

10551143
*path = NULL;
@@ -1083,50 +1171,22 @@ ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *cur_node
10831171
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, ctx_node, top_ext, &expr2, oper, target, format, prefix_data,
10841172
&path2), cleanup);
10851173
node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
1086-
if ((node2->nodetype != LYS_LEAF) && (node2->nodetype != LYS_LEAFLIST)) {
1087-
LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is not leaf nor leaflist.",
1174+
if (node2 == ctx_node) {
1175+
LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is node itself.",
10881176
node2->name);
10891177
ret = LY_EVALID;
10901178
goto cleanup;
10911179
}
1092-
deref_leaf_node = (const struct lysc_node_leaf *)node2;
1093-
if (deref_leaf_node->type->basetype != LY_TYPE_LEAFREF) {
1094-
LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is not leafref.", node2->name);
1180+
if ((node2->nodetype != LYS_LEAF) && (node2->nodetype != LYS_LEAFLIST)) {
1181+
LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is not leaf nor leaflist.",
1182+
node2->name);
10951183
ret = LY_EVALID;
10961184
goto cleanup;
10971185
}
1098-
lref = (const struct lysc_type_leafref *)deref_leaf_node->type;
1099-
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
1100-
ly_path_free(path2);
1101-
path2 = NULL;
11021186

1103-
/* compile dereferenced leafref expression and append it to the path */
1104-
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, node2, top_ext, lref->path, oper, target, format, prefix_data,
1105-
&path2), cleanup);
1106-
node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
1107-
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
1108-
ly_path_free(path2);
1109-
path2 = NULL;
1110-
1111-
/* properly parsed path must always continue with ')' and '/' */
1112-
assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
1113-
(*tok_idx)++;
1114-
assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_OPER_PATH));
1115-
(*tok_idx)++;
1116-
1117-
/* prepare expr representing rest of the path after deref */
1118-
expr2.tokens = &expr->tokens[*tok_idx];
1119-
expr2.tok_pos = &expr->tok_pos[*tok_idx];
1120-
expr2.tok_len = &expr->tok_len[*tok_idx];
1121-
expr2.repeat = &expr->repeat[*tok_idx];
1122-
expr2.used = expr->used - *tok_idx;
1123-
expr2.size = expr->size - *tok_idx;
1124-
expr2.expr = expr->expr;
1125-
1126-
/* compile rest of the path and append it to the path */
1127-
LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, node2, top_ext, &expr2, oper, target, format, prefix_data, &path2),
1128-
cleanup);
11291187
LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
1188+
deref_leaf_node = (const struct lysc_node_leaf *)node2;
1189+
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);
11301190

11311191
cleanup:
11321192
ly_path_free(path2);

src/xpath.c

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3980,6 +3980,83 @@ xpath_current(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set,
39803980
return LY_SUCCESS;
39813981
}
39823982

3983+
/**
3984+
* @brief Executes deref function on specific type and value. It performs evaluation in recursive manner,
3985+
* which supports usage of union types as deref targets. Returns LYXP_SET_NODE_SET with either
3986+
* leafref or instance-identifier target node(s).
3987+
*
3988+
* @param[in] leaf The target deref data node
3989+
* @param[in] sleaf The target deref schema node
3990+
* @param[in] value The currently evaluated value of target deref node depending on current type
3991+
* @param[in] cur_type The currently evaluated type of target deref node
3992+
* @param[in] log Whether to generate log message or not
3993+
* @param[in,out] set Context and result set at the same time.
3994+
* @param[in] options XPath options.
3995+
* @return LY_ERR
3996+
*/
3997+
static LY_ERR
3998+
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)
3999+
{
4000+
LY_ERR r;
4001+
LY_ERR ret = LY_SUCCESS;
4002+
char *errmsg = NULL;
4003+
struct lyd_node *node;
4004+
struct ly_set *targets = NULL;
4005+
uint32_t i;
4006+
const struct lysc_type_union *union_type;
4007+
LY_ARRAY_COUNT_TYPE u;
4008+
4009+
if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
4010+
if (cur_type->basetype == LY_TYPE_LEAFREF) {
4011+
/* find leafref target */
4012+
r = lyplg_type_resolve_leafref((struct lysc_type_leafref *)cur_type, &leaf->node, value, set->tree,
4013+
set->ext, &targets, &errmsg);
4014+
if (r) {
4015+
if (log) {
4016+
LOGERR(set->ctx, LY_EINVAL, "%s", errmsg);
4017+
}
4018+
free(errmsg);
4019+
ret = LY_EINVAL;
4020+
goto cleanup;
4021+
}
4022+
4023+
/* insert nodes into set */
4024+
for (i = 0; i < targets->count; ++i) {
4025+
set_insert_node(set, targets->dnodes[i], 0, LYXP_NODE_ELEM, 0);
4026+
}
4027+
} else if (cur_type->basetype == LY_TYPE_INST) {
4028+
if (ly_path_eval(value->target, set->tree, NULL, set->ext, &node)) {
4029+
if (log) {
4030+
LOGERR(set->ctx, LY_EINVAL, "Invalid instance-identifier \"%s\" value - required instance not found.",
4031+
lyd_get_value(&leaf->node));
4032+
}
4033+
ret = LY_EINVAL;
4034+
goto cleanup;
4035+
}
4036+
4037+
/* insert it */
4038+
set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
4039+
} else if (cur_type->basetype == LY_TYPE_UNION) {
4040+
union_type = (const struct lysc_type_union *)cur_type;
4041+
ret = LY_EINVAL;
4042+
LY_ARRAY_FOR(union_type->types, u) {
4043+
if (!xpath_deref_type(leaf, sleaf, &value->subvalue->value, union_type->types[u], 0, set)) {
4044+
ret = LY_SUCCESS;
4045+
goto cleanup;
4046+
}
4047+
}
4048+
if (log) {
4049+
LOGERR(set->ctx, LY_EINVAL, "Invalid leafref or instance-identifier \"%s\" value - required instance not found.",
4050+
lyd_get_value(&leaf->node));
4051+
}
4052+
}
4053+
}
4054+
4055+
cleanup:
4056+
ly_set_free(targets, NULL);
4057+
return ret;
4058+
}
4059+
39834060
/**
39844061
* @brief Execute the YANG 1.1 deref(node-set) function. Returns LYXP_SET_NODE_SET with either
39854062
* leafref or instance-identifier target node(s).
@@ -3998,13 +4075,9 @@ xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set
39984075
struct lysc_type_leafref *lref;
39994076
const struct lysc_node *target;
40004077
struct ly_path *p;
4001-
struct lyd_node *node;
4002-
char *errmsg = NULL;
40034078
uint8_t oper;
40044079
LY_ERR r;
40054080
LY_ERR ret = LY_SUCCESS;
4006-
struct ly_set *targets = NULL;
4007-
uint32_t i;
40084081

40094082
if (options & LYXP_SCNODE_ALL) {
40104083
if (args[0]->type != LYXP_SET_SCNODE_SET) {
@@ -4050,39 +4123,10 @@ xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set
40504123
if (args[0]->used) {
40514124
leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node;
40524125
sleaf = (struct lysc_node_leaf *)leaf->schema;
4053-
if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
4054-
if (sleaf->type->basetype == LY_TYPE_LEAFREF) {
4055-
/* find leafref target */
4056-
r = lyplg_type_resolve_leafref((struct lysc_type_leafref *)sleaf->type, &leaf->node, &leaf->value, set->tree,
4057-
set->ext, &targets, &errmsg);
4058-
if (r) {
4059-
LOGERR(set->ctx, LY_EINVAL, "%s", errmsg);
4060-
free(errmsg);
4061-
ret = LY_EINVAL;
4062-
goto cleanup;
4063-
}
4064-
4065-
/* insert nodes into set */
4066-
for (i = 0; i < targets->count; ++i) {
4067-
set_insert_node(set, targets->dnodes[i], 0, LYXP_NODE_ELEM, 0);
4068-
}
4069-
} else {
4070-
assert(sleaf->type->basetype == LY_TYPE_INST);
4071-
if (ly_path_eval(leaf->value.target, set->tree, NULL, set->ext, &node)) {
4072-
LOGERR(set->ctx, LY_EINVAL, "Invalid instance-identifier \"%s\" value - required instance not found.",
4073-
lyd_get_value(&leaf->node));
4074-
ret = LY_EINVAL;
4075-
goto cleanup;
4076-
}
4077-
4078-
/* insert it */
4079-
set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
4080-
}
4081-
}
4126+
ret = xpath_deref_type(leaf, sleaf, &leaf->value, sleaf->type, 1, set);
40824127
}
40834128

40844129
cleanup:
4085-
ly_set_free(targets, NULL);
40864130
return ret;
40874131
}
40884132

tests/utests/types/leafref.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,45 @@ test_data_xpath_json(void **state)
278278
lyd_free_all(tree);
279279
}
280280

281+
static void
282+
test_data_xpath_deref_union(void **state)
283+
{
284+
const char *schema, *data;
285+
struct lyd_node *tree;
286+
287+
ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED);
288+
289+
/* json xpath test */
290+
schema = MODULE_CREATE_YANG("xp_test",
291+
"list l1 {key k; leaf k {type string;} leaf v {type string;}}"
292+
"list l2 {key k; leaf k {type uint8;} leaf v {type string;}}"
293+
"leaf ref1 {type union {type leafref {path \"../l1/k\";} type leafref {path \"../l2/k\";}}}"
294+
"leaf ref2 {type leafref {path \"deref(../ref1)/../v\";}}"
295+
"leaf ref3 {type union {type leafref {path \"../l1/k\";} type leafref {path \"../l2/k\";}}}"
296+
"leaf ref4 {type leafref {path \"deref(../ref3)/../v\";}}"
297+
"leaf ref5 {type union {type leafref {path \"../l1/k\";} type string;}}"
298+
"leaf ref6 {type leafref {path \"deref(../ref5)/../v\";}}");
299+
300+
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
301+
302+
data = "{"
303+
" \"xp_test:l1\": [{\"k\": \"key1\", \"v\": \"value1\"}, {\"k\": \"key2\", \"v\": \"value2\"}],"
304+
" \"xp_test:l2\": [{\"k\": 1, \"v\": \"value3\" }, {\"k\": 2, \"v\": \"value4\"}],"
305+
" \"xp_test:ref1\": \"key2\","
306+
" \"xp_test:ref2\": \"value2\","
307+
" \"xp_test:ref3\": 2,"
308+
" \"xp_test:ref4\": \"value4\","
309+
" \"xp_test:ref5\": \"key2\","
310+
" \"xp_test:ref6\": \"value2\""
311+
"}";
312+
CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
313+
lyd_free_all(tree);
314+
}
315+
281316
static void
282317
test_xpath_invalid_schema(void **state)
283318
{
284-
const char *schema1, *schema2;
319+
const char *schema1, *schema2, *schema3;
285320

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

309344
UTEST_INVALID_MODULE(schema2, LYS_IN_YANG, NULL, LY_EVALID)
310345
CHECK_LOG_CTX("Deref function target node \"r1\" is not leafref.", "/xp_test:r2", 0);
346+
347+
schema3 = MODULE_CREATE_YANG("xp_test",
348+
"leaf v1 {type string;}"
349+
"leaf r1 {type leafref {path \"deref(../r1)/../v1\";}}");
350+
UTEST_INVALID_MODULE(schema3, LYS_IN_YANG, NULL, LY_EVALID)
351+
CHECK_LOG_CTX("Deref function target node \"r1\" is node itself.", "/xp_test:r1", 0);
311352
}
312353

313354
int
@@ -319,6 +360,7 @@ main(void)
319360
UTEST(test_plugin_lyb),
320361
UTEST(test_plugin_sort),
321362
UTEST(test_data_xpath_json),
363+
UTEST(test_data_xpath_deref_union),
322364
UTEST(test_xpath_invalid_schema)
323365
};
324366

0 commit comments

Comments
 (0)