Skip to content

Commit b602087

Browse files
committed
diff BUGFIX reverse userord nodes diff reversed order
When reversing diff with user-ordered nodes, their relative order needs to be reversed for the diff to be correct.
1 parent 4801a7f commit b602087

File tree

2 files changed

+240
-92
lines changed

2 files changed

+240
-92
lines changed

src/diff.c

Lines changed: 195 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -3075,117 +3075,220 @@ lyd_diff_reverse_metadata_diff(struct lyd_node *node)
30753075
return rc;
30763076
}
30773077

3078-
LIBYANG_API_DEF LY_ERR
3079-
lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
3078+
/**
3079+
* @brief Process a user-ordered node for a reverse diff.
3080+
*
3081+
* @param[in] node Reversed diff user-ordered node. NULL if only collected nodes should be reversed.
3082+
* @param[in,out] schema_p Current siblings user-ordered schema node.
3083+
* @param[in,out] nodes_p Collected diff nodes of @p schema_p whose order is to be reversed.
3084+
* @return LY_ERR value.
3085+
*/
3086+
static LY_ERR
3087+
lyd_diff_reverse_userord(struct lyd_node *node, const struct lysc_node **schema_p, struct lyd_node ***nodes_p)
30803088
{
30813089
LY_ERR rc = LY_SUCCESS;
3082-
const struct lys_module *mod = NULL;
3083-
struct lyd_node *root, *elem, *iter;
3084-
enum lyd_diff_op op;
3090+
struct lyd_node **ptr, *anchor;
3091+
LY_ARRAY_COUNT_TYPE u;
30853092

3086-
LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
3093+
assert(node || *schema_p);
30873094

3088-
if (!src_diff) {
3089-
*diff = NULL;
3090-
return LY_SUCCESS;
3095+
/* all the schema node instances were collected, reverse their order */
3096+
if (!node || (*schema_p && (node->schema != *schema_p))) {
3097+
/* unlink all the nodes except for the last */
3098+
for (u = 0; u < LY_ARRAY_COUNT(*nodes_p) - 1; ++u) {
3099+
lyd_unlink_tree((*nodes_p)[u]);
3100+
}
3101+
3102+
/* use the last as the anchor, becomes the first node */
3103+
anchor = (*nodes_p)[u];
3104+
3105+
/* link them in reverse order back */
3106+
if (u) {
3107+
do {
3108+
--u;
3109+
LY_CHECK_GOTO(rc = lyd_insert_after(anchor, (*nodes_p)[u]), cleanup);
3110+
} while (u);
3111+
}
3112+
3113+
/* clear the collected nodes */
3114+
*schema_p = NULL;
3115+
LY_ARRAY_FREE(*nodes_p);
3116+
*nodes_p = NULL;
30913117
}
30923118

3093-
/* duplicate diff */
3094-
LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE | LYD_DUP_NO_LYDS, diff));
3095-
3096-
LY_LIST_FOR(*diff, root) {
3097-
LYD_TREE_DFS_BEGIN(root, elem) {
3098-
/* skip all keys */
3099-
if (!lysc_is_key(elem->schema)) {
3100-
/* find module with metadata needed for later in the current node context */
3101-
if (!mod || (mod->ctx != LYD_CTX(elem))) {
3102-
mod = ly_ctx_get_module_latest(LYD_CTX(elem), "yang");
3103-
LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); rc = LY_EINT, cleanup);
3104-
}
3119+
if (!node) {
3120+
/* nothing more to do */
3121+
goto cleanup;
3122+
}
31053123

3106-
/* find operation attribute, if any */
3107-
LY_CHECK_GOTO(rc = lyd_diff_get_op(elem, &op, NULL), cleanup);
3124+
/* first node */
3125+
if (!*schema_p) {
3126+
*schema_p = node->schema;
3127+
}
3128+
assert(*schema_p == node->schema);
31083129

3109-
switch (op) {
3110-
case LYD_DIFF_OP_CREATE:
3111-
/* reverse create to delete */
3112-
LY_CHECK_GOTO(rc = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
3130+
/* collect it */
3131+
LY_ARRAY_NEW_GOTO(LYD_CTX(node), *nodes_p, ptr, rc, cleanup);
3132+
*ptr = node;
31133133

3114-
/* check all the children for the same operation, nothing else is expected */
3115-
LY_LIST_FOR(lyd_child(elem), iter) {
3116-
lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
3117-
}
3134+
cleanup:
3135+
return rc;
3136+
}
31183137

3119-
LYD_TREE_DFS_continue = 1;
3120-
break;
3121-
case LYD_DIFF_OP_DELETE:
3122-
/* reverse delete to create */
3123-
LY_CHECK_GOTO(rc = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
3138+
/**
3139+
* @brief Reverse all sibling diff nodes, recursively.
3140+
*
3141+
* @param[in,out] sibling First sibling to reverse.
3142+
* @param[in] yang_mod YANG module 'yang' to use for metadata.
3143+
* @return LY_ERR value.
3144+
*/
3145+
static LY_ERR
3146+
lyd_diff_reverse_siblings_r(struct lyd_node *sibling, const struct lys_module *yang_mod)
3147+
{
3148+
LY_ERR rc = LY_SUCCESS;
3149+
struct lyd_node *iter, *iter2, **userord = NULL;
3150+
const struct lysc_node *userord_schema = NULL;
3151+
enum lyd_diff_op op;
3152+
ly_bool recursive;
31243153

3125-
/* check all the children for the same operation, nothing else is expected */
3126-
LY_LIST_FOR(lyd_child(elem), iter) {
3127-
lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
3128-
}
3154+
LY_LIST_FOR(sibling, iter) {
3155+
/* skip all keys */
3156+
if (lysc_is_key(iter->schema)) {
3157+
continue;
3158+
}
31293159

3130-
LYD_TREE_DFS_continue = 1;
3131-
break;
3132-
case LYD_DIFF_OP_REPLACE:
3133-
switch (elem->schema->nodetype) {
3134-
case LYS_LEAF:
3135-
/* leaf value change */
3136-
LY_CHECK_GOTO(rc = lyd_diff_reverse_value(elem, mod), cleanup);
3137-
LY_CHECK_GOTO(rc = lyd_diff_reverse_default(elem, mod), cleanup);
3138-
break;
3139-
case LYS_ANYXML:
3140-
case LYS_ANYDATA:
3141-
/* any value change */
3142-
LY_CHECK_GOTO(rc = lyd_diff_reverse_value(elem, mod), cleanup);
3143-
break;
3144-
case LYS_LEAFLIST:
3145-
/* leaf-list move */
3146-
LY_CHECK_GOTO(rc = lyd_diff_reverse_default(elem, mod), cleanup);
3147-
if (lysc_is_dup_inst_list(elem->schema)) {
3148-
LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
3149-
} else {
3150-
LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
3151-
}
3152-
break;
3153-
case LYS_LIST:
3154-
/* list move */
3155-
if (lysc_is_dup_inst_list(elem->schema)) {
3156-
LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
3157-
} else {
3158-
LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
3159-
}
3160-
break;
3161-
default:
3162-
LOGINT(LYD_CTX(src_diff));
3163-
rc = LY_EINT;
3164-
goto cleanup;
3165-
}
3166-
break;
3167-
case LYD_DIFF_OP_NONE:
3168-
switch (elem->schema->nodetype) {
3169-
case LYS_LEAF:
3170-
case LYS_LEAFLIST:
3171-
/* default flag change */
3172-
LY_CHECK_GOTO(rc = lyd_diff_reverse_default(elem, mod), cleanup);
3173-
break;
3174-
default:
3175-
/* nothing to do */
3176-
break;
3177-
}
3178-
break;
3160+
/* update module if needed */
3161+
if (LYD_CTX(iter) != yang_mod->ctx) {
3162+
yang_mod = ly_ctx_get_module_implemented(LYD_CTX(iter), "yang");
3163+
assert(yang_mod);
3164+
}
3165+
3166+
/* find operation attribute, if any */
3167+
LY_CHECK_GOTO(rc = lyd_diff_get_op(iter, &op, NULL), cleanup);
3168+
3169+
recursive = 1;
3170+
switch (op) {
3171+
case LYD_DIFF_OP_CREATE:
3172+
/* reverse create to delete */
3173+
LY_CHECK_GOTO(rc = lyd_diff_change_op(iter, LYD_DIFF_OP_DELETE), cleanup);
3174+
3175+
/* check all the children for the same operation, nothing else is expected */
3176+
LY_LIST_FOR(lyd_child(iter), iter2) {
3177+
lyd_diff_reverse_remove_op_r(iter2, LYD_DIFF_OP_CREATE);
3178+
}
3179+
3180+
recursive = 0;
3181+
break;
3182+
3183+
case LYD_DIFF_OP_DELETE:
3184+
/* reverse delete to create */
3185+
LY_CHECK_GOTO(rc = lyd_diff_change_op(iter, LYD_DIFF_OP_CREATE), cleanup);
3186+
3187+
/* check all the children for the same operation, nothing else is expected */
3188+
LY_LIST_FOR(lyd_child(iter), iter2) {
3189+
lyd_diff_reverse_remove_op_r(iter2, LYD_DIFF_OP_DELETE);
3190+
}
3191+
3192+
recursive = 0;
3193+
break;
3194+
3195+
case LYD_DIFF_OP_REPLACE:
3196+
switch (iter->schema->nodetype) {
3197+
case LYS_LEAF:
3198+
/* leaf value change */
3199+
LY_CHECK_GOTO(rc = lyd_diff_reverse_value(iter, yang_mod), cleanup);
3200+
LY_CHECK_GOTO(rc = lyd_diff_reverse_default(iter, yang_mod), cleanup);
3201+
break;
3202+
case LYS_ANYXML:
3203+
case LYS_ANYDATA:
3204+
/* any value change */
3205+
LY_CHECK_GOTO(rc = lyd_diff_reverse_value(iter, yang_mod), cleanup);
3206+
break;
3207+
case LYS_LEAFLIST:
3208+
/* leaf-list move */
3209+
LY_CHECK_GOTO(rc = lyd_diff_reverse_default(iter, yang_mod), cleanup);
3210+
if (lysc_is_dup_inst_list(iter->schema)) {
3211+
LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(iter, yang_mod, "orig-position", "position"), cleanup);
3212+
} else {
3213+
LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(iter, yang_mod, "orig-value", "value"), cleanup);
3214+
}
3215+
break;
3216+
case LYS_LIST:
3217+
/* list move */
3218+
if (lysc_is_dup_inst_list(iter->schema)) {
3219+
LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(iter, yang_mod, "orig-position", "position"), cleanup);
3220+
} else {
3221+
LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(iter, yang_mod, "orig-key", "key"), cleanup);
31793222
}
3223+
break;
3224+
default:
3225+
LOGINT(LYD_CTX(iter));
3226+
rc = LY_EINT;
3227+
goto cleanup;
31803228
}
3229+
break;
31813230

3182-
/* reverse any metadata diff */
3183-
LY_CHECK_GOTO(rc = lyd_diff_reverse_metadata_diff(elem), cleanup);
3231+
case LYD_DIFF_OP_NONE:
3232+
switch (iter->schema->nodetype) {
3233+
case LYS_LEAF:
3234+
case LYS_LEAFLIST:
3235+
/* default flag change */
3236+
LY_CHECK_GOTO(rc = lyd_diff_reverse_default(iter, yang_mod), cleanup);
3237+
break;
3238+
default:
3239+
/* nothing to do */
3240+
break;
3241+
}
3242+
break;
3243+
}
3244+
3245+
/* reverse any metadata diff */
3246+
LY_CHECK_GOTO(rc = lyd_diff_reverse_metadata_diff(iter), cleanup);
3247+
3248+
/* revursively reverse all descendants */
3249+
if (recursive) {
3250+
LY_CHECK_GOTO(rc = lyd_diff_reverse_siblings_r(lyd_child(iter), yang_mod), cleanup);
3251+
}
31843252

3185-
LYD_TREE_DFS_END(root, elem);
3253+
if (lysc_is_userordered(iter->schema)) {
3254+
/* special user-ordered nodes processing */
3255+
LY_CHECK_GOTO(rc = lyd_diff_reverse_userord(iter, &userord_schema, &userord), cleanup);
31863256
}
31873257
}
31883258

