blob: ad72ed9cdcf79b1b31aa138290724500da9ca1d5 [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 */
Radek Krejcif8dc59a2020-11-25 13:47:44 +010014#define _POSIX_C_SOURCE 200809L /* strdup */
Michal Vaskod59035b2020-07-08 12:00:06 +020015
16#include "diff.h"
17
18#include <assert.h>
19#include <stddef.h>
Radek Krejci47fab892020-11-05 17:02:41 +010020#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020021#include <string.h>
22
23#include "common.h"
Radek Krejci47fab892020-11-05 17:02:41 +010024#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020025#include "log.h"
Radek Krejci47fab892020-11-05 17:02:41 +010026#include "plugins_types.h"
27#include "set.h"
28#include "tree.h"
29#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020030#include "tree_data_internal.h"
31#include "tree_schema.h"
32#include "tree_schema_internal.h"
33
34static const char *
35lyd_diff_op2str(enum lyd_diff_op op)
36{
37 switch (op) {
38 case LYD_DIFF_OP_CREATE:
39 return "create";
40 case LYD_DIFF_OP_DELETE:
41 return "delete";
42 case LYD_DIFF_OP_REPLACE:
43 return "replace";
44 case LYD_DIFF_OP_NONE:
45 return "none";
46 }
47
48 LOGINT(NULL);
49 return NULL;
50}
51
Michal Vaskoe6323f62020-07-09 15:49:02 +020052static enum lyd_diff_op
53lyd_diff_str2op(const char *str)
54{
55 switch (str[0]) {
56 case 'c':
57 assert(!strcmp(str, "create"));
58 return LYD_DIFF_OP_CREATE;
59 case 'd':
60 assert(!strcmp(str, "delete"));
61 return LYD_DIFF_OP_DELETE;
62 case 'r':
63 assert(!strcmp(str, "replace"));
64 return LYD_DIFF_OP_REPLACE;
65 case 'n':
66 assert(!strcmp(str, "none"));
67 return LYD_DIFF_OP_NONE;
68 }
69
70 LOGINT(NULL);
71 return 0;
72}
73
Michal Vaskod59035b2020-07-08 12:00:06 +020074LY_ERR
75lyd_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 +020076 const char *key, const char *value, const char *orig_key, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +020077{
78 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
79 const struct lyd_node *parent = NULL;
80 const struct lys_module *yang_mod;
81
82 assert(diff);
83
Michal Vasko53d48422020-11-13 18:02:29 +010084 /* replace leaf always needs orig-default and orig-value */
85 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
86
87 /* create on userord needs key/value */
88 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
89 key);
90 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
91 (op != LYD_DIFF_OP_CREATE) || value);
92
93 /* move on userord needs both key and orig-key/value and orig-value */
94 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
95 (key && orig_key));
96 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
97 (op != LYD_DIFF_OP_REPLACE) || (value && orig_value));
98
Michal Vaskod59035b2020-07-08 12:00:06 +020099 /* find the first existing parent */
100 siblings = *diff;
101 while (1) {
102 /* find next node parent */
103 parent = node;
104 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100105 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200106 }
107 if (parent == node) {
108 /* no more parents to find */
109 break;
110 }
111
112 /* check whether it exists in the diff */
113 if (lyd_find_sibling_first(siblings, parent, &match)) {
114 break;
115 }
116
117 /* another parent found */
118 diff_parent = match;
119
120 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200121 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200122 }
123
124 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200125 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100126 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200127
128 /* find the first duplicated parent */
129 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100130 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200131 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100132 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200133 }
134 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100135 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200136 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100137 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200138 }
139 }
140
141 /* no parent existed, must be manually connected */
142 if (!diff_parent) {
143 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200144 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200145 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200146 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200147 }
148
149 /* get module with the operation metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200150 yang_mod = LYD_CTX(node)->list.objs[1];
Michal Vaskod59035b2020-07-08 12:00:06 +0200151 assert(!strcmp(yang_mod->name, "yang"));
152
153 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200154 if (diff_parent && (diff_parent != dup)) {
Michal Vasko871a0252020-11-11 18:35:24 +0100155 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200156 }
157
158 /* add subtree operation */
Michal Vasko871a0252020-11-11 18:35:24 +0100159 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 +0200160
161 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200162 if (orig_default) {
Michal Vasko871a0252020-11-11 18:35:24 +0100163 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 +0200164 }
165
166 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200167 if (orig_value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100168 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 +0200169 }
170
171 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200172 if (key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100173 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200174 }
175
176 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200177 if (value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100178 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200179 }
180
181 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200182 if (orig_key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100183 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 +0200184 }
185
186 return LY_SUCCESS;
187}
188
189/**
190 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
191 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100192 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200193 * @param[in] schema Schema node of the list/leaf-list.
194 * @param[in,out] userord Sized array of userord items.
195 * @return Userord item for all the user-ordered list/leaf-list instances.
196 */
197static struct lyd_diff_userord *
198lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
199{
200 struct lyd_diff_userord *item;
201 const struct lyd_node *iter, **node;
202 LY_ARRAY_COUNT_TYPE u;
203
204 LY_ARRAY_FOR(*userord, u) {
205 if ((*userord)[u].schema == schema) {
206 return &(*userord)[u];
207 }
208 }
209
210 /* it was not added yet, add it now */
211 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
212
213 item->schema = schema;
214 item->pos = 0;
215 item->inst = NULL;
216
217 /* store all the instance pointers in the current order */
218 if (first) {
219 if (first->parent) {
220 iter = first->parent->child;
221 } else {
Radek Krejci1e008d22020-08-17 11:37:37 +0200222 for (iter = first; iter->prev->next; iter = iter->prev) {}
Michal Vaskod59035b2020-07-08 12:00:06 +0200223 }
Michal Vaskod989ba02020-08-24 10:59:24 +0200224 for ( ; iter; iter = iter->next) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200225 if (iter->schema == first->schema) {
226 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
227 *node = iter;
228 }
229 }
230 }
231
232 return item;
233}
234
235/**
236 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
237 * lists/leaf-lists.
238 *
239 * @param[in] first Node from the first tree, can be NULL (on create).
240 * @param[in] second Node from the second tree, can be NULL (on delete).
241 * @param[in] options Diff options.
242 * @param[in,out] userord Sized array of userord items for keeping the current node order.
243 * @param[out] op Operation.
244 * @param[out] orig_default Original default metadata.
245 * @param[out] value Value metadata.
246 * @param[out] orig_value Original value metadata
247 * @param[out] key Key metadata.
248 * @param[out] orig_key Original key metadata.
249 * @return LY_SUCCESS on success,
250 * @return LY_ENOT if there is no change to be added into diff,
251 * @return LY_ERR value on other errors.
252 */
253static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200254lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Radek Krejci0f969882020-08-21 16:56:47 +0200255 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
256 char **orig_value, char **key, char **orig_key)
Michal Vaskod59035b2020-07-08 12:00:06 +0200257{
258 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200259 size_t buflen, bufused, first_pos, second_pos;
260 struct lyd_diff_userord *userord_item;
261
262 assert(first || second);
263
264 *orig_default = NULL;
265 *value = NULL;
266 *orig_value = NULL;
267 *key = NULL;
268 *orig_key = NULL;
269
270 schema = first ? first->schema : second->schema;
271 assert(lysc_is_userordered(schema));
272
273 /* get userord entry */
274 userord_item = lyd_diff_userord_get(first, schema, userord);
275 LY_CHECK_RET(!userord_item, LY_EMEM);
276
277 /* prepare position of the next instance */
278 second_pos = userord_item->pos++;
279
280 /* find user-ordered first position */
281 if (first) {
282 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
283 if (userord_item->inst[first_pos] == first) {
284 break;
285 }
286 }
287 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
288 } else {
289 first_pos = 0;
290 }
291
292 /* learn operation first */
293 if (!second) {
294 *op = LYD_DIFF_OP_DELETE;
295 } else if (!first) {
296 *op = LYD_DIFF_OP_CREATE;
297 } else {
298 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
Michal Vasko8f359bf2020-07-28 10:41:15 +0200299 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200300 /* in first, there is a different instance on the second position, we are going to move 'first' node */
301 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200302 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200303 /* default flag change */
304 *op = LYD_DIFF_OP_NONE;
305 } else {
306 /* no changes */
307 return LY_ENOT;
308 }
309 }
310
311 /*
312 * set each attribute correctly based on the operation and node type
313 */
314
315 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100316 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200317 if (first->flags & LYD_DEFAULT) {
318 *orig_default = "true";
319 } else {
320 *orig_default = "false";
321 }
322 }
323
324 /* value */
325 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
326 if (second_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200327 *value = strdup(LYD_CANON_VALUE(userord_item->inst[second_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200328 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200329 } else {
330 *value = strdup("");
331 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
332 }
333 }
334
335 /* orig-value */
336 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
337 if (first_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200338 *orig_value = strdup(LYD_CANON_VALUE(userord_item->inst[first_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200339 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200340 } else {
341 *orig_value = strdup("");
342 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
343 }
344 }
345
346 /* key */
Michal Vasko44f3d2c2020-08-24 09:49:38 +0200347 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200348 if (second_pos) {
349 buflen = bufused = 0;
350 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
351 } else {
352 *key = strdup("");
353 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
354 }
355 }
356
357 /* orig-key */
358 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
359 if (first_pos) {
360 buflen = bufused = 0;
361 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
362 } else {
363 *orig_key = strdup("");
364 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
365 }
366 }
367
368 /*
369 * update our instances - apply the change
370 */
371 if (*op == LYD_DIFF_OP_CREATE) {
372 /* insert the instance */
Michal Vasko5cde11b2020-12-08 10:04:48 +0100373 LY_ARRAY_CREATE_RET(schema->module->ctx, userord_item->inst, 1, LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200374 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
375 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
376 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
377 }
378 LY_ARRAY_INCREMENT(userord_item->inst);
379 userord_item->inst[second_pos] = second;
380
381 } else if (*op == LYD_DIFF_OP_DELETE) {
382 /* remove the instance */
383 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
384 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
385 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
386 }
387 LY_ARRAY_DECREMENT(userord_item->inst);
388
389 } else if (*op == LYD_DIFF_OP_REPLACE) {
390 /* move the instances */
391 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
392 (first_pos - second_pos) * sizeof *userord_item->inst);
393 userord_item->inst[second_pos] = first;
394 }
395
396 return LY_SUCCESS;
397}
398
399/**
400 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
401 * lists/leaf-lists.
402 *
403 * @param[in] first Node from the first tree, can be NULL (on create).
404 * @param[in] second Node from the second tree, can be NULL (on delete).
405 * @param[in] options Diff options.
406 * @param[out] op Operation.
407 * @param[out] orig_default Original default metadata.
408 * @param[out] orig_value Original value metadata.
409 * @return LY_SUCCESS on success,
410 * @return LY_ENOT if there is no change to be added into diff,
411 * @return LY_ERR value on other errors.
412 */
413static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200414lyd_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 +0200415 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200416{
417 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200418
419 assert(first || second);
420
421 *orig_default = NULL;
422 *orig_value = NULL;
423
424 schema = first ? first->schema : second->schema;
425 assert(!lysc_is_userordered(schema));
426
427 /* learn operation first */
428 if (!second) {
429 *op = LYD_DIFF_OP_DELETE;
430 } else if (!first) {
431 *op = LYD_DIFF_OP_CREATE;
432 } else {
433 switch (schema->nodetype) {
434 case LYS_CONTAINER:
435 case LYS_RPC:
436 case LYS_ACTION:
437 case LYS_NOTIF:
438 /* no changes */
439 return LY_ENOT;
440 case LYS_LIST:
441 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200442 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200443 /* default flag change */
444 *op = LYD_DIFF_OP_NONE;
445 } else {
446 /* no changes */
447 return LY_ENOT;
448 }
449 break;
450 case LYS_LEAF:
451 case LYS_ANYXML:
452 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200453 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200454 /* different values */
455 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200456 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200457 /* default flag change */
458 *op = LYD_DIFF_OP_NONE;
459 } else {
460 /* no changes */
461 return LY_ENOT;
462 }
463 break;
464 default:
465 LOGINT_RET(schema->module->ctx);
466 }
467 }
468
469 /*
470 * set each attribute correctly based on the operation and node type
471 */
472
473 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100474 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200475 if (first->flags & LYD_DEFAULT) {
476 *orig_default = "true";
477 } else {
478 *orig_default = "false";
479 }
480 }
481
482 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100483 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
484 if (schema->nodetype == LYS_LEAF) {
485 *orig_value = strdup(LYD_CANON_VALUE(first));
486 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
487 } else {
488 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
489 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200490 }
491
492 return LY_SUCCESS;
493}
494
495/**
496 * @brief Perform diff for all siblings at certain depth, recursively.
497 *
498 * For user-ordered lists/leaf-lists a specific structure is used for storing
499 * the current order. The idea is to apply all the generated diff changes
500 * virtually on the first tree so that we can continue to generate correct
501 * changes after some were already generated.
502 *
503 * The algorithm then uses second tree position-based changes with a before
504 * (preceding) item anchor.
505 *
506 * Example:
507 *
508 * Virtual first tree leaf-list order:
509 * 1 2 [3] 4 5
510 *
511 * Second tree leaf-list order:
512 * 1 2 [5] 3 4
513 *
514 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
515 * match - they do not - move nodes so that the 3rd position node is final ->
516 * -> move node 5 to the 3rd position -> move node 5 after node 2.
517 *
518 * Required properties:
519 * Stored operations (move) should not be affected by later operations -
520 * - would cause a redundantly long list of operations, possibly inifinite.
521 *
522 * Implemenation justification:
523 * First, all delete operations and only then move/create operations are stored.
524 * Also, preceding anchor is used and after each iteration another node is
525 * at its final position. That results in the invariant that all preceding
526 * nodes are final and will not be changed by the later operations, meaning
527 * they can safely be used as anchors for the later operations.
528 *
529 * @param[in] first First tree first sibling.
530 * @param[in] second Second tree first sibling.
531 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200532 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200533 * @param[in,out] diff Diff to append to.
534 * @return LY_ERR value.
535 */
536static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200537lyd_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 +0200538 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200539{
540 LY_ERR ret = LY_SUCCESS;
541 const struct lyd_node *iter_first, *iter_second;
542 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200543 struct lyd_diff_userord *userord = NULL;
544 LY_ARRAY_COUNT_TYPE u;
545 enum lyd_diff_op op;
546 const char *orig_default;
547 char *orig_value, *key, *value, *orig_key;
548
Michal Vaskod59035b2020-07-08 12:00:06 +0200549 /* compare first tree to the second tree - delete, replace, none */
550 LY_LIST_FOR(first, iter_first) {
551 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200552 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200553 /* skip default nodes */
554 continue;
555 }
556
557 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
558 /* try to find the exact instance */
559 lyd_find_sibling_first(second, iter_first, &match_second);
560 } else {
561 /* try to simply find the node, there cannot be more instances */
562 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
563 }
564
Michal Vasko3a41dff2020-07-15 14:30:28 +0200565 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200566 /* ignore default nodes */
567 match_second = NULL;
568 }
569
570 if (lysc_is_userordered(iter_first->schema)) {
571 if (match_second) {
572 /* we are handling only user-ordered node delete now */
573 continue;
574 }
575
576 /* get all the attributes */
577 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200578 &value, &orig_value, &key, &orig_key), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200579
580 /* there must be changes, it is deleted */
581 assert(op == LYD_DIFF_OP_DELETE);
582 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
583
584 free(orig_value);
585 free(key);
586 free(value);
587 free(orig_key);
588 LY_CHECK_GOTO(ret, cleanup);
589 } else {
590 /* get all the attributes */
591 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
592
593 /* add into diff if there are any changes */
594 if (!ret) {
595 if (op == LYD_DIFF_OP_DELETE) {
596 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
597 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100598 assert(match_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200599 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
600 }
601
602 free(orig_value);
603 LY_CHECK_GOTO(ret, cleanup);
604 } else if (ret == LY_ENOT) {
605 ret = LY_SUCCESS;
606 } else {
607 goto cleanup;
608 }
609 }
610
611 /* check descendants, if any, recursively */
612 if (match_second) {
Radek Krejcia1c1e542020-09-29 16:06:52 +0200613 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 +0200614 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200615 }
616
617 if (nosiblings) {
618 break;
619 }
620 }
621
622 /* reset all cached positions */
623 LY_ARRAY_FOR(userord, u) {
624 userord[u].pos = 0;
625 }
626
627 /* compare second tree to the first tree - create, user-ordered move */
628 LY_LIST_FOR(second, iter_second) {
629 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200630 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200631 /* skip default nodes */
632 continue;
633 }
634
635 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
636 lyd_find_sibling_first(first, iter_second, &match_first);
637 } else {
638 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
639 }
640
Michal Vasko3a41dff2020-07-15 14:30:28 +0200641 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200642 /* ignore default nodes */
643 match_first = NULL;
644 }
645
646 if (lysc_is_userordered(iter_second->schema)) {
647 /* get all the attributes */
648 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200649 &value, &orig_value, &key, &orig_key);
Michal Vaskod59035b2020-07-08 12:00:06 +0200650
651 /* add into diff if there are any changes */
652 if (!ret) {
653 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
654
655 free(orig_value);
656 free(key);
657 free(value);
658 free(orig_key);
659 LY_CHECK_GOTO(ret, cleanup);
660 } else if (ret == LY_ENOT) {
661 ret = LY_SUCCESS;
662 } else {
663 goto cleanup;
664 }
665 } else if (!match_first) {
666 /* get all the attributes */
667 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
668
669 /* there must be changes, it is created */
670 assert(op == LYD_DIFF_OP_CREATE);
671 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
672
673 free(orig_value);
674 LY_CHECK_GOTO(ret, cleanup);
675 } /* else was handled */
676
677 if (nosiblings) {
678 break;
679 }
680 }
681
682cleanup:
683 LY_ARRAY_FOR(userord, u) {
684 LY_ARRAY_FREE(userord[u].inst);
685 }
686 LY_ARRAY_FREE(userord);
687 return ret;
688}
689
Michal Vasko3a41dff2020-07-15 14:30:28 +0200690static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200691lyd_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 +0200692{
693 const struct ly_ctx *ctx;
694
695 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
696
697 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200698 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200699 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200700 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200701 } else {
702 ctx = NULL;
703 }
704
705 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
706 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
707 return LY_EINVAL;
708 }
709
710 *diff = NULL;
711
Michal Vasko3a41dff2020-07-15 14:30:28 +0200712 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
713}
714
715API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200716lyd_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 +0200717{
718 return lyd_diff(first, second, options, 1, diff);
719}
720
721API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200722lyd_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 +0200723{
724 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200725}
726
727/**
728 * @brief Find a matching node in data tree for a diff node.
729 *
730 * @param[in] first_node First sibling in the data tree.
731 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200732 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200733 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200734static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200735lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
736{
737 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
738 /* try to find the exact instance */
739 lyd_find_sibling_first(first_node, diff_node, match_p);
740 } else {
741 /* try to simply find the node, there cannot be more instances */
742 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
743 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200744}
745
746/**
747 * @brief Learn operation of a diff node.
748 *
749 * @param[in] diff_node Diff node.
750 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200751 * @return LY_ERR value.
752 */
753static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200754lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200755{
756 struct lyd_meta *meta = NULL;
757 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200758 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200759
Michal Vasko9e685082021-01-29 14:49:09 +0100760 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200761 LY_LIST_FOR(diff_parent->meta, meta) {
762 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200763 str = meta->value.canonical;
Michal Vaskod59035b2020-07-08 12:00:06 +0200764 if ((str[0] == 'r') && (diff_parent != diff_node)) {
765 /* we do not care about this operation if it's in our parent */
766 continue;
767 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200768 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200769 break;
770 }
771 }
772 if (meta) {
773 break;
774 }
775 }
Michal Vaskob7be7a82020-08-20 09:09:04 +0200776 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200777
Michal Vaskod59035b2020-07-08 12:00:06 +0200778 return LY_SUCCESS;
779}
780
781/**
782 * @brief Insert a diff node into a data tree.
783 *
784 * @param[in,out] first_node First sibling of the data tree.
785 * @param[in] parent_node Data tree sibling parent node.
786 * @param[in] new_node Node to insert.
787 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
788 * instance will be inserted at the first position.
789 * @return err_info, NULL on success.
790 */
791static LY_ERR
792lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Radek Krejci0f969882020-08-21 16:56:47 +0200793 const char *key_or_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200794{
795 LY_ERR ret;
796 struct lyd_node *anchor;
797
798 assert(new_node);
799
800 if (!*first_node) {
801 if (!parent_node) {
802 /* no parent or siblings */
803 *first_node = new_node;
804 return LY_SUCCESS;
805 }
806
807 /* simply insert into parent, no other children */
808 if (key_or_value) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200809 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200810 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200811 return LY_EINVAL;
812 }
Michal Vaskob104f112020-07-17 09:54:54 +0200813 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200814 }
815
Michal Vasko9e685082021-01-29 14:49:09 +0100816 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200817
Michal Vaskod59035b2020-07-08 12:00:06 +0200818 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200819 /* simple insert */
820 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200821 }
822
823 if (key_or_value) {
824 /* find the anchor sibling */
825 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
826 if (ret == LY_ENOTFOUND) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200827 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200828 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200829 return LY_EINVAL;
830 } else if (ret) {
831 return ret;
832 }
833
834 /* insert after */
835 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
836 assert(new_node->prev == anchor);
837 if (*first_node == new_node) {
838 *first_node = anchor;
839 }
840 } else {
841 if ((*first_node)->schema->flags & LYS_KEY) {
842 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
843
844 /* find last key */
845 anchor = *first_node;
846 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
847 anchor = anchor->next;
848 }
849 /* insert after the last key */
850 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
851 } else {
852 /* insert at the beginning */
853 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
854 *first_node = new_node;
855 }
856 }
857
858 return LY_SUCCESS;
859}
860
861/**
862 * @brief Apply diff subtree on data tree nodes, recursively.
863 *
864 * @param[in,out] first_node First sibling of the data tree.
865 * @param[in] parent_node Parent of the first sibling.
866 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200867 * @param[in] diff_cb Optional diff callback.
868 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200869 * @return LY_ERR value.
870 */
871static LY_ERR
872lyd_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 +0200873 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +0200874{
875 LY_ERR ret;
876 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200877 const char *str_val;
878 enum lyd_diff_op op;
879 struct lyd_meta *meta;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200880 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200881
882 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200883 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200884
Michal Vaskoe6323f62020-07-09 15:49:02 +0200885 /* handle specific user-ordered (leaf-)lists operations separately */
886 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
887 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200888 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200889 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskoe8022002020-12-03 14:10:14 +0100890 LY_CHECK_ERR_RET(!match, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200891 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200892 /* duplicate the node */
893 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200894 }
895
Michal Vaskoe6323f62020-07-09 15:49:02 +0200896 /* get "key" or "value" metadata string value */
897 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 +0200898 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200899 str_val = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200900
Michal Vaskod59035b2020-07-08 12:00:06 +0200901 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200902 if (str_val[0]) {
903 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200904 } else {
905 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
906 }
907 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200908 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200909 lyd_free_tree(match);
910 }
911 return ret;
912 }
913
914 goto next_iter_r;
915 }
916
917 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200918 switch (op) {
919 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200920 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200921 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskoe8022002020-12-03 14:10:14 +0100922 LY_CHECK_ERR_RET(!match, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200923
924 if (match->schema->nodetype & LYD_NODE_TERM) {
925 /* special case of only dflt flag change */
926 if (diff_node->flags & LYD_DEFAULT) {
927 match->flags |= LYD_DEFAULT;
928 } else {
929 match->flags &= ~LYD_DEFAULT;
930 }
931 } else {
932 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200933 if (!lyd_child_no_keys(diff_node)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200934 LOGINT_RET(ctx);
935 }
936 }
937 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200938 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200939 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200940 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200941
942 /* insert it at the end */
943 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +0200944 if (parent_node) {
945 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200946 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200947 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200948 }
949 if (ret) {
950 lyd_free_tree(match);
951 return ret;
952 }
953
954 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200955 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200956 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200957 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskoe8022002020-12-03 14:10:14 +0100958 LY_CHECK_ERR_RET(!match, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200959
960 /* remove it */
961 if ((match == *first_node) && !match->parent) {
962 assert(!parent_node);
963 /* we have removed the top-level node */
964 *first_node = (*first_node)->next;
965 }
966 lyd_free_tree(match);
967
968 /* we are not going recursively in this case, the whole subtree was already deleted */
969 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200970 case LYD_DIFF_OP_REPLACE:
Michal Vaskobaba84e2021-02-05 16:33:30 +0100971 LY_CHECK_ERR_RET(!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA)), LOGINT(ctx), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200972
973 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200974 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskoe8022002020-12-03 14:10:14 +0100975 LY_CHECK_ERR_RET(!match, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200976
Michal Vaskobaba84e2021-02-05 16:33:30 +0100977 /* update the value */
978 if (diff_node->schema->nodetype == LYS_LEAF) {
979 ret = lyd_change_term(match, LYD_CANON_VALUE(diff_node));
980 if (ret && (ret != LY_EEXIST)) {
981 LOGINT_RET(ctx);
982 }
983 } else {
984 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
985 ret = lyd_any_copy_value(match, &any->value, any->value_type);
986 LY_CHECK_RET(ret);
Michal Vaskod59035b2020-07-08 12:00:06 +0200987 }
988
989 /* with flags */
990 match->flags = diff_node->flags;
991 break;
992 default:
993 LOGINT_RET(ctx);
994 }
995
996next_iter_r:
997 if (diff_cb) {
998 /* call callback */
999 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1000 }
1001
1002 /* apply diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001003 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe0665742021-02-11 11:08:44 +01001004 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data));
Michal Vaskod59035b2020-07-08 12:00:06 +02001005 }
1006
1007 return LY_SUCCESS;
1008}
1009
1010API LY_ERR
1011lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001012 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001013{
1014 const struct lyd_node *root;
1015
1016 LY_LIST_FOR(diff, root) {
1017 if (mod && (lyd_owner_module(root) != mod)) {
1018 /* skip data nodes from different modules */
1019 continue;
1020 }
1021
1022 /* apply relevant nodes from the diff datatree */
1023 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1024 }
1025
1026 return LY_SUCCESS;
1027}
1028
1029API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001030lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001031{
1032 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1033}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001034
1035/**
1036 * @brief Update operations on a diff node when the new operation is NONE.
1037 *
1038 * @param[in] diff_match Node from the diff.
1039 * @param[in] cur_op Current operation of the diff node.
1040 * @param[in] src_diff Current source diff node.
1041 * @return LY_ERR value.
1042 */
1043static LY_ERR
1044lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1045{
1046 switch (cur_op) {
1047 case LYD_DIFF_OP_NONE:
1048 case LYD_DIFF_OP_CREATE:
1049 case LYD_DIFF_OP_REPLACE:
1050 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1051 /* NONE on a term means only its dflt flag was changed */
1052 diff_match->flags &= ~LYD_DEFAULT;
1053 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1054 }
1055 break;
1056 default:
1057 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001058 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001059 }
1060
1061 return LY_SUCCESS;
1062}
1063
1064/**
1065 * @brief Remove an attribute from a node.
1066 *
1067 * @param[in] node Node with the metadata.
1068 * @param[in] name Metadata name.
1069 */
1070static void
1071lyd_diff_del_meta(struct lyd_node *node, const char *name)
1072{
1073 struct lyd_meta *meta;
1074
1075 LY_LIST_FOR(node->meta, meta) {
1076 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001077 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001078 return;
1079 }
1080 }
1081
1082 assert(0);
1083}
1084
1085/**
1086 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001087 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001088 *
1089 * @param[in] node Node to change.
1090 * @param[in] op Operation to set.
1091 * @return LY_ERR value.
1092 */
1093static LY_ERR
1094lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1095{
1096 struct lyd_meta *meta;
1097
1098 LY_LIST_FOR(node->meta, meta) {
1099 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001100 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001101 break;
1102 }
1103 }
1104
Michal Vasko871a0252020-11-11 18:35:24 +01001105 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001106}
1107
1108/**
1109 * @brief Update operations on a diff node when the new operation is REPLACE.
1110 *
1111 * @param[in] diff_match Node from the diff.
1112 * @param[in] cur_op Current operation of the diff node.
1113 * @param[in] src_diff Current source diff node.
1114 * @return LY_ERR value.
1115 */
1116static LY_ERR
1117lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1118{
1119 LY_ERR ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001120 const char *str_val, *meta_name;
1121 struct lyd_meta *meta;
1122 const struct lys_module *mod;
1123 const struct lyd_node_any *any;
1124
1125 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001126 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001127 assert(mod);
1128
1129 switch (cur_op) {
1130 case LYD_DIFF_OP_REPLACE:
1131 case LYD_DIFF_OP_CREATE:
1132 switch (diff_match->schema->nodetype) {
1133 case LYS_LIST:
1134 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001135 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001136 * keep orig_key/orig_value (only replace oper) and replace key/value */
1137 assert(lysc_is_userordered(diff_match->schema));
1138 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1139
1140 lyd_diff_del_meta(diff_match, meta_name);
1141 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001142 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001143 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001144 break;
1145 case LYS_LEAF:
1146 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001147 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001148 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001149 }
1150
Michal Vaskoe6323f62020-07-09 15:49:02 +02001151 /* modify the node value */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001152 if (lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff))) {
1153 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001154 }
1155
Michal Vasko8caadab2020-11-05 17:38:15 +01001156 if (cur_op == LYD_DIFF_OP_REPLACE) {
1157 /* compare values whether there is any change at all */
1158 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1159 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_match)), LY_EINT);
1160 str_val = meta->value.canonical;
1161 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1162 if (!ret) {
1163 /* values are the same, remove orig-value meta and set oper to NONE */
1164 lyd_free_meta_single(meta);
1165 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1166 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001167 }
1168
1169 /* modify the default flag */
1170 diff_match->flags &= ~LYD_DEFAULT;
1171 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1172 break;
1173 case LYS_ANYXML:
1174 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001175 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001176 /* replaced with the exact same value, impossible */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001177 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001178 }
1179
1180 /* modify the node value */
1181 any = (struct lyd_node_any *)src_diff;
1182 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1183 break;
1184 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001185 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001186 }
1187 break;
1188 case LYD_DIFF_OP_NONE:
1189 /* it is moved now */
1190 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1191
1192 /* change the operation */
1193 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1194
1195 /* set orig-key and key metadata */
1196 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001197 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001198 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001199
1200 meta = lyd_find_meta(src_diff->meta, mod, "key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001201 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001202 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001203 break;
1204 default:
1205 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001206 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001207 }
1208
1209 return LY_SUCCESS;
1210}
1211
1212/**
1213 * @brief Update operations in a diff node when the new operation is CREATE.
1214 *
1215 * @param[in] diff_match Node from the diff.
1216 * @param[in] cur_op Current operation of the diff node.
1217 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001218 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001219 * @return LY_ERR value.
1220 */
1221static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001222lyd_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 +02001223{
1224 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001225 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001226 uint32_t trg_flags;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001227
1228 switch (cur_op) {
1229 case LYD_DIFF_OP_DELETE:
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001230 if ((options & LYD_DIFF_MERGE_DEFAULTS) && (diff_match->schema->nodetype == LYS_LEAF)) {
1231 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
Michal Vasko5632e0d2020-07-31 14:13:37 +02001232 sleaf = (struct lysc_node_leaf *)diff_match->schema;
Michal Vasko5632e0d2020-07-31 14:13:37 +02001233 }
1234
Michal Vasko871a0252020-11-11 18:35:24 +01001235 /* remember current flags */
1236 trg_flags = diff_match->flags;
1237
Michal Vasko69730152020-10-09 16:30:07 +02001238 if (sleaf && sleaf->dflt &&
1239 !sleaf->dflt->realtype->plugin->compare(sleaf->dflt, &((struct lyd_node_term *)src_diff)->value)) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001240 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1241 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vasko5632e0d2020-07-31 14:13:37 +02001242 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001243 /* deleted + created -> operation NONE */
1244 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001245 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001246 /* we deleted it, but it was created with a different value -> operation REPLACE */
1247 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1248
1249 /* current value is the previous one (meta) */
Michal Vasko871a0252020-11-11 18:35:24 +01001250 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
1251 LYD_CANON_VALUE(diff_match), 0, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001252
1253 /* update the value itself */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001254 LY_CHECK_RET(lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff)));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001255 }
1256
1257 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001258 /* add orig-dflt metadata */
1259 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1260 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1261
Michal Vaskoe6323f62020-07-09 15:49:02 +02001262 /* update dflt flag itself */
1263 diff_match->flags &= ~LYD_DEFAULT;
1264 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1265 } else {
1266 /* but the operation of its children should remain DELETE */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001267 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001268 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1269 }
1270 }
1271 break;
1272 default:
1273 /* create and replace operations are not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001274 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001275 }
1276
1277 return LY_SUCCESS;
1278}
1279
1280/**
1281 * @brief Update operations on a diff node when the new operation is DELETE.
1282 *
1283 * @param[in] diff_match Node from the diff.
1284 * @param[in] cur_op Current operation of the diff node.
1285 * @param[in] src_diff Current source diff node.
1286 * @return LY_ERR value.
1287 */
1288static LY_ERR
1289lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1290{
1291 struct lyd_node *next, *child;
1292
1293 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001294 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 +02001295
1296 switch (cur_op) {
1297 case LYD_DIFF_OP_CREATE:
1298 /* it was created, but then deleted -> set NONE operation */
1299 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1300
1301 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1302 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001303 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1304 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001305 } else {
1306 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001307 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001308 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1309 }
1310 }
1311 break;
1312 case LYD_DIFF_OP_REPLACE:
1313 /* similar to none operation but also remove the redundant attribute */
1314 lyd_diff_del_meta(diff_match, "orig-value");
Radek Krejcif13b87b2020-12-01 22:02:17 +01001315 /* fall through */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001316 case LYD_DIFF_OP_NONE:
1317 /* it was not modified, but should be deleted -> set DELETE operation */
1318 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1319
Michal Vasko5632e0d2020-07-31 14:13:37 +02001320 /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001321 LY_LIST_FOR_SAFE(lyd_child_no_keys(diff_match), next, child) {
1322 if (lyd_find_sibling_first(lyd_child(src_diff), child, NULL) == LY_ENOTFOUND) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001323 lyd_free_tree(child);
1324 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001325 }
1326 break;
1327 default:
1328 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001329 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001330 }
1331
1332 return LY_SUCCESS;
1333}
1334
1335/**
1336 * @brief Check whether this diff node is redundant (does not change data).
1337 *
1338 * @param[in] diff Diff node.
1339 * @return 0 if not, non-zero if it is.
1340 */
1341static int
1342lyd_diff_is_redundant(struct lyd_node *diff)
1343{
1344 enum lyd_diff_op op;
1345 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1346 struct lyd_node *child;
1347 const struct lys_module *mod;
1348 const char *str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001349
1350 assert(diff);
1351
Radek Krejcia1c1e542020-09-29 16:06:52 +02001352 child = lyd_child_no_keys(diff);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001353 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001354 assert(mod);
1355
1356 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001357 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001358
1359 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1360 /* check for redundant move */
1361 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1362 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1363 assert(orig_val_meta && val_meta);
1364
1365 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1366 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001367 lyd_free_meta_single(orig_val_meta);
1368 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001369 if (child) {
1370 /* change operation to NONE, we have siblings */
1371 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1372 return 0;
1373 }
1374
1375 /* redundant node, BUT !!
1376 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1377 * because the data that this is applied on should not change for the diff lifetime.
1378 * However, when we are merging 2 diffs, this conversion is actually lossy because
1379 * if the data change, the move operation can also change its meaning. In this specific
1380 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1381 */
1382 return 1;
1383 }
1384 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1385 /* check whether at least the default flags are different */
1386 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1387 assert(meta);
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001388 str = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001389
1390 /* if previous and current dflt flags are the same, this node is redundant */
1391 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1392 return 1;
1393 }
1394 return 0;
1395 }
1396
1397 if (!child && (op == LYD_DIFF_OP_NONE)) {
1398 return 1;
1399 }
1400
1401 return 0;
1402}
1403
1404/**
1405 * @brief Merge sysrepo diff with another diff, recursively.
1406 *
1407 * @param[in] src_diff Source diff node.
1408 * @param[in] diff_parent Current sysrepo diff parent.
1409 * @param[in] diff_cb Optional diff callback.
1410 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001411 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001412 * @param[in,out] diff Diff root node.
1413 * @return LY_ERR value.
1414 */
1415static LY_ERR
1416lyd_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 +01001417 uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001418{
1419 LY_ERR ret = LY_SUCCESS;
1420 struct lyd_node *child, *diff_node = NULL;
1421 enum lyd_diff_op src_op, cur_op;
1422
1423 /* get source node operation */
1424 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1425
1426 /* find an equal node in the current diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001427 lyd_diff_find_node(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, &diff_node);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001428
1429 if (diff_node) {
1430 /* get target (current) operation */
1431 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1432
1433 /* merge operations */
1434 switch (src_op) {
1435 case LYD_DIFF_OP_REPLACE:
1436 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1437 break;
1438 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001439 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
1440 /* special case of creating duplicate state (leaf-)list instances */
1441 goto add_diff;
1442 }
1443
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001444 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001445 break;
1446 case LYD_DIFF_OP_DELETE:
1447 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1448 break;
1449 case LYD_DIFF_OP_NONE:
1450 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1451 break;
1452 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001453 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001454 }
1455 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001456 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001457 return ret;
1458 }
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 /* merge src_diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001469 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001470 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, options, diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001471 }
1472 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001473add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001474 /* add new diff node with all descendants */
Michal Vasko871a0252020-11-11 18:35:24 +01001475 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS,
1476 &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001477
1478 /* insert node into diff if not already */
1479 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001480 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001481 }
1482
1483 /* update operation */
1484 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1485
1486 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001487 /* call callback with no source diff node since it was duplicated and just added */
1488 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001489 }
1490
1491 /* update diff parent */
1492 diff_parent = diff_node;
1493 }
1494
1495 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001496 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001497 if (diff_parent == *diff) {
1498 *diff = (*diff)->next;
1499 }
1500 lyd_free_tree(diff_parent);
1501 }
1502
1503 return LY_SUCCESS;
1504}
1505
1506API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001507lyd_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 +01001508 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001509{
1510 const struct lyd_node *src_root;
1511
1512 LY_LIST_FOR(src_diff, src_root) {
1513 if (mod && (lyd_owner_module(src_root) != mod)) {
1514 /* skip data nodes from different modules */
1515 continue;
1516 }
1517
1518 /* apply relevant nodes from the diff datatree */
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001519 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, options, diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001520 }
1521
1522 return LY_SUCCESS;
1523}
1524
1525API LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001526lyd_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 +01001527 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001528{
1529 if (!src_sibling) {
1530 return LY_SUCCESS;
1531 }
1532
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001533 return lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, options, diff_first);
Michal Vasko04f85912020-08-07 12:14:58 +02001534}
1535
1536API LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001537lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001538{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001539 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001540}
Michal Vasko4231fb62020-07-13 13:54:47 +02001541
1542static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001543lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001544{
1545 LY_ERR ret = LY_SUCCESS;
1546 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001547 const char *val1 = NULL;
1548 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001549 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001550
Michal Vaskobaba84e2021-02-05 16:33:30 +01001551 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1552
1553 meta = lyd_find_meta(node->meta, mod, "orig-value");
1554 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001555
1556 /* orig-value */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001557 val1 = meta->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001558
1559 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001560 if (node->schema->nodetype == LYS_LEAF) {
1561 val2 = strdup(LYD_CANON_VALUE(node));
1562 } else {
1563 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1564 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001565
1566 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001567 flags = node->flags;
1568 if (node->schema->nodetype == LYS_LEAF) {
1569 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1570 } else {
1571 union lyd_any_value anyval = {.str = val1};
1572 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1573 }
1574 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001575 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1576
1577cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001578 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001579 return ret;
1580}
1581
1582static LY_ERR
1583lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1584{
1585 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001586 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001587
1588 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001589 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001590
1591 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001592 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001593 flag1 = LYD_DEFAULT;
1594 } else {
1595 flag1 = 0;
1596 }
1597
1598 /* current default */
1599 flag2 = node->flags & LYD_DEFAULT;
1600
Michal Vasko610e93b2020-11-09 20:58:32 +01001601 if (flag1 == flag2) {
1602 /* no default state change so nothing to reverse */
1603 return LY_SUCCESS;
1604 }
1605
Michal Vasko4231fb62020-07-13 13:54:47 +02001606 /* switch defaults */
1607 node->flags &= ~LYD_DEFAULT;
1608 node->flags |= flag1;
1609 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1610
1611 return LY_SUCCESS;
1612}
1613
1614static LY_ERR
1615lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1616{
1617 LY_ERR ret = LY_SUCCESS;
1618 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001619 const char *val1 = NULL;
1620 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001621
1622 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001623 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001624
1625 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001626 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001627
1628 /* value1 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001629 val1 = meta1->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001630
1631 /* value2 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001632 val2 = strdup(meta2->value.canonical);
Michal Vasko4231fb62020-07-13 13:54:47 +02001633
1634 /* switch values */
1635 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1636 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1637
1638cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001639 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001640 return ret;
1641}
1642
1643API LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001644lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001645{
1646 LY_ERR ret = LY_SUCCESS;
1647 const struct lys_module *mod;
Michal Vasko56daf732020-08-10 10:57:18 +02001648 struct lyd_node *root, *elem;
Michal Vasko4231fb62020-07-13 13:54:47 +02001649 enum lyd_diff_op op;
1650
1651 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1652
1653 if (!src_diff) {
1654 *diff = NULL;
1655 return LY_SUCCESS;
1656 }
1657
1658 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001659 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001660
1661 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001662 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1663 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001664
1665 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001666 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001667 /* skip all keys */
1668 if (!lysc_is_key(elem->schema)) {
1669 /* find operation attribute, if any */
1670 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001671
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001672 switch (op) {
1673 case LYD_DIFF_OP_CREATE:
1674 /* reverse create to delete */
1675 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001676 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001677 case LYD_DIFF_OP_DELETE:
1678 /* reverse delete to create */
1679 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001680 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001681 case LYD_DIFF_OP_REPLACE:
1682 switch (elem->schema->nodetype) {
1683 case LYS_LEAF:
1684 /* leaf value change */
1685 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1686 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1687 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01001688 case LYS_ANYXML:
1689 case LYS_ANYDATA:
1690 /* any value change */
1691 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1692 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001693 case LYS_LEAFLIST:
1694 /* leaf-list move */
1695 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1696 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1697 break;
1698 case LYS_LIST:
1699 /* list move */
1700 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1701 break;
1702 default:
1703 LOGINT(LYD_CTX(src_diff));
1704 ret = LY_EINT;
1705 goto cleanup;
1706 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001707 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001708 case LYD_DIFF_OP_NONE:
1709 switch (elem->schema->nodetype) {
1710 case LYS_LEAF:
1711 case LYS_LEAFLIST:
1712 /* default flag change */
1713 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1714 break;
1715 default:
1716 /* nothing to do */
1717 break;
1718 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001719 break;
1720 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001721 }
1722
Michal Vasko56daf732020-08-10 10:57:18 +02001723 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02001724 }
1725 }
1726
1727cleanup:
1728 if (ret) {
1729 lyd_free_siblings(*diff);
1730 *diff = NULL;
1731 }
1732 return ret;
1733}