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