3259+
if (userord_schema) {
3260+
/* finish user-ordered nodes processing */
3261+
LY_CHECK_GOTO(rc = lyd_diff_reverse_userord(NULL, &userord_schema, &userord), cleanup);
3262+
}
3263+
3264+
cleanup:
3265+
LY_ARRAY_FREE(userord);
3266+
return rc;
3267+
}
3268+
3269+
LIBYANG_API_DEF LY_ERR
3270+
lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
3271+
{
3272+
LY_ERR rc = LY_SUCCESS;
3273+
const struct lys_module *mod = NULL;
3274+
3275+
LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
3276+
3277+
if (!src_diff) {
3278+
*diff = NULL;
3279+
return LY_SUCCESS;
3280+
}
3281+
3282+
/* duplicate diff */
3283+
LY_CHECK_GOTO(rc = lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE | LYD_DUP_NO_LYDS, diff), cleanup);
3284+
3285+
/* find 'yang' module */
3286+
mod = ly_ctx_get_module_implemented(LYD_CTX(src_diff), "yang");
3287+
assert(mod);
3288+
3289+
/* reverse it */
3290+
LY_CHECK_GOTO(rc = lyd_diff_reverse_siblings_r(*diff, mod), cleanup);
3291+
31893292
cleanup:
31903293
if (rc) {
31913294
lyd_free_siblings(*diff);

tests/utests/data/test_diff.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,50 @@ test_userord_list3(void **state)
10351035
TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge);
10361036
}
10371037

