blob: 744f0391a56bb57a2b760c78bbe1d5424cb62fda [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 *
Michal Vaskoe78faec2021-04-08 17:24:43 +02006 * Copyright (c) 2020 - 2021 CESNET, z.s.p.o.
Michal Vaskod59035b2020-07-08 12:00:06 +02007 *
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 */
Christian Hopps32874e12021-05-01 09:43:54 -040014#define _GNU_SOURCE /* asprintf, strdup */
Michal Vaskod59035b2020-07-08 12:00:06 +020015
16#include "diff.h"
17
18#include <assert.h>
19#include <stddef.h>
Michal Vaskoe78faec2021-04-08 17:24:43 +020020#include <stdint.h>
21#include <stdio.h>
Radek Krejci47fab892020-11-05 17:02:41 +010022#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020023#include <string.h>
24
Michal Vaskoe78faec2021-04-08 17:24:43 +020025#include "compat.h"
Radek Krejci47fab892020-11-05 17:02:41 +010026#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020027#include "log.h"
Michal Vasko8f702ee2024-02-20 15:44:24 +010028#include "ly_common.h"
Michal Vasko19175b62022-04-01 09:17:07 +020029#include "plugins_exts.h"
Michal Vaskob4750962022-10-06 15:33:35 +020030#include "plugins_exts/metadata.h"
Radek Krejci47fab892020-11-05 17:02:41 +010031#include "plugins_types.h"
32#include "set.h"
33#include "tree.h"
34#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020035#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010036#include "tree_edit.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020037#include "tree_schema.h"
38#include "tree_schema_internal.h"
39
Michal Vasko52afd7d2022-01-18 14:08:34 +010040#define LOGERR_META(ctx, meta_name, node) \
41 { \
42 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
43 LOGERR(ctx, LY_EINVAL, "Failed to find metadata \"%s\" for node \"%s\".", meta_name, __path); \
44 free(__path); \
45 }
46
47#define LOGERR_NOINST(ctx, node) \
48 { \
49 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
50 LOGERR(ctx, LY_EINVAL, "Failed to find node \"%s\" instance in data.", __path); \
51 free(__path); \
52 }
53
54#define LOGERR_UNEXPVAL(ctx, node, data_source) \
55 { \
56 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
57 LOGERR(ctx, LY_EINVAL, "Unexpected value of node \"%s\" in %s.", __path, data_source); \
58 free(__path); \
59 }
60
61#define LOGERR_MERGEOP(ctx, node, src_op, trg_op) \
62 { \
63 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
64 LOGERR(ctx, LY_EINVAL, "Unable to merge operation \"%s\" with \"%s\" for node \"%s\".", \
65 lyd_diff_op2str(trg_op), lyd_diff_op2str(src_op), __path); \
66 free(__path); \
67 }
68
Michal Vaskod59035b2020-07-08 12:00:06 +020069static const char *
70lyd_diff_op2str(enum lyd_diff_op op)
71{
72 switch (op) {
73 case LYD_DIFF_OP_CREATE:
74 return "create";
75 case LYD_DIFF_OP_DELETE:
76 return "delete";
77 case LYD_DIFF_OP_REPLACE:
78 return "replace";
79 case LYD_DIFF_OP_NONE:
80 return "none";
81 }
82
83 LOGINT(NULL);
84 return NULL;
85}
86
Michal Vaskoe6323f62020-07-09 15:49:02 +020087static enum lyd_diff_op
88lyd_diff_str2op(const char *str)
89{
90 switch (str[0]) {
91 case 'c':
92 assert(!strcmp(str, "create"));
93 return LYD_DIFF_OP_CREATE;
94 case 'd':
95 assert(!strcmp(str, "delete"));
96 return LYD_DIFF_OP_DELETE;
97 case 'r':
98 assert(!strcmp(str, "replace"));
99 return LYD_DIFF_OP_REPLACE;
100 case 'n':
101 assert(!strcmp(str, "none"));
102 return LYD_DIFF_OP_NONE;
103 }
104
105 LOGINT(NULL);
106 return 0;
107}
108
Michal Vaskocffc3f92022-06-15 07:57:24 +0200109/**
110 * @brief Create diff metadata for a nested user-ordered node with the effective operation "create".
111 *
112 * @param[in] node User-rodered node to update.
113 * @return LY_ERR value.
114 */
115static LY_ERR
116lyd_diff_add_create_nested_userord(struct lyd_node *node)
117{
118 LY_ERR rc = LY_SUCCESS;
119 const char *meta_name, *meta_val;
120 size_t buflen = 0, bufused = 0;
121 uint32_t pos;
122 char *dyn = NULL;
123
124 assert(lysc_is_userordered(node->schema));
125
126 /* get correct metadata name and value */
127 if (lysc_is_dup_inst_list(node->schema)) {
128 meta_name = "yang:position";
129
130 pos = lyd_list_pos(node);
131 if (asprintf(&dyn, "%" PRIu32, pos) == -1) {
132 LOGMEM(LYD_CTX(node));
133 rc = LY_EMEM;
134 goto cleanup;
135 }
136 meta_val = dyn;
137 } else if (node->schema->nodetype == LYS_LIST) {
138 meta_name = "yang:key";
139
140 if (node->prev->next && (node->prev->schema == node->schema)) {
141 LY_CHECK_GOTO(rc = lyd_path_list_predicate(node->prev, &dyn, &buflen, &bufused, 0), cleanup);
142 meta_val = dyn;
143 } else {
144 meta_val = "";
145 }
146 } else {
147 meta_name = "yang:value";
148
149 if (node->prev->next && (node->prev->schema == node->schema)) {
150 meta_val = lyd_get_value(node->prev);
151 } else {
152 meta_val = "";
153 }
154 }
155
156 /* create the metadata */
stewegd4cde642024-02-21 08:34:16 +0100157 LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_val, LYD_NEW_VAL_STORE_ONLY, NULL), cleanup);
Michal Vaskocffc3f92022-06-15 07:57:24 +0200158
159cleanup:
160 free(dyn);
161 return rc;
162}
163
Michal Vaskofb51a842023-06-20 08:50:24 +0200164/**
165 * @brief Find metadata/an attribute of a node.
166 *
167 * @param[in] node Node to search.
168 * @param[in] name Metadata/attribute name.
169 * @param[out] meta Metadata found, NULL if not found.
170 * @param[out] attr Attribute found, NULL if not found.
171 */
172static void
173lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr)
174{
175 struct lyd_meta *m;
176 struct lyd_attr *a;
177
178 if (meta) {
179 *meta = NULL;
180 }
181 if (attr) {
182 *attr = NULL;
183 }
184
185 if (node->schema) {
186 assert(meta);
187
188 LY_LIST_FOR(node->meta, m) {
189 if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) {
190 *meta = m;
191 break;
192 }
193 }
194 } else {
195 assert(attr);
196
197 LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) {
198 /* name */
199 if (strcmp(a->name.name, name)) {
200 continue;
201 }
202
203 /* module */
204 switch (a->format) {
205 case LY_VALUE_JSON:
206 if (strcmp(a->name.module_name, "yang")) {
207 continue;
208 }
209 break;
210 case LY_VALUE_XML:
211 if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) {
212 continue;
213 }
214 break;
215 default:
216 LOGINT(LYD_CTX(node));
217 return;
218 }
219
220 *attr = a;
221 break;
222 }
223 }
224}
225
226/**
227 * @brief Learn operation of a diff node.
228 *
229 * @param[in] diff_node Diff node.
230 * @param[out] op Operation.
231 * @return LY_ERR value.
232 */
233static LY_ERR
234lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
235{
236 struct lyd_meta *meta = NULL;
237 struct lyd_attr *attr = NULL;
238 const struct lyd_node *diff_parent;
239 const char *str;
240 char *path;
241
242 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
243 lyd_diff_find_meta(diff_parent, "operation", &meta, &attr);
244 if (!meta && !attr) {
245 continue;
246 }
247
248 str = meta ? lyd_get_meta_value(meta) : attr->value;
249 if ((str[0] == 'r') && (diff_parent != diff_node)) {
250 /* we do not care about this operation if it's in our parent */
251 continue;
252 }
253 *op = lyd_diff_str2op(str);
254 return LY_SUCCESS;
255 }
256
257 /* operation not found */
258 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
259 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
260 free(path);
261 return LY_EINT;
262}
263
264/**
265 * @brief Remove metadata/an attribute from a node.
266 *
267 * @param[in] node Node to update.
268 * @param[in] name Metadata/attribute name.
269 */
270static void
271lyd_diff_del_meta(struct lyd_node *node, const char *name)
272{
273 struct lyd_meta *meta;
274 struct lyd_attr *attr;
275
276 lyd_diff_find_meta(node, name, &meta, &attr);
277
278 if (meta) {
279 lyd_free_meta_single(meta);
280 } else if (attr) {
281 lyd_free_attr_single(LYD_CTX(node), attr);
282 }
283}
284
aPieceka9369d22024-02-15 11:53:29 +0100285/**
286 * @brief Insert a node into siblings.
287 *
288 * - if the node is part of some other tree, it is automatically unlinked.
289 * - difference with the lyd_insert_sibling() is that the subsequent nodes are never inserted.
290 * - insert ignores node ordering, which is fine since it's not desirable to sort diff nodes.
291 *
292 * @param[in] sibling Siblings to insert into, can even be NULL.
293 * @param[in] node Node to insert.
294 * @param[out] first Return the first sibling after insertion. Can be the address of @p sibling.
295 */
296static void
297lyd_diff_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_node **first_sibling)
298{
299 assert(node && first_sibling);
300
301 lyd_unlink_ignore_lyds(NULL, node);
302 *first_sibling = lyd_first_sibling(sibling);
303 lyd_insert_node(NULL, first_sibling, node, LYD_INSERT_NODE_LAST_BY_SCHEMA);
304}
305
Michal Vaskod59035b2020-07-08 12:00:06 +0200306LY_ERR
307lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200308 const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
309 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200310{
Michal Vaskocffc3f92022-06-15 07:57:24 +0200311 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
Michal Vaskod59035b2020-07-08 12:00:06 +0200312 const struct lyd_node *parent = NULL;
Michal Vaskofb51a842023-06-20 08:50:24 +0200313 enum lyd_diff_op cur_op;
Michal Vaskod4525672023-06-20 09:15:42 +0200314 struct lyd_meta *meta;
Michal Vasko9af40912023-06-21 08:03:43 +0200315 uint32_t diff_opts;
Michal Vaskod59035b2020-07-08 12:00:06 +0200316
317 assert(diff);
318
Michal Vasko53d48422020-11-13 18:02:29 +0100319 /* replace leaf always needs orig-default and orig-value */
320 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
321
322 /* create on userord needs key/value */
323 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200324 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100325 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200326 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100327
328 /* move on userord needs both key and orig-key/value and orig-value */
329 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200330 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100331 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200332 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
333 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100334
Michal Vaskod59035b2020-07-08 12:00:06 +0200335 /* find the first existing parent */
336 siblings = *diff;
Michal Vaskofb51a842023-06-20 08:50:24 +0200337 do {
Michal Vaskod59035b2020-07-08 12:00:06 +0200338 /* find next node parent */
339 parent = node;
340 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100341 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200342 }
Michal Vaskofb51a842023-06-20 08:50:24 +0200343
344 if (lysc_is_dup_inst_list(parent->schema)) {
345 /* assume it never exists, we are not able to distinguish whether it does or not */
346 match = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200347 break;
348 }
349
350 /* check whether it exists in the diff */
351 if (lyd_find_sibling_first(siblings, parent, &match)) {
352 break;
353 }
354
355 /* another parent found */
356 diff_parent = match;
357
358 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200359 siblings = lyd_child_no_keys(match);
Michal Vaskofb51a842023-06-20 08:50:24 +0200360 } while (parent != node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200361
Michal Vaskofb51a842023-06-20 08:50:24 +0200362 if (match && (parent == node)) {
363 /* special case when there is already an operation on our descendant */
364 assert(!lyd_diff_get_op(diff_parent, &cur_op) && (cur_op == LYD_DIFF_OP_NONE));
365 (void)cur_op;
366
Michal Vasko9af40912023-06-21 08:03:43 +0200367 /* move it to the end where it is expected (matters for user-ordered lists) */
368 if (lysc_is_userordered(diff_parent->schema)) {
369 for (elem = diff_parent; elem->next && (elem->next->schema == elem->schema); elem = elem->next) {}
370 if (elem != diff_parent) {
371 LY_CHECK_RET(lyd_insert_after(elem, diff_parent));
372 }
373 }
374
Michal Vaskod4525672023-06-20 09:15:42 +0200375 /* will be replaced by the new operation but keep the current op for descendants */
Michal Vaskofb51a842023-06-20 08:50:24 +0200376 lyd_diff_del_meta(diff_parent, "operation");
Michal Vaskod4525672023-06-20 09:15:42 +0200377 LY_LIST_FOR(lyd_child_no_keys(diff_parent), elem) {
378 lyd_diff_find_meta(elem, "operation", &meta, NULL);
379 if (meta) {
380 /* explicit operation, fine */
381 continue;
382 }
383
384 /* set the none operation */
stewegd4cde642024-02-21 08:34:16 +0100385 LY_CHECK_RET(lyd_new_meta(NULL, elem, NULL, "yang:operation", "none", LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskod4525672023-06-20 09:15:42 +0200386 }
387
Michal Vaskofb51a842023-06-20 08:50:24 +0200388 dup = diff_parent;
Michal Vasko695a7f22023-02-14 10:02:10 +0100389 } else {
aPiecek1462ab12024-02-07 09:13:29 +0100390 diff_opts = LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_LYDS;
Michal Vasko2be7c8a2023-06-21 08:04:21 +0200391 if ((op != LYD_DIFF_OP_REPLACE) || !lysc_is_userordered(node->schema) || (node->schema->flags & LYS_CONFIG_R)) {
392 /* move applies only to the user-ordered list, no descendants */
393 diff_opts |= LYD_DUP_RECURSIVE;
394 }
395
Michal Vaskofb51a842023-06-20 08:50:24 +0200396 /* duplicate the subtree (and connect to the diff if possible) */
397 if (diff_parent) {
398 LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
Michal Vasko2be7c8a2023-06-21 08:04:21 +0200399 diff_opts, &dup));
Michal Vaskofb51a842023-06-20 08:50:24 +0200400 } else {
Michal Vasko2be7c8a2023-06-21 08:04:21 +0200401 LY_CHECK_RET(lyd_dup_single(node, NULL, diff_opts, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200402 }
Michal Vaskofb51a842023-06-20 08:50:24 +0200403
404 /* find the first duplicated parent */
405 if (!diff_parent) {
406 diff_parent = lyd_parent(dup);
407 while (diff_parent && diff_parent->parent) {
408 diff_parent = lyd_parent(diff_parent);
409 }
410 } else {
411 diff_parent = dup;
412 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
413 diff_parent = lyd_parent(diff_parent);
414 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200415 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200416
Michal Vaskofb51a842023-06-20 08:50:24 +0200417 /* no parent existed, must be manually connected */
418 if (!diff_parent) {
419 /* there actually was no parent to duplicate */
aPieceka9369d22024-02-15 11:53:29 +0100420 lyd_diff_insert_sibling(*diff, dup, diff);
Michal Vaskofb51a842023-06-20 08:50:24 +0200421 } else if (!diff_parent->parent) {
aPieceka9369d22024-02-15 11:53:29 +0100422 lyd_diff_insert_sibling(*diff, diff_parent, diff);
Michal Vaskofb51a842023-06-20 08:50:24 +0200423 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200424
Michal Vaskofb51a842023-06-20 08:50:24 +0200425 /* add parent operation, if any */
426 if (diff_parent && (diff_parent != dup)) {
stewegd4cde642024-02-21 08:34:16 +0100427 LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskofb51a842023-06-20 08:50:24 +0200428 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200429 }
430
431 /* add subtree operation */
stewegd4cde642024-02-21 08:34:16 +0100432 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200433
Michal Vaskocffc3f92022-06-15 07:57:24 +0200434 if (op == LYD_DIFF_OP_CREATE) {
435 /* all nested user-ordered (leaf-)lists need special metadata for create op */
436 LYD_TREE_DFS_BEGIN(dup, elem) {
437 if ((elem != dup) && lysc_is_userordered(elem->schema)) {
438 LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
439 }
440 LYD_TREE_DFS_END(dup, elem);
441 }
442 }
443
Michal Vaskod59035b2020-07-08 12:00:06 +0200444 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200445 if (orig_default) {
stewegd4cde642024-02-21 08:34:16 +0100446 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200447 }
448
449 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200450 if (orig_value) {
stewegd4cde642024-02-21 08:34:16 +0100451 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200452 }
453
454 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200455 if (key) {
stewegd4cde642024-02-21 08:34:16 +0100456 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200457 }
458
459 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200460 if (value) {
stewegd4cde642024-02-21 08:34:16 +0100461 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200462 }
463
Michal Vaskoe78faec2021-04-08 17:24:43 +0200464 /* position */
465 if (position) {
stewegd4cde642024-02-21 08:34:16 +0100466 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200467 }
468
Michal Vaskod59035b2020-07-08 12:00:06 +0200469 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200470 if (orig_key) {
stewegd4cde642024-02-21 08:34:16 +0100471 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200472 }
473
Michal Vaskoe78faec2021-04-08 17:24:43 +0200474 /* orig-position */
475 if (orig_position) {
stewegd4cde642024-02-21 08:34:16 +0100476 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200477 }
478
Michal Vaskod59035b2020-07-08 12:00:06 +0200479 return LY_SUCCESS;
480}
481
482/**
483 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
484 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100485 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200486 * @param[in] schema Schema node of the list/leaf-list.
487 * @param[in,out] userord Sized array of userord items.
488 * @return Userord item for all the user-ordered list/leaf-list instances.
489 */
490static struct lyd_diff_userord *
491lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
492{
493 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200494 struct lyd_node *iter;
495 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200496 LY_ARRAY_COUNT_TYPE u;
497
498 LY_ARRAY_FOR(*userord, u) {
499 if ((*userord)[u].schema == schema) {
500 return &(*userord)[u];
501 }
502 }
503
504 /* it was not added yet, add it now */
505 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
506
507 item->schema = schema;
508 item->pos = 0;
509 item->inst = NULL;
510
511 /* store all the instance pointers in the current order */
512 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200513 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
514 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
515 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200516 }
517 }
518
519 return item;
520}
521
522/**
523 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
524 * lists/leaf-lists.
525 *
526 * @param[in] first Node from the first tree, can be NULL (on create).
527 * @param[in] second Node from the second tree, can be NULL (on delete).
528 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100529 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200530 * @param[out] op Operation.
531 * @param[out] orig_default Original default metadata.
532 * @param[out] value Value metadata.
533 * @param[out] orig_value Original value metadata
534 * @param[out] key Key metadata.
535 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200536 * @param[out] position Position metadata.
537 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200538 * @return LY_SUCCESS on success,
539 * @return LY_ENOT if there is no change to be added into diff,
540 * @return LY_ERR value on other errors.
541 */
542static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200543lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100544 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200545 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200546{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200547 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200548 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200549 size_t buflen, bufused;
Michal Vaskoee9b9482023-06-19 13:17:48 +0200550 uint32_t first_pos, second_pos, comp_opts;
Michal Vaskod59035b2020-07-08 12:00:06 +0200551
552 assert(first || second);
553
554 *orig_default = NULL;
555 *value = NULL;
556 *orig_value = NULL;
557 *key = NULL;
558 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200559 *position = NULL;
560 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200561
562 schema = first ? first->schema : second->schema;
563 assert(lysc_is_userordered(schema));
564
Michal Vaskod59035b2020-07-08 12:00:06 +0200565 /* find user-ordered first position */
566 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200567 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200568 if (userord_item->inst[first_pos] == first) {
569 break;
570 }
571 }
572 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
573 } else {
574 first_pos = 0;
575 }
576
Michal Vaskoe78faec2021-04-08 17:24:43 +0200577 /* prepare position of the next instance */
578 second_pos = userord_item->pos++;
579
Michal Vaskod59035b2020-07-08 12:00:06 +0200580 /* learn operation first */
581 if (!second) {
582 *op = LYD_DIFF_OP_DELETE;
583 } else if (!first) {
584 *op = LYD_DIFF_OP_CREATE;
585 } else {
Michal Vaskoee9b9482023-06-19 13:17:48 +0200586 comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0;
587 if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200588 /* in first, there is a different instance on the second position, we are going to move 'first' node */
589 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200590 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200591 /* default flag change */
592 *op = LYD_DIFF_OP_NONE;
593 } else {
594 /* no changes */
595 return LY_ENOT;
596 }
597 }
598
599 /*
600 * set each attribute correctly based on the operation and node type
601 */
602
603 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100604 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200605 if (first->flags & LYD_DEFAULT) {
606 *orig_default = "true";
607 } else {
608 *orig_default = "false";
609 }
610 }
611
612 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200613 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
614 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200615 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200616 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200617 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200618 } else {
619 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200620 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200621 }
622 }
623
624 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200625 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
626 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200627 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200628 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200629 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200630 } else {
631 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200632 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200633 }
634 }
635
636 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200637 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
638 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200639 if (second_pos) {
640 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200641 LY_CHECK_GOTO(rc = lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200642 } else {
643 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200644 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200645 }
646 }
647
648 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200649 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
650 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200651 if (first_pos) {
652 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200653 LY_CHECK_GOTO(rc = lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200654 } else {
655 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200656 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200657 }
658 }
659
Michal Vaskoe78faec2021-04-08 17:24:43 +0200660 /* position */
661 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
662 if (second_pos) {
663 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
664 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200665 rc = LY_EMEM;
666 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200667 }
668 } else {
669 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200670 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200671 }
672 }
673
674 /* orig-position */
675 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
676 if (first_pos) {
677 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
678 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200679 rc = LY_EMEM;
680 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200681 }
682 } else {
683 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200684 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200685 }
686 }
687
Michal Vaskod59035b2020-07-08 12:00:06 +0200688 /*
689 * update our instances - apply the change
690 */
691 if (*op == LYD_DIFF_OP_CREATE) {
692 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200693 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200694 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
695 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
696 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
697 }
698 LY_ARRAY_INCREMENT(userord_item->inst);
699 userord_item->inst[second_pos] = second;
700
701 } else if (*op == LYD_DIFF_OP_DELETE) {
702 /* remove the instance */
703 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
704 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
705 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
706 }
707 LY_ARRAY_DECREMENT(userord_item->inst);
708
709 } else if (*op == LYD_DIFF_OP_REPLACE) {
710 /* move the instances */
711 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
712 (first_pos - second_pos) * sizeof *userord_item->inst);
713 userord_item->inst[second_pos] = first;
714 }
715
Michal Vaskof9b052a2022-06-08 10:26:53 +0200716cleanup:
717 if (rc) {
718 free(*value);
719 *value = NULL;
720 free(*orig_value);
721 *orig_value = NULL;
722 free(*key);
723 *key = NULL;
724 free(*orig_key);
725 *orig_key = NULL;
726 free(*position);
727 *position = NULL;
728 free(*orig_position);
729 *orig_position = NULL;
730 }
731 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200732}
733
734/**
735 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
736 * lists/leaf-lists.
737 *
738 * @param[in] first Node from the first tree, can be NULL (on create).
739 * @param[in] second Node from the second tree, can be NULL (on delete).
740 * @param[in] options Diff options.
741 * @param[out] op Operation.
742 * @param[out] orig_default Original default metadata.
743 * @param[out] orig_value Original value metadata.
744 * @return LY_SUCCESS on success,
745 * @return LY_ENOT if there is no change to be added into diff,
746 * @return LY_ERR value on other errors.
747 */
748static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200749lyd_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 +0200750 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200751{
752 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200753 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200754
755 assert(first || second);
756
757 *orig_default = NULL;
758 *orig_value = NULL;
759
760 schema = first ? first->schema : second->schema;
761 assert(!lysc_is_userordered(schema));
762
763 /* learn operation first */
764 if (!second) {
765 *op = LYD_DIFF_OP_DELETE;
766 } else if (!first) {
767 *op = LYD_DIFF_OP_CREATE;
768 } else {
769 switch (schema->nodetype) {
770 case LYS_CONTAINER:
771 case LYS_RPC:
772 case LYS_ACTION:
773 case LYS_NOTIF:
774 /* no changes */
775 return LY_ENOT;
776 case LYS_LIST:
777 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200778 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200779 /* default flag change */
780 *op = LYD_DIFF_OP_NONE;
781 } else {
782 /* no changes */
783 return LY_ENOT;
784 }
785 break;
786 case LYS_LEAF:
787 case LYS_ANYXML:
788 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200789 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200790 /* different values */
791 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200792 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200793 /* default flag change */
794 *op = LYD_DIFF_OP_NONE;
795 } else {
796 /* no changes */
797 return LY_ENOT;
798 }
799 break;
800 default:
801 LOGINT_RET(schema->module->ctx);
802 }
803 }
804
805 /*
806 * set each attribute correctly based on the operation and node type
807 */
808
809 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100810 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200811 if (first->flags & LYD_DEFAULT) {
812 *orig_default = "true";
813 } else {
814 *orig_default = "false";
815 }
816 }
817
818 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100819 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
820 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200821 str_val = lyd_get_value(first);
822 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100823 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
824 } else {
825 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
826 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200827 }
828
829 return LY_SUCCESS;
830}
831
832/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200833 * @brief Find a matching instance of a node in a data tree.
834 *
835 * @param[in] siblings Siblings to search in.
836 * @param[in] target Target node to search for.
837 * @param[in] defaults Whether to consider (or ignore) default values.
Michal Vasko271d2e32023-01-31 15:43:19 +0100838 * @param[in,out] dup_inst_ht Duplicate instance cache.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200839 * @param[out] match Found match, NULL if no matching node found.
840 * @return LY_ERR value.
841 */
842static LY_ERR
843lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vasko8efac242023-03-30 08:24:56 +0200844 struct ly_ht **dup_inst_ht, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200845{
Michal Vasko2bd856f2022-12-02 14:03:29 +0100846 LY_ERR r;
847
Michal Vaskodb91fc32023-05-02 14:39:40 +0200848 if (!target->schema) {
849 /* try to find the same opaque node */
850 r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match);
851 } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200852 /* try to find the exact instance */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100853 r = lyd_find_sibling_first(siblings, target, match);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200854 } else {
855 /* try to simply find the node, there cannot be more instances */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100856 r = lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
857 }
858 if (r && (r != LY_ENOTFOUND)) {
859 return r;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200860 }
861
Michal Vaskod7c048c2021-05-18 16:12:55 +0200862 /* update match as needed */
Michal Vasko271d2e32023-01-31 15:43:19 +0100863 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200864
865 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
866 /* ignore default nodes */
867 *match = NULL;
868 }
869 return LY_SUCCESS;
870}
871
872/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200873 * @brief Perform diff for all siblings at certain depth, recursively.
874 *
875 * For user-ordered lists/leaf-lists a specific structure is used for storing
876 * the current order. The idea is to apply all the generated diff changes
877 * virtually on the first tree so that we can continue to generate correct
878 * changes after some were already generated.
879 *
880 * The algorithm then uses second tree position-based changes with a before
881 * (preceding) item anchor.
882 *
883 * Example:
884 *
885 * Virtual first tree leaf-list order:
886 * 1 2 [3] 4 5
887 *
888 * Second tree leaf-list order:
889 * 1 2 [5] 3 4
890 *
891 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
892 * match - they do not - move nodes so that the 3rd position node is final ->
893 * -> move node 5 to the 3rd position -> move node 5 after node 2.
894 *
895 * Required properties:
896 * Stored operations (move) should not be affected by later operations -
897 * - would cause a redundantly long list of operations, possibly inifinite.
898 *
899 * Implemenation justification:
900 * First, all delete operations and only then move/create operations are stored.
901 * Also, preceding anchor is used and after each iteration another node is
902 * at its final position. That results in the invariant that all preceding
903 * nodes are final and will not be changed by the later operations, meaning
904 * they can safely be used as anchors for the later operations.
905 *
906 * @param[in] first First tree first sibling.
907 * @param[in] second Second tree first sibling.
908 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200909 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200910 * @param[in,out] diff Diff to append to.
911 * @return LY_ERR value.
912 */
913static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200914lyd_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 +0200915 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200916{
917 LY_ERR ret = LY_SUCCESS;
918 const struct lyd_node *iter_first, *iter_second;
919 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100920 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vasko8efac242023-03-30 08:24:56 +0200921 struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200922 LY_ARRAY_COUNT_TYPE u;
923 enum lyd_diff_op op;
924 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200925 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200926
Michal Vaskod59035b2020-07-08 12:00:06 +0200927 /* compare first tree to the second tree - delete, replace, none */
928 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200929 if (!iter_first->schema) {
930 continue;
931 }
932
Michal Vaskod59035b2020-07-08 12:00:06 +0200933 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200934 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200935 /* skip default nodes */
936 continue;
937 }
938
Michal Vaskoe78faec2021-04-08 17:24:43 +0200939 /* find a match in the second tree */
940 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
941 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200942
943 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100944 /* get (create) userord entry */
945 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
946 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
947
Michal Vaskoe78faec2021-04-08 17:24:43 +0200948 /* we are handling only user-ordered node delete now */
949 if (!match_second) {
950 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100951 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
952 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200953
954 /* there must be changes, it is deleted */
955 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100956 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
957 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200958
959 free(orig_value);
960 free(key);
961 free(value);
962 free(position);
963 free(orig_key);
964 free(orig_position);
965 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200966 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200967 } else {
968 /* get all the attributes */
969 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
970
971 /* add into diff if there are any changes */
972 if (!ret) {
973 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200974 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200975 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100976 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200977 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200978 }
979
980 free(orig_value);
981 LY_CHECK_GOTO(ret, cleanup);
982 } else if (ret == LY_ENOT) {
983 ret = LY_SUCCESS;
984 } else {
985 goto cleanup;
986 }
987 }
988
989 /* check descendants, if any, recursively */
990 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200991 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
992 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200993 }
994
995 if (nosiblings) {
996 break;
997 }
998 }
999
1000 /* reset all cached positions */
1001 LY_ARRAY_FOR(userord, u) {
1002 userord[u].pos = 0;
1003 }
1004
1005 /* compare second tree to the first tree - create, user-ordered move */
1006 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +02001007 if (!iter_second->schema) {
1008 continue;
1009 }
1010
Michal Vaskod59035b2020-07-08 12:00:06 +02001011 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +02001012 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001013 /* skip default nodes */
1014 continue;
1015 }
1016
Michal Vaskoe78faec2021-04-08 17:24:43 +02001017 /* find a match in the first tree */
1018 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
1019 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +02001020
1021 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +01001022 /* get userord entry */
Michal Vaskoaa51cc52022-12-06 09:57:11 +01001023 userord_item = lyd_diff_userord_get(match_first, iter_second->schema, &userord);
Michal Vasko5da938a2022-03-01 09:19:02 +01001024 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
1025
Michal Vaskod59035b2020-07-08 12:00:06 +02001026 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +01001027 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +02001028 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001029
1030 /* add into diff if there are any changes */
1031 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001032 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
1033 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001034
1035 free(orig_value);
1036 free(key);
1037 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001038 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001039 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001040 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001041 LY_CHECK_GOTO(ret, cleanup);
1042 } else if (ret == LY_ENOT) {
1043 ret = LY_SUCCESS;
1044 } else {
1045 goto cleanup;
1046 }
1047 } else if (!match_first) {
1048 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001049 LY_CHECK_GOTO(ret = lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +02001050
1051 /* there must be changes, it is created */
1052 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001053 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001054
1055 free(orig_value);
1056 LY_CHECK_GOTO(ret, cleanup);
1057 } /* else was handled */
1058
1059 if (nosiblings) {
1060 break;
1061 }
1062 }
1063
1064cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001065 lyd_dup_inst_free(dup_inst_first);
1066 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +02001067 LY_ARRAY_FOR(userord, u) {
1068 LY_ARRAY_FREE(userord[u].inst);
1069 }
1070 LY_ARRAY_FREE(userord);
1071 return ret;
1072}
1073
Michal Vasko3a41dff2020-07-15 14:30:28 +02001074static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +01001075lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
1076 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001077{
1078 const struct ly_ctx *ctx;
1079
1080 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1081
1082 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001083 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +02001084 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001085 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +02001086 } else {
1087 ctx = NULL;
1088 }
1089
1090 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
1091 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
1092 return LY_EINVAL;
1093 }
1094
1095 *diff = NULL;
1096
Michal Vasko3a41dff2020-07-15 14:30:28 +02001097 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
1098}
1099
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001100LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02001101lyd_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 +02001102{
1103 return lyd_diff(first, second, options, 1, diff);
1104}
1105
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001106LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02001107lyd_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 +02001108{
1109 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001110}
1111
1112/**
Michal Vaskod59035b2020-07-08 12:00:06 +02001113 * @brief Insert a diff node into a data tree.
1114 *
1115 * @param[in,out] first_node First sibling of the data tree.
1116 * @param[in] parent_node Data tree sibling parent node.
1117 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001118 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
1119 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +02001120 * @return err_info, NULL on success.
1121 */
1122static LY_ERR
1123lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +02001124 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +02001125{
1126 LY_ERR ret;
1127 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001128 uint32_t pos, anchor_pos;
1129 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +02001130
1131 assert(new_node);
1132
1133 if (!*first_node) {
1134 if (!parent_node) {
1135 /* no parent or siblings */
1136 *first_node = new_node;
1137 return LY_SUCCESS;
1138 }
1139
1140 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001141 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001142 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +02001143 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +02001144 return LY_EINVAL;
1145 }
Michal Vaskob104f112020-07-17 09:54:54 +02001146 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001147 }
1148
Michal Vasko9e685082021-01-29 14:49:09 +01001149 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001150
Michal Vaskod59035b2020-07-08 12:00:06 +02001151 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +02001152 /* simple insert */
1153 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001154 }
1155
Michal Vaskoe78faec2021-04-08 17:24:43 +02001156 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001157 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001158 if (lysc_is_dup_inst_list(new_node->schema)) {
1159 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001160 if (!anchor_pos) {
1161 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1162 return LY_EINVAL;
1163 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001164
1165 found = 0;
1166 pos = 1;
1167 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1168 if (pos == anchor_pos) {
1169 found = 1;
1170 break;
1171 }
1172 ++pos;
1173 }
1174 if (!found) {
1175 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1176 new_node->schema->name);
1177 return LY_EINVAL;
1178 }
1179 } else {
1180 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1181 if (ret == LY_ENOTFOUND) {
1182 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1183 new_node->schema->name);
1184 return LY_EINVAL;
1185 } else if (ret) {
1186 return ret;
1187 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001188 }
1189
1190 /* insert after */
1191 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1192 assert(new_node->prev == anchor);
1193 if (*first_node == new_node) {
1194 *first_node = anchor;
1195 }
1196 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001197 /* find the first instance */
1198 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1199 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001200
Michal Vaskoea7d3232022-04-19 12:01:36 +02001201 if (anchor) {
1202 /* insert before the first instance */
1203 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1204 if ((*first_node)->prev->next) {
1205 assert(!new_node->prev->next);
1206 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001207 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001208 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001209 /* insert anywhere */
1210 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001211 }
1212 }
1213
1214 return LY_SUCCESS;
1215}
1216
1217/**
1218 * @brief Apply diff subtree on data tree nodes, recursively.
1219 *
1220 * @param[in,out] first_node First sibling of the data tree.
1221 * @param[in] parent_node Parent of the first sibling.
1222 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001223 * @param[in] diff_cb Optional diff callback.
1224 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001225 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001226 * @return LY_ERR value.
1227 */
1228static LY_ERR
1229lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Michal Vasko8efac242023-03-30 08:24:56 +02001230 lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001231{
1232 LY_ERR ret;
1233 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001234 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001235 enum lyd_diff_op op;
1236 struct lyd_meta *meta;
Michal Vasko8efac242023-03-30 08:24:56 +02001237 struct ly_ht *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001238 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001239
1240 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001241 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001242
Michal Vaskoe6323f62020-07-09 15:49:02 +02001243 /* handle specific user-ordered (leaf-)lists operations separately */
1244 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1245 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001246 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001247 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001248 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001249 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001250 /* duplicate the node */
1251 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001252 }
1253
Michal Vaskoe78faec2021-04-08 17:24:43 +02001254 /* get "key", "value", or "position" metadata string value */
1255 if (lysc_is_dup_inst_list(diff_node->schema)) {
1256 meta_str = "yang:position";
1257 } else if (diff_node->schema->nodetype == LYS_LIST) {
1258 meta_str = "yang:key";
1259 } else {
1260 meta_str = "yang:value";
1261 }
1262 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001263 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001264 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001265
Michal Vaskod59035b2020-07-08 12:00:06 +02001266 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001267 if (str_val[0]) {
1268 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001269 } else {
1270 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1271 }
1272 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001273 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001274 lyd_free_tree(match);
1275 }
1276 return ret;
1277 }
1278
1279 goto next_iter_r;
1280 }
1281
1282 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001283 switch (op) {
1284 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001285 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001286 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001287 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001288
1289 if (match->schema->nodetype & LYD_NODE_TERM) {
1290 /* special case of only dflt flag change */
1291 if (diff_node->flags & LYD_DEFAULT) {
1292 match->flags |= LYD_DEFAULT;
1293 } else {
1294 match->flags &= ~LYD_DEFAULT;
1295 }
1296 } else {
1297 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001298 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001299 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1300 LYD_NAME(diff_node));
1301 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001302 }
1303 }
1304 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001305 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001306 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001307 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001308
1309 /* insert it at the end */
1310 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001311 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001312 if (match->flags & LYD_EXT) {
Michal Vasko193dacd2022-10-13 08:43:05 +02001313 ret = lyplg_ext_insert(parent_node, match);
Michal Vasko19175b62022-04-01 09:17:07 +02001314 } else {
1315 ret = lyd_insert_child(parent_node, match);
1316 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001317 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001318 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001319 }
1320 if (ret) {
1321 lyd_free_tree(match);
1322 return ret;
1323 }
1324
1325 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001326 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001327 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001328 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001329 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001330
1331 /* remove it */
1332 if ((match == *first_node) && !match->parent) {
1333 assert(!parent_node);
1334 /* we have removed the top-level node */
1335 *first_node = (*first_node)->next;
1336 }
1337 lyd_free_tree(match);
1338
1339 /* we are not going recursively in this case, the whole subtree was already deleted */
1340 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001341 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001342 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1343 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1344 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1345 return LY_EINVAL;
1346 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001347
1348 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001349 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001350 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001351
Michal Vaskobaba84e2021-02-05 16:33:30 +01001352 /* update the value */
1353 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001354 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001355 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001356 } else {
1357 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vasko26bbb272022-08-02 14:54:33 +02001358
Michal Vaskoe78faec2021-04-08 17:24:43 +02001359 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001360 }
1361
1362 /* with flags */
1363 match->flags = diff_node->flags;
1364 break;
1365 default:
1366 LOGINT_RET(ctx);
1367 }
1368
1369next_iter_r:
1370 if (diff_cb) {
1371 /* call callback */
1372 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1373 }
1374
1375 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001376 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001377 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001378 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1379 if (ret) {
1380 break;
1381 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001382 }
1383
Michal Vaskod7c048c2021-05-18 16:12:55 +02001384 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001385 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001386}
1387
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001388LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001389lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001390 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001391{
1392 const struct lyd_node *root;
Michal Vasko8efac242023-03-30 08:24:56 +02001393 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001394 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001395
1396 LY_LIST_FOR(diff, root) {
1397 if (mod && (lyd_owner_module(root) != mod)) {
1398 /* skip data nodes from different modules */
1399 continue;
1400 }
1401
1402 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001403 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1404 if (ret) {
1405 break;
1406 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001407 }
1408
Michal Vaskod7c048c2021-05-18 16:12:55 +02001409 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001410 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001411}
1412
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001413LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001414lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001415{
1416 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1417}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001418
1419/**
1420 * @brief Update operations on a diff node when the new operation is NONE.
1421 *
1422 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001423 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001424 * @param[in] src_diff Current source diff node.
1425 * @return LY_ERR value.
1426 */
1427static LY_ERR
1428lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1429{
1430 switch (cur_op) {
1431 case LYD_DIFF_OP_NONE:
1432 case LYD_DIFF_OP_CREATE:
1433 case LYD_DIFF_OP_REPLACE:
1434 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1435 /* NONE on a term means only its dflt flag was changed */
1436 diff_match->flags &= ~LYD_DEFAULT;
1437 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1438 }
1439 break;
1440 default:
1441 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001442 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1443 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001444 }
1445
1446 return LY_SUCCESS;
1447}
1448
1449/**
Michal Vaskoe6323f62020-07-09 15:49:02 +02001450 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001451 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001452 *
1453 * @param[in] node Node to change.
1454 * @param[in] op Operation to set.
1455 * @return LY_ERR value.
1456 */
1457static LY_ERR
1458lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1459{
Michal Vaskodb91fc32023-05-02 14:39:40 +02001460 lyd_diff_del_meta(node, "operation");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001461
Michal Vaskodb91fc32023-05-02 14:39:40 +02001462 if (node->schema) {
stewegd4cde642024-02-21 08:34:16 +01001463 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), LYD_NEW_VAL_STORE_ONLY, NULL);
Michal Vaskodb91fc32023-05-02 14:39:40 +02001464 } else {
1465 return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001466 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001467}
1468
1469/**
1470 * @brief Update operations on a diff node when the new operation is REPLACE.
1471 *
1472 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001473 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001474 * @param[in] src_diff Current source diff node.
1475 * @return LY_ERR value.
1476 */
1477static LY_ERR
1478lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1479{
1480 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001481 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001482 struct lyd_meta *meta;
1483 const struct lys_module *mod;
1484 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001485 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001486
1487 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001488 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001489 assert(mod);
1490
1491 switch (cur_op) {
1492 case LYD_DIFF_OP_REPLACE:
1493 case LYD_DIFF_OP_CREATE:
1494 switch (diff_match->schema->nodetype) {
1495 case LYS_LIST:
1496 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001497 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001498 * keep orig_key/orig_value (only replace oper) and replace key/value */
1499 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001500 if (lysc_is_dup_inst_list(diff_match->schema)) {
1501 meta_name = "position";
1502 } else if (diff_match->schema->nodetype == LYS_LIST) {
1503 meta_name = "key";
1504 } else {
1505 meta_name = "value";
1506 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001507
1508 lyd_diff_del_meta(diff_match, meta_name);
1509 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001510 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001511 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001512 break;
1513 case LYS_LEAF:
1514 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001515 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001516 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1517 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001518 }
1519
Michal Vaskoe6323f62020-07-09 15:49:02 +02001520 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001521 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001522 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001523 }
1524
Michal Vasko8caadab2020-11-05 17:38:15 +01001525 if (cur_op == LYD_DIFF_OP_REPLACE) {
1526 /* compare values whether there is any change at all */
1527 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001528 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001529 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001530 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1531 if (!ret) {
1532 /* values are the same, remove orig-value meta and set oper to NONE */
1533 lyd_free_meta_single(meta);
1534 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1535 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001536 }
1537
1538 /* modify the default flag */
1539 diff_match->flags &= ~LYD_DEFAULT;
1540 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1541 break;
1542 case LYS_ANYXML:
1543 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001544 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001545 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1546 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001547 }
1548
1549 /* modify the node value */
1550 any = (struct lyd_node_any *)src_diff;
1551 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1552 break;
1553 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001554 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001555 }
1556 break;
1557 case LYD_DIFF_OP_NONE:
Michal Vasko2a141b32023-12-14 13:58:14 +01001558 switch (diff_match->schema->nodetype) {
1559 case LYS_LIST:
1560 /* it is moved now */
1561 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001562
Michal Vasko2a141b32023-12-14 13:58:14 +01001563 /* change the operation */
1564 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001565
Michal Vasko2a141b32023-12-14 13:58:14 +01001566 /* set orig-meta and meta */
1567 if (lysc_is_dup_inst_list(diff_match->schema)) {
1568 meta_name = "position";
1569 orig_meta_name = "orig-position";
1570 } else {
1571 meta_name = "key";
1572 orig_meta_name = "orig-key";
1573 }
1574
1575 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
1576 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
1577 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1578
1579 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
1580 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
1581 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1582 break;
1583 case LYS_LEAF:
1584 /* only dflt flag changed, now value changed as well, update the operation */
1585 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1586
1587 /* modify the node value */
1588 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
1589 LOGINT_RET(LYD_CTX(src_diff));
1590 }
1591 break;
1592 default:
1593 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001594 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001595 break;
1596 default:
1597 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001598 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1599 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001600 }
1601
1602 return LY_SUCCESS;
1603}
1604
1605/**
1606 * @brief Update operations in a diff node when the new operation is CREATE.
1607 *
Michal Vasko4f331162023-08-15 10:07:44 +02001608 * @param[in,out] diff_match Node from the diff, may be replaced.
1609 * @param[in,out] diff Diff root node, may be updated.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001610 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001611 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001612 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001613 * @return LY_ERR value.
1614 */
1615static LY_ERR
Michal Vasko4f331162023-08-15 10:07:44 +02001616lyd_diff_merge_create(struct lyd_node **diff_match, struct lyd_node **diff, enum lyd_diff_op cur_op,
1617 const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001618{
Michal Vasko4f331162023-08-15 10:07:44 +02001619 struct lyd_node *child, *src_dup, *to_free = NULL;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001620 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001621 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001622 const char *meta_name, *orig_meta_name;
1623 struct lyd_meta *meta, *orig_meta;
Michal Vasko4f331162023-08-15 10:07:44 +02001624 const struct ly_ctx *ctx = LYD_CTX(*diff_match);
1625 LY_ERR r;
1626
1627 /* create operation is valid only for data nodes */
1628 LY_CHECK_ERR_RET(!src_diff->schema, LOGINT(ctx), LY_EINT);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001629
1630 switch (cur_op) {
1631 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001632 /* remember current flags */
Michal Vasko4f331162023-08-15 10:07:44 +02001633 trg_flags = (*diff_match)->flags;
Michal Vasko871a0252020-11-11 18:35:24 +01001634
Michal Vasko4f331162023-08-15 10:07:44 +02001635 if (lysc_is_userordered(src_diff->schema)) {
1636 assert((*diff_match)->schema);
1637
Michal Vaskoe78faec2021-04-08 17:24:43 +02001638 /* get anchor metadata */
Michal Vasko4f331162023-08-15 10:07:44 +02001639 if (lysc_is_dup_inst_list((*diff_match)->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001640 meta_name = "yang:position";
1641 orig_meta_name = "yang:orig-position";
Michal Vasko4f331162023-08-15 10:07:44 +02001642 } else if ((*diff_match)->schema->nodetype == LYS_LIST) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001643 meta_name = "yang:key";
1644 orig_meta_name = "yang:orig-key";
1645 } else {
1646 meta_name = "yang:value";
1647 orig_meta_name = "yang:orig-value";
1648 }
1649 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001650 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko4f331162023-08-15 10:07:44 +02001651 orig_meta = lyd_find_meta((*diff_match)->meta, NULL, orig_meta_name);
1652 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, *diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001653
1654 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1655 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001656 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001657 /* deleted + created at another position -> operation REPLACE */
Michal Vasko4f331162023-08-15 10:07:44 +02001658 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001659
1660 /* add anchor metadata */
Michal Vasko4f331162023-08-15 10:07:44 +02001661 LY_CHECK_RET(lyd_dup_meta_single(meta, *diff_match, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001662 } else {
1663 /* deleted + created at the same position -> operation NONE */
Michal Vasko4f331162023-08-15 10:07:44 +02001664 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001665
1666 /* delete anchor metadata */
1667 lyd_free_meta_single(orig_meta);
1668 }
Michal Vasko4f331162023-08-15 10:07:44 +02001669 } else if (src_diff->schema->nodetype == LYS_LEAF) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001670 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1671 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
Michal Vasko4f331162023-08-15 10:07:44 +02001672 sleaf = (struct lysc_node_leaf *)src_diff->schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001673 }
1674
aPiecek0a6705b2023-11-14 14:20:58 +01001675 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(ctx, sleaf->dflt,
Radek Krejci55c4bd22021-04-26 08:09:04 +02001676 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001677 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
Michal Vasko4f331162023-08-15 10:07:44 +02001678 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
1679 } else if (!lyd_compare_single(*diff_match, src_diff, 0)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001680 /* deleted + created -> operation NONE */
Michal Vasko4f331162023-08-15 10:07:44 +02001681 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
1682 } else if ((*diff_match)->schema) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001683 /* we deleted it, but it was created with a different value -> operation REPLACE */
Michal Vasko4f331162023-08-15 10:07:44 +02001684 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001685
1686 /* current value is the previous one (meta) */
Michal Vasko4f331162023-08-15 10:07:44 +02001687 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-value",
stewegd4cde642024-02-21 08:34:16 +01001688 lyd_get_value(*diff_match), LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001689
1690 /* update the value itself */
Michal Vasko4f331162023-08-15 10:07:44 +02001691 LY_CHECK_RET(lyd_change_term(*diff_match, lyd_get_value(src_diff)));
1692 } else {
1693 /* also operation REPLACE but we need to change an opaque node into a data node */
1694 LY_CHECK_RET(lyd_dup_single(src_diff, (*diff_match)->parent, LYD_DUP_NO_META | LYD_DUP_WITH_FLAGS, &src_dup));
1695 if (!(*diff_match)->parent) {
1696 /* will always be inserted before diff_match, which is opaque */
1697 LY_CHECK_RET(lyd_insert_sibling(*diff_match, src_dup, diff));
1698 }
1699 to_free = *diff_match;
1700 *diff_match = src_dup;
1701
stewegd4cde642024-02-21 08:34:16 +01001702 r = lyd_new_meta(ctx, src_dup, NULL, "yang:orig-value", lyd_get_value(to_free), LYD_NEW_VAL_STORE_ONLY, NULL);
Michal Vasko4f331162023-08-15 10:07:44 +02001703 lyd_free_tree(to_free);
1704 LY_CHECK_RET(r);
stewegd4cde642024-02-21 08:34:16 +01001705 LY_CHECK_RET(lyd_new_meta(ctx, src_dup, NULL, "yang:operation", lyd_diff_op2str(LYD_DIFF_OP_REPLACE), LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001706 }
1707 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001708 /* deleted + created -> operation NONE */
Michal Vasko4f331162023-08-15 10:07:44 +02001709 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001710 }
1711
Michal Vasko4f331162023-08-15 10:07:44 +02001712 assert((*diff_match)->schema);
1713 if ((*diff_match)->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001714 /* add orig-dflt metadata */
Michal Vasko4f331162023-08-15 10:07:44 +02001715 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-default",
stewegd4cde642024-02-21 08:34:16 +01001716 trg_flags & LYD_DEFAULT ? "true" : "false", LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vasko4b715ca2020-11-11 18:39:57 +01001717
Michal Vaskoe6323f62020-07-09 15:49:02 +02001718 /* update dflt flag itself */
Michal Vasko4f331162023-08-15 10:07:44 +02001719 (*diff_match)->flags &= ~LYD_DEFAULT;
1720 (*diff_match)->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001721 }
1722
1723 /* but the operation of its children should remain DELETE */
Michal Vasko4f331162023-08-15 10:07:44 +02001724 LY_LIST_FOR(lyd_child_no_keys(*diff_match), child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001725 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001726 }
1727 break;
1728 default:
1729 /* create and replace operations are not valid */
Michal Vasko4f331162023-08-15 10:07:44 +02001730 LOGERR_MERGEOP(LYD_CTX(src_diff), *diff_match, cur_op, LYD_DIFF_OP_CREATE);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001731 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001732 }
1733
1734 return LY_SUCCESS;
1735}
1736
1737/**
1738 * @brief Update operations on a diff node when the new operation is DELETE.
1739 *
1740 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001741 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001742 * @param[in] src_diff Current source diff node.
1743 * @return LY_ERR value.
1744 */
1745static LY_ERR
1746lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1747{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001748 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001749 struct lyd_meta *meta;
Michal Vaskoa24a55f2024-01-17 16:07:36 +01001750 struct lyd_attr *attr;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001751 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001752 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoa24a55f2024-01-17 16:07:36 +01001753 LY_ERR r;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001754
1755 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001756 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 +02001757
1758 switch (cur_op) {
1759 case LYD_DIFF_OP_CREATE:
1760 /* it was created, but then deleted -> set NONE operation */
1761 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1762
1763 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1764 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001765 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
stewegd4cde642024-02-21 08:34:16 +01001766 src_diff->flags & LYD_DEFAULT ? "true" : "false", LYD_NEW_VAL_STORE_ONLY, NULL));
Michal Vaskoa24a55f2024-01-17 16:07:36 +01001767 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001768 break;
1769 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001770 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001771 if (lysc_is_userordered(diff_match->schema)) {
1772 if (lysc_is_dup_inst_list(diff_match->schema)) {
1773 meta_name = "position";
1774 } else if (diff_match->schema->nodetype == LYS_LIST) {
1775 meta_name = "key";
1776 } else {
1777 meta_name = "value";
1778 }
1779 } else {
1780 assert(diff_match->schema->nodetype == LYS_LEAF);
1781
1782 /* switch value for the original one */
1783 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001784 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001785 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001786 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1787 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001788 }
1789
1790 /* switch default for the original one, then remove the meta */
1791 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001792 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001793 diff_match->flags &= ~LYD_DEFAULT;
1794 if (meta->value.boolean) {
1795 diff_match->flags |= LYD_DEFAULT;
1796 }
1797 lyd_free_meta_single(meta);
1798
1799 meta_name = "orig-value";
1800 }
1801 lyd_diff_del_meta(diff_match, meta_name);
1802
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001803 /* it was being changed, but should be deleted instead -> set DELETE operation */
1804 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1805 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001806 case LYD_DIFF_OP_NONE:
1807 /* it was not modified, but should be deleted -> set DELETE operation */
1808 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001809 break;
1810 default:
1811 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001812 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1813 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001814 }
1815
Michal Vaskoa24a55f2024-01-17 16:07:36 +01001816 if (!lysc_is_dup_inst_list(diff_match->schema)) {
1817 /* keep operation without one for descendants that are yet to be merged */
1818 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1819 lyd_diff_find_meta(child, "operation", &meta, &attr);
1820 if (meta || attr) {
1821 continue;
1822 }
1823
1824 if (!child->schema) {
1825 r = lyd_find_sibling_opaq_next(lyd_child(src_diff), LYD_NAME(child), NULL);
1826 } else if (child->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
1827 r = lyd_find_sibling_first(lyd_child(src_diff), child, NULL);
1828 } else {
1829 r = lyd_find_sibling_val(lyd_child(src_diff), child->schema, NULL, 0, NULL);
1830 }
1831 if (!r) {
1832 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1833 } else if (r != LY_ENOTFOUND) {
1834 return r;
1835 }
1836 }
1837 } /* else key-less list, for which all the descendants act as keys */
1838
Michal Vaskoe6323f62020-07-09 15:49:02 +02001839 return LY_SUCCESS;
1840}
1841
1842/**
1843 * @brief Check whether this diff node is redundant (does not change data).
1844 *
1845 * @param[in] diff Diff node.
1846 * @return 0 if not, non-zero if it is.
1847 */
1848static int
1849lyd_diff_is_redundant(struct lyd_node *diff)
1850{
1851 enum lyd_diff_op op;
1852 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1853 struct lyd_node *child;
1854 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001855 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001856
1857 assert(diff);
1858
Michal Vaskoe78faec2021-04-08 17:24:43 +02001859 if (lysc_is_dup_inst_list(diff->schema)) {
1860 /* all descendants are keys */
1861 child = NULL;
1862 } else {
1863 child = lyd_child_no_keys(diff);
1864 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001865 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001866 assert(mod);
1867
1868 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001869 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001870
1871 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001872 /* get metadata names */
1873 if (lysc_is_dup_inst_list(diff->schema)) {
1874 meta_name = "position";
1875 orig_meta_name = "orig-position";
1876 } else if (diff->schema->nodetype == LYS_LIST) {
1877 meta_name = "key";
1878 orig_meta_name = "orig-key";
1879 } else {
1880 meta_name = "value";
1881 orig_meta_name = "orig-value";
1882 }
1883
Michal Vaskoe6323f62020-07-09 15:49:02 +02001884 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001885 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1886 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001887 assert(orig_val_meta && val_meta);
1888
1889 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1890 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001891 lyd_free_meta_single(orig_val_meta);
1892 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001893 if (child) {
1894 /* change operation to NONE, we have siblings */
1895 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1896 return 0;
1897 }
1898
1899 /* redundant node, BUT !!
1900 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1901 * because the data that this is applied on should not change for the diff lifetime.
1902 * However, when we are merging 2 diffs, this conversion is actually lossy because
1903 * if the data change, the move operation can also change its meaning. In this specific
1904 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1905 */
1906 return 1;
1907 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001908 } else if (op == LYD_DIFF_OP_NONE) {
1909 if (!diff->schema) {
1910 /* opaque node with none must be redundant */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001911 return 1;
1912 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001913
1914 if (diff->schema->nodetype & LYD_NODE_TERM) {
1915 /* check whether at least the default flags are different */
1916 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1917 assert(meta);
1918 str = lyd_get_meta_value(meta);
1919
1920 /* if previous and current dflt flags are the same, this node is redundant */
1921 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1922 return 1;
1923 }
1924 return 0;
1925 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001926 }
1927
1928 if (!child && (op == LYD_DIFF_OP_NONE)) {
1929 return 1;
1930 }
1931
1932 return 0;
1933}
1934
1935/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001936 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001937 *
1938 * @param[in] src_diff Source diff node.
1939 * @param[in] diff_parent Current sysrepo diff parent.
1940 * @param[in] diff_cb Optional diff callback.
1941 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001942 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001943 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001944 * @param[in,out] diff Diff root node.
1945 * @return LY_ERR value.
1946 */
1947static LY_ERR
1948lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vasko8efac242023-03-30 08:24:56 +02001949 struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001950{
1951 LY_ERR ret = LY_SUCCESS;
1952 struct lyd_node *child, *diff_node = NULL;
1953 enum lyd_diff_op src_op, cur_op;
Michal Vasko8efac242023-03-30 08:24:56 +02001954 struct ly_ht *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001955
1956 /* get source node operation */
1957 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1958
1959 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001960 LY_CHECK_RET(lyd_diff_find_match(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, 1, dup_inst, &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001961
1962 if (diff_node) {
1963 /* get target (current) operation */
1964 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1965
1966 /* merge operations */
1967 switch (src_op) {
1968 case LYD_DIFF_OP_REPLACE:
1969 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1970 break;
1971 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001972 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001973 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001974 goto add_diff;
1975 }
1976
Michal Vasko4f331162023-08-15 10:07:44 +02001977 ret = lyd_diff_merge_create(&diff_node, diff, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001978 break;
1979 case LYD_DIFF_OP_DELETE:
1980 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1981 break;
1982 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001983 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1984 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001985 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1986 break;
1987 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001988 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001989 }
1990 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001991 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001992 return ret;
1993 }
1994
1995 if (diff_cb) {
1996 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001997 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001998 }
1999
2000 /* update diff parent */
2001 diff_parent = diff_node;
2002
Michal Vaskoe78faec2021-04-08 17:24:43 +02002003 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
2004 * so there is nothing to merge for these "keys" */
2005 if (!lysc_is_dup_inst_list(src_diff->schema)) {
2006 /* merge src_diff recursively */
2007 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
2008 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
2009 if (ret) {
2010 break;
2011 }
2012 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02002013 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002014 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02002015 }
2016 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01002017add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02002018 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02002019 if ((src_diff->flags & LYD_EXT) && diff_parent) {
2020 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
aPieceka9369d22024-02-15 11:53:29 +01002021 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_LYDS, &diff_node));
Michal Vasko9ad76852022-07-12 10:18:03 +02002022 } else {
2023 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
aPieceka9369d22024-02-15 11:53:29 +01002024 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_LYDS, &diff_node));
Michal Vasko9ad76852022-07-12 10:18:03 +02002025 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02002026
2027 /* insert node into diff if not already */
2028 if (!diff_parent) {
aPieceka9369d22024-02-15 11:53:29 +01002029 lyd_diff_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02002030 }
2031
2032 /* update operation */
2033 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
2034
2035 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01002036 /* call callback with no source diff node since it was duplicated and just added */
2037 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02002038 }
2039
2040 /* update diff parent */
2041 diff_parent = diff_node;
2042 }
2043
2044 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02002045 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02002046 if (diff_parent == *diff) {
2047 *diff = (*diff)->next;
2048 }
2049 lyd_free_tree(diff_parent);
2050 }
2051
2052 return LY_SUCCESS;
2053}
2054
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002055LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02002056lyd_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 +01002057 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02002058{
2059 const struct lyd_node *src_root;
Michal Vasko8efac242023-03-30 08:24:56 +02002060 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02002061 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02002062
2063 LY_LIST_FOR(src_diff, src_root) {
2064 if (mod && (lyd_owner_module(src_root) != mod)) {
2065 /* skip data nodes from different modules */
2066 continue;
2067 }
2068
2069 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002070 LY_CHECK_GOTO(ret = lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, &dup_inst, options, diff), cleanup);
Michal Vaskoe6323f62020-07-09 15:49:02 +02002071 }
2072
Michal Vaskoe78faec2021-04-08 17:24:43 +02002073cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02002074 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002075 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02002076}
2077
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002078LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02002079lyd_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 +01002080 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02002081{
Michal Vaskoe78faec2021-04-08 17:24:43 +02002082 LY_ERR ret;
Michal Vasko8efac242023-03-30 08:24:56 +02002083 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02002084
Michal Vasko04f85912020-08-07 12:14:58 +02002085 if (!src_sibling) {
2086 return LY_SUCCESS;
2087 }
2088
Michal Vaskoe78faec2021-04-08 17:24:43 +02002089 ret = lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, &dup_inst, options, diff_first);
Michal Vaskod7c048c2021-05-18 16:12:55 +02002090 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002091 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02002092}
2093
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002094LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01002095lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02002096{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01002097 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02002098}
Michal Vasko4231fb62020-07-13 13:54:47 +02002099
2100static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01002101lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02002102{
2103 LY_ERR ret = LY_SUCCESS;
2104 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002105 const char *val1 = NULL;
2106 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002107 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002108
Michal Vaskobaba84e2021-02-05 16:33:30 +01002109 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
2110
2111 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01002112 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002113
2114 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002115 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02002116
2117 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01002118 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002119 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01002120 } else {
2121 LY_CHECK_RET(lyd_any_value_str(node, &val2));
2122 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002123
2124 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01002125 flags = node->flags;
2126 if (node->schema->nodetype == LYS_LEAF) {
2127 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
2128 } else {
2129 union lyd_any_value anyval = {.str = val1};
Michal Vasko26bbb272022-08-02 14:54:33 +02002130
Michal Vaskobaba84e2021-02-05 16:33:30 +01002131 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
2132 }
2133 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002134 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
2135
2136cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002137 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002138 return ret;
2139}
2140
2141static LY_ERR
2142lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
2143{
2144 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002145 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02002146
2147 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01002148 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02002149
2150 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002151 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02002152 flag1 = LYD_DEFAULT;
2153 } else {
2154 flag1 = 0;
2155 }
2156
2157 /* current default */
2158 flag2 = node->flags & LYD_DEFAULT;
2159
Michal Vasko610e93b2020-11-09 20:58:32 +01002160 if (flag1 == flag2) {
2161 /* no default state change so nothing to reverse */
2162 return LY_SUCCESS;
2163 }
2164
Michal Vasko4231fb62020-07-13 13:54:47 +02002165 /* switch defaults */
2166 node->flags &= ~LYD_DEFAULT;
2167 node->flags |= flag1;
2168 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
2169
2170 return LY_SUCCESS;
2171}
2172
2173static LY_ERR
2174lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
2175{
2176 LY_ERR ret = LY_SUCCESS;
2177 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002178 const char *val1 = NULL;
2179 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02002180
2181 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002182 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002183
2184 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002185 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002186
2187 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002188 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02002189
2190 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002191 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02002192
2193 /* switch values */
2194 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2195 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2196
2197cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002198 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002199 return ret;
2200}
2201
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002202/**
2203 * @brief Remove specific operation from all the nodes in a subtree.
2204 *
2205 * @param[in] diff Diff subtree to process.
2206 * @param[in] op Only expected operation.
2207 * @return LY_ERR value.
2208 */
2209static LY_ERR
2210lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2211{
2212 struct lyd_node *elem;
2213 struct lyd_meta *meta;
2214
2215 LYD_TREE_DFS_BEGIN(diff, elem) {
2216 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2217 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002218 LY_CHECK_ERR_RET(lyd_diff_str2op(lyd_get_meta_value(meta)) != op, LOGINT(LYD_CTX(diff)), LY_EINT);
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002219 lyd_free_meta_single(meta);
2220 }
2221
2222 LYD_TREE_DFS_END(diff, elem);
2223 }
2224
2225 return LY_SUCCESS;
2226}
2227
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002228LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002229lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002230{
2231 LY_ERR ret = LY_SUCCESS;
2232 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002233 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002234 enum lyd_diff_op op;
2235
2236 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2237
2238 if (!src_diff) {
2239 *diff = NULL;
2240 return LY_SUCCESS;
2241 }
2242
2243 /* duplicate diff */
aPieceka9369d22024-02-15 11:53:29 +01002244 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE | LYD_DUP_NO_LYDS, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002245
2246 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002247 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2248 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002249
2250 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002251 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002252 /* skip all keys */
2253 if (!lysc_is_key(elem->schema)) {
2254 /* find operation attribute, if any */
2255 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002256
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002257 switch (op) {
2258 case LYD_DIFF_OP_CREATE:
2259 /* reverse create to delete */
2260 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002261
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002262 /* check all the children for the same operation, nothing else is expected */
2263 LY_LIST_FOR(lyd_child(elem), iter) {
2264 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2265 }
2266
Michal Vasko9e070522021-03-05 14:00:14 +01002267 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002268 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002269 case LYD_DIFF_OP_DELETE:
2270 /* reverse delete to create */
2271 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002272
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002273 /* check all the children for the same operation, nothing else is expected */
2274 LY_LIST_FOR(lyd_child(elem), iter) {
2275 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2276 }
2277
Michal Vasko9e070522021-03-05 14:00:14 +01002278 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002279 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002280 case LYD_DIFF_OP_REPLACE:
2281 switch (elem->schema->nodetype) {
2282 case LYS_LEAF:
2283 /* leaf value change */
2284 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2285 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2286 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002287 case LYS_ANYXML:
2288 case LYS_ANYDATA:
2289 /* any value change */
2290 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2291 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002292 case LYS_LEAFLIST:
2293 /* leaf-list move */
2294 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002295 if (lysc_is_dup_inst_list(elem->schema)) {
2296 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2297 } else {
2298 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2299 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002300 break;
2301 case LYS_LIST:
2302 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002303 if (lysc_is_dup_inst_list(elem->schema)) {
2304 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2305 } else {
2306 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2307 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002308 break;
2309 default:
2310 LOGINT(LYD_CTX(src_diff));
2311 ret = LY_EINT;
2312 goto cleanup;
2313 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002314 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002315 case LYD_DIFF_OP_NONE:
2316 switch (elem->schema->nodetype) {
2317 case LYS_LEAF:
2318 case LYS_LEAFLIST:
2319 /* default flag change */
2320 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2321 break;
2322 default:
2323 /* nothing to do */
2324 break;
2325 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002326 break;
2327 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002328 }
2329
Michal Vasko56daf732020-08-10 10:57:18 +02002330 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002331 }
2332 }
2333
2334cleanup:
2335 if (ret) {
2336 lyd_free_siblings(*diff);
2337 *diff = NULL;
2338 }
2339 return ret;
2340}