blob: d456a64b5f069bbbfeaf6aa07a4cc84540b9928a [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>
Radek Krejci47fab892020-11-05 17:02:41 +010021#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020022#include <string.h>
23
24#include "common.h"
Radek Krejci47fab892020-11-05 17:02:41 +010025#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020026#include "log.h"
Radek Krejci47fab892020-11-05 17:02:41 +010027#include "plugins_types.h"
28#include "set.h"
29#include "tree.h"
30#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020031#include "tree_data_internal.h"
32#include "tree_schema.h"
33#include "tree_schema_internal.h"
34
35static const char *
36lyd_diff_op2str(enum lyd_diff_op op)
37{
38 switch (op) {
39 case LYD_DIFF_OP_CREATE:
40 return "create";
41 case LYD_DIFF_OP_DELETE:
42 return "delete";
43 case LYD_DIFF_OP_REPLACE:
44 return "replace";
45 case LYD_DIFF_OP_NONE:
46 return "none";
47 }
48
49 LOGINT(NULL);
50 return NULL;
51}
52
Michal Vaskoe6323f62020-07-09 15:49:02 +020053static enum lyd_diff_op
54lyd_diff_str2op(const char *str)
55{
56 switch (str[0]) {
57 case 'c':
58 assert(!strcmp(str, "create"));
59 return LYD_DIFF_OP_CREATE;
60 case 'd':
61 assert(!strcmp(str, "delete"));
62 return LYD_DIFF_OP_DELETE;
63 case 'r':
64 assert(!strcmp(str, "replace"));
65 return LYD_DIFF_OP_REPLACE;
66 case 'n':
67 assert(!strcmp(str, "none"));
68 return LYD_DIFF_OP_NONE;
69 }
70
71 LOGINT(NULL);
72 return 0;
73}
74
Michal Vaskod59035b2020-07-08 12:00:06 +020075LY_ERR
76lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
Radek Krejci0f969882020-08-21 16:56:47 +020077 const char *key, const char *value, const char *orig_key, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +020078{
79 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
80 const struct lyd_node *parent = NULL;
81 const struct lys_module *yang_mod;
82
83 assert(diff);
84
85 /* find the first existing parent */
86 siblings = *diff;
87 while (1) {
88 /* find next node parent */
89 parent = node;
90 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
91 parent = (struct lyd_node *)parent->parent;
92 }
93 if (parent == node) {
94 /* no more parents to find */
95 break;
96 }
97
98 /* check whether it exists in the diff */
99 if (lyd_find_sibling_first(siblings, parent, &match)) {
100 break;
101 }
102
103 /* another parent found */
104 diff_parent = match;
105
106 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200107 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200108 }
109
110 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200111 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko69730152020-10-09 16:30:07 +0200112 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200113
114 /* find the first duplicated parent */
115 if (!diff_parent) {
116 diff_parent = (struct lyd_node *)dup->parent;
117 while (diff_parent && diff_parent->parent) {
118 diff_parent = (struct lyd_node *)diff_parent->parent;
119 }
120 } else {
121 diff_parent = (struct lyd_node *)dup;
122 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
123 diff_parent = (struct lyd_node *)diff_parent->parent;
124 }
125 }
126
127 /* no parent existed, must be manually connected */
128 if (!diff_parent) {
129 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200130 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200131 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200132 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200133 }
134
135 /* get module with the operation metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200136 yang_mod = LYD_CTX(node)->list.objs[1];
Michal Vaskod59035b2020-07-08 12:00:06 +0200137 assert(!strcmp(yang_mod->name, "yang"));
138
139 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200140 if (diff_parent && (diff_parent != dup)) {
141 LY_CHECK_RET(lyd_new_meta(diff_parent, yang_mod, "operation", "none", NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200142 }
143
144 /* add subtree operation */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200145 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "operation", lyd_diff_op2str(op), NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200146
147 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200148 if (orig_default) {
149 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-default", orig_default, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200150 }
151
152 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200153 if (orig_value) {
154 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-value", orig_value, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200155 }
156
157 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200158 if (key) {
159 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "key", key, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200160 }
161
162 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200163 if (value) {
164 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "value", value, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200165 }
166
167 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200168 if (orig_key) {
169 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-key", orig_key, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200170 }
171
172 return LY_SUCCESS;
173}
174
175/**
176 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
177 *
178 * @param[in] first
179 * @param[in] schema Schema node of the list/leaf-list.
180 * @param[in,out] userord Sized array of userord items.
181 * @return Userord item for all the user-ordered list/leaf-list instances.
182 */
183static struct lyd_diff_userord *
184lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
185{
186 struct lyd_diff_userord *item;
187 const struct lyd_node *iter, **node;
188 LY_ARRAY_COUNT_TYPE u;
189
190 LY_ARRAY_FOR(*userord, u) {
191 if ((*userord)[u].schema == schema) {
192 return &(*userord)[u];
193 }
194 }
195
196 /* it was not added yet, add it now */
197 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
198
199 item->schema = schema;
200 item->pos = 0;
201 item->inst = NULL;
202
203 /* store all the instance pointers in the current order */
204 if (first) {
205 if (first->parent) {
206 iter = first->parent->child;
207 } else {
Radek Krejci1e008d22020-08-17 11:37:37 +0200208 for (iter = first; iter->prev->next; iter = iter->prev) {}
Michal Vaskod59035b2020-07-08 12:00:06 +0200209 }
Michal Vaskod989ba02020-08-24 10:59:24 +0200210 for ( ; iter; iter = iter->next) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200211 if (iter->schema == first->schema) {
212 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
213 *node = iter;
214 }
215 }
216 }
217
218 return item;
219}
220
221/**
222 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
223 * lists/leaf-lists.
224 *
225 * @param[in] first Node from the first tree, can be NULL (on create).
226 * @param[in] second Node from the second tree, can be NULL (on delete).
227 * @param[in] options Diff options.
228 * @param[in,out] userord Sized array of userord items for keeping the current node order.
229 * @param[out] op Operation.
230 * @param[out] orig_default Original default metadata.
231 * @param[out] value Value metadata.
232 * @param[out] orig_value Original value metadata
233 * @param[out] key Key metadata.
234 * @param[out] orig_key Original key metadata.
235 * @return LY_SUCCESS on success,
236 * @return LY_ENOT if there is no change to be added into diff,
237 * @return LY_ERR value on other errors.
238 */
239static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200240lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Radek Krejci0f969882020-08-21 16:56:47 +0200241 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
242 char **orig_value, char **key, char **orig_key)
Michal Vaskod59035b2020-07-08 12:00:06 +0200243{
244 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200245 size_t buflen, bufused, first_pos, second_pos;
246 struct lyd_diff_userord *userord_item;
247
248 assert(first || second);
249
250 *orig_default = NULL;
251 *value = NULL;
252 *orig_value = NULL;
253 *key = NULL;
254 *orig_key = NULL;
255
256 schema = first ? first->schema : second->schema;
257 assert(lysc_is_userordered(schema));
258
259 /* get userord entry */
260 userord_item = lyd_diff_userord_get(first, schema, userord);
261 LY_CHECK_RET(!userord_item, LY_EMEM);
262
263 /* prepare position of the next instance */
264 second_pos = userord_item->pos++;
265
266 /* find user-ordered first position */
267 if (first) {
268 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
269 if (userord_item->inst[first_pos] == first) {
270 break;
271 }
272 }
273 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
274 } else {
275 first_pos = 0;
276 }
277
278 /* learn operation first */
279 if (!second) {
280 *op = LYD_DIFF_OP_DELETE;
281 } else if (!first) {
282 *op = LYD_DIFF_OP_CREATE;
283 } else {
284 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
Michal Vasko8f359bf2020-07-28 10:41:15 +0200285 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200286 /* in first, there is a different instance on the second position, we are going to move 'first' node */
287 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200288 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200289 /* default flag change */
290 *op = LYD_DIFF_OP_NONE;
291 } else {
292 /* no changes */
293 return LY_ENOT;
294 }
295 }
296
297 /*
298 * set each attribute correctly based on the operation and node type
299 */
300
301 /* orig-default */
Michal Vasko69730152020-10-09 16:30:07 +0200302 if ((options & LYD_DIFF_DEFAULTS) && (schema->nodetype == LYS_LEAFLIST) &&
303 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE)) &&
304 ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200305 if (first->flags & LYD_DEFAULT) {
306 *orig_default = "true";
307 } else {
308 *orig_default = "false";
309 }
310 }
311
312 /* value */
313 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
314 if (second_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200315 *value = strdup(LYD_CANON_VALUE(userord_item->inst[second_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200316 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200317 } else {
318 *value = strdup("");
319 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
320 }
321 }
322
323 /* orig-value */
324 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
325 if (first_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200326 *orig_value = strdup(LYD_CANON_VALUE(userord_item->inst[first_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200327 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200328 } else {
329 *orig_value = strdup("");
330 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
331 }
332 }
333
334 /* key */
Michal Vasko44f3d2c2020-08-24 09:49:38 +0200335 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200336 if (second_pos) {
337 buflen = bufused = 0;
338 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
339 } else {
340 *key = strdup("");
341 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
342 }
343 }
344
345 /* orig-key */
346 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
347 if (first_pos) {
348 buflen = bufused = 0;
349 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
350 } else {
351 *orig_key = strdup("");
352 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
353 }
354 }
355
356 /*
357 * update our instances - apply the change
358 */
359 if (*op == LYD_DIFF_OP_CREATE) {
360 /* insert the instance */
361 LY_ARRAY_RESIZE_ERR_RET(schema->module->ctx, userord_item->inst, LY_ARRAY_COUNT(userord_item->inst) + 1,
Michal Vasko69730152020-10-09 16:30:07 +0200362 ; , LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200363 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
364 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
365 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
366 }
367 LY_ARRAY_INCREMENT(userord_item->inst);
368 userord_item->inst[second_pos] = second;
369
370 } else if (*op == LYD_DIFF_OP_DELETE) {
371 /* remove the instance */
372 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
373 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
374 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
375 }
376 LY_ARRAY_DECREMENT(userord_item->inst);
377
378 } else if (*op == LYD_DIFF_OP_REPLACE) {
379 /* move the instances */
380 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
381 (first_pos - second_pos) * sizeof *userord_item->inst);
382 userord_item->inst[second_pos] = first;
383 }
384
385 return LY_SUCCESS;
386}
387
388/**
389 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
390 * lists/leaf-lists.
391 *
392 * @param[in] first Node from the first tree, can be NULL (on create).
393 * @param[in] second Node from the second tree, can be NULL (on delete).
394 * @param[in] options Diff options.
395 * @param[out] op Operation.
396 * @param[out] orig_default Original default metadata.
397 * @param[out] orig_value Original value metadata.
398 * @return LY_SUCCESS on success,
399 * @return LY_ENOT if there is no change to be added into diff,
400 * @return LY_ERR value on other errors.
401 */
402static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200403lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, enum lyd_diff_op *op,
Radek Krejci0f969882020-08-21 16:56:47 +0200404 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200405{
406 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200407
408 assert(first || second);
409
410 *orig_default = NULL;
411 *orig_value = NULL;
412
413 schema = first ? first->schema : second->schema;
414 assert(!lysc_is_userordered(schema));
415
416 /* learn operation first */
417 if (!second) {
418 *op = LYD_DIFF_OP_DELETE;
419 } else if (!first) {
420 *op = LYD_DIFF_OP_CREATE;
421 } else {
422 switch (schema->nodetype) {
423 case LYS_CONTAINER:
424 case LYS_RPC:
425 case LYS_ACTION:
426 case LYS_NOTIF:
427 /* no changes */
428 return LY_ENOT;
429 case LYS_LIST:
430 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200431 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200432 /* default flag change */
433 *op = LYD_DIFF_OP_NONE;
434 } else {
435 /* no changes */
436 return LY_ENOT;
437 }
438 break;
439 case LYS_LEAF:
440 case LYS_ANYXML:
441 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200442 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200443 /* different values */
444 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200445 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200446 /* default flag change */
447 *op = LYD_DIFF_OP_NONE;
448 } else {
449 /* no changes */
450 return LY_ENOT;
451 }
452 break;
453 default:
454 LOGINT_RET(schema->module->ctx);
455 }
456 }
457
458 /*
459 * set each attribute correctly based on the operation and node type
460 */
461
462 /* orig-default */
Michal Vasko69730152020-10-09 16:30:07 +0200463 if ((options & LYD_DIFF_DEFAULTS) && (schema->nodetype & LYD_NODE_TERM) &&
464 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE)) &&
465 ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200466 if (first->flags & LYD_DEFAULT) {
467 *orig_default = "true";
468 } else {
469 *orig_default = "false";
470 }
471 }
472
473 /* orig-value */
474 if ((schema->nodetype == LYS_LEAF) && (*op == LYD_DIFF_OP_REPLACE)) {
475 /* leaf */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200476 *orig_value = strdup(LYD_CANON_VALUE(first));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200477 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200478 }
479
480 return LY_SUCCESS;
481}
482
483/**
484 * @brief Perform diff for all siblings at certain depth, recursively.
485 *
486 * For user-ordered lists/leaf-lists a specific structure is used for storing
487 * the current order. The idea is to apply all the generated diff changes
488 * virtually on the first tree so that we can continue to generate correct
489 * changes after some were already generated.
490 *
491 * The algorithm then uses second tree position-based changes with a before
492 * (preceding) item anchor.
493 *
494 * Example:
495 *
496 * Virtual first tree leaf-list order:
497 * 1 2 [3] 4 5
498 *
499 * Second tree leaf-list order:
500 * 1 2 [5] 3 4
501 *
502 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
503 * match - they do not - move nodes so that the 3rd position node is final ->
504 * -> move node 5 to the 3rd position -> move node 5 after node 2.
505 *
506 * Required properties:
507 * Stored operations (move) should not be affected by later operations -
508 * - would cause a redundantly long list of operations, possibly inifinite.
509 *
510 * Implemenation justification:
511 * First, all delete operations and only then move/create operations are stored.
512 * Also, preceding anchor is used and after each iteration another node is
513 * at its final position. That results in the invariant that all preceding
514 * nodes are final and will not be changed by the later operations, meaning
515 * they can safely be used as anchors for the later operations.
516 *
517 * @param[in] first First tree first sibling.
518 * @param[in] second Second tree first sibling.
519 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200520 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200521 * @param[in,out] diff Diff to append to.
522 * @return LY_ERR value.
523 */
524static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200525lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
Radek Krejci0f969882020-08-21 16:56:47 +0200526 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200527{
528 LY_ERR ret = LY_SUCCESS;
529 const struct lyd_node *iter_first, *iter_second;
530 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200531 struct lyd_diff_userord *userord = NULL;
532 LY_ARRAY_COUNT_TYPE u;
533 enum lyd_diff_op op;
534 const char *orig_default;
535 char *orig_value, *key, *value, *orig_key;
536
Michal Vaskod59035b2020-07-08 12:00:06 +0200537 /* compare first tree to the second tree - delete, replace, none */
538 LY_LIST_FOR(first, iter_first) {
539 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200540 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200541 /* skip default nodes */
542 continue;
543 }
544
545 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
546 /* try to find the exact instance */
547 lyd_find_sibling_first(second, iter_first, &match_second);
548 } else {
549 /* try to simply find the node, there cannot be more instances */
550 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
551 }
552
Michal Vasko3a41dff2020-07-15 14:30:28 +0200553 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200554 /* ignore default nodes */
555 match_second = NULL;
556 }
557
558 if (lysc_is_userordered(iter_first->schema)) {
559 if (match_second) {
560 /* we are handling only user-ordered node delete now */
561 continue;
562 }
563
564 /* get all the attributes */
565 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200566 &value, &orig_value, &key, &orig_key), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200567
568 /* there must be changes, it is deleted */
569 assert(op == LYD_DIFF_OP_DELETE);
570 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
571
572 free(orig_value);
573 free(key);
574 free(value);
575 free(orig_key);
576 LY_CHECK_GOTO(ret, cleanup);
577 } else {
578 /* get all the attributes */
579 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
580
581 /* add into diff if there are any changes */
582 if (!ret) {
583 if (op == LYD_DIFF_OP_DELETE) {
584 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
585 } else {
586 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
587 }
588
589 free(orig_value);
590 LY_CHECK_GOTO(ret, cleanup);
591 } else if (ret == LY_ENOT) {
592 ret = LY_SUCCESS;
593 } else {
594 goto cleanup;
595 }
596 }
597
598 /* check descendants, if any, recursively */
599 if (match_second) {
Radek Krejcia1c1e542020-09-29 16:06:52 +0200600 LY_CHECK_GOTO(lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second), options,
Michal Vasko69730152020-10-09 16:30:07 +0200601 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200602 }
603
604 if (nosiblings) {
605 break;
606 }
607 }
608
609 /* reset all cached positions */
610 LY_ARRAY_FOR(userord, u) {
611 userord[u].pos = 0;
612 }
613
614 /* compare second tree to the first tree - create, user-ordered move */
615 LY_LIST_FOR(second, iter_second) {
616 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200617 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200618 /* skip default nodes */
619 continue;
620 }
621
622 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
623 lyd_find_sibling_first(first, iter_second, &match_first);
624 } else {
625 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
626 }
627
Michal Vasko3a41dff2020-07-15 14:30:28 +0200628 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200629 /* ignore default nodes */
630 match_first = NULL;
631 }
632
633 if (lysc_is_userordered(iter_second->schema)) {
634 /* get all the attributes */
635 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200636 &value, &orig_value, &key, &orig_key);
Michal Vaskod59035b2020-07-08 12:00:06 +0200637
638 /* add into diff if there are any changes */
639 if (!ret) {
640 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
641
642 free(orig_value);
643 free(key);
644 free(value);
645 free(orig_key);
646 LY_CHECK_GOTO(ret, cleanup);
647 } else if (ret == LY_ENOT) {
648 ret = LY_SUCCESS;
649 } else {
650 goto cleanup;
651 }
652 } else if (!match_first) {
653 /* get all the attributes */
654 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
655
656 /* there must be changes, it is created */
657 assert(op == LYD_DIFF_OP_CREATE);
658 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
659
660 free(orig_value);
661 LY_CHECK_GOTO(ret, cleanup);
662 } /* else was handled */
663
664 if (nosiblings) {
665 break;
666 }
667 }
668
669cleanup:
670 LY_ARRAY_FOR(userord, u) {
671 LY_ARRAY_FREE(userord[u].inst);
672 }
673 LY_ARRAY_FREE(userord);
674 return ret;
675}
676
Michal Vasko3a41dff2020-07-15 14:30:28 +0200677static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200678lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200679{
680 const struct ly_ctx *ctx;
681
682 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
683
684 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200685 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200686 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200687 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200688 } else {
689 ctx = NULL;
690 }
691
692 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
693 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
694 return LY_EINVAL;
695 }
696
697 *diff = NULL;
698
Michal Vasko3a41dff2020-07-15 14:30:28 +0200699 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
700}
701
702API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200703lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff)
Michal Vasko3a41dff2020-07-15 14:30:28 +0200704{
705 return lyd_diff(first, second, options, 1, diff);
706}
707
708API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200709lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff)
Michal Vasko3a41dff2020-07-15 14:30:28 +0200710{
711 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200712}
713
714/**
715 * @brief Find a matching node in data tree for a diff node.
716 *
717 * @param[in] first_node First sibling in the data tree.
718 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200719 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200720 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200721static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200722lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
723{
724 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
725 /* try to find the exact instance */
726 lyd_find_sibling_first(first_node, diff_node, match_p);
727 } else {
728 /* try to simply find the node, there cannot be more instances */
729 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
730 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200731}
732
733/**
734 * @brief Learn operation of a diff node.
735 *
736 * @param[in] diff_node Diff node.
737 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200738 * @return LY_ERR value.
739 */
740static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200741lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200742{
743 struct lyd_meta *meta = NULL;
744 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200745 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200746
747 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
748 LY_LIST_FOR(diff_parent->meta, meta) {
749 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200750 str = meta->value.canonical;
Michal Vaskod59035b2020-07-08 12:00:06 +0200751 if ((str[0] == 'r') && (diff_parent != diff_node)) {
752 /* we do not care about this operation if it's in our parent */
753 continue;
754 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200755 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200756 break;
757 }
758 }
759 if (meta) {
760 break;
761 }
762 }
Michal Vaskob7be7a82020-08-20 09:09:04 +0200763 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200764
Michal Vaskod59035b2020-07-08 12:00:06 +0200765 return LY_SUCCESS;
766}
767
768/**
769 * @brief Insert a diff node into a data tree.
770 *
771 * @param[in,out] first_node First sibling of the data tree.
772 * @param[in] parent_node Data tree sibling parent node.
773 * @param[in] new_node Node to insert.
774 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
775 * instance will be inserted at the first position.
776 * @return err_info, NULL on success.
777 */
778static LY_ERR
779lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Radek Krejci0f969882020-08-21 16:56:47 +0200780 const char *key_or_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200781{
782 LY_ERR ret;
783 struct lyd_node *anchor;
784
785 assert(new_node);
786
787 if (!*first_node) {
788 if (!parent_node) {
789 /* no parent or siblings */
790 *first_node = new_node;
791 return LY_SUCCESS;
792 }
793
794 /* simply insert into parent, no other children */
795 if (key_or_value) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200796 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200797 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200798 return LY_EINVAL;
799 }
Michal Vaskob104f112020-07-17 09:54:54 +0200800 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200801 }
802
803 assert(!(*first_node)->parent || ((struct lyd_node *)(*first_node)->parent == parent_node));
804
Michal Vaskod59035b2020-07-08 12:00:06 +0200805 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200806 /* simple insert */
807 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200808 }
809
810 if (key_or_value) {
811 /* find the anchor sibling */
812 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
813 if (ret == LY_ENOTFOUND) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200814 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200815 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200816 return LY_EINVAL;
817 } else if (ret) {
818 return ret;
819 }
820
821 /* insert after */
822 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
823 assert(new_node->prev == anchor);
824 if (*first_node == new_node) {
825 *first_node = anchor;
826 }
827 } else {
828 if ((*first_node)->schema->flags & LYS_KEY) {
829 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
830
831 /* find last key */
832 anchor = *first_node;
833 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
834 anchor = anchor->next;
835 }
836 /* insert after the last key */
837 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
838 } else {
839 /* insert at the beginning */
840 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
841 *first_node = new_node;
842 }
843 }
844
845 return LY_SUCCESS;
846}
847
848/**
849 * @brief Apply diff subtree on data tree nodes, recursively.
850 *
851 * @param[in,out] first_node First sibling of the data tree.
852 * @param[in] parent_node Parent of the first sibling.
853 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200854 * @param[in] diff_cb Optional diff callback.
855 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200856 * @return LY_ERR value.
857 */
858static LY_ERR
859lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Radek Krejci0f969882020-08-21 16:56:47 +0200860 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +0200861{
862 LY_ERR ret;
863 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200864 const char *str_val;
865 enum lyd_diff_op op;
866 struct lyd_meta *meta;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200867 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200868
869 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200870 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200871
Michal Vaskoe6323f62020-07-09 15:49:02 +0200872 /* handle specific user-ordered (leaf-)lists operations separately */
873 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
874 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200875 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200876 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200877 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200878 /* duplicate the node */
879 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200880 }
881
Michal Vaskoe6323f62020-07-09 15:49:02 +0200882 /* get "key" or "value" metadata string value */
883 meta = lyd_find_meta(diff_node->meta, NULL, diff_node->schema->nodetype == LYS_LIST ? "yang:key" : "yang:value");
Michal Vaskob7be7a82020-08-20 09:09:04 +0200884 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200885 str_val = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200886
Michal Vaskod59035b2020-07-08 12:00:06 +0200887 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200888 if (str_val[0]) {
889 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200890 } else {
891 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
892 }
893 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200894 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200895 lyd_free_tree(match);
896 }
897 return ret;
898 }
899
900 goto next_iter_r;
901 }
902
903 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200904 switch (op) {
905 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200906 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200907 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200908
909 if (match->schema->nodetype & LYD_NODE_TERM) {
910 /* special case of only dflt flag change */
911 if (diff_node->flags & LYD_DEFAULT) {
912 match->flags |= LYD_DEFAULT;
913 } else {
914 match->flags &= ~LYD_DEFAULT;
915 }
916 } else {
917 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200918 if (!lyd_child_no_keys(diff_node)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200919 LOGINT_RET(ctx);
920 }
921 }
922 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200923 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200924 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200925 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200926
927 /* insert it at the end */
928 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +0200929 if (parent_node) {
930 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200931 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200932 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200933 }
934 if (ret) {
935 lyd_free_tree(match);
936 return ret;
937 }
938
939 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200940 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200941 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200942 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200943
944 /* remove it */
945 if ((match == *first_node) && !match->parent) {
946 assert(!parent_node);
947 /* we have removed the top-level node */
948 *first_node = (*first_node)->next;
949 }
950 lyd_free_tree(match);
951
952 /* we are not going recursively in this case, the whole subtree was already deleted */
953 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200954 case LYD_DIFF_OP_REPLACE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200955 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
956
957 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200958 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200959
960 /* update its value */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200961 ret = lyd_change_term(match, LYD_CANON_VALUE(diff_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200962 if (ret && (ret != LY_EEXIST)) {
963 LOGINT_RET(ctx);
964 }
965
966 /* with flags */
967 match->flags = diff_node->flags;
968 break;
969 default:
970 LOGINT_RET(ctx);
971 }
972
973next_iter_r:
974 if (diff_cb) {
975 /* call callback */
976 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
977 }
978
979 /* apply diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200980 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200981 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
982 }
983
984 return LY_SUCCESS;
985}
986
987API LY_ERR
988lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +0200989 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +0200990{
991 const struct lyd_node *root;
992
993 LY_LIST_FOR(diff, root) {
994 if (mod && (lyd_owner_module(root) != mod)) {
995 /* skip data nodes from different modules */
996 continue;
997 }
998
999 /* apply relevant nodes from the diff datatree */
1000 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1001 }
1002
1003 return LY_SUCCESS;
1004}
1005
1006API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001007lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001008{
1009 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1010}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001011
1012/**
1013 * @brief Update operations on a diff node when the new operation is NONE.
1014 *
1015 * @param[in] diff_match Node from the diff.
1016 * @param[in] cur_op Current operation of the diff node.
1017 * @param[in] src_diff Current source diff node.
1018 * @return LY_ERR value.
1019 */
1020static LY_ERR
1021lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1022{
1023 switch (cur_op) {
1024 case LYD_DIFF_OP_NONE:
1025 case LYD_DIFF_OP_CREATE:
1026 case LYD_DIFF_OP_REPLACE:
1027 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1028 /* NONE on a term means only its dflt flag was changed */
1029 diff_match->flags &= ~LYD_DEFAULT;
1030 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1031 }
1032 break;
1033 default:
1034 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001035 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001036 }
1037
1038 return LY_SUCCESS;
1039}
1040
1041/**
1042 * @brief Remove an attribute from a node.
1043 *
1044 * @param[in] node Node with the metadata.
1045 * @param[in] name Metadata name.
1046 */
1047static void
1048lyd_diff_del_meta(struct lyd_node *node, const char *name)
1049{
1050 struct lyd_meta *meta;
1051
1052 LY_LIST_FOR(node->meta, meta) {
1053 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001054 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001055 return;
1056 }
1057 }
1058
1059 assert(0);
1060}
1061
1062/**
1063 * @brief Set a specific operation of a node. Delete the previous operation, if any.
1064 *
1065 * @param[in] node Node to change.
1066 * @param[in] op Operation to set.
1067 * @return LY_ERR value.
1068 */
1069static LY_ERR
1070lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1071{
1072 struct lyd_meta *meta;
1073
1074 LY_LIST_FOR(node->meta, meta) {
1075 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001076 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001077 break;
1078 }
1079 }
1080
Michal Vasko3a41dff2020-07-15 14:30:28 +02001081 return lyd_new_meta(node, NULL, "yang:operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001082}
1083
1084/**
1085 * @brief Update operations on a diff node when the new operation is REPLACE.
1086 *
1087 * @param[in] diff_match Node from the diff.
1088 * @param[in] cur_op Current operation of the diff node.
1089 * @param[in] src_diff Current source diff node.
1090 * @return LY_ERR value.
1091 */
1092static LY_ERR
1093lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1094{
1095 LY_ERR ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001096 const char *str_val, *meta_name;
1097 struct lyd_meta *meta;
1098 const struct lys_module *mod;
1099 const struct lyd_node_any *any;
1100
1101 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001102 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001103 assert(mod);
1104
1105 switch (cur_op) {
1106 case LYD_DIFF_OP_REPLACE:
1107 case LYD_DIFF_OP_CREATE:
1108 switch (diff_match->schema->nodetype) {
1109 case LYS_LIST:
1110 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001111 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001112 * keep orig_key/orig_value (only replace oper) and replace key/value */
1113 assert(lysc_is_userordered(diff_match->schema));
1114 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1115
1116 lyd_diff_del_meta(diff_match, meta_name);
1117 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001118 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001119 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001120 break;
1121 case LYS_LEAF:
1122 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001123 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001124 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001125 }
1126
Michal Vaskoe6323f62020-07-09 15:49:02 +02001127 /* modify the node value */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001128 if (lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff))) {
1129 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001130 }
1131
Michal Vasko8caadab2020-11-05 17:38:15 +01001132 if (cur_op == LYD_DIFF_OP_REPLACE) {
1133 /* compare values whether there is any change at all */
1134 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1135 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_match)), LY_EINT);
1136 str_val = meta->value.canonical;
1137 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1138 if (!ret) {
1139 /* values are the same, remove orig-value meta and set oper to NONE */
1140 lyd_free_meta_single(meta);
1141 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1142 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001143 }
1144
1145 /* modify the default flag */
1146 diff_match->flags &= ~LYD_DEFAULT;
1147 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1148 break;
1149 case LYS_ANYXML:
1150 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001151 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001152 /* replaced with the exact same value, impossible */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001153 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001154 }
1155
1156 /* modify the node value */
1157 any = (struct lyd_node_any *)src_diff;
1158 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1159 break;
1160 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001161 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001162 }
1163 break;
1164 case LYD_DIFF_OP_NONE:
1165 /* it is moved now */
1166 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1167
1168 /* change the operation */
1169 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1170
1171 /* set orig-key and key metadata */
1172 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001173 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001174 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001175
1176 meta = lyd_find_meta(src_diff->meta, mod, "key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001177 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001178 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001179 break;
1180 default:
1181 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001182 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001183 }
1184
1185 return LY_SUCCESS;
1186}
1187
1188/**
1189 * @brief Update operations in a diff node when the new operation is CREATE.
1190 *
1191 * @param[in] diff_match Node from the diff.
1192 * @param[in] cur_op Current operation of the diff node.
1193 * @param[in] src_diff Current source diff node.
1194 * @return LY_ERR value.
1195 */
1196static LY_ERR
1197lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1198{
1199 struct lyd_node *child;
Michal Vasko5632e0d2020-07-31 14:13:37 +02001200 const struct lysc_node_leaf *sleaf;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001201
1202 switch (cur_op) {
1203 case LYD_DIFF_OP_DELETE:
Michal Vasko5632e0d2020-07-31 14:13:37 +02001204 if (diff_match->schema->nodetype == LYS_LEAF) {
1205 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1206 } else {
1207 sleaf = NULL;
1208 }
1209
Michal Vasko69730152020-10-09 16:30:07 +02001210 if (sleaf && sleaf->dflt &&
1211 !sleaf->dflt->realtype->plugin->compare(sleaf->dflt, &((struct lyd_node_term *)src_diff)->value)) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001212 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1213 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1214
1215 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1216 /* add orig-dflt metadata */
1217 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
Michal Vasko69730152020-10-09 16:30:07 +02001218 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vasko5632e0d2020-07-31 14:13:37 +02001219 }
1220 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001221 /* deleted + created -> operation NONE */
1222 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1223
1224 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1225 /* add orig-dflt metadata */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001226 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
Michal Vasko69730152020-10-09 16:30:07 +02001227 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001228 }
1229 } else {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001230 assert(sleaf);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001231 /* we deleted it, but it was created with a different value -> operation REPLACE */
1232 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
Michal Vasko5632e0d2020-07-31 14:13:37 +02001233 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001234
Michal Vasko5632e0d2020-07-31 14:13:37 +02001235 if (lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001236 /* current value is the previous one (meta) */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001237 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-value", LYD_CANON_VALUE(diff_match), NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001238
1239 /* update the value itself */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001240 LY_CHECK_RET(lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff)));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001241 }
1242
1243 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1244 /* update dflt flag itself */
1245 diff_match->flags &= ~LYD_DEFAULT;
1246 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1247 } else {
1248 /* but the operation of its children should remain DELETE */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001249 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001250 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1251 }
1252 }
1253 break;
1254 default:
1255 /* create and replace operations are not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001256 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001257 }
1258
1259 return LY_SUCCESS;
1260}
1261
1262/**
1263 * @brief Update operations on a diff node when the new operation is DELETE.
1264 *
1265 * @param[in] diff_match Node from the diff.
1266 * @param[in] cur_op Current operation of the diff node.
1267 * @param[in] src_diff Current source diff node.
1268 * @return LY_ERR value.
1269 */
1270static LY_ERR
1271lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1272{
1273 struct lyd_node *next, *child;
1274
1275 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001276 LY_CHECK_ERR_RET(lyd_compare_single(diff_match, src_diff, 0), LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001277
1278 switch (cur_op) {
1279 case LYD_DIFF_OP_CREATE:
1280 /* it was created, but then deleted -> set NONE operation */
1281 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1282
1283 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1284 /* add orig-default meta because it is expected */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001285 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
Michal Vasko69730152020-10-09 16:30:07 +02001286 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001287 } else {
1288 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001289 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001290 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1291 }
1292 }
1293 break;
1294 case LYD_DIFF_OP_REPLACE:
1295 /* similar to none operation but also remove the redundant attribute */
1296 lyd_diff_del_meta(diff_match, "orig-value");
Radek Krejci0f969882020-08-21 16:56:47 +02001297 /* fallthrough */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001298 case LYD_DIFF_OP_NONE:
1299 /* it was not modified, but should be deleted -> set DELETE operation */
1300 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1301
Michal Vasko5632e0d2020-07-31 14:13:37 +02001302 /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001303 LY_LIST_FOR_SAFE(lyd_child_no_keys(diff_match), next, child) {
1304 if (lyd_find_sibling_first(lyd_child(src_diff), child, NULL) == LY_ENOTFOUND) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001305 lyd_free_tree(child);
1306 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001307 }
1308 break;
1309 default:
1310 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001311 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001312 }
1313
1314 return LY_SUCCESS;
1315}
1316
1317/**
1318 * @brief Check whether this diff node is redundant (does not change data).
1319 *
1320 * @param[in] diff Diff node.
1321 * @return 0 if not, non-zero if it is.
1322 */
1323static int
1324lyd_diff_is_redundant(struct lyd_node *diff)
1325{
1326 enum lyd_diff_op op;
1327 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1328 struct lyd_node *child;
1329 const struct lys_module *mod;
1330 const char *str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001331
1332 assert(diff);
1333
Radek Krejcia1c1e542020-09-29 16:06:52 +02001334 child = lyd_child_no_keys(diff);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001335 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001336 assert(mod);
1337
1338 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001339 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001340
1341 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1342 /* check for redundant move */
1343 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1344 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1345 assert(orig_val_meta && val_meta);
1346
1347 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1348 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001349 lyd_free_meta_single(orig_val_meta);
1350 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001351 if (child) {
1352 /* change operation to NONE, we have siblings */
1353 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1354 return 0;
1355 }
1356
1357 /* redundant node, BUT !!
1358 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1359 * because the data that this is applied on should not change for the diff lifetime.
1360 * However, when we are merging 2 diffs, this conversion is actually lossy because
1361 * if the data change, the move operation can also change its meaning. In this specific
1362 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1363 */
1364 return 1;
1365 }
1366 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1367 /* check whether at least the default flags are different */
1368 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1369 assert(meta);
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001370 str = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001371
1372 /* if previous and current dflt flags are the same, this node is redundant */
1373 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1374 return 1;
1375 }
1376 return 0;
1377 }
1378
1379 if (!child && (op == LYD_DIFF_OP_NONE)) {
1380 return 1;
1381 }
1382
1383 return 0;
1384}
1385
1386/**
1387 * @brief Merge sysrepo diff with another diff, recursively.
1388 *
1389 * @param[in] src_diff Source diff node.
1390 * @param[in] diff_parent Current sysrepo diff parent.
1391 * @param[in] diff_cb Optional diff callback.
1392 * @param[in] cb_data User data for @p diff_cb.
1393 * @param[in,out] diff Diff root node.
1394 * @return LY_ERR value.
1395 */
1396static LY_ERR
1397lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Radek Krejci0f969882020-08-21 16:56:47 +02001398 struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001399{
1400 LY_ERR ret = LY_SUCCESS;
1401 struct lyd_node *child, *diff_node = NULL;
1402 enum lyd_diff_op src_op, cur_op;
1403
1404 /* get source node operation */
1405 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1406
1407 /* find an equal node in the current diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001408 lyd_diff_find_node(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, &diff_node);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001409
1410 if (diff_node) {
1411 /* get target (current) operation */
1412 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1413
1414 /* merge operations */
1415 switch (src_op) {
1416 case LYD_DIFF_OP_REPLACE:
1417 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1418 break;
1419 case LYD_DIFF_OP_CREATE:
1420 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff);
1421 break;
1422 case LYD_DIFF_OP_DELETE:
1423 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1424 break;
1425 case LYD_DIFF_OP_NONE:
1426 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1427 break;
1428 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001429 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001430 }
1431 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001432 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001433 return ret;
1434 }
1435
1436 if (diff_cb) {
1437 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001438 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001439 }
1440
1441 /* update diff parent */
1442 diff_parent = diff_node;
1443
1444 /* merge src_diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001445 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001446 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, diff));
1447 }
1448 } else {
1449 /* add new diff node with all descendants */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001450 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE, &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001451
1452 /* insert node into diff if not already */
1453 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001454 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001455 }
1456
1457 /* update operation */
1458 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1459
1460 if (diff_cb) {
1461 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001462 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001463 }
1464
1465 /* update diff parent */
1466 diff_parent = diff_node;
1467 }
1468
1469 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001470 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001471 if (diff_parent == *diff) {
1472 *diff = (*diff)->next;
1473 }
1474 lyd_free_tree(diff_parent);
1475 }
1476
1477 return LY_SUCCESS;
1478}
1479
1480API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001481lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001482 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001483{
1484 const struct lyd_node *src_root;
1485
1486 LY_LIST_FOR(src_diff, src_root) {
1487 if (mod && (lyd_owner_module(src_root) != mod)) {
1488 /* skip data nodes from different modules */
1489 continue;
1490 }
1491
1492 /* apply relevant nodes from the diff datatree */
1493 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, diff));
1494 }
1495
1496 return LY_SUCCESS;
1497}
1498
1499API LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001500lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent, const struct lyd_node *src_sibling,
Radek Krejci0f969882020-08-21 16:56:47 +02001501 lyd_diff_cb diff_cb, void *cb_data)
Michal Vasko04f85912020-08-07 12:14:58 +02001502{
1503 if (!src_sibling) {
1504 return LY_SUCCESS;
1505 }
1506
1507 return lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, diff_first);
1508}
1509
1510API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001511lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001512{
Michal Vaskofb737aa2020-08-06 13:53:53 +02001513 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001514}
Michal Vasko4231fb62020-07-13 13:54:47 +02001515
1516static LY_ERR
1517lyd_diff_reverse_value(struct lyd_node *leaf, const struct lys_module *mod)
1518{
1519 LY_ERR ret = LY_SUCCESS;
1520 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001521 const char *val1 = NULL;
1522 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001523 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001524
1525 meta = lyd_find_meta(leaf->meta, mod, "orig-value");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001526 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(leaf)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001527
1528 /* orig-value */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001529 val1 = meta->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001530
1531 /* current value */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001532 val2 = strdup(LYD_CANON_VALUE(leaf));
Michal Vasko4231fb62020-07-13 13:54:47 +02001533
1534 /* switch values, keep default flag */
1535 flags = leaf->flags;
1536 LY_CHECK_GOTO(ret = lyd_change_term(leaf, val1), cleanup);
1537 leaf->flags = flags;
1538 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1539
1540cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001541 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001542 return ret;
1543}
1544
1545static LY_ERR
1546lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1547{
1548 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001549 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001550
1551 meta = lyd_find_meta(node->meta, mod, "orig-default");
1552 if (!meta) {
1553 /* default flag did not change */
1554 return LY_SUCCESS;
1555 }
1556
1557 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001558 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001559 flag1 = LYD_DEFAULT;
1560 } else {
1561 flag1 = 0;
1562 }
1563
1564 /* current default */
1565 flag2 = node->flags & LYD_DEFAULT;
1566
1567 /* switch defaults */
1568 node->flags &= ~LYD_DEFAULT;
1569 node->flags |= flag1;
1570 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1571
1572 return LY_SUCCESS;
1573}
1574
1575static LY_ERR
1576lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1577{
1578 LY_ERR ret = LY_SUCCESS;
1579 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001580 const char *val1 = NULL;
1581 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001582
1583 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001584 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001585
1586 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001587 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001588
1589 /* value1 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001590 val1 = meta1->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001591
1592 /* value2 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001593 val2 = strdup(meta2->value.canonical);
Michal Vasko4231fb62020-07-13 13:54:47 +02001594
1595 /* switch values */
1596 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1597 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1598
1599cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001600 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001601 return ret;
1602}
1603
1604API LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001605lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001606{
1607 LY_ERR ret = LY_SUCCESS;
1608 const struct lys_module *mod;
Michal Vasko56daf732020-08-10 10:57:18 +02001609 struct lyd_node *root, *elem;
Michal Vasko4231fb62020-07-13 13:54:47 +02001610 enum lyd_diff_op op;
1611
1612 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1613
1614 if (!src_diff) {
1615 *diff = NULL;
1616 return LY_SUCCESS;
1617 }
1618
1619 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001620 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001621
1622 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001623 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1624 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001625
1626 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001627 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001628 /* skip all keys */
1629 if (!lysc_is_key(elem->schema)) {
1630 /* find operation attribute, if any */
1631 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001632
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001633 switch (op) {
1634 case LYD_DIFF_OP_CREATE:
1635 /* reverse create to delete */
1636 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001637 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001638 case LYD_DIFF_OP_DELETE:
1639 /* reverse delete to create */
1640 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001641 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001642 case LYD_DIFF_OP_REPLACE:
1643 switch (elem->schema->nodetype) {
1644 case LYS_LEAF:
1645 /* leaf value change */
1646 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1647 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1648 break;
1649 case LYS_LEAFLIST:
1650 /* leaf-list move */
1651 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1652 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1653 break;
1654 case LYS_LIST:
1655 /* list move */
1656 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1657 break;
1658 default:
1659 LOGINT(LYD_CTX(src_diff));
1660 ret = LY_EINT;
1661 goto cleanup;
1662 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001663 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001664 case LYD_DIFF_OP_NONE:
1665 switch (elem->schema->nodetype) {
1666 case LYS_LEAF:
1667 case LYS_LEAFLIST:
1668 /* default flag change */
1669 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1670 break;
1671 default:
1672 /* nothing to do */
1673 break;
1674 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001675 break;
1676 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001677 }
1678
Michal Vasko56daf732020-08-10 10:57:18 +02001679 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02001680 }
1681 }
1682
1683cleanup:
1684 if (ret) {
1685 lyd_free_siblings(*diff);
1686 *diff = NULL;
1687 }
1688 return ret;
1689}