1038+
static void
1039+
test_userord_llist_reverse(void **state)
1040+
{
1041+
struct lyd_node *data1, *data2, *diff, *rdiff;
1042+
const char *xml1 =
1043+
"<df xmlns=\"urn:libyang:tests:defaults\">\n"
1044+
" <llist>1</llist>\n"
1045+
" <llist>2</llist>\n"
1046+
" <llist>3</llist>\n"
1047+
" <llist>4</llist>\n"
1048+
" <llist>5</llist>\n"
1049+
"</df>\n";
1050+
const char *xml2 =
1051+
"<df xmlns=\"urn:libyang:tests:defaults\">\n"
1052+
" <llist>1</llist>\n"
1053+
" <llist>4</llist>\n"
1054+
" <llist>3</llist>\n"
1055+
" <llist>2</llist>\n"
1056+
" <llist>5</llist>\n"
1057+
"</df>\n";
1058+
1059+
(void) state;
1060+
1061+
/* create */
1062+
CHECK_PARSE_LYD(xml1, data1);
1063+
CHECK_PARSE_LYD(xml2, data2);
1064+
1065+
/* diff 1 -> 2 */
1066+
CHECK_PARSE_LYD_DIFF(data1, data2, 0, diff);
1067+
1068+
/* reverse */
1069+
assert_int_equal(LY_SUCCESS, lyd_diff_reverse_all(diff, &rdiff));
1070+
1071+
/* apply and compare */
1072+
assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&data2, rdiff));
1073+
CHECK_LYD(data1, data2);
1074+
1075+
/* cleanup */
1076+
lyd_free_all(data1);
1077+
lyd_free_all(data2);
1078+
lyd_free_all(diff);
1079+
lyd_free_all(rdiff);
1080+
}
1081+
10381082
static void
10391083
test_keyless_list(void **state)
10401084
{
@@ -1393,6 +1437,7 @@ main(void)
13931437
UTEST(test_userord_list, setup),
13941438
UTEST(test_userord_list2, setup),
13951439
UTEST(test_userord_list3, setup),
1440+
UTEST(test_userord_llist_reverse, setup),
13961441
UTEST(test_keyless_list, setup),
13971442
UTEST(test_state_llist, setup),
13981443
UTEST(test_wd, setup),

0 commit comments

Comments
 (0)