blob: 000827707d75d105a04b59db7491b70389aae10a [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 Vasko871a0252020-11-11 18:35:24 +0100112 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &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)) {
Michal Vasko871a0252020-11-11 18:35:24 +0100141 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200142 }
143
144 /* add subtree operation */
Michal Vasko871a0252020-11-11 18:35:24 +0100145 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200146
147 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200148 if (orig_default) {
Michal Vasko871a0252020-11-11 18:35:24 +0100149 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200150 }
151
152 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200153 if (orig_value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100154 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200155 }
156
157 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200158 if (key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100159 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200160 }
161
162 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200163 if (value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100164 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200165 }
166
167 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200168 if (orig_key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100169 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-key", orig_key, 0, 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 Vasko4b715ca2020-11-11 18:39:57 +0100302 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200303 if (first->flags & LYD_DEFAULT) {
304 *orig_default = "true";
305 } else {
306 *orig_default = "false";
307 }
308 }
309
310 /* value */
311 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
312 if (second_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200313 *value = strdup(LYD_CANON_VALUE(userord_item->inst[second_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200314 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200315 } else {
316 *value = strdup("");
317 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
318 }
319 }
320
321 /* orig-value */
322 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
323 if (first_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200324 *orig_value = strdup(LYD_CANON_VALUE(userord_item->inst[first_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200325 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200326 } else {
327 *orig_value = strdup("");
328 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
329 }
330 }
331
332 /* key */
Michal Vasko44f3d2c2020-08-24 09:49:38 +0200333 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200334 if (second_pos) {
335 buflen = bufused = 0;
336 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
337 } else {
338 *key = strdup("");
339 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
340 }
341 }
342
343 /* orig-key */
344 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
345 if (first_pos) {
346 buflen = bufused = 0;
347 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
348 } else {
349 *orig_key = strdup("");
350 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
351 }
352 }
353
354 /*
355 * update our instances - apply the change
356 */
357 if (*op == LYD_DIFF_OP_CREATE) {
358 /* insert the instance */
359 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 +0200360 ; , LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200361 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
362 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
363 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
364 }
365 LY_ARRAY_INCREMENT(userord_item->inst);
366 userord_item->inst[second_pos] = second;
367
368 } else if (*op == LYD_DIFF_OP_DELETE) {
369 /* remove the instance */
370 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
371 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
372 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
373 }
374 LY_ARRAY_DECREMENT(userord_item->inst);
375
376 } else if (*op == LYD_DIFF_OP_REPLACE) {
377 /* move the instances */
378 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
379 (first_pos - second_pos) * sizeof *userord_item->inst);
380 userord_item->inst[second_pos] = first;
381 }
382
383 return LY_SUCCESS;
384}
385
386/**
387 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
388 * lists/leaf-lists.
389 *
390 * @param[in] first Node from the first tree, can be NULL (on create).
391 * @param[in] second Node from the second tree, can be NULL (on delete).
392 * @param[in] options Diff options.
393 * @param[out] op Operation.
394 * @param[out] orig_default Original default metadata.
395 * @param[out] orig_value Original value metadata.
396 * @return LY_SUCCESS on success,
397 * @return LY_ENOT if there is no change to be added into diff,
398 * @return LY_ERR value on other errors.
399 */
400static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200401lyd_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 +0200402 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200403{
404 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200405
406 assert(first || second);
407
408 *orig_default = NULL;
409 *orig_value = NULL;
410
411 schema = first ? first->schema : second->schema;
412 assert(!lysc_is_userordered(schema));
413
414 /* learn operation first */
415 if (!second) {
416 *op = LYD_DIFF_OP_DELETE;
417 } else if (!first) {
418 *op = LYD_DIFF_OP_CREATE;
419 } else {
420 switch (schema->nodetype) {
421 case LYS_CONTAINER:
422 case LYS_RPC:
423 case LYS_ACTION:
424 case LYS_NOTIF:
425 /* no changes */
426 return LY_ENOT;
427 case LYS_LIST:
428 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200429 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200430 /* default flag change */
431 *op = LYD_DIFF_OP_NONE;
432 } else {
433 /* no changes */
434 return LY_ENOT;
435 }
436 break;
437 case LYS_LEAF:
438 case LYS_ANYXML:
439 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200440 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200441 /* different values */
442 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200443 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200444 /* default flag change */
445 *op = LYD_DIFF_OP_NONE;
446 } else {
447 /* no changes */
448 return LY_ENOT;
449 }
450 break;
451 default:
452 LOGINT_RET(schema->module->ctx);
453 }
454 }
455
456 /*
457 * set each attribute correctly based on the operation and node type
458 */
459
460 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100461 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200462 if (first->flags & LYD_DEFAULT) {
463 *orig_default = "true";
464 } else {
465 *orig_default = "false";
466 }
467 }
468
469 /* orig-value */
470 if ((schema->nodetype == LYS_LEAF) && (*op == LYD_DIFF_OP_REPLACE)) {
471 /* leaf */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200472 *orig_value = strdup(LYD_CANON_VALUE(first));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200473 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200474 }
475
476 return LY_SUCCESS;
477}
478
479/**
480 * @brief Perform diff for all siblings at certain depth, recursively.
481 *
482 * For user-ordered lists/leaf-lists a specific structure is used for storing
483 * the current order. The idea is to apply all the generated diff changes
484 * virtually on the first tree so that we can continue to generate correct
485 * changes after some were already generated.
486 *
487 * The algorithm then uses second tree position-based changes with a before
488 * (preceding) item anchor.
489 *
490 * Example:
491 *
492 * Virtual first tree leaf-list order:
493 * 1 2 [3] 4 5
494 *
495 * Second tree leaf-list order:
496 * 1 2 [5] 3 4
497 *
498 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
499 * match - they do not - move nodes so that the 3rd position node is final ->
500 * -> move node 5 to the 3rd position -> move node 5 after node 2.
501 *
502 * Required properties:
503 * Stored operations (move) should not be affected by later operations -
504 * - would cause a redundantly long list of operations, possibly inifinite.
505 *
506 * Implemenation justification:
507 * First, all delete operations and only then move/create operations are stored.
508 * Also, preceding anchor is used and after each iteration another node is
509 * at its final position. That results in the invariant that all preceding
510 * nodes are final and will not be changed by the later operations, meaning
511 * they can safely be used as anchors for the later operations.
512 *
513 * @param[in] first First tree first sibling.
514 * @param[in] second Second tree first sibling.
515 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200516 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200517 * @param[in,out] diff Diff to append to.
518 * @return LY_ERR value.
519 */
520static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200521lyd_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 +0200522 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200523{
524 LY_ERR ret = LY_SUCCESS;
525 const struct lyd_node *iter_first, *iter_second;
526 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200527 struct lyd_diff_userord *userord = NULL;
528 LY_ARRAY_COUNT_TYPE u;
529 enum lyd_diff_op op;
530 const char *orig_default;
531 char *orig_value, *key, *value, *orig_key;
532
Michal Vaskod59035b2020-07-08 12:00:06 +0200533 /* compare first tree to the second tree - delete, replace, none */
534 LY_LIST_FOR(first, iter_first) {
535 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200536 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200537 /* skip default nodes */
538 continue;
539 }
540
541 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
542 /* try to find the exact instance */
543 lyd_find_sibling_first(second, iter_first, &match_second);
544 } else {
545 /* try to simply find the node, there cannot be more instances */
546 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
547 }
548
Michal Vasko3a41dff2020-07-15 14:30:28 +0200549 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200550 /* ignore default nodes */
551 match_second = NULL;
552 }
553
554 if (lysc_is_userordered(iter_first->schema)) {
555 if (match_second) {
556 /* we are handling only user-ordered node delete now */
557 continue;
558 }
559
560 /* get all the attributes */
561 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200562 &value, &orig_value, &key, &orig_key), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200563
564 /* there must be changes, it is deleted */
565 assert(op == LYD_DIFF_OP_DELETE);
566 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
567
568 free(orig_value);
569 free(key);
570 free(value);
571 free(orig_key);
572 LY_CHECK_GOTO(ret, cleanup);
573 } else {
574 /* get all the attributes */
575 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
576
577 /* add into diff if there are any changes */
578 if (!ret) {
579 if (op == LYD_DIFF_OP_DELETE) {
580 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
581 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100582 assert(match_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200583 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
584 }
585
586 free(orig_value);
587 LY_CHECK_GOTO(ret, cleanup);
588 } else if (ret == LY_ENOT) {
589 ret = LY_SUCCESS;
590 } else {
591 goto cleanup;
592 }
593 }
594
595 /* check descendants, if any, recursively */
596 if (match_second) {
Radek Krejcia1c1e542020-09-29 16:06:52 +0200597 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 +0200598 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200599 }
600
601 if (nosiblings) {
602 break;
603 }
604 }
605
606 /* reset all cached positions */
607 LY_ARRAY_FOR(userord, u) {
608 userord[u].pos = 0;
609 }
610
611 /* compare second tree to the first tree - create, user-ordered move */
612 LY_LIST_FOR(second, iter_second) {
613 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200614 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200615 /* skip default nodes */
616 continue;
617 }
618
619 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
620 lyd_find_sibling_first(first, iter_second, &match_first);
621 } else {
622 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
623 }
624
Michal Vasko3a41dff2020-07-15 14:30:28 +0200625 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200626 /* ignore default nodes */
627 match_first = NULL;
628 }
629
630 if (lysc_is_userordered(iter_second->schema)) {
631 /* get all the attributes */
632 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200633 &value, &orig_value, &key, &orig_key);
Michal Vaskod59035b2020-07-08 12:00:06 +0200634
635 /* add into diff if there are any changes */
636 if (!ret) {
637 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
638
639 free(orig_value);
640 free(key);
641 free(value);
642 free(orig_key);
643 LY_CHECK_GOTO(ret, cleanup);
644 } else if (ret == LY_ENOT) {
645 ret = LY_SUCCESS;
646 } else {
647 goto cleanup;
648 }
649 } else if (!match_first) {
650 /* get all the attributes */
651 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
652
653 /* there must be changes, it is created */
654 assert(op == LYD_DIFF_OP_CREATE);
655 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
656
657 free(orig_value);
658 LY_CHECK_GOTO(ret, cleanup);
659 } /* else was handled */
660
661 if (nosiblings) {
662 break;
663 }
664 }
665
666cleanup:
667 LY_ARRAY_FOR(userord, u) {
668 LY_ARRAY_FREE(userord[u].inst);
669 }
670 LY_ARRAY_FREE(userord);
671 return ret;
672}
673
Michal Vasko3a41dff2020-07-15 14:30:28 +0200674static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200675lyd_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 +0200676{
677 const struct ly_ctx *ctx;
678
679 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
680
681 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200682 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200683 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200684 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200685 } else {
686 ctx = NULL;
687 }
688
689 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
690 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
691 return LY_EINVAL;
692 }
693
694 *diff = NULL;
695
Michal Vasko3a41dff2020-07-15 14:30:28 +0200696 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
697}
698
699API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200700lyd_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 +0200701{
702 return lyd_diff(first, second, options, 1, diff);
703}
704
705API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200706lyd_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 +0200707{
708 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200709}
710
711/**
712 * @brief Find a matching node in data tree for a diff node.
713 *
714 * @param[in] first_node First sibling in the data tree.
715 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200716 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200717 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200718static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200719lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
720{
721 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
722 /* try to find the exact instance */
723 lyd_find_sibling_first(first_node, diff_node, match_p);
724 } else {
725 /* try to simply find the node, there cannot be more instances */
726 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
727 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200728}
729
730/**
731 * @brief Learn operation of a diff node.
732 *
733 * @param[in] diff_node Diff node.
734 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200735 * @return LY_ERR value.
736 */
737static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200738lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200739{
740 struct lyd_meta *meta = NULL;
741 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200742 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200743
744 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
745 LY_LIST_FOR(diff_parent->meta, meta) {
746 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200747 str = meta->value.canonical;
Michal Vaskod59035b2020-07-08 12:00:06 +0200748 if ((str[0] == 'r') && (diff_parent != diff_node)) {
749 /* we do not care about this operation if it's in our parent */
750 continue;
751 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200752 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200753 break;
754 }
755 }
756 if (meta) {
757 break;
758 }
759 }
Michal Vaskob7be7a82020-08-20 09:09:04 +0200760 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200761
Michal Vaskod59035b2020-07-08 12:00:06 +0200762 return LY_SUCCESS;
763}
764
765/**
766 * @brief Insert a diff node into a data tree.
767 *
768 * @param[in,out] first_node First sibling of the data tree.
769 * @param[in] parent_node Data tree sibling parent node.
770 * @param[in] new_node Node to insert.
771 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
772 * instance will be inserted at the first position.
773 * @return err_info, NULL on success.
774 */
775static LY_ERR
776lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Radek Krejci0f969882020-08-21 16:56:47 +0200777 const char *key_or_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200778{
779 LY_ERR ret;
780 struct lyd_node *anchor;
781
782 assert(new_node);
783
784 if (!*first_node) {
785 if (!parent_node) {
786 /* no parent or siblings */
787 *first_node = new_node;
788 return LY_SUCCESS;
789 }
790
791 /* simply insert into parent, no other children */
792 if (key_or_value) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200793 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200794 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200795 return LY_EINVAL;
796 }
Michal Vaskob104f112020-07-17 09:54:54 +0200797 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200798 }
799
800 assert(!(*first_node)->parent || ((struct lyd_node *)(*first_node)->parent == parent_node));
801
Michal Vaskod59035b2020-07-08 12:00:06 +0200802 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200803 /* simple insert */
804 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200805 }
806
807 if (key_or_value) {
808 /* find the anchor sibling */
809 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
810 if (ret == LY_ENOTFOUND) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200811 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200812 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200813 return LY_EINVAL;
814 } else if (ret) {
815 return ret;
816 }
817
818 /* insert after */
819 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
820 assert(new_node->prev == anchor);
821 if (*first_node == new_node) {
822 *first_node = anchor;
823 }
824 } else {
825 if ((*first_node)->schema->flags & LYS_KEY) {
826 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
827
828 /* find last key */
829 anchor = *first_node;
830 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
831 anchor = anchor->next;
832 }
833 /* insert after the last key */
834 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
835 } else {
836 /* insert at the beginning */
837 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
838 *first_node = new_node;
839 }
840 }
841
842 return LY_SUCCESS;
843}
844
845/**
846 * @brief Apply diff subtree on data tree nodes, recursively.
847 *
848 * @param[in,out] first_node First sibling of the data tree.
849 * @param[in] parent_node Parent of the first sibling.
850 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200851 * @param[in] diff_cb Optional diff callback.
852 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200853 * @return LY_ERR value.
854 */
855static LY_ERR
856lyd_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 +0200857 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +0200858{
859 LY_ERR ret;
860 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200861 const char *str_val;
862 enum lyd_diff_op op;
863 struct lyd_meta *meta;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200864 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200865
866 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200867 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200868
Michal Vaskoe6323f62020-07-09 15:49:02 +0200869 /* handle specific user-ordered (leaf-)lists operations separately */
870 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
871 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200872 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200873 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200874 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200875 /* duplicate the node */
876 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200877 }
878
Michal Vaskoe6323f62020-07-09 15:49:02 +0200879 /* get "key" or "value" metadata string value */
880 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 +0200881 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200882 str_val = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200883
Michal Vaskod59035b2020-07-08 12:00:06 +0200884 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200885 if (str_val[0]) {
886 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200887 } else {
888 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
889 }
890 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200891 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200892 lyd_free_tree(match);
893 }
894 return ret;
895 }
896
897 goto next_iter_r;
898 }
899
900 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200901 switch (op) {
902 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200903 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200904 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200905
906 if (match->schema->nodetype & LYD_NODE_TERM) {
907 /* special case of only dflt flag change */
908 if (diff_node->flags & LYD_DEFAULT) {
909 match->flags |= LYD_DEFAULT;
910 } else {
911 match->flags &= ~LYD_DEFAULT;
912 }
913 } else {
914 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200915 if (!lyd_child_no_keys(diff_node)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200916 LOGINT_RET(ctx);
917 }
918 }
919 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200920 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200921 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200922 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200923
924 /* insert it at the end */
925 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +0200926 if (parent_node) {
927 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200928 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200929 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200930 }
931 if (ret) {
932 lyd_free_tree(match);
933 return ret;
934 }
935
936 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200937 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200938 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200939 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200940
941 /* remove it */
942 if ((match == *first_node) && !match->parent) {
943 assert(!parent_node);
944 /* we have removed the top-level node */
945 *first_node = (*first_node)->next;
946 }
947 lyd_free_tree(match);
948
949 /* we are not going recursively in this case, the whole subtree was already deleted */
950 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200951 case LYD_DIFF_OP_REPLACE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200952 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
953
954 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200955 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200956
957 /* update its value */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200958 ret = lyd_change_term(match, LYD_CANON_VALUE(diff_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200959 if (ret && (ret != LY_EEXIST)) {
960 LOGINT_RET(ctx);
961 }
962
963 /* with flags */
964 match->flags = diff_node->flags;
965 break;
966 default:
967 LOGINT_RET(ctx);
968 }
969
970next_iter_r:
971 if (diff_cb) {
972 /* call callback */
973 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
974 }
975
976 /* apply diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200977 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200978 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
979 }
980
981 return LY_SUCCESS;
982}
983
984API LY_ERR
985lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +0200986 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +0200987{
988 const struct lyd_node *root;
989
990 LY_LIST_FOR(diff, root) {
991 if (mod && (lyd_owner_module(root) != mod)) {
992 /* skip data nodes from different modules */
993 continue;
994 }
995
996 /* apply relevant nodes from the diff datatree */
997 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
998 }
999
1000 return LY_SUCCESS;
1001}
1002
1003API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001004lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001005{
1006 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1007}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001008
1009/**
1010 * @brief Update operations on a diff node when the new operation is NONE.
1011 *
1012 * @param[in] diff_match Node from the diff.
1013 * @param[in] cur_op Current operation of the diff node.
1014 * @param[in] src_diff Current source diff node.
1015 * @return LY_ERR value.
1016 */
1017static LY_ERR
1018lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1019{
1020 switch (cur_op) {
1021 case LYD_DIFF_OP_NONE:
1022 case LYD_DIFF_OP_CREATE:
1023 case LYD_DIFF_OP_REPLACE:
1024 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1025 /* NONE on a term means only its dflt flag was changed */
1026 diff_match->flags &= ~LYD_DEFAULT;
1027 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1028 }
1029 break;
1030 default:
1031 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001032 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001033 }
1034
1035 return LY_SUCCESS;
1036}
1037
1038/**
1039 * @brief Remove an attribute from a node.
1040 *
1041 * @param[in] node Node with the metadata.
1042 * @param[in] name Metadata name.
1043 */
1044static void
1045lyd_diff_del_meta(struct lyd_node *node, const char *name)
1046{
1047 struct lyd_meta *meta;
1048
1049 LY_LIST_FOR(node->meta, meta) {
1050 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001051 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001052 return;
1053 }
1054 }
1055
1056 assert(0);
1057}
1058
1059/**
1060 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001061 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001062 *
1063 * @param[in] node Node to change.
1064 * @param[in] op Operation to set.
1065 * @return LY_ERR value.
1066 */
1067static LY_ERR
1068lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1069{
1070 struct lyd_meta *meta;
1071
1072 LY_LIST_FOR(node->meta, meta) {
1073 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001074 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001075 break;
1076 }
1077 }
1078
Michal Vasko871a0252020-11-11 18:35:24 +01001079 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001080}
1081
1082/**
1083 * @brief Update operations on a diff node when the new operation is REPLACE.
1084 *
1085 * @param[in] diff_match Node from the diff.
1086 * @param[in] cur_op Current operation of the diff node.
1087 * @param[in] src_diff Current source diff node.
1088 * @return LY_ERR value.
1089 */
1090static LY_ERR
1091lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1092{
1093 LY_ERR ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001094 const char *str_val, *meta_name;
1095 struct lyd_meta *meta;
1096 const struct lys_module *mod;
1097 const struct lyd_node_any *any;
1098
1099 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001100 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001101 assert(mod);
1102
1103 switch (cur_op) {
1104 case LYD_DIFF_OP_REPLACE:
1105 case LYD_DIFF_OP_CREATE:
1106 switch (diff_match->schema->nodetype) {
1107 case LYS_LIST:
1108 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001109 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001110 * keep orig_key/orig_value (only replace oper) and replace key/value */
1111 assert(lysc_is_userordered(diff_match->schema));
1112 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1113
1114 lyd_diff_del_meta(diff_match, meta_name);
1115 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001116 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001117 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001118 break;
1119 case LYS_LEAF:
1120 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001121 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001122 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001123 }
1124
Michal Vaskoe6323f62020-07-09 15:49:02 +02001125 /* modify the node value */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001126 if (lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff))) {
1127 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001128 }
1129
Michal Vasko8caadab2020-11-05 17:38:15 +01001130 if (cur_op == LYD_DIFF_OP_REPLACE) {
1131 /* compare values whether there is any change at all */
1132 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1133 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_match)), LY_EINT);
1134 str_val = meta->value.canonical;
1135 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1136 if (!ret) {
1137 /* values are the same, remove orig-value meta and set oper to NONE */
1138 lyd_free_meta_single(meta);
1139 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1140 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001141 }
1142
1143 /* modify the default flag */
1144 diff_match->flags &= ~LYD_DEFAULT;
1145 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1146 break;
1147 case LYS_ANYXML:
1148 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001149 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001150 /* replaced with the exact same value, impossible */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001151 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001152 }
1153
1154 /* modify the node value */
1155 any = (struct lyd_node_any *)src_diff;
1156 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1157 break;
1158 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001159 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001160 }
1161 break;
1162 case LYD_DIFF_OP_NONE:
1163 /* it is moved now */
1164 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1165
1166 /* change the operation */
1167 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1168
1169 /* set orig-key and key metadata */
1170 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001171 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001172 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001173
1174 meta = lyd_find_meta(src_diff->meta, mod, "key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001175 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001176 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001177 break;
1178 default:
1179 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001180 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001181 }
1182
1183 return LY_SUCCESS;
1184}
1185
1186/**
1187 * @brief Update operations in a diff node when the new operation is CREATE.
1188 *
1189 * @param[in] diff_match Node from the diff.
1190 * @param[in] cur_op Current operation of the diff node.
1191 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001192 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001193 * @return LY_ERR value.
1194 */
1195static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001196lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001197{
1198 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001199 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001200 uint32_t trg_flags;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001201
1202 switch (cur_op) {
1203 case LYD_DIFF_OP_DELETE:
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001204 if ((options & LYD_DIFF_MERGE_DEFAULTS) && (diff_match->schema->nodetype == LYS_LEAF)) {
1205 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
Michal Vasko5632e0d2020-07-31 14:13:37 +02001206 sleaf = (struct lysc_node_leaf *)diff_match->schema;
Michal Vasko5632e0d2020-07-31 14:13:37 +02001207 }
1208
Michal Vasko871a0252020-11-11 18:35:24 +01001209 /* remember current flags */
1210 trg_flags = diff_match->flags;
1211
Michal Vasko69730152020-10-09 16:30:07 +02001212 if (sleaf && sleaf->dflt &&
1213 !sleaf->dflt->realtype->plugin->compare(sleaf->dflt, &((struct lyd_node_term *)src_diff)->value)) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001214 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1215 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vasko5632e0d2020-07-31 14:13:37 +02001216 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001217 /* deleted + created -> operation NONE */
1218 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001219 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001220 /* we deleted it, but it was created with a different value -> operation REPLACE */
1221 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1222
1223 /* current value is the previous one (meta) */
Michal Vasko871a0252020-11-11 18:35:24 +01001224 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
1225 LYD_CANON_VALUE(diff_match), 0, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001226
1227 /* update the value itself */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001228 LY_CHECK_RET(lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff)));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001229 }
1230
1231 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001232 /* add orig-dflt metadata */
1233 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1234 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1235
Michal Vaskoe6323f62020-07-09 15:49:02 +02001236 /* update dflt flag itself */
1237 diff_match->flags &= ~LYD_DEFAULT;
1238 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1239 } else {
1240 /* but the operation of its children should remain DELETE */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001241 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001242 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1243 }
1244 }
1245 break;
1246 default:
1247 /* create and replace operations are not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001248 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001249 }
1250
1251 return LY_SUCCESS;
1252}
1253
1254/**
1255 * @brief Update operations on a diff node when the new operation is DELETE.
1256 *
1257 * @param[in] diff_match Node from the diff.
1258 * @param[in] cur_op Current operation of the diff node.
1259 * @param[in] src_diff Current source diff node.
1260 * @return LY_ERR value.
1261 */
1262static LY_ERR
1263lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1264{
1265 struct lyd_node *next, *child;
1266
1267 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001268 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 +02001269
1270 switch (cur_op) {
1271 case LYD_DIFF_OP_CREATE:
1272 /* it was created, but then deleted -> set NONE operation */
1273 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1274
1275 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1276 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001277 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1278 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001279 } else {
1280 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001281 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001282 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1283 }
1284 }
1285 break;
1286 case LYD_DIFF_OP_REPLACE:
1287 /* similar to none operation but also remove the redundant attribute */
1288 lyd_diff_del_meta(diff_match, "orig-value");
Radek Krejci0f969882020-08-21 16:56:47 +02001289 /* fallthrough */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001290 case LYD_DIFF_OP_NONE:
1291 /* it was not modified, but should be deleted -> set DELETE operation */
1292 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1293
Michal Vasko5632e0d2020-07-31 14:13:37 +02001294 /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001295 LY_LIST_FOR_SAFE(lyd_child_no_keys(diff_match), next, child) {
1296 if (lyd_find_sibling_first(lyd_child(src_diff), child, NULL) == LY_ENOTFOUND) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001297 lyd_free_tree(child);
1298 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001299 }
1300 break;
1301 default:
1302 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001303 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001304 }
1305
1306 return LY_SUCCESS;
1307}
1308
1309/**
1310 * @brief Check whether this diff node is redundant (does not change data).
1311 *
1312 * @param[in] diff Diff node.
1313 * @return 0 if not, non-zero if it is.
1314 */
1315static int
1316lyd_diff_is_redundant(struct lyd_node *diff)
1317{
1318 enum lyd_diff_op op;
1319 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1320 struct lyd_node *child;
1321 const struct lys_module *mod;
1322 const char *str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001323
1324 assert(diff);
1325
Radek Krejcia1c1e542020-09-29 16:06:52 +02001326 child = lyd_child_no_keys(diff);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001327 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001328 assert(mod);
1329
1330 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001331 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001332
1333 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1334 /* check for redundant move */
1335 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1336 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1337 assert(orig_val_meta && val_meta);
1338
1339 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1340 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001341 lyd_free_meta_single(orig_val_meta);
1342 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001343 if (child) {
1344 /* change operation to NONE, we have siblings */
1345 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1346 return 0;
1347 }
1348
1349 /* redundant node, BUT !!
1350 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1351 * because the data that this is applied on should not change for the diff lifetime.
1352 * However, when we are merging 2 diffs, this conversion is actually lossy because
1353 * if the data change, the move operation can also change its meaning. In this specific
1354 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1355 */
1356 return 1;
1357 }
1358 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1359 /* check whether at least the default flags are different */
1360 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1361 assert(meta);
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001362 str = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001363
1364 /* if previous and current dflt flags are the same, this node is redundant */
1365 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1366 return 1;
1367 }
1368 return 0;
1369 }
1370
1371 if (!child && (op == LYD_DIFF_OP_NONE)) {
1372 return 1;
1373 }
1374
1375 return 0;
1376}
1377
1378/**
1379 * @brief Merge sysrepo diff with another diff, recursively.
1380 *
1381 * @param[in] src_diff Source diff node.
1382 * @param[in] diff_parent Current sysrepo diff parent.
1383 * @param[in] diff_cb Optional diff callback.
1384 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001385 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001386 * @param[in,out] diff Diff root node.
1387 * @return LY_ERR value.
1388 */
1389static LY_ERR
1390lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001391 uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001392{
1393 LY_ERR ret = LY_SUCCESS;
1394 struct lyd_node *child, *diff_node = NULL;
1395 enum lyd_diff_op src_op, cur_op;
1396
1397 /* get source node operation */
1398 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1399
1400 /* find an equal node in the current diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001401 lyd_diff_find_node(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, &diff_node);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001402
1403 if (diff_node) {
1404 /* get target (current) operation */
1405 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1406
1407 /* merge operations */
1408 switch (src_op) {
1409 case LYD_DIFF_OP_REPLACE:
1410 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1411 break;
1412 case LYD_DIFF_OP_CREATE:
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001413 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001414 break;
1415 case LYD_DIFF_OP_DELETE:
1416 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1417 break;
1418 case LYD_DIFF_OP_NONE:
1419 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1420 break;
1421 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001422 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001423 }
1424 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001425 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001426 return ret;
1427 }
1428
1429 if (diff_cb) {
1430 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001431 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001432 }
1433
1434 /* update diff parent */
1435 diff_parent = diff_node;
1436
1437 /* merge src_diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001438 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001439 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, options, diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001440 }
1441 } else {
1442 /* add new diff node with all descendants */
Michal Vasko871a0252020-11-11 18:35:24 +01001443 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS,
1444 &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001445
1446 /* insert node into diff if not already */
1447 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001448 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001449 }
1450
1451 /* update operation */
1452 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1453
1454 if (diff_cb) {
1455 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001456 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001457 }
1458
1459 /* update diff parent */
1460 diff_parent = diff_node;
1461 }
1462
1463 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001464 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001465 if (diff_parent == *diff) {
1466 *diff = (*diff)->next;
1467 }
1468 lyd_free_tree(diff_parent);
1469 }
1470
1471 return LY_SUCCESS;
1472}
1473
1474API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001475lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, const struct lys_module *mod,
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001476 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001477{
1478 const struct lyd_node *src_root;
1479
1480 LY_LIST_FOR(src_diff, src_root) {
1481 if (mod && (lyd_owner_module(src_root) != mod)) {
1482 /* skip data nodes from different modules */
1483 continue;
1484 }
1485
1486 /* apply relevant nodes from the diff datatree */
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001487 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, options, diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001488 }
1489
1490 return LY_SUCCESS;
1491}
1492
1493API LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001494lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent, const struct lyd_node *src_sibling,
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001495 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001496{
1497 if (!src_sibling) {
1498 return LY_SUCCESS;
1499 }
1500
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001501 return lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, options, diff_first);
Michal Vasko04f85912020-08-07 12:14:58 +02001502}
1503
1504API LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001505lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001506{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001507 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001508}
Michal Vasko4231fb62020-07-13 13:54:47 +02001509
1510static LY_ERR
1511lyd_diff_reverse_value(struct lyd_node *leaf, const struct lys_module *mod)
1512{
1513 LY_ERR ret = LY_SUCCESS;
1514 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001515 const char *val1 = NULL;
1516 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001517 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001518
1519 meta = lyd_find_meta(leaf->meta, mod, "orig-value");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001520 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(leaf)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001521
1522 /* orig-value */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001523 val1 = meta->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001524
1525 /* current value */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001526 val2 = strdup(LYD_CANON_VALUE(leaf));
Michal Vasko4231fb62020-07-13 13:54:47 +02001527
1528 /* switch values, keep default flag */
1529 flags = leaf->flags;
1530 LY_CHECK_GOTO(ret = lyd_change_term(leaf, val1), cleanup);
1531 leaf->flags = flags;
1532 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1533
1534cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001535 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001536 return ret;
1537}
1538
1539static LY_ERR
1540lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1541{
1542 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001543 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001544
1545 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001546 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001547
1548 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001549 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001550 flag1 = LYD_DEFAULT;
1551 } else {
1552 flag1 = 0;
1553 }
1554
1555 /* current default */
1556 flag2 = node->flags & LYD_DEFAULT;
1557
Michal Vasko610e93b2020-11-09 20:58:32 +01001558 if (flag1 == flag2) {
1559 /* no default state change so nothing to reverse */
1560 return LY_SUCCESS;
1561 }
1562
Michal Vasko4231fb62020-07-13 13:54:47 +02001563 /* switch defaults */
1564 node->flags &= ~LYD_DEFAULT;
1565 node->flags |= flag1;
1566 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1567
1568 return LY_SUCCESS;
1569}
1570
1571static LY_ERR
1572lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1573{
1574 LY_ERR ret = LY_SUCCESS;
1575 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001576 const char *val1 = NULL;
1577 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001578
1579 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001580 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001581
1582 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001583 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001584
1585 /* value1 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001586 val1 = meta1->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001587
1588 /* value2 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001589 val2 = strdup(meta2->value.canonical);
Michal Vasko4231fb62020-07-13 13:54:47 +02001590
1591 /* switch values */
1592 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1593 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1594
1595cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001596 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001597 return ret;
1598}
1599
1600API LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001601lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001602{
1603 LY_ERR ret = LY_SUCCESS;
1604 const struct lys_module *mod;
Michal Vasko56daf732020-08-10 10:57:18 +02001605 struct lyd_node *root, *elem;
Michal Vasko4231fb62020-07-13 13:54:47 +02001606 enum lyd_diff_op op;
1607
1608 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1609
1610 if (!src_diff) {
1611 *diff = NULL;
1612 return LY_SUCCESS;
1613 }
1614
1615 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001616 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001617
1618 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001619 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1620 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001621
1622 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001623 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001624 /* skip all keys */
1625 if (!lysc_is_key(elem->schema)) {
1626 /* find operation attribute, if any */
1627 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001628
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001629 switch (op) {
1630 case LYD_DIFF_OP_CREATE:
1631 /* reverse create to delete */
1632 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001633 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001634 case LYD_DIFF_OP_DELETE:
1635 /* reverse delete to create */
1636 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001637 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001638 case LYD_DIFF_OP_REPLACE:
1639 switch (elem->schema->nodetype) {
1640 case LYS_LEAF:
1641 /* leaf value change */
1642 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1643 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1644 break;
1645 case LYS_LEAFLIST:
1646 /* leaf-list move */
1647 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1648 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1649 break;
1650 case LYS_LIST:
1651 /* list move */
1652 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1653 break;
1654 default:
1655 LOGINT(LYD_CTX(src_diff));
1656 ret = LY_EINT;
1657 goto cleanup;
1658 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001659 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001660 case LYD_DIFF_OP_NONE:
1661 switch (elem->schema->nodetype) {
1662 case LYS_LEAF:
1663 case LYS_LEAFLIST:
1664 /* default flag change */
1665 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1666 break;
1667 default:
1668 /* nothing to do */
1669 break;
1670 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001671 break;
1672 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001673 }
1674
Michal Vasko56daf732020-08-10 10:57:18 +02001675 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02001676 }
1677 }
1678
1679cleanup:
1680 if (ret) {
1681 lyd_free_siblings(*diff);
1682 *diff = NULL;
1683 }
1684 return ret;
1685}