blob: 01fa8ff4fab7d757d802d28be9de9de35815bb25 [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
46LY_ERR
47lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
48 const char *key, const char *value, const char *orig_key, struct lyd_node **diff)
49{
50 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
51 const struct lyd_node *parent = NULL;
52 const struct lys_module *yang_mod;
53
54 assert(diff);
55
56 /* find the first existing parent */
57 siblings = *diff;
58 while (1) {
59 /* find next node parent */
60 parent = node;
61 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
62 parent = (struct lyd_node *)parent->parent;
63 }
64 if (parent == node) {
65 /* no more parents to find */
66 break;
67 }
68
69 /* check whether it exists in the diff */
70 if (lyd_find_sibling_first(siblings, parent, &match)) {
71 break;
72 }
73
74 /* another parent found */
75 diff_parent = match;
76
77 /* move down in the diff */
78 siblings = LYD_CHILD(match);
79 }
80
81 /* duplicate the subtree (and connect to the diff if possible) */
82 dup = lyd_dup(node, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS);
83 LY_CHECK_RET(!dup, LY_EMEM);
84
85 /* find the first duplicated parent */
86 if (!diff_parent) {
87 diff_parent = (struct lyd_node *)dup->parent;
88 while (diff_parent && diff_parent->parent) {
89 diff_parent = (struct lyd_node *)diff_parent->parent;
90 }
91 } else {
92 diff_parent = (struct lyd_node *)dup;
93 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
94 diff_parent = (struct lyd_node *)diff_parent->parent;
95 }
96 }
97
98 /* no parent existed, must be manually connected */
99 if (!diff_parent) {
100 /* there actually was no parent to duplicate */
101 if (*diff) {
102 lyd_insert_sibling(*diff, dup);
103 } else {
104 *diff = dup;
105 }
106 } else if (!diff_parent->parent) {
107 if (*diff) {
108 lyd_insert_sibling(*diff, diff_parent);
109 } else {
110 *diff = diff_parent;
111 }
112 }
113
114 /* get module with the operation metadata */
115 yang_mod = LYD_NODE_CTX(node)->list.objs[1];
116 assert(!strcmp(yang_mod->name, "yang"));
117
118 /* add parent operation, if any */
119 if (diff_parent && (diff_parent != dup) && !lyd_new_meta(diff_parent, yang_mod, "operation", "none")) {
120 return LY_EMEM;
121 }
122
123 /* add subtree operation */
124 if (!lyd_new_meta(dup, yang_mod, "operation", lyd_diff_op2str(op))) {
125 return LY_EMEM;
126 }
127
128 /* orig-default */
129 if (orig_default && !lyd_new_meta(dup, yang_mod, "orig-default", orig_default)) {
130 return LY_EMEM;
131 }
132
133 /* orig-value */
134 if (orig_value && !lyd_new_meta(dup, yang_mod, "orig-value", orig_value)) {
135 return LY_EMEM;
136 }
137
138 /* key */
139 if (key && !lyd_new_meta(dup, yang_mod, "key", key)) {
140 return LY_EMEM;
141 }
142
143 /* value */
144 if (value && !lyd_new_meta(dup, yang_mod, "value", value)) {
145 return LY_EMEM;
146 }
147
148 /* orig-key */
149 if (orig_key && !lyd_new_meta(dup, yang_mod, "orig-key", orig_key)) {
150 return LY_EMEM;
151 }
152
153 return LY_SUCCESS;
154}
155
156/**
157 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
158 *
159 * @param[in] first
160 * @param[in] schema Schema node of the list/leaf-list.
161 * @param[in,out] userord Sized array of userord items.
162 * @return Userord item for all the user-ordered list/leaf-list instances.
163 */
164static struct lyd_diff_userord *
165lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
166{
167 struct lyd_diff_userord *item;
168 const struct lyd_node *iter, **node;
169 LY_ARRAY_COUNT_TYPE u;
170
171 LY_ARRAY_FOR(*userord, u) {
172 if ((*userord)[u].schema == schema) {
173 return &(*userord)[u];
174 }
175 }
176
177 /* it was not added yet, add it now */
178 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
179
180 item->schema = schema;
181 item->pos = 0;
182 item->inst = NULL;
183
184 /* store all the instance pointers in the current order */
185 if (first) {
186 if (first->parent) {
187 iter = first->parent->child;
188 } else {
189 for (iter = first; iter->prev->next; iter = iter->prev);
190 }
191 for (; iter; iter = iter->next) {
192 if (iter->schema == first->schema) {
193 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
194 *node = iter;
195 }
196 }
197 }
198
199 return item;
200}
201
202/**
203 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
204 * lists/leaf-lists.
205 *
206 * @param[in] first Node from the first tree, can be NULL (on create).
207 * @param[in] second Node from the second tree, can be NULL (on delete).
208 * @param[in] options Diff options.
209 * @param[in,out] userord Sized array of userord items for keeping the current node order.
210 * @param[out] op Operation.
211 * @param[out] orig_default Original default metadata.
212 * @param[out] value Value metadata.
213 * @param[out] orig_value Original value metadata
214 * @param[out] key Key metadata.
215 * @param[out] orig_key Original key metadata.
216 * @return LY_SUCCESS on success,
217 * @return LY_ENOT if there is no change to be added into diff,
218 * @return LY_ERR value on other errors.
219 */
220static LY_ERR
221lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, int options,
222 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
223 char **orig_value, char **key, char **orig_key)
224{
225 const struct lysc_node *schema;
226 int dynamic;
227 size_t buflen, bufused, first_pos, second_pos;
228 struct lyd_diff_userord *userord_item;
229
230 assert(first || second);
231
232 *orig_default = NULL;
233 *value = NULL;
234 *orig_value = NULL;
235 *key = NULL;
236 *orig_key = NULL;
237
238 schema = first ? first->schema : second->schema;
239 assert(lysc_is_userordered(schema));
240
241 /* get userord entry */
242 userord_item = lyd_diff_userord_get(first, schema, userord);
243 LY_CHECK_RET(!userord_item, LY_EMEM);
244
245 /* prepare position of the next instance */
246 second_pos = userord_item->pos++;
247
248 /* find user-ordered first position */
249 if (first) {
250 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
251 if (userord_item->inst[first_pos] == first) {
252 break;
253 }
254 }
255 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
256 } else {
257 first_pos = 0;
258 }
259
260 /* learn operation first */
261 if (!second) {
262 *op = LYD_DIFF_OP_DELETE;
263 } else if (!first) {
264 *op = LYD_DIFF_OP_CREATE;
265 } else {
266 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
267 if (lyd_compare(second, userord_item->inst[second_pos], 0)) {
268 /* in first, there is a different instance on the second position, we are going to move 'first' node */
269 *op = LYD_DIFF_OP_REPLACE;
270 } else if ((options & LYD_DIFF_WITHDEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
271 /* default flag change */
272 *op = LYD_DIFF_OP_NONE;
273 } else {
274 /* no changes */
275 return LY_ENOT;
276 }
277 }
278
279 /*
280 * set each attribute correctly based on the operation and node type
281 */
282
283 /* orig-default */
284 if ((options & LYD_DIFF_WITHDEFAULTS) && (schema->nodetype == LYS_LEAFLIST)
285 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
286 if (first->flags & LYD_DEFAULT) {
287 *orig_default = "true";
288 } else {
289 *orig_default = "false";
290 }
291 }
292
293 /* value */
294 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
295 if (second_pos) {
296 *value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[second_pos - 1], &dynamic);
297 if (!dynamic) {
298 *value = strdup(*value);
299 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
300 }
301 } else {
302 *value = strdup("");
303 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
304 }
305 }
306
307 /* orig-value */
308 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
309 if (first_pos) {
310 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[first_pos - 1], &dynamic);
311 if (!dynamic) {
312 *orig_value = strdup(*orig_value);
313 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
314 }
315 } else {
316 *orig_value = strdup("");
317 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
318 }
319 }
320
321 /* key */
322 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op ==LYD_DIFF_OP_CREATE))) {
323 if (second_pos) {
324 buflen = bufused = 0;
325 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
326 } else {
327 *key = strdup("");
328 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
329 }
330 }
331
332 /* orig-key */
333 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
334 if (first_pos) {
335 buflen = bufused = 0;
336 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
337 } else {
338 *orig_key = strdup("");
339 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
340 }
341 }
342
343 /*
344 * update our instances - apply the change
345 */
346 if (*op == LYD_DIFF_OP_CREATE) {
347 /* insert the instance */
348 LY_ARRAY_RESIZE_ERR_RET(schema->module->ctx, userord_item->inst, LY_ARRAY_COUNT(userord_item->inst) + 1,
349 ;, LY_EMEM);
350 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
351 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
352 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
353 }
354 LY_ARRAY_INCREMENT(userord_item->inst);
355 userord_item->inst[second_pos] = second;
356
357 } else if (*op == LYD_DIFF_OP_DELETE) {
358 /* remove the instance */
359 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
360 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
361 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
362 }
363 LY_ARRAY_DECREMENT(userord_item->inst);
364
365 } else if (*op == LYD_DIFF_OP_REPLACE) {
366 /* move the instances */
367 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
368 (first_pos - second_pos) * sizeof *userord_item->inst);
369 userord_item->inst[second_pos] = first;
370 }
371
372 return LY_SUCCESS;
373}
374
375/**
376 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
377 * lists/leaf-lists.
378 *
379 * @param[in] first Node from the first tree, can be NULL (on create).
380 * @param[in] second Node from the second tree, can be NULL (on delete).
381 * @param[in] options Diff options.
382 * @param[out] op Operation.
383 * @param[out] orig_default Original default metadata.
384 * @param[out] orig_value Original value metadata.
385 * @return LY_SUCCESS on success,
386 * @return LY_ENOT if there is no change to be added into diff,
387 * @return LY_ERR value on other errors.
388 */
389static LY_ERR
390lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, int options, enum lyd_diff_op *op,
391 const char **orig_default, char **orig_value)
392{
393 const struct lysc_node *schema;
394 int dynamic;
395
396 assert(first || second);
397
398 *orig_default = NULL;
399 *orig_value = NULL;
400
401 schema = first ? first->schema : second->schema;
402 assert(!lysc_is_userordered(schema));
403
404 /* learn operation first */
405 if (!second) {
406 *op = LYD_DIFF_OP_DELETE;
407 } else if (!first) {
408 *op = LYD_DIFF_OP_CREATE;
409 } else {
410 switch (schema->nodetype) {
411 case LYS_CONTAINER:
412 case LYS_RPC:
413 case LYS_ACTION:
414 case LYS_NOTIF:
415 /* no changes */
416 return LY_ENOT;
417 case LYS_LIST:
418 case LYS_LEAFLIST:
419 if ((options & LYD_DIFF_WITHDEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
420 /* default flag change */
421 *op = LYD_DIFF_OP_NONE;
422 } else {
423 /* no changes */
424 return LY_ENOT;
425 }
426 break;
427 case LYS_LEAF:
428 case LYS_ANYXML:
429 case LYS_ANYDATA:
430 if (lyd_compare(first, second, 0)) {
431 /* different values */
432 *op = LYD_DIFF_OP_REPLACE;
433 } else if ((options & LYD_DIFF_WITHDEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
434 /* default flag change */
435 *op = LYD_DIFF_OP_NONE;
436 } else {
437 /* no changes */
438 return LY_ENOT;
439 }
440 break;
441 default:
442 LOGINT_RET(schema->module->ctx);
443 }
444 }
445
446 /*
447 * set each attribute correctly based on the operation and node type
448 */
449
450 /* orig-default */
451 if ((options & LYD_DIFF_WITHDEFAULTS) && (schema->nodetype & LYD_NODE_TERM)
452 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
453 if (first->flags & LYD_DEFAULT) {
454 *orig_default = "true";
455 } else {
456 *orig_default = "false";
457 }
458 }
459
460 /* orig-value */
461 if ((schema->nodetype == LYS_LEAF) && (*op == LYD_DIFF_OP_REPLACE)) {
462 /* leaf */
463 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)first, &dynamic);
464 if (!dynamic) {
465 *orig_value = strdup(*orig_value);
466 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
467 }
468 }
469
470 return LY_SUCCESS;
471}
472
473/**
474 * @brief Perform diff for all siblings at certain depth, recursively.
475 *
476 * For user-ordered lists/leaf-lists a specific structure is used for storing
477 * the current order. The idea is to apply all the generated diff changes
478 * virtually on the first tree so that we can continue to generate correct
479 * changes after some were already generated.
480 *
481 * The algorithm then uses second tree position-based changes with a before
482 * (preceding) item anchor.
483 *
484 * Example:
485 *
486 * Virtual first tree leaf-list order:
487 * 1 2 [3] 4 5
488 *
489 * Second tree leaf-list order:
490 * 1 2 [5] 3 4
491 *
492 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
493 * match - they do not - move nodes so that the 3rd position node is final ->
494 * -> move node 5 to the 3rd position -> move node 5 after node 2.
495 *
496 * Required properties:
497 * Stored operations (move) should not be affected by later operations -
498 * - would cause a redundantly long list of operations, possibly inifinite.
499 *
500 * Implemenation justification:
501 * First, all delete operations and only then move/create operations are stored.
502 * Also, preceding anchor is used and after each iteration another node is
503 * at its final position. That results in the invariant that all preceding
504 * nodes are final and will not be changed by the later operations, meaning
505 * they can safely be used as anchors for the later operations.
506 *
507 * @param[in] first First tree first sibling.
508 * @param[in] second Second tree first sibling.
509 * @param[in] options Diff options.
510 * @param[in,out] diff Diff to append to.
511 * @return LY_ERR value.
512 */
513static LY_ERR
514lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
515{
516 LY_ERR ret = LY_SUCCESS;
517 const struct lyd_node *iter_first, *iter_second;
518 struct lyd_node *match_second, *match_first;
519 int nosiblings = 0;
520 struct lyd_diff_userord *userord = NULL;
521 LY_ARRAY_COUNT_TYPE u;
522 enum lyd_diff_op op;
523 const char *orig_default;
524 char *orig_value, *key, *value, *orig_key;
525
526 if (options & LYD_DIFF_NOSIBLINGS) {
527 /* remember it for this function call only, should not be passed recursively */
528 nosiblings = 1;
529 options &= ~LYD_DIFF_NOSIBLINGS;
530 }
531
532 /* compare first tree to the second tree - delete, replace, none */
533 LY_LIST_FOR(first, iter_first) {
534 assert(!(iter_first->schema->flags & LYS_KEY));
535 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_WITHDEFAULTS)) {
536 /* skip default nodes */
537 continue;
538 }
539
540 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
541 /* try to find the exact instance */
542 lyd_find_sibling_first(second, iter_first, &match_second);
543 } else {
544 /* try to simply find the node, there cannot be more instances */
545 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
546 }
547
548 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_WITHDEFAULTS)) {
549 /* ignore default nodes */
550 match_second = NULL;
551 }
552
553 if (lysc_is_userordered(iter_first->schema)) {
554 if (match_second) {
555 /* we are handling only user-ordered node delete now */
556 continue;
557 }
558
559 /* get all the attributes */
560 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
561 &value, &orig_value, &key, &orig_key), cleanup);
562
563 /* there must be changes, it is deleted */
564 assert(op == LYD_DIFF_OP_DELETE);
565 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
566
567 free(orig_value);
568 free(key);
569 free(value);
570 free(orig_key);
571 LY_CHECK_GOTO(ret, cleanup);
572 } else {
573 /* get all the attributes */
574 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
575
576 /* add into diff if there are any changes */
577 if (!ret) {
578 if (op == LYD_DIFF_OP_DELETE) {
579 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
580 } else {
581 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
582 }
583
584 free(orig_value);
585 LY_CHECK_GOTO(ret, cleanup);
586 } else if (ret == LY_ENOT) {
587 ret = LY_SUCCESS;
588 } else {
589 goto cleanup;
590 }
591 }
592
593 /* check descendants, if any, recursively */
594 if (match_second) {
595 LY_CHECK_GOTO(lyd_diff_siblings_r(LYD_CHILD(iter_first), LYD_CHILD(match_second), options, diff), cleanup);
596 }
597
598 if (nosiblings) {
599 break;
600 }
601 }
602
603 /* reset all cached positions */
604 LY_ARRAY_FOR(userord, u) {
605 userord[u].pos = 0;
606 }
607
608 /* compare second tree to the first tree - create, user-ordered move */
609 LY_LIST_FOR(second, iter_second) {
610 assert(!(iter_second->schema->flags & LYS_KEY));
611 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_WITHDEFAULTS)) {
612 /* skip default nodes */
613 continue;
614 }
615
616 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
617 lyd_find_sibling_first(first, iter_second, &match_first);
618 } else {
619 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
620 }
621
622 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_WITHDEFAULTS)) {
623 /* ignore default nodes */
624 match_first = NULL;
625 }
626
627 if (lysc_is_userordered(iter_second->schema)) {
628 /* get all the attributes */
629 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
630 &value, &orig_value, &key, &orig_key);
631
632 /* add into diff if there are any changes */
633 if (!ret) {
634 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
635
636 free(orig_value);
637 free(key);
638 free(value);
639 free(orig_key);
640 LY_CHECK_GOTO(ret, cleanup);
641 } else if (ret == LY_ENOT) {
642 ret = LY_SUCCESS;
643 } else {
644 goto cleanup;
645 }
646 } else if (!match_first) {
647 /* get all the attributes */
648 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
649
650 /* there must be changes, it is created */
651 assert(op == LYD_DIFF_OP_CREATE);
652 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
653
654 free(orig_value);
655 LY_CHECK_GOTO(ret, cleanup);
656 } /* else was handled */
657
658 if (nosiblings) {
659 break;
660 }
661 }
662
663cleanup:
664 LY_ARRAY_FOR(userord, u) {
665 LY_ARRAY_FREE(userord[u].inst);
666 }
667 LY_ARRAY_FREE(userord);
668 return ret;
669}
670
671API LY_ERR
672lyd_diff(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
673{
674 const struct ly_ctx *ctx;
675
676 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
677
678 if (first) {
679 ctx = LYD_NODE_CTX(first);
680 } else if (second) {
681 ctx = LYD_NODE_CTX(second);
682 } else {
683 ctx = NULL;
684 }
685
686 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
687 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
688 return LY_EINVAL;
689 }
690
691 *diff = NULL;
692
693 return lyd_diff_siblings_r(first, second, options, diff);
694}
695
696/**
697 * @brief Find a matching node in data tree for a diff node.
698 *
699 * @param[in] first_node First sibling in the data tree.
700 * @param[in] diff_node Diff node to match.
701 * @param[out] match_p Matching node.
702 * @return LY_ERR value.
703 */
704static LY_ERR
705lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
706{
707 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
708 /* try to find the exact instance */
709 lyd_find_sibling_first(first_node, diff_node, match_p);
710 } else {
711 /* try to simply find the node, there cannot be more instances */
712 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
713 }
714 LY_CHECK_ERR_RET(!*match_p, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
715
716 return LY_SUCCESS;
717}
718
719/**
720 * @brief Learn operation of a diff node.
721 *
722 * @param[in] diff_node Diff node.
723 * @param[out] op Operation.
724 * @param[out] key_or_value Optional list instance keys predicate or leaf-list value for move operation.
725 * @return LY_ERR value.
726 */
727static LY_ERR
728lyd_diff_get_op(const struct lyd_node *diff_node, const char **op, const char **key_or_value)
729{
730 struct lyd_meta *meta = NULL;
731 const struct lyd_node *diff_parent;
732 const char *meta_name, *str;
733 int dynamic;
734
735 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
736 LY_LIST_FOR(diff_parent->meta, meta) {
737 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
738 str = lyd_meta2str(meta, &dynamic);
739 assert(!dynamic);
740 if ((str[0] == 'r') && (diff_parent != diff_node)) {
741 /* we do not care about this operation if it's in our parent */
742 continue;
743 }
744 *op = str;
745 break;
746 }
747 }
748 if (meta) {
749 break;
750 }
751 }
752 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
753
754 *key_or_value = NULL;
755 if (lysc_is_userordered(diff_node->schema) && (((*op)[0] == 'c') || ((*op)[0] == 'r'))) {
756 if (diff_node->schema->nodetype == LYS_LIST) {
757 meta_name = "key";
758 } else {
759 meta_name = "value";
760 }
761
762 LY_LIST_FOR(diff_node->meta, meta) {
763 if (!strcmp(meta->name, meta_name) && !strcmp(meta->annotation->module->name, "yang")) {
764 str = lyd_meta2str(meta, &dynamic);
765 assert(!dynamic);
766 *key_or_value = str;
767 break;
768 }
769 }
770 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
771 }
772
773 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.
863 * @return LY_ERR value.
864 */
865static LY_ERR
866lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
867 lyd_diff_cb diff_cb, void *cb_data)
868{
869 LY_ERR ret;
870 struct lyd_node *match, *diff_child;
871 const char *op, *key_or_value, *str_val;
872 int dynamic;
873 const struct ly_ctx *ctx = LYD_NODE_CTX(diff_node);
874
875 /* read all the valid attributes */
876 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op, &key_or_value));
877
878 /* handle user-ordered (leaf-)lists separately */
879 if (key_or_value) {
880 assert((op[0] == 'c') || (op[0] == 'r'));
881 if (op[0] == 'r') {
882 /* find the node (we must have some siblings because the node was only moved) */
883 LY_CHECK_RET(lyd_diff_find_node(*first_node, diff_node, &match));
884 } else {
885 /* duplicate the node(s) */
886 match = lyd_dup(diff_node, NULL, LYD_DUP_NO_META);
887 LY_CHECK_RET(!match, LY_EMEM);
888 }
889
890 /* insert/move the node */
891 if (key_or_value[0]) {
892 ret = lyd_diff_insert(first_node, parent_node, match, key_or_value);
893 } else {
894 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
895 }
896 if (ret) {
897 if (op[0] == 'c') {
898 lyd_free_tree(match);
899 }
900 return ret;
901 }
902
903 goto next_iter_r;
904 }
905
906 /* apply operation */
907 switch (op[0]) {
908 case 'n':
909 /* find the node */
910 LY_CHECK_RET(lyd_diff_find_node(*first_node, diff_node, &match));
911
912 if (match->schema->nodetype & LYD_NODE_TERM) {
913 /* special case of only dflt flag change */
914 if (diff_node->flags & LYD_DEFAULT) {
915 match->flags |= LYD_DEFAULT;
916 } else {
917 match->flags &= ~LYD_DEFAULT;
918 }
919 } else {
920 /* none operation on nodes without children is redundant and hence forbidden */
921 if (!LYD_CHILD(diff_node)) {
922 LOGINT_RET(ctx);
923 }
924 }
925 break;
926 case 'c':
927 /* duplicate the node */
928 match = lyd_dup(diff_node, NULL, LYD_DUP_NO_META);
929 LY_CHECK_RET(!match, LY_EMEM);
930
931 /* insert it at the end */
932 ret = 0;
933 if (*first_node) {
934 ret = lyd_insert_after((*first_node)->prev, match);
935 } else if (parent_node) {
936 ret = lyd_insert(parent_node, match);
937 } else {
938 *first_node = match;
939 }
940 if (ret) {
941 lyd_free_tree(match);
942 return ret;
943 }
944
945 break;
946 case 'd':
947 /* find the node */
948 LY_CHECK_RET(lyd_diff_find_node(*first_node, diff_node, &match));
949
950 /* remove it */
951 if ((match == *first_node) && !match->parent) {
952 assert(!parent_node);
953 /* we have removed the top-level node */
954 *first_node = (*first_node)->next;
955 }
956 lyd_free_tree(match);
957
958 /* we are not going recursively in this case, the whole subtree was already deleted */
959 return LY_SUCCESS;
960 case 'r':
961 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
962
963 /* find the node */
964 LY_CHECK_RET(lyd_diff_find_node(*first_node, diff_node, &match));
965
966 /* update its value */
967 str_val = lyd_value2str((struct lyd_node_term *)diff_node, &dynamic);
968 ret = lyd_change_term(match, str_val);
969 if (dynamic) {
970 free((char *)str_val);
971 }
972 if (ret && (ret != LY_EEXIST)) {
973 LOGINT_RET(ctx);
974 }
975
976 /* with flags */
977 match->flags = diff_node->flags;
978 break;
979 default:
980 LOGINT_RET(ctx);
981 }
982
983next_iter_r:
984 if (diff_cb) {
985 /* call callback */
986 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
987 }
988
989 /* apply diff recursively */
990 LY_LIST_FOR(LYD_CHILD(diff_node), diff_child) {
991 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
992 }
993
994 return LY_SUCCESS;
995}
996
997API LY_ERR
998lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
999 lyd_diff_cb diff_cb, void *cb_data)
1000{
1001 const struct lyd_node *root;
1002
1003 LY_LIST_FOR(diff, root) {
1004 if (mod && (lyd_owner_module(root) != mod)) {
1005 /* skip data nodes from different modules */
1006 continue;
1007 }
1008
1009 /* apply relevant nodes from the diff datatree */
1010 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1011 }
1012
1013 return LY_SUCCESS;
1014}
1015
1016API LY_ERR
1017lyd_diff_apply(struct lyd_node **data, const struct lyd_node *diff)
1018{
1019 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1020}