blob: fcb70798c1752f40fe1e87a8627341c036dd1655 [file] [log] [blame]
Michal Vaskod59035b2020-07-08 12:00:06 +02001/**
2 * @file diff.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief diff functions
5 *
6 * Copyright (c) 2020 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14#define _XOPEN_SOURCE 500
15
16#include "diff.h"
17
18#include <assert.h>
19#include <stddef.h>
20#include <string.h>
21
22#include "common.h"
23#include "log.h"
24#include "tree_data_internal.h"
25#include "tree_schema.h"
26#include "tree_schema_internal.h"
27
28static const char *
29lyd_diff_op2str(enum lyd_diff_op op)
30{
31 switch (op) {
32 case LYD_DIFF_OP_CREATE:
33 return "create";
34 case LYD_DIFF_OP_DELETE:
35 return "delete";
36 case LYD_DIFF_OP_REPLACE:
37 return "replace";
38 case LYD_DIFF_OP_NONE:
39 return "none";
40 }
41
42 LOGINT(NULL);
43 return NULL;
44}
45
Michal Vaskoe6323f62020-07-09 15:49:02 +020046static enum lyd_diff_op
47lyd_diff_str2op(const char *str)
48{
49 switch (str[0]) {
50 case 'c':
51 assert(!strcmp(str, "create"));
52 return LYD_DIFF_OP_CREATE;
53 case 'd':
54 assert(!strcmp(str, "delete"));
55 return LYD_DIFF_OP_DELETE;
56 case 'r':
57 assert(!strcmp(str, "replace"));
58 return LYD_DIFF_OP_REPLACE;
59 case 'n':
60 assert(!strcmp(str, "none"));
61 return LYD_DIFF_OP_NONE;
62 }
63
64 LOGINT(NULL);
65 return 0;
66}
67
Michal Vaskod59035b2020-07-08 12:00:06 +020068LY_ERR
69lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
70 const char *key, const char *value, const char *orig_key, struct lyd_node **diff)
71{
72 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
73 const struct lyd_node *parent = NULL;
74 const struct lys_module *yang_mod;
75
76 assert(diff);
77
78 /* find the first existing parent */
79 siblings = *diff;
80 while (1) {
81 /* find next node parent */
82 parent = node;
83 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
84 parent = (struct lyd_node *)parent->parent;
85 }
86 if (parent == node) {
87 /* no more parents to find */
88 break;
89 }
90
91 /* check whether it exists in the diff */
92 if (lyd_find_sibling_first(siblings, parent, &match)) {
93 break;
94 }
95
96 /* another parent found */
97 diff_parent = match;
98
99 /* move down in the diff */
100 siblings = LYD_CHILD(match);
101 }
102
103 /* duplicate the subtree (and connect to the diff if possible) */
104 dup = lyd_dup(node, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS);
105 LY_CHECK_RET(!dup, LY_EMEM);
106
107 /* find the first duplicated parent */
108 if (!diff_parent) {
109 diff_parent = (struct lyd_node *)dup->parent;
110 while (diff_parent && diff_parent->parent) {
111 diff_parent = (struct lyd_node *)diff_parent->parent;
112 }
113 } else {
114 diff_parent = (struct lyd_node *)dup;
115 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
116 diff_parent = (struct lyd_node *)diff_parent->parent;
117 }
118 }
119
120 /* no parent existed, must be manually connected */
121 if (!diff_parent) {
122 /* there actually was no parent to duplicate */
123 if (*diff) {
124 lyd_insert_sibling(*diff, dup);
125 } else {
126 *diff = dup;
127 }
128 } else if (!diff_parent->parent) {
129 if (*diff) {
130 lyd_insert_sibling(*diff, diff_parent);
131 } else {
132 *diff = diff_parent;
133 }
134 }
135
136 /* get module with the operation metadata */
137 yang_mod = LYD_NODE_CTX(node)->list.objs[1];
138 assert(!strcmp(yang_mod->name, "yang"));
139
140 /* add parent operation, if any */
141 if (diff_parent && (diff_parent != dup) && !lyd_new_meta(diff_parent, yang_mod, "operation", "none")) {
142 return LY_EMEM;
143 }
144
145 /* add subtree operation */
146 if (!lyd_new_meta(dup, yang_mod, "operation", lyd_diff_op2str(op))) {
147 return LY_EMEM;
148 }
149
150 /* orig-default */
151 if (orig_default && !lyd_new_meta(dup, yang_mod, "orig-default", orig_default)) {
152 return LY_EMEM;
153 }
154
155 /* orig-value */
156 if (orig_value && !lyd_new_meta(dup, yang_mod, "orig-value", orig_value)) {
157 return LY_EMEM;
158 }
159
160 /* key */
161 if (key && !lyd_new_meta(dup, yang_mod, "key", key)) {
162 return LY_EMEM;
163 }
164
165 /* value */
166 if (value && !lyd_new_meta(dup, yang_mod, "value", value)) {
167 return LY_EMEM;
168 }
169
170 /* orig-key */
171 if (orig_key && !lyd_new_meta(dup, yang_mod, "orig-key", orig_key)) {
172 return LY_EMEM;
173 }
174
175 return LY_SUCCESS;
176}
177
178/**
179 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
180 *
181 * @param[in] first
182 * @param[in] schema Schema node of the list/leaf-list.
183 * @param[in,out] userord Sized array of userord items.
184 * @return Userord item for all the user-ordered list/leaf-list instances.
185 */
186static struct lyd_diff_userord *
187lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
188{
189 struct lyd_diff_userord *item;
190 const struct lyd_node *iter, **node;
191 LY_ARRAY_COUNT_TYPE u;
192
193 LY_ARRAY_FOR(*userord, u) {
194 if ((*userord)[u].schema == schema) {
195 return &(*userord)[u];
196 }
197 }
198
199 /* it was not added yet, add it now */
200 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
201
202 item->schema = schema;
203 item->pos = 0;
204 item->inst = NULL;
205
206 /* store all the instance pointers in the current order */
207 if (first) {
208 if (first->parent) {
209 iter = first->parent->child;
210 } else {
211 for (iter = first; iter->prev->next; iter = iter->prev);
212 }
213 for (; iter; iter = iter->next) {
214 if (iter->schema == first->schema) {
215 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
216 *node = iter;
217 }
218 }
219 }
220
221 return item;
222}
223
224/**
225 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
226 * lists/leaf-lists.
227 *
228 * @param[in] first Node from the first tree, can be NULL (on create).
229 * @param[in] second Node from the second tree, can be NULL (on delete).
230 * @param[in] options Diff options.
231 * @param[in,out] userord Sized array of userord items for keeping the current node order.
232 * @param[out] op Operation.
233 * @param[out] orig_default Original default metadata.
234 * @param[out] value Value metadata.
235 * @param[out] orig_value Original value metadata
236 * @param[out] key Key metadata.
237 * @param[out] orig_key Original key metadata.
238 * @return LY_SUCCESS on success,
239 * @return LY_ENOT if there is no change to be added into diff,
240 * @return LY_ERR value on other errors.
241 */
242static LY_ERR
243lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, int options,
244 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
245 char **orig_value, char **key, char **orig_key)
246{
247 const struct lysc_node *schema;
248 int dynamic;
249 size_t buflen, bufused, first_pos, second_pos;
250 struct lyd_diff_userord *userord_item;
251
252 assert(first || second);
253
254 *orig_default = NULL;
255 *value = NULL;
256 *orig_value = NULL;
257 *key = NULL;
258 *orig_key = NULL;
259
260 schema = first ? first->schema : second->schema;
261 assert(lysc_is_userordered(schema));
262
263 /* get userord entry */
264 userord_item = lyd_diff_userord_get(first, schema, userord);
265 LY_CHECK_RET(!userord_item, LY_EMEM);
266
267 /* prepare position of the next instance */
268 second_pos = userord_item->pos++;
269
270 /* find user-ordered first position */
271 if (first) {
272 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
273 if (userord_item->inst[first_pos] == first) {
274 break;
275 }
276 }
277 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
278 } else {
279 first_pos = 0;
280 }
281
282 /* learn operation first */
283 if (!second) {
284 *op = LYD_DIFF_OP_DELETE;
285 } else if (!first) {
286 *op = LYD_DIFF_OP_CREATE;
287 } else {
288 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
289 if (lyd_compare(second, userord_item->inst[second_pos], 0)) {
290 /* in first, there is a different instance on the second position, we are going to move 'first' node */
291 *op = LYD_DIFF_OP_REPLACE;
292 } else if ((options & LYD_DIFF_WITHDEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
293 /* default flag change */
294 *op = LYD_DIFF_OP_NONE;
295 } else {
296 /* no changes */
297 return LY_ENOT;
298 }
299 }
300
301 /*
302 * set each attribute correctly based on the operation and node type
303 */
304
305 /* orig-default */
306 if ((options & LYD_DIFF_WITHDEFAULTS) && (schema->nodetype == LYS_LEAFLIST)
Michal Vaskoe6323f62020-07-09 15:49:02 +0200307 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))
308 && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200309 if (first->flags & LYD_DEFAULT) {
310 *orig_default = "true";
311 } else {
312 *orig_default = "false";
313 }
314 }
315
316 /* value */
317 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
318 if (second_pos) {
319 *value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[second_pos - 1], &dynamic);
320 if (!dynamic) {
321 *value = strdup(*value);
322 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
323 }
324 } else {
325 *value = strdup("");
326 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
327 }
328 }
329
330 /* orig-value */
331 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
332 if (first_pos) {
333 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[first_pos - 1], &dynamic);
334 if (!dynamic) {
335 *orig_value = strdup(*orig_value);
336 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
337 }
338 } else {
339 *orig_value = strdup("");
340 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
341 }
342 }
343
344 /* key */
345 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op ==LYD_DIFF_OP_CREATE))) {
346 if (second_pos) {
347 buflen = bufused = 0;
348 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
349 } else {
350 *key = strdup("");
351 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
352 }
353 }
354
355 /* orig-key */
356 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
357 if (first_pos) {
358 buflen = bufused = 0;
359 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
360 } else {
361 *orig_key = strdup("");
362 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
363 }
364 }
365
366 /*
367 * update our instances - apply the change
368 */
369 if (*op == LYD_DIFF_OP_CREATE) {
370 /* insert the instance */
371 LY_ARRAY_RESIZE_ERR_RET(schema->module->ctx, userord_item->inst, LY_ARRAY_COUNT(userord_item->inst) + 1,
372 ;, LY_EMEM);
373 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
374 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
375 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
376 }
377 LY_ARRAY_INCREMENT(userord_item->inst);
378 userord_item->inst[second_pos] = second;
379
380 } else if (*op == LYD_DIFF_OP_DELETE) {
381 /* remove the instance */
382 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
383 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
384 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
385 }
386 LY_ARRAY_DECREMENT(userord_item->inst);
387
388 } else if (*op == LYD_DIFF_OP_REPLACE) {
389 /* move the instances */
390 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
391 (first_pos - second_pos) * sizeof *userord_item->inst);
392 userord_item->inst[second_pos] = first;
393 }
394
395 return LY_SUCCESS;
396}
397
398/**
399 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
400 * lists/leaf-lists.
401 *
402 * @param[in] first Node from the first tree, can be NULL (on create).
403 * @param[in] second Node from the second tree, can be NULL (on delete).
404 * @param[in] options Diff options.
405 * @param[out] op Operation.
406 * @param[out] orig_default Original default metadata.
407 * @param[out] orig_value Original value metadata.
408 * @return LY_SUCCESS on success,
409 * @return LY_ENOT if there is no change to be added into diff,
410 * @return LY_ERR value on other errors.
411 */
412static LY_ERR
413lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, int options, enum lyd_diff_op *op,
414 const char **orig_default, char **orig_value)
415{
416 const struct lysc_node *schema;
417 int dynamic;
418
419 assert(first || second);
420
421 *orig_default = NULL;
422 *orig_value = NULL;
423
424 schema = first ? first->schema : second->schema;
425 assert(!lysc_is_userordered(schema));
426
427 /* learn operation first */
428 if (!second) {
429 *op = LYD_DIFF_OP_DELETE;
430 } else if (!first) {
431 *op = LYD_DIFF_OP_CREATE;
432 } else {
433 switch (schema->nodetype) {
434 case LYS_CONTAINER:
435 case LYS_RPC:
436 case LYS_ACTION:
437 case LYS_NOTIF:
438 /* no changes */
439 return LY_ENOT;
440 case LYS_LIST:
441 case LYS_LEAFLIST:
442 if ((options & LYD_DIFF_WITHDEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
443 /* default flag change */
444 *op = LYD_DIFF_OP_NONE;
445 } else {
446 /* no changes */
447 return LY_ENOT;
448 }
449 break;
450 case LYS_LEAF:
451 case LYS_ANYXML:
452 case LYS_ANYDATA:
453 if (lyd_compare(first, second, 0)) {
454 /* different values */
455 *op = LYD_DIFF_OP_REPLACE;
456 } else if ((options & LYD_DIFF_WITHDEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
457 /* default flag change */
458 *op = LYD_DIFF_OP_NONE;
459 } else {
460 /* no changes */
461 return LY_ENOT;
462 }
463 break;
464 default:
465 LOGINT_RET(schema->module->ctx);
466 }
467 }
468
469 /*
470 * set each attribute correctly based on the operation and node type
471 */
472
473 /* orig-default */
474 if ((options & LYD_DIFF_WITHDEFAULTS) && (schema->nodetype & LYD_NODE_TERM)
Michal Vaskoe6323f62020-07-09 15:49:02 +0200475 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))
476 && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200477 if (first->flags & LYD_DEFAULT) {
478 *orig_default = "true";
479 } else {
480 *orig_default = "false";
481 }
482 }
483
484 /* orig-value */
485 if ((schema->nodetype == LYS_LEAF) && (*op == LYD_DIFF_OP_REPLACE)) {
486 /* leaf */
487 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)first, &dynamic);
488 if (!dynamic) {
489 *orig_value = strdup(*orig_value);
490 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
491 }
492 }
493
494 return LY_SUCCESS;
495}
496
497/**
498 * @brief Perform diff for all siblings at certain depth, recursively.
499 *
500 * For user-ordered lists/leaf-lists a specific structure is used for storing
501 * the current order. The idea is to apply all the generated diff changes
502 * virtually on the first tree so that we can continue to generate correct
503 * changes after some were already generated.
504 *
505 * The algorithm then uses second tree position-based changes with a before
506 * (preceding) item anchor.
507 *
508 * Example:
509 *
510 * Virtual first tree leaf-list order:
511 * 1 2 [3] 4 5
512 *
513 * Second tree leaf-list order:
514 * 1 2 [5] 3 4
515 *
516 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
517 * match - they do not - move nodes so that the 3rd position node is final ->
518 * -> move node 5 to the 3rd position -> move node 5 after node 2.
519 *
520 * Required properties:
521 * Stored operations (move) should not be affected by later operations -
522 * - would cause a redundantly long list of operations, possibly inifinite.
523 *
524 * Implemenation justification:
525 * First, all delete operations and only then move/create operations are stored.
526 * Also, preceding anchor is used and after each iteration another node is
527 * at its final position. That results in the invariant that all preceding
528 * nodes are final and will not be changed by the later operations, meaning
529 * they can safely be used as anchors for the later operations.
530 *
531 * @param[in] first First tree first sibling.
532 * @param[in] second Second tree first sibling.
533 * @param[in] options Diff options.
534 * @param[in,out] diff Diff to append to.
535 * @return LY_ERR value.
536 */
537static LY_ERR
538lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
539{
540 LY_ERR ret = LY_SUCCESS;
541 const struct lyd_node *iter_first, *iter_second;
542 struct lyd_node *match_second, *match_first;
543 int nosiblings = 0;
544 struct lyd_diff_userord *userord = NULL;
545 LY_ARRAY_COUNT_TYPE u;
546 enum lyd_diff_op op;
547 const char *orig_default;
548 char *orig_value, *key, *value, *orig_key;
549
550 if (options & LYD_DIFF_NOSIBLINGS) {
551 /* remember it for this function call only, should not be passed recursively */
552 nosiblings = 1;
553 options &= ~LYD_DIFF_NOSIBLINGS;
554 }
555
556 /* compare first tree to the second tree - delete, replace, none */
557 LY_LIST_FOR(first, iter_first) {
558 assert(!(iter_first->schema->flags & LYS_KEY));
559 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_WITHDEFAULTS)) {
560 /* skip default nodes */
561 continue;
562 }
563
564 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
565 /* try to find the exact instance */
566 lyd_find_sibling_first(second, iter_first, &match_second);
567 } else {
568 /* try to simply find the node, there cannot be more instances */
569 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
570 }
571
572 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_WITHDEFAULTS)) {
573 /* ignore default nodes */
574 match_second = NULL;
575 }
576
577 if (lysc_is_userordered(iter_first->schema)) {
578 if (match_second) {
579 /* we are handling only user-ordered node delete now */
580 continue;
581 }
582
583 /* get all the attributes */
584 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
585 &value, &orig_value, &key, &orig_key), cleanup);
586
587 /* there must be changes, it is deleted */
588 assert(op == LYD_DIFF_OP_DELETE);
589 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
590
591 free(orig_value);
592 free(key);
593 free(value);
594 free(orig_key);
595 LY_CHECK_GOTO(ret, cleanup);
596 } else {
597 /* get all the attributes */
598 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
599
600 /* add into diff if there are any changes */
601 if (!ret) {
602 if (op == LYD_DIFF_OP_DELETE) {
603 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
604 } else {
605 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
606 }
607
608 free(orig_value);
609 LY_CHECK_GOTO(ret, cleanup);
610 } else if (ret == LY_ENOT) {
611 ret = LY_SUCCESS;
612 } else {
613 goto cleanup;
614 }
615 }
616
617 /* check descendants, if any, recursively */
618 if (match_second) {
619 LY_CHECK_GOTO(lyd_diff_siblings_r(LYD_CHILD(iter_first), LYD_CHILD(match_second), options, diff), cleanup);
620 }
621
622 if (nosiblings) {
623 break;
624 }
625 }
626
627 /* reset all cached positions */
628 LY_ARRAY_FOR(userord, u) {
629 userord[u].pos = 0;
630 }
631
632 /* compare second tree to the first tree - create, user-ordered move */
633 LY_LIST_FOR(second, iter_second) {
634 assert(!(iter_second->schema->flags & LYS_KEY));
635 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_WITHDEFAULTS)) {
636 /* skip default nodes */
637 continue;
638 }
639
640 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
641 lyd_find_sibling_first(first, iter_second, &match_first);
642 } else {
643 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
644 }
645
646 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_WITHDEFAULTS)) {
647 /* ignore default nodes */
648 match_first = NULL;
649 }
650
651 if (lysc_is_userordered(iter_second->schema)) {
652 /* get all the attributes */
653 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
654 &value, &orig_value, &key, &orig_key);
655
656 /* add into diff if there are any changes */
657 if (!ret) {
658 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
659
660 free(orig_value);
661 free(key);
662 free(value);
663 free(orig_key);
664 LY_CHECK_GOTO(ret, cleanup);
665 } else if (ret == LY_ENOT) {
666 ret = LY_SUCCESS;
667 } else {
668 goto cleanup;
669 }
670 } else if (!match_first) {
671 /* get all the attributes */
672 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
673
674 /* there must be changes, it is created */
675 assert(op == LYD_DIFF_OP_CREATE);
676 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
677
678 free(orig_value);
679 LY_CHECK_GOTO(ret, cleanup);
680 } /* else was handled */
681
682 if (nosiblings) {
683 break;
684 }
685 }
686
687cleanup:
688 LY_ARRAY_FOR(userord, u) {
689 LY_ARRAY_FREE(userord[u].inst);
690 }
691 LY_ARRAY_FREE(userord);
692 return ret;
693}
694
695API LY_ERR
696lyd_diff(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
697{
698 const struct ly_ctx *ctx;
699
700 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
701
702 if (first) {
703 ctx = LYD_NODE_CTX(first);
704 } else if (second) {
705 ctx = LYD_NODE_CTX(second);
706 } else {
707 ctx = NULL;
708 }
709
710 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
711 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
712 return LY_EINVAL;
713 }
714
715 *diff = NULL;
716
717 return lyd_diff_siblings_r(first, second, options, diff);
718}
719
720/**
721 * @brief Find a matching node in data tree for a diff node.
722 *
723 * @param[in] first_node First sibling in the data tree.
724 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200725 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200726 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200727static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200728lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
729{
730 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
731 /* try to find the exact instance */
732 lyd_find_sibling_first(first_node, diff_node, match_p);
733 } else {
734 /* try to simply find the node, there cannot be more instances */
735 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
736 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200737}
738
739/**
740 * @brief Learn operation of a diff node.
741 *
742 * @param[in] diff_node Diff node.
743 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200744 * @return LY_ERR value.
745 */
746static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200747lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200748{
749 struct lyd_meta *meta = NULL;
750 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200751 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200752 int dynamic;
753
754 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
755 LY_LIST_FOR(diff_parent->meta, meta) {
756 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
757 str = lyd_meta2str(meta, &dynamic);
758 assert(!dynamic);
759 if ((str[0] == 'r') && (diff_parent != diff_node)) {
760 /* we do not care about this operation if it's in our parent */
761 continue;
762 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200763 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200764 break;
765 }
766 }
767 if (meta) {
768 break;
769 }
770 }
771 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
772
Michal Vaskod59035b2020-07-08 12:00:06 +0200773 return LY_SUCCESS;
774}
775
776/**
777 * @brief Insert a diff node into a data tree.
778 *
779 * @param[in,out] first_node First sibling of the data tree.
780 * @param[in] parent_node Data tree sibling parent node.
781 * @param[in] new_node Node to insert.
782 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
783 * instance will be inserted at the first position.
784 * @return err_info, NULL on success.
785 */
786static LY_ERR
787lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
788 const char *key_or_value)
789{
790 LY_ERR ret;
791 struct lyd_node *anchor;
792
793 assert(new_node);
794
795 if (!*first_node) {
796 if (!parent_node) {
797 /* no parent or siblings */
798 *first_node = new_node;
799 return LY_SUCCESS;
800 }
801
802 /* simply insert into parent, no other children */
803 if (key_or_value) {
804 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
805 new_node->schema->name);
806 return LY_EINVAL;
807 }
808 return lyd_insert(parent_node, new_node);
809 }
810
811 assert(!(*first_node)->parent || ((struct lyd_node *)(*first_node)->parent == parent_node));
812
813 /* simple insert */
814 if (!lysc_is_userordered(new_node->schema)) {
815 /* insert at the end */
816 return lyd_insert_sibling(*first_node, new_node);
817 }
818
819 if (key_or_value) {
820 /* find the anchor sibling */
821 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
822 if (ret == LY_ENOTFOUND) {
823 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
824 new_node->schema->name);
825 return LY_EINVAL;
826 } else if (ret) {
827 return ret;
828 }
829
830 /* insert after */
831 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
832 assert(new_node->prev == anchor);
833 if (*first_node == new_node) {
834 *first_node = anchor;
835 }
836 } else {
837 if ((*first_node)->schema->flags & LYS_KEY) {
838 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
839
840 /* find last key */
841 anchor = *first_node;
842 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
843 anchor = anchor->next;
844 }
845 /* insert after the last key */
846 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
847 } else {
848 /* insert at the beginning */
849 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
850 *first_node = new_node;
851 }
852 }
853
854 return LY_SUCCESS;
855}
856
857/**
858 * @brief Apply diff subtree on data tree nodes, recursively.
859 *
860 * @param[in,out] first_node First sibling of the data tree.
861 * @param[in] parent_node Parent of the first sibling.
862 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200863 * @param[in] diff_cb Optional diff callback.
864 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200865 * @return LY_ERR value.
866 */
867static LY_ERR
868lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
869 lyd_diff_cb diff_cb, void *cb_data)
870{
871 LY_ERR ret;
872 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200873 const char *str_val;
874 enum lyd_diff_op op;
875 struct lyd_meta *meta;
Michal Vaskod59035b2020-07-08 12:00:06 +0200876 int dynamic;
877 const struct ly_ctx *ctx = LYD_NODE_CTX(diff_node);
878
879 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200880 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200881
Michal Vaskoe6323f62020-07-09 15:49:02 +0200882 /* handle specific user-ordered (leaf-)lists operations separately */
883 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
884 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200885 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200886 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200887 } else {
888 /* duplicate the node(s) */
889 match = lyd_dup(diff_node, NULL, LYD_DUP_NO_META);
890 LY_CHECK_RET(!match, LY_EMEM);
891 }
892
Michal Vaskoe6323f62020-07-09 15:49:02 +0200893 /* get "key" or "value" metadata string value */
894 meta = lyd_find_meta(diff_node->meta, NULL, diff_node->schema->nodetype == LYS_LIST ? "yang:key" : "yang:value");
895 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
896 str_val = lyd_meta2str(meta, &dynamic);
897
Michal Vaskod59035b2020-07-08 12:00:06 +0200898 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200899 if (str_val[0]) {
900 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200901 } else {
902 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
903 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200904 if (dynamic) {
905 free((char *)str_val);
906 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200907 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200908 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200909 lyd_free_tree(match);
910 }
911 return ret;
912 }
913
914 goto next_iter_r;
915 }
916
917 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200918 switch (op) {
919 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200920 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200921 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200922
923 if (match->schema->nodetype & LYD_NODE_TERM) {
924 /* special case of only dflt flag change */
925 if (diff_node->flags & LYD_DEFAULT) {
926 match->flags |= LYD_DEFAULT;
927 } else {
928 match->flags &= ~LYD_DEFAULT;
929 }
930 } else {
931 /* none operation on nodes without children is redundant and hence forbidden */
932 if (!LYD_CHILD(diff_node)) {
933 LOGINT_RET(ctx);
934 }
935 }
936 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200937 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200938 /* duplicate the node */
939 match = lyd_dup(diff_node, NULL, LYD_DUP_NO_META);
940 LY_CHECK_RET(!match, LY_EMEM);
941
942 /* insert it at the end */
943 ret = 0;
944 if (*first_node) {
945 ret = lyd_insert_after((*first_node)->prev, match);
946 } else if (parent_node) {
947 ret = lyd_insert(parent_node, match);
948 } else {
949 *first_node = match;
950 }
951 if (ret) {
952 lyd_free_tree(match);
953 return ret;
954 }
955
956 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200957 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200958 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200959 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200960
961 /* remove it */
962 if ((match == *first_node) && !match->parent) {
963 assert(!parent_node);
964 /* we have removed the top-level node */
965 *first_node = (*first_node)->next;
966 }
967 lyd_free_tree(match);
968
969 /* we are not going recursively in this case, the whole subtree was already deleted */
970 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200971 case LYD_DIFF_OP_REPLACE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200972 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
973
974 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200975 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200976
977 /* update its value */
978 str_val = lyd_value2str((struct lyd_node_term *)diff_node, &dynamic);
979 ret = lyd_change_term(match, str_val);
980 if (dynamic) {
981 free((char *)str_val);
982 }
983 if (ret && (ret != LY_EEXIST)) {
984 LOGINT_RET(ctx);
985 }
986
987 /* with flags */
988 match->flags = diff_node->flags;
989 break;
990 default:
991 LOGINT_RET(ctx);
992 }
993
994next_iter_r:
995 if (diff_cb) {
996 /* call callback */
997 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
998 }
999
1000 /* apply diff recursively */
1001 LY_LIST_FOR(LYD_CHILD(diff_node), diff_child) {
1002 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
1003 }
1004
1005 return LY_SUCCESS;
1006}
1007
1008API LY_ERR
1009lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
1010 lyd_diff_cb diff_cb, void *cb_data)
1011{
1012 const struct lyd_node *root;
1013
1014 LY_LIST_FOR(diff, root) {
1015 if (mod && (lyd_owner_module(root) != mod)) {
1016 /* skip data nodes from different modules */
1017 continue;
1018 }
1019
1020 /* apply relevant nodes from the diff datatree */
1021 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1022 }
1023
1024 return LY_SUCCESS;
1025}
1026
1027API LY_ERR
1028lyd_diff_apply(struct lyd_node **data, const struct lyd_node *diff)
1029{
1030 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1031}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001032
1033/**
1034 * @brief Update operations on a diff node when the new operation is NONE.
1035 *
1036 * @param[in] diff_match Node from the diff.
1037 * @param[in] cur_op Current operation of the diff node.
1038 * @param[in] src_diff Current source diff node.
1039 * @return LY_ERR value.
1040 */
1041static LY_ERR
1042lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1043{
1044 switch (cur_op) {
1045 case LYD_DIFF_OP_NONE:
1046 case LYD_DIFF_OP_CREATE:
1047 case LYD_DIFF_OP_REPLACE:
1048 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1049 /* NONE on a term means only its dflt flag was changed */
1050 diff_match->flags &= ~LYD_DEFAULT;
1051 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1052 }
1053 break;
1054 default:
1055 /* delete operation is not valid */
1056 LOGINT_RET(LYD_NODE_CTX(src_diff));
1057 }
1058
1059 return LY_SUCCESS;
1060}
1061
1062/**
1063 * @brief Remove an attribute from a node.
1064 *
1065 * @param[in] node Node with the metadata.
1066 * @param[in] name Metadata name.
1067 */
1068static void
1069lyd_diff_del_meta(struct lyd_node *node, const char *name)
1070{
1071 struct lyd_meta *meta;
1072
1073 LY_LIST_FOR(node->meta, meta) {
1074 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
1075 lyd_free_meta(LYD_NODE_CTX(node), meta, 0);
1076 return;
1077 }
1078 }
1079
1080 assert(0);
1081}
1082
1083/**
1084 * @brief Set a specific operation of a node. Delete the previous operation, if any.
1085 *
1086 * @param[in] node Node to change.
1087 * @param[in] op Operation to set.
1088 * @return LY_ERR value.
1089 */
1090static LY_ERR
1091lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1092{
1093 struct lyd_meta *meta;
1094
1095 LY_LIST_FOR(node->meta, meta) {
1096 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
1097 lyd_free_meta(LYD_NODE_CTX(node), meta, 0);
1098 break;
1099 }
1100 }
1101
1102 if (!lyd_new_meta(node, NULL, "yang:operation", lyd_diff_op2str(op))) {
1103 return LY_EINT;
1104 }
1105 return LY_SUCCESS;
1106}
1107
1108/**
1109 * @brief Update operations on a diff node when the new operation is REPLACE.
1110 *
1111 * @param[in] diff_match Node from the diff.
1112 * @param[in] cur_op Current operation of the diff node.
1113 * @param[in] src_diff Current source diff node.
1114 * @return LY_ERR value.
1115 */
1116static LY_ERR
1117lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1118{
1119 LY_ERR ret;
1120 int dynamic;
1121 const char *str_val, *meta_name;
1122 struct lyd_meta *meta;
1123 const struct lys_module *mod;
1124 const struct lyd_node_any *any;
1125
1126 /* get "yang" module for the metadata */
1127 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff_match), "yang");
1128 assert(mod);
1129
1130 switch (cur_op) {
1131 case LYD_DIFF_OP_REPLACE:
1132 case LYD_DIFF_OP_CREATE:
1133 switch (diff_match->schema->nodetype) {
1134 case LYS_LIST:
1135 case LYS_LEAFLIST:
1136 /* it was created/moved somewhere somewhere, but now it will be created/moved somewhere else,
1137 * keep orig_key/orig_value (only replace oper) and replace key/value */
1138 assert(lysc_is_userordered(diff_match->schema));
1139 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1140
1141 lyd_diff_del_meta(diff_match, meta_name);
1142 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
1143 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
1144 if (!lyd_dup_meta(meta, diff_match)) {
1145 return LY_EINT;
1146 }
1147 break;
1148 case LYS_LEAF:
1149 /* replaced with the exact same value, impossible */
1150 if (!lyd_compare(diff_match, src_diff, 0)) {
1151 LOGINT_RET(LYD_NODE_CTX(src_diff));
1152 }
1153
1154 /* get leaf value */
1155 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1156
1157 /* modify the node value */
1158 ret = lyd_change_term(diff_match, str_val);
1159 if (dynamic) {
1160 free((char *)str_val);
1161 }
1162 if (ret) {
1163 LOGINT_RET(LYD_NODE_CTX(src_diff));
1164 }
1165
1166 /* compare values whether there is any change at all */
1167 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1168 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_match)), LY_EINT);
1169 str_val = lyd_meta2str(meta, &dynamic);
1170 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val), lydjson_resolve_prefix,
1171 NULL, LYD_JSON, NULL);
1172 if (dynamic) {
1173 free((char *)str_val);
1174 }
1175 if (!ret) {
1176 /* values are the same, remove orig-value meta and set oper to NONE */
1177 lyd_free_meta(LYD_NODE_CTX(diff_match), meta, 0);
1178 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1179 }
1180
1181 /* modify the default flag */
1182 diff_match->flags &= ~LYD_DEFAULT;
1183 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1184 break;
1185 case LYS_ANYXML:
1186 case LYS_ANYDATA:
1187 if (!lyd_compare(diff_match, src_diff, 0)) {
1188 /* replaced with the exact same value, impossible */
1189 LOGINT_RET(LYD_NODE_CTX(src_diff));
1190 }
1191
1192 /* modify the node value */
1193 any = (struct lyd_node_any *)src_diff;
1194 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1195 break;
1196 default:
1197 LOGINT_RET(LYD_NODE_CTX(src_diff));
1198 }
1199 break;
1200 case LYD_DIFF_OP_NONE:
1201 /* it is moved now */
1202 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1203
1204 /* change the operation */
1205 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1206
1207 /* set orig-key and key metadata */
1208 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
1209 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
1210 if (!lyd_dup_meta(meta, diff_match)) {
1211 return LY_EINT;
1212 }
1213
1214 meta = lyd_find_meta(src_diff->meta, mod, "key");
1215 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
1216 if (!lyd_dup_meta(meta, diff_match)) {
1217 return LY_EINT;
1218 }
1219 break;
1220 default:
1221 /* delete operation is not valid */
1222 LOGINT_RET(LYD_NODE_CTX(src_diff));
1223 }
1224
1225 return LY_SUCCESS;
1226}
1227
1228/**
1229 * @brief Update operations in a diff node when the new operation is CREATE.
1230 *
1231 * @param[in] diff_match Node from the diff.
1232 * @param[in] cur_op Current operation of the diff node.
1233 * @param[in] src_diff Current source diff node.
1234 * @return LY_ERR value.
1235 */
1236static LY_ERR
1237lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1238{
1239 struct lyd_node *child;
1240 struct lyd_meta *meta;
1241 const char *str_val;
1242 int dynamic;
1243 LY_ERR ret;
1244
1245 switch (cur_op) {
1246 case LYD_DIFF_OP_DELETE:
1247 /* delete operation, if any */
1248 if (!lyd_compare(diff_match, src_diff, 0)) {
1249 /* deleted + created -> operation NONE */
1250 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1251
1252 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1253 /* add orig-dflt metadata */
1254 if (!lyd_new_meta(diff_match, NULL, "yang:orig-default", diff_match->flags & LYD_DEFAULT ? "true" : "false")) {
1255 return LY_EINT;
1256 }
1257 }
1258 } else {
1259 assert(diff_match->schema->nodetype == LYS_LEAF);
1260 /* we deleted it, but it was created with a different value -> operation REPLACE */
1261 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1262
1263 /* current value is the previous one (meta) */
1264 str_val = lyd_value2str((struct lyd_node_term *)diff_match, &dynamic);
1265 meta = lyd_new_meta(diff_match, NULL, "yang:orig-value", str_val);
1266 if (dynamic) {
1267 free((char *)str_val);
1268 }
1269 LY_CHECK_RET(!meta, LY_EINT);
1270
1271 /* update the value itself */
1272 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1273 ret = lyd_change_term(diff_match, str_val);
1274 if (dynamic) {
1275 free((char *)str_val);
1276 }
1277 LY_CHECK_RET(ret);
1278 }
1279
1280 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1281 /* update dflt flag itself */
1282 diff_match->flags &= ~LYD_DEFAULT;
1283 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1284 } else {
1285 /* but the operation of its children should remain DELETE */
1286 LY_LIST_FOR(LYD_CHILD(diff_match), child) {
1287 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1288 }
1289 }
1290 break;
1291 default:
1292 /* create and replace operations are not valid */
1293 LOGINT_RET(LYD_NODE_CTX(src_diff));
1294 }
1295
1296 return LY_SUCCESS;
1297}
1298
1299/**
1300 * @brief Update operations on a diff node when the new operation is DELETE.
1301 *
1302 * @param[in] diff_match Node from the diff.
1303 * @param[in] cur_op Current operation of the diff node.
1304 * @param[in] src_diff Current source diff node.
1305 * @return LY_ERR value.
1306 */
1307static LY_ERR
1308lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1309{
1310 struct lyd_node *next, *child;
1311
1312 /* we can delete only exact existing nodes */
1313 LY_CHECK_ERR_RET(lyd_compare(diff_match, src_diff, 0), LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
1314
1315 switch (cur_op) {
1316 case LYD_DIFF_OP_CREATE:
1317 /* it was created, but then deleted -> set NONE operation */
1318 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1319
1320 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1321 /* add orig-default meta because it is expected */
1322 if (!lyd_new_meta(diff_match, NULL, "yang:orig-default", diff_match->flags & LYD_DEFAULT ? "true" : "false")) {
1323 return LY_EINT;
1324 }
1325 } else {
1326 /* keep operation for all descendants (for now) */
1327 LY_LIST_FOR(LYD_CHILD(diff_match), child) {
1328 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1329 }
1330 }
1331 break;
1332 case LYD_DIFF_OP_REPLACE:
1333 /* similar to none operation but also remove the redundant attribute */
1334 lyd_diff_del_meta(diff_match, "orig-value");
1335 /* fallthrough */
1336 case LYD_DIFF_OP_NONE:
1337 /* it was not modified, but should be deleted -> set DELETE operation */
1338 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1339
1340 /* all descendants will be deleted even without being in the diff, so remove them */
1341 LY_LIST_FOR_SAFE(LYD_CHILD(diff_match), next, child) {
1342 lyd_free_tree(child);
1343 }
1344 break;
1345 default:
1346 /* delete operation is not valid */
1347 LOGINT_RET(LYD_NODE_CTX(src_diff));
1348 }
1349
1350 return LY_SUCCESS;
1351}
1352
1353/**
1354 * @brief Check whether this diff node is redundant (does not change data).
1355 *
1356 * @param[in] diff Diff node.
1357 * @return 0 if not, non-zero if it is.
1358 */
1359static int
1360lyd_diff_is_redundant(struct lyd_node *diff)
1361{
1362 enum lyd_diff_op op;
1363 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1364 struct lyd_node *child;
1365 const struct lys_module *mod;
1366 const char *str;
1367 int dynamic;
1368
1369 assert(diff);
1370
1371 child = LYD_CHILD(diff);
1372 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff), "yang");
1373 assert(mod);
1374
1375 /* get node operation */
1376 lyd_diff_get_op(diff, &op);
1377
1378 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1379 /* check for redundant move */
1380 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1381 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1382 assert(orig_val_meta && val_meta);
1383
1384 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1385 /* there is actually no move */
1386 lyd_free_meta(LYD_NODE_CTX(diff), orig_val_meta, 0);
1387 lyd_free_meta(LYD_NODE_CTX(diff), val_meta, 0);
1388 if (child) {
1389 /* change operation to NONE, we have siblings */
1390 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1391 return 0;
1392 }
1393
1394 /* redundant node, BUT !!
1395 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1396 * because the data that this is applied on should not change for the diff lifetime.
1397 * However, when we are merging 2 diffs, this conversion is actually lossy because
1398 * if the data change, the move operation can also change its meaning. In this specific
1399 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1400 */
1401 return 1;
1402 }
1403 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1404 /* check whether at least the default flags are different */
1405 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1406 assert(meta);
1407 str = lyd_meta2str(meta, &dynamic);
1408 assert(!dynamic);
1409
1410 /* if previous and current dflt flags are the same, this node is redundant */
1411 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1412 return 1;
1413 }
1414 return 0;
1415 }
1416
1417 if (!child && (op == LYD_DIFF_OP_NONE)) {
1418 return 1;
1419 }
1420
1421 return 0;
1422}
1423
1424/**
1425 * @brief Merge sysrepo diff with another diff, recursively.
1426 *
1427 * @param[in] src_diff Source diff node.
1428 * @param[in] diff_parent Current sysrepo diff parent.
1429 * @param[in] diff_cb Optional diff callback.
1430 * @param[in] cb_data User data for @p diff_cb.
1431 * @param[in,out] diff Diff root node.
1432 * @return LY_ERR value.
1433 */
1434static LY_ERR
1435lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
1436 struct lyd_node **diff)
1437{
1438 LY_ERR ret = LY_SUCCESS;
1439 struct lyd_node *child, *diff_node = NULL;
1440 enum lyd_diff_op src_op, cur_op;
1441
1442 /* get source node operation */
1443 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1444
1445 /* find an equal node in the current diff */
1446 lyd_diff_find_node(diff_parent ? LYD_CHILD(diff_parent) : *diff, src_diff, &diff_node);
1447
1448 if (diff_node) {
1449 /* get target (current) operation */
1450 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1451
1452 /* merge operations */
1453 switch (src_op) {
1454 case LYD_DIFF_OP_REPLACE:
1455 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1456 break;
1457 case LYD_DIFF_OP_CREATE:
1458 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff);
1459 break;
1460 case LYD_DIFF_OP_DELETE:
1461 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1462 break;
1463 case LYD_DIFF_OP_NONE:
1464 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1465 break;
1466 default:
1467 LOGINT_RET(LYD_NODE_CTX(src_diff));
1468 }
1469 if (ret) {
1470 LOGERR(LYD_NODE_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
1471 return ret;
1472 }
1473
1474 if (diff_cb) {
1475 /* call callback */
1476 LY_CHECK_RET(diff_cb(diff_node, NULL, cb_data));
1477 }
1478
1479 /* update diff parent */
1480 diff_parent = diff_node;
1481
1482 /* merge src_diff recursively */
1483 LY_LIST_FOR(LYD_CHILD(src_diff), child) {
1484 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, diff));
1485 }
1486 } else {
1487 /* add new diff node with all descendants */
1488 diff_node = lyd_dup(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE);
1489 LY_CHECK_RET(!diff_node, LY_EINT);
1490
1491 /* insert node into diff if not already */
1492 if (!diff_parent) {
1493 if (*diff) {
1494 lyd_insert_sibling(*diff, diff_node);
1495 } else {
1496 *diff = diff_node;
1497 }
1498 }
1499
1500 /* update operation */
1501 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1502
1503 if (diff_cb) {
1504 /* call callback */
1505 LY_CHECK_RET(diff_cb(diff_node, NULL, cb_data));
1506 }
1507
1508 /* update diff parent */
1509 diff_parent = diff_node;
1510 }
1511
1512 /* remove any redundant nodes */
1513 if (diff_parent && lyd_diff_is_redundant(diff_parent)) {
1514 if (diff_parent == *diff) {
1515 *diff = (*diff)->next;
1516 }
1517 lyd_free_tree(diff_parent);
1518 }
1519
1520 return LY_SUCCESS;
1521}
1522
1523API LY_ERR
1524lyd_diff_merge_module(const struct lyd_node *src_diff, const struct lys_module *mod, lyd_diff_cb diff_cb, void *cb_data,
1525 struct lyd_node **diff)
1526{
1527 const struct lyd_node *src_root;
1528
1529 LY_LIST_FOR(src_diff, src_root) {
1530 if (mod && (lyd_owner_module(src_root) != mod)) {
1531 /* skip data nodes from different modules */
1532 continue;
1533 }
1534
1535 /* apply relevant nodes from the diff datatree */
1536 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, diff));
1537 }
1538
1539 return LY_SUCCESS;
1540}
1541
1542API LY_ERR
1543lyd_diff_merge(const struct lyd_node *src_diff, struct lyd_node **diff)
1544{
1545 return lyd_diff_merge_module(src_diff, NULL, NULL, NULL, diff);
1546}