blob: e522c9e64389e97f6be0daa1b323dbecb3398c31 [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 */
157 LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_val, 0, NULL), cleanup);
158
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
Michal Vaskod59035b2020-07-08 12:00:06 +0200285LY_ERR
286lyd_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 +0200287 const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
288 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200289{
Michal Vaskocffc3f92022-06-15 07:57:24 +0200290 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
Michal Vaskod59035b2020-07-08 12:00:06 +0200291 const struct lyd_node *parent = NULL;
Michal Vaskofb51a842023-06-20 08:50:24 +0200292 enum lyd_diff_op cur_op;
Michal Vaskod4525672023-06-20 09:15:42 +0200293 struct lyd_meta *meta;
Michal Vasko9af40912023-06-21 08:03:43 +0200294 uint32_t diff_opts;
Michal Vaskod59035b2020-07-08 12:00:06 +0200295
296 assert(diff);
297
Michal Vasko53d48422020-11-13 18:02:29 +0100298 /* replace leaf always needs orig-default and orig-value */
299 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
300
301 /* create on userord needs key/value */
302 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200303 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100304 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200305 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100306
307 /* move on userord needs both key and orig-key/value and orig-value */
308 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200309 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100310 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200311 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
312 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100313
Michal Vaskod59035b2020-07-08 12:00:06 +0200314 /* find the first existing parent */
315 siblings = *diff;
Michal Vaskofb51a842023-06-20 08:50:24 +0200316 do {
Michal Vaskod59035b2020-07-08 12:00:06 +0200317 /* find next node parent */
318 parent = node;
319 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100320 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200321 }
Michal Vaskofb51a842023-06-20 08:50:24 +0200322
323 if (lysc_is_dup_inst_list(parent->schema)) {
324 /* assume it never exists, we are not able to distinguish whether it does or not */
325 match = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200326 break;
327 }
328
329 /* check whether it exists in the diff */
330 if (lyd_find_sibling_first(siblings, parent, &match)) {
331 break;
332 }
333
334 /* another parent found */
335 diff_parent = match;
336
337 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200338 siblings = lyd_child_no_keys(match);
Michal Vaskofb51a842023-06-20 08:50:24 +0200339 } while (parent != node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200340
Michal Vaskofb51a842023-06-20 08:50:24 +0200341 if (match && (parent == node)) {
342 /* special case when there is already an operation on our descendant */
343 assert(!lyd_diff_get_op(diff_parent, &cur_op) && (cur_op == LYD_DIFF_OP_NONE));
344 (void)cur_op;
345
Michal Vasko9af40912023-06-21 08:03:43 +0200346 /* move it to the end where it is expected (matters for user-ordered lists) */
347 if (lysc_is_userordered(diff_parent->schema)) {
348 for (elem = diff_parent; elem->next && (elem->next->schema == elem->schema); elem = elem->next) {}
349 if (elem != diff_parent) {
350 LY_CHECK_RET(lyd_insert_after(elem, diff_parent));
351 }
352 }
353
Michal Vaskod4525672023-06-20 09:15:42 +0200354 /* will be replaced by the new operation but keep the current op for descendants */
Michal Vaskofb51a842023-06-20 08:50:24 +0200355 lyd_diff_del_meta(diff_parent, "operation");
Michal Vaskod4525672023-06-20 09:15:42 +0200356 LY_LIST_FOR(lyd_child_no_keys(diff_parent), elem) {
357 lyd_diff_find_meta(elem, "operation", &meta, NULL);
358 if (meta) {
359 /* explicit operation, fine */
360 continue;
361 }
362
363 /* set the none operation */
364 LY_CHECK_RET(lyd_new_meta(NULL, elem, NULL, "yang:operation", "none", 0, NULL));
365 }
366
Michal Vaskofb51a842023-06-20 08:50:24 +0200367 dup = diff_parent;
Michal Vasko695a7f22023-02-14 10:02:10 +0100368 } else {
Michal Vasko2be7c8a2023-06-21 08:04:21 +0200369 diff_opts = LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS;
370 if ((op != LYD_DIFF_OP_REPLACE) || !lysc_is_userordered(node->schema) || (node->schema->flags & LYS_CONFIG_R)) {
371 /* move applies only to the user-ordered list, no descendants */
372 diff_opts |= LYD_DUP_RECURSIVE;
373 }
374
Michal Vaskofb51a842023-06-20 08:50:24 +0200375 /* duplicate the subtree (and connect to the diff if possible) */
376 if (diff_parent) {
377 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 +0200378 diff_opts, &dup));
Michal Vaskofb51a842023-06-20 08:50:24 +0200379 } else {
Michal Vasko2be7c8a2023-06-21 08:04:21 +0200380 LY_CHECK_RET(lyd_dup_single(node, NULL, diff_opts, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200381 }
Michal Vaskofb51a842023-06-20 08:50:24 +0200382
383 /* find the first duplicated parent */
384 if (!diff_parent) {
385 diff_parent = lyd_parent(dup);
386 while (diff_parent && diff_parent->parent) {
387 diff_parent = lyd_parent(diff_parent);
388 }
389 } else {
390 diff_parent = dup;
391 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
392 diff_parent = lyd_parent(diff_parent);
393 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200394 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200395
Michal Vaskofb51a842023-06-20 08:50:24 +0200396 /* no parent existed, must be manually connected */
397 if (!diff_parent) {
398 /* there actually was no parent to duplicate */
399 lyd_insert_sibling(*diff, dup, diff);
400 } else if (!diff_parent->parent) {
401 lyd_insert_sibling(*diff, diff_parent, diff);
402 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200403
Michal Vaskofb51a842023-06-20 08:50:24 +0200404 /* add parent operation, if any */
405 if (diff_parent && (diff_parent != dup)) {
406 LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
407 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200408 }
409
410 /* add subtree operation */
Michal Vasko2e552792022-11-02 12:15:31 +0100411 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200412
Michal Vaskocffc3f92022-06-15 07:57:24 +0200413 if (op == LYD_DIFF_OP_CREATE) {
414 /* all nested user-ordered (leaf-)lists need special metadata for create op */
415 LYD_TREE_DFS_BEGIN(dup, elem) {
416 if ((elem != dup) && lysc_is_userordered(elem->schema)) {
417 LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
418 }
419 LYD_TREE_DFS_END(dup, elem);
420 }
421 }
422
Michal Vaskod59035b2020-07-08 12:00:06 +0200423 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200424 if (orig_default) {
Michal Vasko2e552792022-11-02 12:15:31 +0100425 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200426 }
427
428 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200429 if (orig_value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100430 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200431 }
432
433 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200434 if (key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100435 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200436 }
437
438 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200439 if (value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100440 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200441 }
442
Michal Vaskoe78faec2021-04-08 17:24:43 +0200443 /* position */
444 if (position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100445 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200446 }
447
Michal Vaskod59035b2020-07-08 12:00:06 +0200448 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200449 if (orig_key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100450 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200451 }
452
Michal Vaskoe78faec2021-04-08 17:24:43 +0200453 /* orig-position */
454 if (orig_position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100455 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200456 }
457
Michal Vaskod59035b2020-07-08 12:00:06 +0200458 return LY_SUCCESS;
459}
460
461/**
462 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
463 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100464 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200465 * @param[in] schema Schema node of the list/leaf-list.
466 * @param[in,out] userord Sized array of userord items.
467 * @return Userord item for all the user-ordered list/leaf-list instances.
468 */
469static struct lyd_diff_userord *
470lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
471{
472 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200473 struct lyd_node *iter;
474 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200475 LY_ARRAY_COUNT_TYPE u;
476
477 LY_ARRAY_FOR(*userord, u) {
478 if ((*userord)[u].schema == schema) {
479 return &(*userord)[u];
480 }
481 }
482
483 /* it was not added yet, add it now */
484 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
485
486 item->schema = schema;
487 item->pos = 0;
488 item->inst = NULL;
489
490 /* store all the instance pointers in the current order */
491 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200492 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
493 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
494 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200495 }
496 }
497
498 return item;
499}
500
501/**
502 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
503 * lists/leaf-lists.
504 *
505 * @param[in] first Node from the first tree, can be NULL (on create).
506 * @param[in] second Node from the second tree, can be NULL (on delete).
507 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100508 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200509 * @param[out] op Operation.
510 * @param[out] orig_default Original default metadata.
511 * @param[out] value Value metadata.
512 * @param[out] orig_value Original value metadata
513 * @param[out] key Key metadata.
514 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200515 * @param[out] position Position metadata.
516 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200517 * @return LY_SUCCESS on success,
518 * @return LY_ENOT if there is no change to be added into diff,
519 * @return LY_ERR value on other errors.
520 */
521static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200522lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100523 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200524 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200525{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200526 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200527 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200528 size_t buflen, bufused;
Michal Vaskoee9b9482023-06-19 13:17:48 +0200529 uint32_t first_pos, second_pos, comp_opts;
Michal Vaskod59035b2020-07-08 12:00:06 +0200530
531 assert(first || second);
532
533 *orig_default = NULL;
534 *value = NULL;
535 *orig_value = NULL;
536 *key = NULL;
537 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200538 *position = NULL;
539 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200540
541 schema = first ? first->schema : second->schema;
542 assert(lysc_is_userordered(schema));
543
Michal Vaskod59035b2020-07-08 12:00:06 +0200544 /* find user-ordered first position */
545 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200546 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200547 if (userord_item->inst[first_pos] == first) {
548 break;
549 }
550 }
551 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
552 } else {
553 first_pos = 0;
554 }
555
Michal Vaskoe78faec2021-04-08 17:24:43 +0200556 /* prepare position of the next instance */
557 second_pos = userord_item->pos++;
558
Michal Vaskod59035b2020-07-08 12:00:06 +0200559 /* learn operation first */
560 if (!second) {
561 *op = LYD_DIFF_OP_DELETE;
562 } else if (!first) {
563 *op = LYD_DIFF_OP_CREATE;
564 } else {
Michal Vaskoee9b9482023-06-19 13:17:48 +0200565 comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0;
566 if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200567 /* in first, there is a different instance on the second position, we are going to move 'first' node */
568 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200569 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200570 /* default flag change */
571 *op = LYD_DIFF_OP_NONE;
572 } else {
573 /* no changes */
574 return LY_ENOT;
575 }
576 }
577
578 /*
579 * set each attribute correctly based on the operation and node type
580 */
581
582 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100583 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200584 if (first->flags & LYD_DEFAULT) {
585 *orig_default = "true";
586 } else {
587 *orig_default = "false";
588 }
589 }
590
591 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200592 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
593 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200594 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200595 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200596 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200597 } else {
598 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200599 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200600 }
601 }
602
603 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200604 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
605 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200606 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200607 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200608 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200609 } else {
610 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200611 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200612 }
613 }
614
615 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200616 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
617 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200618 if (second_pos) {
619 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200620 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 +0200621 } else {
622 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200623 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200624 }
625 }
626
627 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200628 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
629 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200630 if (first_pos) {
631 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200632 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 +0200633 } else {
634 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200635 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200636 }
637 }
638
Michal Vaskoe78faec2021-04-08 17:24:43 +0200639 /* position */
640 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
641 if (second_pos) {
642 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
643 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200644 rc = LY_EMEM;
645 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200646 }
647 } else {
648 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200649 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200650 }
651 }
652
653 /* orig-position */
654 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
655 if (first_pos) {
656 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
657 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200658 rc = LY_EMEM;
659 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200660 }
661 } else {
662 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200663 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200664 }
665 }
666
Michal Vaskod59035b2020-07-08 12:00:06 +0200667 /*
668 * update our instances - apply the change
669 */
670 if (*op == LYD_DIFF_OP_CREATE) {
671 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200672 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200673 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
674 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
675 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
676 }
677 LY_ARRAY_INCREMENT(userord_item->inst);
678 userord_item->inst[second_pos] = second;
679
680 } else if (*op == LYD_DIFF_OP_DELETE) {
681 /* remove the instance */
682 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
683 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
684 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
685 }
686 LY_ARRAY_DECREMENT(userord_item->inst);
687
688 } else if (*op == LYD_DIFF_OP_REPLACE) {
689 /* move the instances */
690 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
691 (first_pos - second_pos) * sizeof *userord_item->inst);
692 userord_item->inst[second_pos] = first;
693 }
694
Michal Vaskof9b052a2022-06-08 10:26:53 +0200695cleanup:
696 if (rc) {
697 free(*value);
698 *value = NULL;
699 free(*orig_value);
700 *orig_value = NULL;
701 free(*key);
702 *key = NULL;
703 free(*orig_key);
704 *orig_key = NULL;
705 free(*position);
706 *position = NULL;
707 free(*orig_position);
708 *orig_position = NULL;
709 }
710 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200711}
712
713/**
714 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
715 * lists/leaf-lists.
716 *
717 * @param[in] first Node from the first tree, can be NULL (on create).
718 * @param[in] second Node from the second tree, can be NULL (on delete).
719 * @param[in] options Diff options.
720 * @param[out] op Operation.
721 * @param[out] orig_default Original default metadata.
722 * @param[out] orig_value Original value metadata.
723 * @return LY_SUCCESS on success,
724 * @return LY_ENOT if there is no change to be added into diff,
725 * @return LY_ERR value on other errors.
726 */
727static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200728lyd_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 +0200729 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200730{
731 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200732 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200733
734 assert(first || second);
735
736 *orig_default = NULL;
737 *orig_value = NULL;
738
739 schema = first ? first->schema : second->schema;
740 assert(!lysc_is_userordered(schema));
741
742 /* learn operation first */
743 if (!second) {
744 *op = LYD_DIFF_OP_DELETE;
745 } else if (!first) {
746 *op = LYD_DIFF_OP_CREATE;
747 } else {
748 switch (schema->nodetype) {
749 case LYS_CONTAINER:
750 case LYS_RPC:
751 case LYS_ACTION:
752 case LYS_NOTIF:
753 /* no changes */
754 return LY_ENOT;
755 case LYS_LIST:
756 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200757 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200758 /* default flag change */
759 *op = LYD_DIFF_OP_NONE;
760 } else {
761 /* no changes */
762 return LY_ENOT;
763 }
764 break;
765 case LYS_LEAF:
766 case LYS_ANYXML:
767 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200768 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200769 /* different values */
770 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200771 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200772 /* default flag change */
773 *op = LYD_DIFF_OP_NONE;
774 } else {
775 /* no changes */
776 return LY_ENOT;
777 }
778 break;
779 default:
780 LOGINT_RET(schema->module->ctx);
781 }
782 }
783
784 /*
785 * set each attribute correctly based on the operation and node type
786 */
787
788 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100789 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200790 if (first->flags & LYD_DEFAULT) {
791 *orig_default = "true";
792 } else {
793 *orig_default = "false";
794 }
795 }
796
797 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100798 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
799 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200800 str_val = lyd_get_value(first);
801 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100802 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
803 } else {
804 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
805 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200806 }
807
808 return LY_SUCCESS;
809}
810
811/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200812 * @brief Find a matching instance of a node in a data tree.
813 *
814 * @param[in] siblings Siblings to search in.
815 * @param[in] target Target node to search for.
816 * @param[in] defaults Whether to consider (or ignore) default values.
Michal Vasko271d2e32023-01-31 15:43:19 +0100817 * @param[in,out] dup_inst_ht Duplicate instance cache.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200818 * @param[out] match Found match, NULL if no matching node found.
819 * @return LY_ERR value.
820 */
821static LY_ERR
822lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vasko8efac242023-03-30 08:24:56 +0200823 struct ly_ht **dup_inst_ht, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200824{
Michal Vasko2bd856f2022-12-02 14:03:29 +0100825 LY_ERR r;
826
Michal Vaskodb91fc32023-05-02 14:39:40 +0200827 if (!target->schema) {
828 /* try to find the same opaque node */
829 r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match);
830 } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200831 /* try to find the exact instance */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100832 r = lyd_find_sibling_first(siblings, target, match);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200833 } else {
834 /* try to simply find the node, there cannot be more instances */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100835 r = lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
836 }
837 if (r && (r != LY_ENOTFOUND)) {
838 return r;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200839 }
840
Michal Vaskod7c048c2021-05-18 16:12:55 +0200841 /* update match as needed */
Michal Vasko271d2e32023-01-31 15:43:19 +0100842 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200843
844 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
845 /* ignore default nodes */
846 *match = NULL;
847 }
848 return LY_SUCCESS;
849}
850
851/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200852 * @brief Perform diff for all siblings at certain depth, recursively.
853 *
854 * For user-ordered lists/leaf-lists a specific structure is used for storing
855 * the current order. The idea is to apply all the generated diff changes
856 * virtually on the first tree so that we can continue to generate correct
857 * changes after some were already generated.
858 *
859 * The algorithm then uses second tree position-based changes with a before
860 * (preceding) item anchor.
861 *
862 * Example:
863 *
864 * Virtual first tree leaf-list order:
865 * 1 2 [3] 4 5
866 *
867 * Second tree leaf-list order:
868 * 1 2 [5] 3 4
869 *
870 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
871 * match - they do not - move nodes so that the 3rd position node is final ->
872 * -> move node 5 to the 3rd position -> move node 5 after node 2.
873 *
874 * Required properties:
875 * Stored operations (move) should not be affected by later operations -
876 * - would cause a redundantly long list of operations, possibly inifinite.
877 *
878 * Implemenation justification:
879 * First, all delete operations and only then move/create operations are stored.
880 * Also, preceding anchor is used and after each iteration another node is
881 * at its final position. That results in the invariant that all preceding
882 * nodes are final and will not be changed by the later operations, meaning
883 * they can safely be used as anchors for the later operations.
884 *
885 * @param[in] first First tree first sibling.
886 * @param[in] second Second tree first sibling.
887 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200888 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200889 * @param[in,out] diff Diff to append to.
890 * @return LY_ERR value.
891 */
892static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200893lyd_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 +0200894 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200895{
896 LY_ERR ret = LY_SUCCESS;
897 const struct lyd_node *iter_first, *iter_second;
898 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100899 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vasko8efac242023-03-30 08:24:56 +0200900 struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200901 LY_ARRAY_COUNT_TYPE u;
902 enum lyd_diff_op op;
903 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200904 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200905
Michal Vaskod59035b2020-07-08 12:00:06 +0200906 /* compare first tree to the second tree - delete, replace, none */
907 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200908 if (!iter_first->schema) {
909 continue;
910 }
911
Michal Vaskod59035b2020-07-08 12:00:06 +0200912 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200913 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200914 /* skip default nodes */
915 continue;
916 }
917
Michal Vaskoe78faec2021-04-08 17:24:43 +0200918 /* find a match in the second tree */
919 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
920 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200921
922 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100923 /* get (create) userord entry */
924 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
925 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
926
Michal Vaskoe78faec2021-04-08 17:24:43 +0200927 /* we are handling only user-ordered node delete now */
928 if (!match_second) {
929 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100930 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
931 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200932
933 /* there must be changes, it is deleted */
934 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100935 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
936 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200937
938 free(orig_value);
939 free(key);
940 free(value);
941 free(position);
942 free(orig_key);
943 free(orig_position);
944 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200945 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200946 } else {
947 /* get all the attributes */
948 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
949
950 /* add into diff if there are any changes */
951 if (!ret) {
952 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200953 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200954 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100955 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200956 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200957 }
958
959 free(orig_value);
960 LY_CHECK_GOTO(ret, cleanup);
961 } else if (ret == LY_ENOT) {
962 ret = LY_SUCCESS;
963 } else {
964 goto cleanup;
965 }
966 }
967
968 /* check descendants, if any, recursively */
969 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200970 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
971 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200972 }
973
974 if (nosiblings) {
975 break;
976 }
977 }
978
979 /* reset all cached positions */
980 LY_ARRAY_FOR(userord, u) {
981 userord[u].pos = 0;
982 }
983
984 /* compare second tree to the first tree - create, user-ordered move */
985 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200986 if (!iter_second->schema) {
987 continue;
988 }
989
Michal Vaskod59035b2020-07-08 12:00:06 +0200990 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200991 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200992 /* skip default nodes */
993 continue;
994 }
995
Michal Vaskoe78faec2021-04-08 17:24:43 +0200996 /* find a match in the first tree */
997 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
998 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200999
1000 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +01001001 /* get userord entry */
Michal Vaskoaa51cc52022-12-06 09:57:11 +01001002 userord_item = lyd_diff_userord_get(match_first, iter_second->schema, &userord);
Michal Vasko5da938a2022-03-01 09:19:02 +01001003 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
1004
Michal Vaskod59035b2020-07-08 12:00:06 +02001005 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +01001006 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +02001007 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001008
1009 /* add into diff if there are any changes */
1010 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001011 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
1012 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001013
1014 free(orig_value);
1015 free(key);
1016 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001017 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001018 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001019 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001020 LY_CHECK_GOTO(ret, cleanup);
1021 } else if (ret == LY_ENOT) {
1022 ret = LY_SUCCESS;
1023 } else {
1024 goto cleanup;
1025 }
1026 } else if (!match_first) {
1027 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001028 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 +02001029
1030 /* there must be changes, it is created */
1031 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001032 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001033
1034 free(orig_value);
1035 LY_CHECK_GOTO(ret, cleanup);
1036 } /* else was handled */
1037
1038 if (nosiblings) {
1039 break;
1040 }
1041 }
1042
1043cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001044 lyd_dup_inst_free(dup_inst_first);
1045 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +02001046 LY_ARRAY_FOR(userord, u) {
1047 LY_ARRAY_FREE(userord[u].inst);
1048 }
1049 LY_ARRAY_FREE(userord);
1050 return ret;
1051}
1052
Michal Vasko3a41dff2020-07-15 14:30:28 +02001053static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +01001054lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
1055 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001056{
1057 const struct ly_ctx *ctx;
1058
1059 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1060
1061 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001062 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +02001063 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001064 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +02001065 } else {
1066 ctx = NULL;
1067 }
1068
1069 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
1070 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
1071 return LY_EINVAL;
1072 }
1073
1074 *diff = NULL;
1075
Michal Vasko3a41dff2020-07-15 14:30:28 +02001076 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
1077}
1078
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001079LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02001080lyd_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 +02001081{
1082 return lyd_diff(first, second, options, 1, diff);
1083}
1084
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001085LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02001086lyd_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 +02001087{
1088 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001089}
1090
1091/**
Michal Vaskod59035b2020-07-08 12:00:06 +02001092 * @brief Insert a diff node into a data tree.
1093 *
1094 * @param[in,out] first_node First sibling of the data tree.
1095 * @param[in] parent_node Data tree sibling parent node.
1096 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001097 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
1098 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +02001099 * @return err_info, NULL on success.
1100 */
1101static LY_ERR
1102lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +02001103 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +02001104{
1105 LY_ERR ret;
1106 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001107 uint32_t pos, anchor_pos;
1108 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +02001109
1110 assert(new_node);
1111
1112 if (!*first_node) {
1113 if (!parent_node) {
1114 /* no parent or siblings */
1115 *first_node = new_node;
1116 return LY_SUCCESS;
1117 }
1118
1119 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001120 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001121 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +02001122 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +02001123 return LY_EINVAL;
1124 }
Michal Vaskob104f112020-07-17 09:54:54 +02001125 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001126 }
1127
Michal Vasko9e685082021-01-29 14:49:09 +01001128 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001129
Michal Vaskod59035b2020-07-08 12:00:06 +02001130 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +02001131 /* simple insert */
1132 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001133 }
1134
Michal Vaskoe78faec2021-04-08 17:24:43 +02001135 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001136 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001137 if (lysc_is_dup_inst_list(new_node->schema)) {
1138 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001139 if (!anchor_pos) {
1140 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1141 return LY_EINVAL;
1142 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001143
1144 found = 0;
1145 pos = 1;
1146 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1147 if (pos == anchor_pos) {
1148 found = 1;
1149 break;
1150 }
1151 ++pos;
1152 }
1153 if (!found) {
1154 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1155 new_node->schema->name);
1156 return LY_EINVAL;
1157 }
1158 } else {
1159 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1160 if (ret == LY_ENOTFOUND) {
1161 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1162 new_node->schema->name);
1163 return LY_EINVAL;
1164 } else if (ret) {
1165 return ret;
1166 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001167 }
1168
1169 /* insert after */
1170 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1171 assert(new_node->prev == anchor);
1172 if (*first_node == new_node) {
1173 *first_node = anchor;
1174 }
1175 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001176 /* find the first instance */
1177 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1178 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001179
Michal Vaskoea7d3232022-04-19 12:01:36 +02001180 if (anchor) {
1181 /* insert before the first instance */
1182 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1183 if ((*first_node)->prev->next) {
1184 assert(!new_node->prev->next);
1185 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001186 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001187 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001188 /* insert anywhere */
1189 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001190 }
1191 }
1192
1193 return LY_SUCCESS;
1194}
1195
1196/**
1197 * @brief Apply diff subtree on data tree nodes, recursively.
1198 *
1199 * @param[in,out] first_node First sibling of the data tree.
1200 * @param[in] parent_node Parent of the first sibling.
1201 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001202 * @param[in] diff_cb Optional diff callback.
1203 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001204 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001205 * @return LY_ERR value.
1206 */
1207static LY_ERR
1208lyd_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 +02001209 lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001210{
1211 LY_ERR ret;
1212 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001213 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001214 enum lyd_diff_op op;
1215 struct lyd_meta *meta;
Michal Vasko8efac242023-03-30 08:24:56 +02001216 struct ly_ht *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001217 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001218
1219 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001220 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001221
Michal Vaskoe6323f62020-07-09 15:49:02 +02001222 /* handle specific user-ordered (leaf-)lists operations separately */
1223 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1224 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001225 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001226 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001227 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001228 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001229 /* duplicate the node */
1230 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001231 }
1232
Michal Vaskoe78faec2021-04-08 17:24:43 +02001233 /* get "key", "value", or "position" metadata string value */
1234 if (lysc_is_dup_inst_list(diff_node->schema)) {
1235 meta_str = "yang:position";
1236 } else if (diff_node->schema->nodetype == LYS_LIST) {
1237 meta_str = "yang:key";
1238 } else {
1239 meta_str = "yang:value";
1240 }
1241 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001242 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001243 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001244
Michal Vaskod59035b2020-07-08 12:00:06 +02001245 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001246 if (str_val[0]) {
1247 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001248 } else {
1249 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1250 }
1251 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001252 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001253 lyd_free_tree(match);
1254 }
1255 return ret;
1256 }
1257
1258 goto next_iter_r;
1259 }
1260
1261 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001262 switch (op) {
1263 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001264 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001265 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001266 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001267
1268 if (match->schema->nodetype & LYD_NODE_TERM) {
1269 /* special case of only dflt flag change */
1270 if (diff_node->flags & LYD_DEFAULT) {
1271 match->flags |= LYD_DEFAULT;
1272 } else {
1273 match->flags &= ~LYD_DEFAULT;
1274 }
1275 } else {
1276 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001277 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001278 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1279 LYD_NAME(diff_node));
1280 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001281 }
1282 }
1283 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001284 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001285 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001286 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001287
1288 /* insert it at the end */
1289 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001290 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001291 if (match->flags & LYD_EXT) {
Michal Vasko193dacd2022-10-13 08:43:05 +02001292 ret = lyplg_ext_insert(parent_node, match);
Michal Vasko19175b62022-04-01 09:17:07 +02001293 } else {
1294 ret = lyd_insert_child(parent_node, match);
1295 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001296 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001297 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001298 }
1299 if (ret) {
1300 lyd_free_tree(match);
1301 return ret;
1302 }
1303
1304 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001305 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001306 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001307 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001308 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001309
1310 /* remove it */
1311 if ((match == *first_node) && !match->parent) {
1312 assert(!parent_node);
1313 /* we have removed the top-level node */
1314 *first_node = (*first_node)->next;
1315 }
1316 lyd_free_tree(match);
1317
1318 /* we are not going recursively in this case, the whole subtree was already deleted */
1319 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001320 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001321 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1322 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1323 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1324 return LY_EINVAL;
1325 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001326
1327 /* 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
Michal Vaskobaba84e2021-02-05 16:33:30 +01001331 /* update the value */
1332 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001333 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001334 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001335 } else {
1336 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vasko26bbb272022-08-02 14:54:33 +02001337
Michal Vaskoe78faec2021-04-08 17:24:43 +02001338 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001339 }
1340
1341 /* with flags */
1342 match->flags = diff_node->flags;
1343 break;
1344 default:
1345 LOGINT_RET(ctx);
1346 }
1347
1348next_iter_r:
1349 if (diff_cb) {
1350 /* call callback */
1351 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1352 }
1353
1354 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001355 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001356 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001357 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1358 if (ret) {
1359 break;
1360 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001361 }
1362
Michal Vaskod7c048c2021-05-18 16:12:55 +02001363 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001364 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001365}
1366
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001367LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001368lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001369 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001370{
1371 const struct lyd_node *root;
Michal Vasko8efac242023-03-30 08:24:56 +02001372 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001373 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001374
1375 LY_LIST_FOR(diff, root) {
1376 if (mod && (lyd_owner_module(root) != mod)) {
1377 /* skip data nodes from different modules */
1378 continue;
1379 }
1380
1381 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001382 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1383 if (ret) {
1384 break;
1385 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001386 }
1387
Michal Vaskod7c048c2021-05-18 16:12:55 +02001388 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001389 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001390}
1391
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001392LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001393lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001394{
1395 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1396}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001397
1398/**
1399 * @brief Update operations on a diff node when the new operation is NONE.
1400 *
1401 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001402 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001403 * @param[in] src_diff Current source diff node.
1404 * @return LY_ERR value.
1405 */
1406static LY_ERR
1407lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1408{
1409 switch (cur_op) {
1410 case LYD_DIFF_OP_NONE:
1411 case LYD_DIFF_OP_CREATE:
1412 case LYD_DIFF_OP_REPLACE:
1413 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1414 /* NONE on a term means only its dflt flag was changed */
1415 diff_match->flags &= ~LYD_DEFAULT;
1416 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1417 }
1418 break;
1419 default:
1420 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001421 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1422 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001423 }
1424
1425 return LY_SUCCESS;
1426}
1427
1428/**
Michal Vaskoe6323f62020-07-09 15:49:02 +02001429 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001430 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001431 *
1432 * @param[in] node Node to change.
1433 * @param[in] op Operation to set.
1434 * @return LY_ERR value.
1435 */
1436static LY_ERR
1437lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1438{
Michal Vaskodb91fc32023-05-02 14:39:40 +02001439 lyd_diff_del_meta(node, "operation");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001440
Michal Vaskodb91fc32023-05-02 14:39:40 +02001441 if (node->schema) {
1442 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
1443 } else {
1444 return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001445 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001446}
1447
1448/**
1449 * @brief Update operations on a diff node when the new operation is REPLACE.
1450 *
1451 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001452 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001453 * @param[in] src_diff Current source diff node.
1454 * @return LY_ERR value.
1455 */
1456static LY_ERR
1457lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1458{
1459 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001460 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001461 struct lyd_meta *meta;
1462 const struct lys_module *mod;
1463 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001464 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001465
1466 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001467 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001468 assert(mod);
1469
1470 switch (cur_op) {
1471 case LYD_DIFF_OP_REPLACE:
1472 case LYD_DIFF_OP_CREATE:
1473 switch (diff_match->schema->nodetype) {
1474 case LYS_LIST:
1475 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001476 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001477 * keep orig_key/orig_value (only replace oper) and replace key/value */
1478 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001479 if (lysc_is_dup_inst_list(diff_match->schema)) {
1480 meta_name = "position";
1481 } else if (diff_match->schema->nodetype == LYS_LIST) {
1482 meta_name = "key";
1483 } else {
1484 meta_name = "value";
1485 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001486
1487 lyd_diff_del_meta(diff_match, meta_name);
1488 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001489 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001490 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001491 break;
1492 case LYS_LEAF:
1493 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001494 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001495 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1496 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001497 }
1498
Michal Vaskoe6323f62020-07-09 15:49:02 +02001499 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001500 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001501 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001502 }
1503
Michal Vasko8caadab2020-11-05 17:38:15 +01001504 if (cur_op == LYD_DIFF_OP_REPLACE) {
1505 /* compare values whether there is any change at all */
1506 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001507 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001508 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001509 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1510 if (!ret) {
1511 /* values are the same, remove orig-value meta and set oper to NONE */
1512 lyd_free_meta_single(meta);
1513 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1514 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001515 }
1516
1517 /* modify the default flag */
1518 diff_match->flags &= ~LYD_DEFAULT;
1519 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1520 break;
1521 case LYS_ANYXML:
1522 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001523 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001524 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1525 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001526 }
1527
1528 /* modify the node value */
1529 any = (struct lyd_node_any *)src_diff;
1530 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1531 break;
1532 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001533 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001534 }
1535 break;
1536 case LYD_DIFF_OP_NONE:
Michal Vasko2a141b32023-12-14 13:58:14 +01001537 switch (diff_match->schema->nodetype) {
1538 case LYS_LIST:
1539 /* it is moved now */
1540 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001541
Michal Vasko2a141b32023-12-14 13:58:14 +01001542 /* change the operation */
1543 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001544
Michal Vasko2a141b32023-12-14 13:58:14 +01001545 /* set orig-meta and meta */
1546 if (lysc_is_dup_inst_list(diff_match->schema)) {
1547 meta_name = "position";
1548 orig_meta_name = "orig-position";
1549 } else {
1550 meta_name = "key";
1551 orig_meta_name = "orig-key";
1552 }
1553
1554 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
1555 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
1556 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1557
1558 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
1559 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
1560 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1561 break;
1562 case LYS_LEAF:
1563 /* only dflt flag changed, now value changed as well, update the operation */
1564 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1565
1566 /* modify the node value */
1567 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
1568 LOGINT_RET(LYD_CTX(src_diff));
1569 }
1570 break;
1571 default:
1572 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001573 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001574 break;
1575 default:
1576 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001577 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1578 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001579 }
1580
1581 return LY_SUCCESS;
1582}
1583
1584/**
1585 * @brief Update operations in a diff node when the new operation is CREATE.
1586 *
Michal Vasko4f331162023-08-15 10:07:44 +02001587 * @param[in,out] diff_match Node from the diff, may be replaced.
1588 * @param[in,out] diff Diff root node, may be updated.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001589 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001590 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001591 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001592 * @return LY_ERR value.
1593 */
1594static LY_ERR
Michal Vasko4f331162023-08-15 10:07:44 +02001595lyd_diff_merge_create(struct lyd_node **diff_match, struct lyd_node **diff, enum lyd_diff_op cur_op,
1596 const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001597{
Michal Vasko4f331162023-08-15 10:07:44 +02001598 struct lyd_node *child, *src_dup, *to_free = NULL;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001599 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001600 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001601 const char *meta_name, *orig_meta_name;
1602 struct lyd_meta *meta, *orig_meta;
Michal Vasko4f331162023-08-15 10:07:44 +02001603 const struct ly_ctx *ctx = LYD_CTX(*diff_match);
1604 LY_ERR r;
1605
1606 /* create operation is valid only for data nodes */
1607 LY_CHECK_ERR_RET(!src_diff->schema, LOGINT(ctx), LY_EINT);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001608
1609 switch (cur_op) {
1610 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001611 /* remember current flags */
Michal Vasko4f331162023-08-15 10:07:44 +02001612 trg_flags = (*diff_match)->flags;
Michal Vasko871a0252020-11-11 18:35:24 +01001613
Michal Vasko4f331162023-08-15 10:07:44 +02001614 if (lysc_is_userordered(src_diff->schema)) {
1615 assert((*diff_match)->schema);
1616
Michal Vaskoe78faec2021-04-08 17:24:43 +02001617 /* get anchor metadata */
Michal Vasko4f331162023-08-15 10:07:44 +02001618 if (lysc_is_dup_inst_list((*diff_match)->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001619 meta_name = "yang:position";
1620 orig_meta_name = "yang:orig-position";
Michal Vasko4f331162023-08-15 10:07:44 +02001621 } else if ((*diff_match)->schema->nodetype == LYS_LIST) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001622 meta_name = "yang:key";
1623 orig_meta_name = "yang:orig-key";
1624 } else {
1625 meta_name = "yang:value";
1626 orig_meta_name = "yang:orig-value";
1627 }
1628 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001629 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko4f331162023-08-15 10:07:44 +02001630 orig_meta = lyd_find_meta((*diff_match)->meta, NULL, orig_meta_name);
1631 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, *diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001632
1633 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1634 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001635 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001636 /* deleted + created at another position -> operation REPLACE */
Michal Vasko4f331162023-08-15 10:07:44 +02001637 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001638
1639 /* add anchor metadata */
Michal Vasko4f331162023-08-15 10:07:44 +02001640 LY_CHECK_RET(lyd_dup_meta_single(meta, *diff_match, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001641 } else {
1642 /* deleted + created at the same position -> operation NONE */
Michal Vasko4f331162023-08-15 10:07:44 +02001643 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001644
1645 /* delete anchor metadata */
1646 lyd_free_meta_single(orig_meta);
1647 }
Michal Vasko4f331162023-08-15 10:07:44 +02001648 } else if (src_diff->schema->nodetype == LYS_LEAF) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001649 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1650 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
Michal Vasko4f331162023-08-15 10:07:44 +02001651 sleaf = (struct lysc_node_leaf *)src_diff->schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001652 }
1653
Radek Krejci55c4bd22021-04-26 08:09:04 +02001654 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1655 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001656 /* 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 +02001657 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
1658 } else if (!lyd_compare_single(*diff_match, src_diff, 0)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001659 /* deleted + created -> operation NONE */
Michal Vasko4f331162023-08-15 10:07:44 +02001660 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
1661 } else if ((*diff_match)->schema) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001662 /* we deleted it, but it was created with a different value -> operation REPLACE */
Michal Vasko4f331162023-08-15 10:07:44 +02001663 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001664
1665 /* current value is the previous one (meta) */
Michal Vasko4f331162023-08-15 10:07:44 +02001666 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-value",
1667 lyd_get_value(*diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001668
1669 /* update the value itself */
Michal Vasko4f331162023-08-15 10:07:44 +02001670 LY_CHECK_RET(lyd_change_term(*diff_match, lyd_get_value(src_diff)));
1671 } else {
1672 /* also operation REPLACE but we need to change an opaque node into a data node */
1673 LY_CHECK_RET(lyd_dup_single(src_diff, (*diff_match)->parent, LYD_DUP_NO_META | LYD_DUP_WITH_FLAGS, &src_dup));
1674 if (!(*diff_match)->parent) {
1675 /* will always be inserted before diff_match, which is opaque */
1676 LY_CHECK_RET(lyd_insert_sibling(*diff_match, src_dup, diff));
1677 }
1678 to_free = *diff_match;
1679 *diff_match = src_dup;
1680
1681 r = lyd_new_meta(ctx, src_dup, NULL, "yang:orig-value", lyd_get_value(to_free), 0, NULL);
1682 lyd_free_tree(to_free);
1683 LY_CHECK_RET(r);
1684 LY_CHECK_RET(lyd_new_meta(ctx, src_dup, NULL, "yang:operation", lyd_diff_op2str(LYD_DIFF_OP_REPLACE), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001685 }
1686 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001687 /* deleted + created -> operation NONE */
Michal Vasko4f331162023-08-15 10:07:44 +02001688 LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001689 }
1690
Michal Vasko4f331162023-08-15 10:07:44 +02001691 assert((*diff_match)->schema);
1692 if ((*diff_match)->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001693 /* add orig-dflt metadata */
Michal Vasko4f331162023-08-15 10:07:44 +02001694 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-default",
Michal Vasko4b715ca2020-11-11 18:39:57 +01001695 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1696
Michal Vaskoe6323f62020-07-09 15:49:02 +02001697 /* update dflt flag itself */
Michal Vasko4f331162023-08-15 10:07:44 +02001698 (*diff_match)->flags &= ~LYD_DEFAULT;
1699 (*diff_match)->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001700 }
1701
1702 /* but the operation of its children should remain DELETE */
Michal Vasko4f331162023-08-15 10:07:44 +02001703 LY_LIST_FOR(lyd_child_no_keys(*diff_match), child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001704 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001705 }
1706 break;
1707 default:
1708 /* create and replace operations are not valid */
Michal Vasko4f331162023-08-15 10:07:44 +02001709 LOGERR_MERGEOP(LYD_CTX(src_diff), *diff_match, cur_op, LYD_DIFF_OP_CREATE);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001710 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001711 }
1712
1713 return LY_SUCCESS;
1714}
1715
1716/**
1717 * @brief Update operations on a diff node when the new operation is DELETE.
1718 *
1719 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001720 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001721 * @param[in] src_diff Current source diff node.
1722 * @return LY_ERR value.
1723 */
1724static LY_ERR
1725lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1726{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001727 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001728 struct lyd_meta *meta;
Michal Vaskoa24a55f2024-01-17 16:07:36 +01001729 struct lyd_attr *attr;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001730 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001731 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoa24a55f2024-01-17 16:07:36 +01001732 LY_ERR r;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001733
1734 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001735 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 +02001736
1737 switch (cur_op) {
1738 case LYD_DIFF_OP_CREATE:
1739 /* it was created, but then deleted -> set NONE operation */
1740 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1741
1742 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1743 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001744 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
Michal Vaskof933e662023-12-05 09:56:30 +01001745 src_diff->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoa24a55f2024-01-17 16:07:36 +01001746 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001747 break;
1748 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001749 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001750 if (lysc_is_userordered(diff_match->schema)) {
1751 if (lysc_is_dup_inst_list(diff_match->schema)) {
1752 meta_name = "position";
1753 } else if (diff_match->schema->nodetype == LYS_LIST) {
1754 meta_name = "key";
1755 } else {
1756 meta_name = "value";
1757 }
1758 } else {
1759 assert(diff_match->schema->nodetype == LYS_LEAF);
1760
1761 /* switch value for the original one */
1762 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001763 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001764 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001765 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1766 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001767 }
1768
1769 /* switch default for the original one, then remove the meta */
1770 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001771 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001772 diff_match->flags &= ~LYD_DEFAULT;
1773 if (meta->value.boolean) {
1774 diff_match->flags |= LYD_DEFAULT;
1775 }
1776 lyd_free_meta_single(meta);
1777
1778 meta_name = "orig-value";
1779 }
1780 lyd_diff_del_meta(diff_match, meta_name);
1781
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001782 /* it was being changed, but should be deleted instead -> set DELETE operation */
1783 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1784 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001785 case LYD_DIFF_OP_NONE:
1786 /* it was not modified, but should be deleted -> set DELETE operation */
1787 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001788 break;
1789 default:
1790 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001791 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1792 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001793 }
1794
Michal Vaskoa24a55f2024-01-17 16:07:36 +01001795 if (!lysc_is_dup_inst_list(diff_match->schema)) {
1796 /* keep operation without one for descendants that are yet to be merged */
1797 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1798 lyd_diff_find_meta(child, "operation", &meta, &attr);
1799 if (meta || attr) {
1800 continue;
1801 }
1802
1803 if (!child->schema) {
1804 r = lyd_find_sibling_opaq_next(lyd_child(src_diff), LYD_NAME(child), NULL);
1805 } else if (child->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
1806 r = lyd_find_sibling_first(lyd_child(src_diff), child, NULL);
1807 } else {
1808 r = lyd_find_sibling_val(lyd_child(src_diff), child->schema, NULL, 0, NULL);
1809 }
1810 if (!r) {
1811 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1812 } else if (r != LY_ENOTFOUND) {
1813 return r;
1814 }
1815 }
1816 } /* else key-less list, for which all the descendants act as keys */
1817
Michal Vaskoe6323f62020-07-09 15:49:02 +02001818 return LY_SUCCESS;
1819}
1820
1821/**
1822 * @brief Check whether this diff node is redundant (does not change data).
1823 *
1824 * @param[in] diff Diff node.
1825 * @return 0 if not, non-zero if it is.
1826 */
1827static int
1828lyd_diff_is_redundant(struct lyd_node *diff)
1829{
1830 enum lyd_diff_op op;
1831 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1832 struct lyd_node *child;
1833 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001834 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001835
1836 assert(diff);
1837
Michal Vaskoe78faec2021-04-08 17:24:43 +02001838 if (lysc_is_dup_inst_list(diff->schema)) {
1839 /* all descendants are keys */
1840 child = NULL;
1841 } else {
1842 child = lyd_child_no_keys(diff);
1843 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001844 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001845 assert(mod);
1846
1847 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001848 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001849
1850 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001851 /* get metadata names */
1852 if (lysc_is_dup_inst_list(diff->schema)) {
1853 meta_name = "position";
1854 orig_meta_name = "orig-position";
1855 } else if (diff->schema->nodetype == LYS_LIST) {
1856 meta_name = "key";
1857 orig_meta_name = "orig-key";
1858 } else {
1859 meta_name = "value";
1860 orig_meta_name = "orig-value";
1861 }
1862
Michal Vaskoe6323f62020-07-09 15:49:02 +02001863 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001864 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1865 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001866 assert(orig_val_meta && val_meta);
1867
1868 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1869 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001870 lyd_free_meta_single(orig_val_meta);
1871 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001872 if (child) {
1873 /* change operation to NONE, we have siblings */
1874 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1875 return 0;
1876 }
1877
1878 /* redundant node, BUT !!
1879 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1880 * because the data that this is applied on should not change for the diff lifetime.
1881 * However, when we are merging 2 diffs, this conversion is actually lossy because
1882 * if the data change, the move operation can also change its meaning. In this specific
1883 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1884 */
1885 return 1;
1886 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001887 } else if (op == LYD_DIFF_OP_NONE) {
1888 if (!diff->schema) {
1889 /* opaque node with none must be redundant */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001890 return 1;
1891 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001892
1893 if (diff->schema->nodetype & LYD_NODE_TERM) {
1894 /* check whether at least the default flags are different */
1895 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1896 assert(meta);
1897 str = lyd_get_meta_value(meta);
1898
1899 /* if previous and current dflt flags are the same, this node is redundant */
1900 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1901 return 1;
1902 }
1903 return 0;
1904 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001905 }
1906
1907 if (!child && (op == LYD_DIFF_OP_NONE)) {
1908 return 1;
1909 }
1910
1911 return 0;
1912}
1913
1914/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001915 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001916 *
1917 * @param[in] src_diff Source diff node.
1918 * @param[in] diff_parent Current sysrepo diff parent.
1919 * @param[in] diff_cb Optional diff callback.
1920 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001921 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001922 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001923 * @param[in,out] diff Diff root node.
1924 * @return LY_ERR value.
1925 */
1926static LY_ERR
1927lyd_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 +02001928 struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001929{
1930 LY_ERR ret = LY_SUCCESS;
1931 struct lyd_node *child, *diff_node = NULL;
1932 enum lyd_diff_op src_op, cur_op;
Michal Vasko8efac242023-03-30 08:24:56 +02001933 struct ly_ht *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001934
1935 /* get source node operation */
1936 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1937
1938 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001939 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 +02001940
1941 if (diff_node) {
1942 /* get target (current) operation */
1943 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1944
1945 /* merge operations */
1946 switch (src_op) {
1947 case LYD_DIFF_OP_REPLACE:
1948 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1949 break;
1950 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001951 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001952 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001953 goto add_diff;
1954 }
1955
Michal Vasko4f331162023-08-15 10:07:44 +02001956 ret = lyd_diff_merge_create(&diff_node, diff, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001957 break;
1958 case LYD_DIFF_OP_DELETE:
1959 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1960 break;
1961 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001962 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1963 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001964 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1965 break;
1966 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001967 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001968 }
1969 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001970 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001971 return ret;
1972 }
1973
1974 if (diff_cb) {
1975 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001976 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001977 }
1978
1979 /* update diff parent */
1980 diff_parent = diff_node;
1981
Michal Vaskoe78faec2021-04-08 17:24:43 +02001982 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1983 * so there is nothing to merge for these "keys" */
1984 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1985 /* merge src_diff recursively */
1986 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1987 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1988 if (ret) {
1989 break;
1990 }
1991 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001992 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001993 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001994 }
1995 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001996add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001997 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02001998 if ((src_diff->flags & LYD_EXT) && diff_parent) {
1999 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
2000 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
2001 } else {
2002 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
2003 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
2004 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02002005
2006 /* insert node into diff if not already */
2007 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02002008 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02002009 }
2010
2011 /* update operation */
2012 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
2013
2014 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01002015 /* call callback with no source diff node since it was duplicated and just added */
2016 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02002017 }
2018
2019 /* update diff parent */
2020 diff_parent = diff_node;
2021 }
2022
2023 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02002024 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02002025 if (diff_parent == *diff) {
2026 *diff = (*diff)->next;
2027 }
2028 lyd_free_tree(diff_parent);
2029 }
2030
2031 return LY_SUCCESS;
2032}
2033
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002034LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02002035lyd_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 +01002036 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02002037{
2038 const struct lyd_node *src_root;
Michal Vasko8efac242023-03-30 08:24:56 +02002039 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02002040 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02002041
2042 LY_LIST_FOR(src_diff, src_root) {
2043 if (mod && (lyd_owner_module(src_root) != mod)) {
2044 /* skip data nodes from different modules */
2045 continue;
2046 }
2047
2048 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002049 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 +02002050 }
2051
Michal Vaskoe78faec2021-04-08 17:24:43 +02002052cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02002053 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002054 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02002055}
2056
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002057LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02002058lyd_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 +01002059 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02002060{
Michal Vaskoe78faec2021-04-08 17:24:43 +02002061 LY_ERR ret;
Michal Vasko8efac242023-03-30 08:24:56 +02002062 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02002063
Michal Vasko04f85912020-08-07 12:14:58 +02002064 if (!src_sibling) {
2065 return LY_SUCCESS;
2066 }
2067
Michal Vaskoe78faec2021-04-08 17:24:43 +02002068 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 +02002069 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002070 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02002071}
2072
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002073LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01002074lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02002075{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01002076 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02002077}
Michal Vasko4231fb62020-07-13 13:54:47 +02002078
2079static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01002080lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02002081{
2082 LY_ERR ret = LY_SUCCESS;
2083 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002084 const char *val1 = NULL;
2085 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002086 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002087
Michal Vaskobaba84e2021-02-05 16:33:30 +01002088 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
2089
2090 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01002091 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002092
2093 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002094 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02002095
2096 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01002097 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002098 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01002099 } else {
2100 LY_CHECK_RET(lyd_any_value_str(node, &val2));
2101 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002102
2103 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01002104 flags = node->flags;
2105 if (node->schema->nodetype == LYS_LEAF) {
2106 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
2107 } else {
2108 union lyd_any_value anyval = {.str = val1};
Michal Vasko26bbb272022-08-02 14:54:33 +02002109
Michal Vaskobaba84e2021-02-05 16:33:30 +01002110 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
2111 }
2112 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002113 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
2114
2115cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002116 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002117 return ret;
2118}
2119
2120static LY_ERR
2121lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
2122{
2123 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002124 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02002125
2126 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01002127 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02002128
2129 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002130 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02002131 flag1 = LYD_DEFAULT;
2132 } else {
2133 flag1 = 0;
2134 }
2135
2136 /* current default */
2137 flag2 = node->flags & LYD_DEFAULT;
2138
Michal Vasko610e93b2020-11-09 20:58:32 +01002139 if (flag1 == flag2) {
2140 /* no default state change so nothing to reverse */
2141 return LY_SUCCESS;
2142 }
2143
Michal Vasko4231fb62020-07-13 13:54:47 +02002144 /* switch defaults */
2145 node->flags &= ~LYD_DEFAULT;
2146 node->flags |= flag1;
2147 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
2148
2149 return LY_SUCCESS;
2150}
2151
2152static LY_ERR
2153lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
2154{
2155 LY_ERR ret = LY_SUCCESS;
2156 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002157 const char *val1 = NULL;
2158 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02002159
2160 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002161 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002162
2163 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002164 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002165
2166 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002167 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02002168
2169 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002170 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02002171
2172 /* switch values */
2173 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2174 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2175
2176cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002177 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002178 return ret;
2179}
2180
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002181/**
2182 * @brief Remove specific operation from all the nodes in a subtree.
2183 *
2184 * @param[in] diff Diff subtree to process.
2185 * @param[in] op Only expected operation.
2186 * @return LY_ERR value.
2187 */
2188static LY_ERR
2189lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2190{
2191 struct lyd_node *elem;
2192 struct lyd_meta *meta;
2193
2194 LYD_TREE_DFS_BEGIN(diff, elem) {
2195 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2196 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002197 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 +01002198 lyd_free_meta_single(meta);
2199 }
2200
2201 LYD_TREE_DFS_END(diff, elem);
2202 }
2203
2204 return LY_SUCCESS;
2205}
2206
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002207LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002208lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002209{
2210 LY_ERR ret = LY_SUCCESS;
2211 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002212 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002213 enum lyd_diff_op op;
2214
2215 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2216
2217 if (!src_diff) {
2218 *diff = NULL;
2219 return LY_SUCCESS;
2220 }
2221
2222 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02002223 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002224
2225 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002226 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2227 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002228
2229 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002230 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002231 /* skip all keys */
2232 if (!lysc_is_key(elem->schema)) {
2233 /* find operation attribute, if any */
2234 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002235
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002236 switch (op) {
2237 case LYD_DIFF_OP_CREATE:
2238 /* reverse create to delete */
2239 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002240
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002241 /* check all the children for the same operation, nothing else is expected */
2242 LY_LIST_FOR(lyd_child(elem), iter) {
2243 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2244 }
2245
Michal Vasko9e070522021-03-05 14:00:14 +01002246 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002247 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002248 case LYD_DIFF_OP_DELETE:
2249 /* reverse delete to create */
2250 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002251
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002252 /* check all the children for the same operation, nothing else is expected */
2253 LY_LIST_FOR(lyd_child(elem), iter) {
2254 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2255 }
2256
Michal Vasko9e070522021-03-05 14:00:14 +01002257 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002258 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002259 case LYD_DIFF_OP_REPLACE:
2260 switch (elem->schema->nodetype) {
2261 case LYS_LEAF:
2262 /* leaf value change */
2263 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2264 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2265 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002266 case LYS_ANYXML:
2267 case LYS_ANYDATA:
2268 /* any value change */
2269 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2270 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002271 case LYS_LEAFLIST:
2272 /* leaf-list move */
2273 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002274 if (lysc_is_dup_inst_list(elem->schema)) {
2275 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2276 } else {
2277 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2278 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002279 break;
2280 case LYS_LIST:
2281 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002282 if (lysc_is_dup_inst_list(elem->schema)) {
2283 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2284 } else {
2285 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2286 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002287 break;
2288 default:
2289 LOGINT(LYD_CTX(src_diff));
2290 ret = LY_EINT;
2291 goto cleanup;
2292 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002293 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002294 case LYD_DIFF_OP_NONE:
2295 switch (elem->schema->nodetype) {
2296 case LYS_LEAF:
2297 case LYS_LEAFLIST:
2298 /* default flag change */
2299 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2300 break;
2301 default:
2302 /* nothing to do */
2303 break;
2304 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002305 break;
2306 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002307 }
2308
Michal Vasko56daf732020-08-10 10:57:18 +02002309 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002310 }
2311 }
2312
2313cleanup:
2314 if (ret) {
2315 lyd_free_siblings(*diff);
2316 *diff = NULL;
2317 }
2318 return ret;
2319}